diff options
author | cvs2svn Import User <samba-bugs@samba.org> | 2002-08-17 07:09:23 +0000 |
---|---|---|
committer | cvs2svn Import User <samba-bugs@samba.org> | 2002-08-17 07:09:23 +0000 |
commit | 592dd249579511f7ce863a42030d9a51ca026c27 (patch) | |
tree | 805985e633b375fd0a88a564a35c38410093a1dd | |
parent | 08663f11ed5bc05ff352bda74774d5e7045da1e5 (diff) | |
parent | 8690b271a6a4feb112e0a6c03fe99ee25f86430b (diff) | |
download | samba-592dd249579511f7ce863a42030d9a51ca026c27.tar.gz samba-592dd249579511f7ce863a42030d9a51ca026c27.tar.bz2 samba-592dd249579511f7ce863a42030d9a51ca026c27.zip |
This commit was manufactured by cvs2svn to create branch 'SAMBA_3_0'.(This used to be commit 6938b5b98abd9ba055a46583a05c4fc07e32f529)
44 files changed, 12664 insertions, 0 deletions
diff --git a/docs/docbook/projdoc/Browsing.sgml b/docs/docbook/projdoc/Browsing.sgml new file mode 100644 index 0000000000..a463ea786b --- /dev/null +++ b/docs/docbook/projdoc/Browsing.sgml @@ -0,0 +1,800 @@ +<chapter id="improved-browsing"> +<chapterinfo> + <author> + <affiliation> + <orgname>Samba Team</orgname> + </affiliation> + </author> + + + <pubdate> (5 July 1998) </pubdate> +</chapterinfo> + +<title>Improved browsing in samba</title> + +<sect1> +<title>Overview of browsing</title> + +<para> +SMB networking provides a mechanism by which clients can access a list +of machines in a network, a so-called "browse list". This list +contains machines that are ready to offer file and/or print services +to other machines within the network. Thus it does not include +machines which aren't currently able to do server tasks. The browse +list is heavily used by all SMB clients. Configuration of SMB +browsing has been problematic for some Samba users, hence this +document. +</para> + +<para> +Browsing will NOT work if name resolution from NetBIOS names to IP +addresses does not function correctly. Use of a WINS server is highly +recommended to aid the resolution of NetBIOS (SMB) names to IP addresses. +WINS allows remote segment clients to obtain NetBIOS name_type information +that can NOT be provided by any other means of name resolution. +</para> + +</sect1> + +<sect1> +<title>Browsing support in samba</title> + +<para> +Samba now fully supports browsing. The browsing is supported by nmbd +and is also controlled by options in the smb.conf file (see smb.conf(5)). +</para> + +<para> +Samba can act as a local browse master for a workgroup and the ability +for samba to support domain logons and scripts is now available. See +DOMAIN.txt for more information on domain logons. +</para> + +<para> +Samba can also act as a domain master browser for a workgroup. This +means that it will collate lists from local browse masters into a +wide area network server list. In order for browse clients to +resolve the names they may find in this list, it is recommended that +both samba and your clients use a WINS server. +</para> + +<para> +Note that you should NOT set Samba to be the domain master for a +workgroup that has the same name as an NT Domain: on each wide area +network, you must only ever have one domain master browser per workgroup, +regardless of whether it is NT, Samba or any other type of domain master +that is providing this service. +</para> + +<para> +[Note that nmbd can be configured as a WINS server, but it is not +necessary to specifically use samba as your WINS server. NTAS can +be configured as your WINS server. In a mixed NT server and +samba environment on a Wide Area Network, it is recommended that +you use the NT server's WINS server capabilities. In a samba-only +environment, it is recommended that you use one and only one nmbd +as your WINS server]. +</para> + +<para> +To get browsing to work you need to run nmbd as usual, but will need +to use the "workgroup" option in smb.conf to control what workgroup +Samba becomes a part of. +</para> + +<para> +Samba also has a useful option for a Samba server to offer itself for +browsing on another subnet. It is recommended that this option is only +used for 'unusual' purposes: announcements over the internet, for +example. See "remote announce" in the smb.conf man page. +</para> +</sect1> + +<sect1> +<title>Problem resolution</title> + +<para> +If something doesn't work then hopefully the log.nmb file will help +you track down the problem. Try a debug level of 2 or 3 for finding +problems. Also note that the current browse list usually gets stored +in text form in a file called browse.dat. +</para> + +<para> +Note that if it doesn't work for you, then you should still be able to +type the server name as \\SERVER in filemanager then hit enter and +filemanager should display the list of available shares. +</para> + +<para> +Some people find browsing fails because they don't have the global +"guest account" set to a valid account. Remember that the IPC$ +connection that lists the shares is done as guest, and thus you must +have a valid guest account. +</para> + +<para> +Also, a lot of people are getting bitten by the problem of too many +parameters on the command line of nmbd in inetd.conf. This trick is to +not use spaces between the option and the parameter (eg: -d2 instead +of -d 2), and to not use the -B and -N options. New versions of nmbd +are now far more likely to correctly find your broadcast and network +address, so in most cases these aren't needed. +</para> + +<para> +The other big problem people have is that their broadcast address, +netmask or IP address is wrong (specified with the "interfaces" option +in smb.conf) +</para> +</sect1> + +<sect1> +<title>Browsing across subnets</title> +<para> +With the release of Samba 1.9.17(alpha1 and above) Samba has been +updated to enable it to support the replication of browse lists +across subnet boundaries. New code and options have been added to +achieve this. This section describes how to set this feature up +in different settings. +</para> + +<para> +To see browse lists that span TCP/IP subnets (ie. networks separated +by routers that don't pass broadcast traffic) you must set up at least +one WINS server. The WINS server acts as a DNS for NetBIOS names, allowing +NetBIOS name to IP address translation to be done by doing a direct +query of the WINS server. This is done via a directed UDP packet on +port 137 to the WINS server machine. The reason for a WINS server is +that by default, all NetBIOS name to IP address translation is done +by broadcasts from the querying machine. This means that machines +on one subnet will not be able to resolve the names of machines on +another subnet without using a WINS server. +</para> + +<para> +Remember, for browsing across subnets to work correctly, all machines, +be they Windows 95, Windows NT, or Samba servers must have the IP address +of a WINS server given to them by a DHCP server, or by manual configuration +(for Win95 and WinNT, this is in the TCP/IP Properties, under Network +settings) for Samba this is in the smb.conf file. +</para> + +<sect2> +<title>How does cross subnet browsing work ?</title> + +<para> +Cross subnet browsing is a complicated dance, containing multiple +moving parts. It has taken Microsoft several years to get the code +that achieves this correct, and Samba lags behind in some areas. +However, with the 1.9.17 release, Samba is capable of cross subnet +browsing when configured correctly. +</para> + +<para> +Consider a network set up as follows : +</para> + +<para> +<programlisting> + (DMB) + N1_A N1_B N1_C N1_D N1_E + | | | | | + ------------------------------------------------------- + | subnet 1 | + +---+ +---+ + |R1 | Router 1 Router 2 |R2 | + +---+ +---+ + | | + | subnet 2 subnet 3 | + -------------------------- ------------------------------------ + | | | | | | | | + N2_A N2_B N2_C N2_D N3_A N3_B N3_C N3_D + (WINS) +</programlisting> +</para> + +<para> +Consisting of 3 subnets (1, 2, 3) connected by two routers +(R1, R2) - these do not pass broadcasts. Subnet 1 has 5 machines +on it, subnet 2 has 4 machines, subnet 3 has 4 machines. Assume +for the moment that all these machines are configured to be in the +same workgroup (for simplicities sake). Machine N1_C on subnet 1 +is configured as Domain Master Browser (ie. it will collate the +browse lists for the workgroup). Machine N2_D is configured as +WINS server and all the other machines are configured to register +their NetBIOS names with it. +</para> + +<para> +As all these machines are booted up, elections for master browsers +will take place on each of the three subnets. Assume that machine +N1_C wins on subnet 1, N2_B wins on subnet 2, and N3_D wins on +subnet 3 - these machines are known as local master browsers for +their particular subnet. N1_C has an advantage in winning as the +local master browser on subnet 1 as it is set up as Domain Master +Browser. +</para> + +<para> +On each of the three networks, machines that are configured to +offer sharing services will broadcast that they are offering +these services. The local master browser on each subnet will +receive these broadcasts and keep a record of the fact that +the machine is offering a service. This list of records is +the basis of the browse list. For this case, assume that +all the machines are configured to offer services so all machines +will be on the browse list. +</para> + +<para> +For each network, the local master browser on that network is +considered 'authoritative' for all the names it receives via +local broadcast. This is because a machine seen by the local +master browser via a local broadcast must be on the same +network as the local master browser and thus is a 'trusted' +and 'verifiable' resource. Machines on other networks that +the local master browsers learn about when collating their +browse lists have not been directly seen - these records are +called 'non-authoritative'. +</para> + +<para> +At this point the browse lists look as follows (these are +the machines you would see in your network neighborhood if +you looked in it on a particular network right now). +</para> + +<para> +<programlisting> +Subnet Browse Master List +------ ------------- ---- +Subnet1 N1_C N1_A, N1_B, N1_C, N1_D, N1_E + +Subnet2 N2_B N2_A, N2_B, N2_C, N2_D + +Subnet3 N3_D N3_A, N3_B, N3_C, N3_D +</programlisting> +</para> + +<para> +Note that at this point all the subnets are separate, no +machine is seen across any of the subnets. +</para> + +<para> +Now examine subnet 2. As soon as N2_B has become the local +master browser it looks for a Domain master browser to synchronize +its browse list with. It does this by querying the WINS server +(N2_D) for the IP address associated with the NetBIOS name +WORKGROUP>1B<. This name was registerd by the Domain master +browser (N1_C) with the WINS server as soon as it was booted. +</para> + +<para> +Once N2_B knows the address of the Domain master browser it +tells it that is the local master browser for subnet 2 by +sending a MasterAnnouncement packet as a UDP port 138 packet. +It then synchronizes with it by doing a NetServerEnum2 call. This +tells the Domain Master Browser to send it all the server +names it knows about. Once the domain master browser receives +the MasterAnnouncement packet it schedules a synchronization +request to the sender of that packet. After both synchronizations +are done the browse lists look like : +</para> + +<para> +<programlisting> +Subnet Browse Master List +------ ------------- ---- +Subnet1 N1_C N1_A, N1_B, N1_C, N1_D, N1_E, + N2_A(*), N2_B(*), N2_C(*), N2_D(*) + +Subnet2 N2_B N2_A, N2_B, N2_C, N2_D + N1_A(*), N1_B(*), N1_C(*), N1_D(*), N1_E(*) + +Subnet3 N3_D N3_A, N3_B, N3_C, N3_D + +Servers with a (*) after them are non-authoritative names. +</programlisting> +</para> + +<para> +At this point users looking in their network neighborhood on +subnets 1 or 2 will see all the servers on both, users on +subnet 3 will still only see the servers on their own subnet. +</para> + +<para> +The same sequence of events that occured for N2_B now occurs +for the local master browser on subnet 3 (N3_D). When it +synchronizes browse lists with the domain master browser (N1_A) +it gets both the server entries on subnet 1, and those on +subnet 2. After N3_D has synchronized with N1_C and vica-versa +the browse lists look like. +</para> + +<para> +<programlisting> +Subnet Browse Master List +------ ------------- ---- +Subnet1 N1_C N1_A, N1_B, N1_C, N1_D, N1_E, + N2_A(*), N2_B(*), N2_C(*), N2_D(*), + N3_A(*), N3_B(*), N3_C(*), N3_D(*) + +Subnet2 N2_B N2_A, N2_B, N2_C, N2_D + N1_A(*), N1_B(*), N1_C(*), N1_D(*), N1_E(*) + +Subnet3 N3_D N3_A, N3_B, N3_C, N3_D + N1_A(*), N1_B(*), N1_C(*), N1_D(*), N1_E(*), + N2_A(*), N2_B(*), N2_C(*), N2_D(*) + +Servers with a (*) after them are non-authoritative names. +</programlisting> +</para> + +<para> +At this point users looking in their network neighborhood on +subnets 1 or 3 will see all the servers on all sunbets, users on +subnet 2 will still only see the servers on subnets 1 and 2, but not 3. +</para> + +<para> +Finally, the local master browser for subnet 2 (N2_B) will sync again +with the domain master browser (N1_C) and will recieve the missing +server entries. Finally - and as a steady state (if no machines +are removed or shut off) the browse lists will look like : +</para> + +<para> +<programlisting> +Subnet Browse Master List +------ ------------- ---- +Subnet1 N1_C N1_A, N1_B, N1_C, N1_D, N1_E, + N2_A(*), N2_B(*), N2_C(*), N2_D(*), + N3_A(*), N3_B(*), N3_C(*), N3_D(*) + +Subnet2 N2_B N2_A, N2_B, N2_C, N2_D + N1_A(*), N1_B(*), N1_C(*), N1_D(*), N1_E(*) + N3_A(*), N3_B(*), N3_C(*), N3_D(*) + +Subnet3 N3_D N3_A, N3_B, N3_C, N3_D + N1_A(*), N1_B(*), N1_C(*), N1_D(*), N1_E(*), + N2_A(*), N2_B(*), N2_C(*), N2_D(*) + +Servers with a (*) after them are non-authoritative names. +</programlisting> +</para> + +<para> +Synchronizations between the domain master browser and local +master browsers will continue to occur, but this should be a +steady state situation. +</para> + +<para> +If either router R1 or R2 fails the following will occur: +</para> + +<orderedlist> +<listitem> + <para> + Names of computers on each side of the inaccessible network fragments + will be maintained for as long as 36 minutes, in the network neighbourhood + lists. + </para> +</listitem> + +<listitem> + <para> + Attempts to connect to these inaccessible computers will fail, but the + names will not be removed from the network neighbourhood lists. + </para> +</listitem> + +<listitem> + <para> + If one of the fragments is cut off from the WINS server, it will only + be able to access servers on its local subnet, by using subnet-isolated + broadcast NetBIOS name resolution. The effects are similar to that of + losing access to a DNS server. + </para> +</listitem> +</orderedlist> +</sect2> +</sect1> + +<sect1> +<title>Setting up a WINS server</title> + +<para> +Either a Samba machine or a Windows NT Server machine may be set up +as a WINS server. To set a Samba machine to be a WINS server you must +add the following option to the smb.conf file on the selected machine : +in the [globals] section add the line +</para> + +<para> +<command> wins support = yes</command> +</para> + +<para> +Versions of Samba previous to 1.9.17 had this parameter default to +yes. If you have any older versions of Samba on your network it is +strongly suggested you upgrade to 1.9.17 or above, or at the very +least set the parameter to 'no' on all these machines. +</para> + +<para> +Machines with "<command>wins support = yes</command>" will keep a list of +all NetBIOS names registered with them, acting as a DNS for NetBIOS names. +</para> + +<para> +You should set up only ONE wins server. Do NOT set the +"<command>wins support = yes</command>" option on more than one Samba +server. +</para> + +<para> +To set up a Windows NT Server as a WINS server you need to set up +the WINS service - see your NT documentation for details. Note that +Windows NT WINS Servers can replicate to each other, allowing more +than one to be set up in a complex subnet environment. As Microsoft +refuse to document these replication protocols Samba cannot currently +participate in these replications. It is possible in the future that +a Samba->Samba WINS replication protocol may be defined, in which +case more than one Samba machine could be set up as a WINS server +but currently only one Samba server should have the "wins support = yes" +parameter set. +</para> + +<para> +After the WINS server has been configured you must ensure that all +machines participating on the network are configured with the address +of this WINS server. If your WINS server is a Samba machine, fill in +the Samba machine IP address in the "Primary WINS Server" field of +the "Control Panel->Network->Protocols->TCP->WINS Server" dialogs +in Windows 95 or Windows NT. To tell a Samba server the IP address +of the WINS server add the following line to the [global] section of +all smb.conf files : +</para> + +<para> +<command> wins server = >name or IP address<</command> +</para> + +<para> +where >name or IP address< is either the DNS name of the WINS server +machine or its IP address. +</para> + +<para> +Note that this line MUST NOT BE SET in the smb.conf file of the Samba +server acting as the WINS server itself. If you set both the +"<command>wins support = yes</command>" option and the +"<command>wins server = >name<</command>" option then +nmbd will fail to start. +</para> + +<para> +There are two possible scenarios for setting up cross subnet browsing. +The first details setting up cross subnet browsing on a network containing +Windows 95, Samba and Windows NT machines that are not configured as +part of a Windows NT Domain. The second details setting up cross subnet +browsing on networks that contain NT Domains. +</para> + +</sect1> + +<sect1> +<title>Setting up Browsing in a WORKGROUP</title> + +<para> +To set up cross subnet browsing on a network containing machines +in up to be in a WORKGROUP, not an NT Domain you need to set up one +Samba server to be the Domain Master Browser (note that this is *NOT* +the same as a Primary Domain Controller, although in an NT Domain the +same machine plays both roles). The role of a Domain master browser is +to collate the browse lists from local master browsers on all the +subnets that have a machine participating in the workgroup. Without +one machine configured as a domain master browser each subnet would +be an isolated workgroup, unable to see any machines on any other +subnet. It is the presense of a domain master browser that makes +cross subnet browsing possible for a workgroup. +</para> + +<para> +In an WORKGROUP environment the domain master browser must be a +Samba server, and there must only be one domain master browser per +workgroup name. To set up a Samba server as a domain master browser, +set the following option in the [global] section of the smb.conf file : +</para> + +<para> +<command> domain master = yes</command> +</para> + +<para> +The domain master browser should also preferrably be the local master +browser for its own subnet. In order to achieve this set the following +options in the [global] section of the smb.conf file : +</para> + +<para> +<programlisting> + domain master = yes + local master = yes + preferred master = yes + os level = 65 +</programlisting> +</para> + +<para> +The domain master browser may be the same machine as the WINS +server, if you require. +</para> + +<para> +Next, you should ensure that each of the subnets contains a +machine that can act as a local master browser for the +workgroup. Any NT machine should be able to do this, as will +Windows 95 machines (although these tend to get rebooted more +often, so it's not such a good idea to use these). To make a +Samba server a local master browser set the following +options in the [global] section of the smb.conf file : +</para> + +<para> +<programlisting> + domain master = no + local master = yes + preferred master = yes + os level = 65 +</programlisting> +</para> + +<para> +Do not do this for more than one Samba server on each subnet, +or they will war with each other over which is to be the local +master browser. +</para> + +<para> +The "local master" parameter allows Samba to act as a local master +browser. The "preferred master" causes nmbd to force a browser +election on startup and the "os level" parameter sets Samba high +enough so that it should win any browser elections. +</para> + +<para> +If you have an NT machine on the subnet that you wish to +be the local master browser then you can disable Samba from +becoming a local master browser by setting the following +options in the [global] section of the smb.conf file : +</para> + +<para> +<programlisting> + domain master = no + local master = no + preferred master = no + os level = 0 +</programlisting> +</para> + +</sect1> + +<sect1> +<title>Setting up Browsing in a DOMAIN</title> + +<para> +If you are adding Samba servers to a Windows NT Domain then +you must not set up a Samba server as a domain master browser. +By default, a Windows NT Primary Domain Controller for a Domain +name is also the Domain master browser for that name, and many +things will break if a Samba server registers the Domain master +browser NetBIOS name (DOMAIN>1B<) with WINS instead of the PDC. +</para> + +<para> +For subnets other than the one containing the Windows NT PDC +you may set up Samba servers as local master browsers as +described. To make a Samba server a local master browser set +the following options in the [global] section of the smb.conf +file : +</para> + +<para> +<programlisting> + domain master = no + local master = yes + preferred master = yes + os level = 65 +</programlisting> +</para> + +<para> +If you wish to have a Samba server fight the election with machines +on the same subnet you may set the "os level" parameter to lower +levels. By doing this you can tune the order of machines that +will become local master browsers if they are running. For +more details on this see the section "FORCING SAMBA TO BE THE MASTER" +below. +</para> + +<para> +If you have Windows NT machines that are members of the domain +on all subnets, and you are sure they will always be running then +you can disable Samba from taking part in browser elections and +ever becoming a local master browser by setting following options +in the [global] section of the smb.conf file : +</para> + +<para> +<command> + domain master = no + local master = no + preferred master = no + os level = 0 +</command> +</para> + +</sect1> + +<sect1> +<title>Forcing samba to be the master</title> + +<para> +Who becomes the "master browser" is determined by an election process +using broadcasts. Each election packet contains a number of parameters +which determine what precedence (bias) a host should have in the +election. By default Samba uses a very low precedence and thus loses +elections to just about anyone else. +</para> + +<para> +If you want Samba to win elections then just set the "os level" global +option in smb.conf to a higher number. It defaults to 0. Using 34 +would make it win all elections over every other system (except other +samba systems!) +</para> + +<para> +A "os level" of 2 would make it beat WfWg and Win95, but not NTAS. A +NTAS domain controller uses level 32. +</para> + +<para>The maximum os level is 255</para> + +<para> +If you want samba to force an election on startup, then set the +"preferred master" global option in smb.conf to "yes". Samba will +then have a slight advantage over other potential master browsers +that are not preferred master browsers. Use this parameter with +care, as if you have two hosts (whether they are windows 95 or NT or +samba) on the same local subnet both set with "preferred master" to +"yes", then periodically and continually they will force an election +in order to become the local master browser. +</para> + +<para> +If you want samba to be a "domain master browser", then it is +recommended that you also set "preferred master" to "yes", because +samba will not become a domain master browser for the whole of your +LAN or WAN if it is not also a local master browser on its own +broadcast isolated subnet. +</para> + +<para> +It is possible to configure two samba servers to attempt to become +the domain master browser for a domain. The first server that comes +up will be the domain master browser. All other samba servers will +attempt to become the domain master browser every 5 minutes. They +will find that another samba server is already the domain master +browser and will fail. This provides automatic redundancy, should +the current domain master browser fail. +</para> + +</sect1> + +<sect1> +<title>Making samba the domain master</title> + +<para> +The domain master is responsible for collating the browse lists of +multiple subnets so that browsing can occur between subnets. You can +make samba act as the domain master by setting "domain master = yes" +in smb.conf. By default it will not be a domain master. +</para> + +<para> +Note that you should NOT set Samba to be the domain master for a +workgroup that has the same name as an NT Domain. +</para> + +<para> +When samba is the domain master and the master browser it will listen +for master announcements (made roughly every twelve minutes) from local +master browsers on other subnets and then contact them to synchronise +browse lists. +</para> + +<para> +If you want samba to be the domain master then I suggest you also set +the "os level" high enough to make sure it wins elections, and set +"preferred master" to "yes", to get samba to force an election on +startup. +</para> + +<para> +Note that all your servers (including samba) and clients should be +using a WINS server to resolve NetBIOS names. If your clients are only +using broadcasting to resolve NetBIOS names, then two things will occur: +</para> + +<orderedlist> +<listitem> + <para> + your local master browsers will be unable to find a domain master + browser, as it will only be looking on the local subnet. + </para> +</listitem> + +<listitem> + <para> + if a client happens to get hold of a domain-wide browse list, and + a user attempts to access a host in that list, it will be unable to + resolve the NetBIOS name of that host. + </para> +</listitem> +</orderedlist> + +<para> +If, however, both samba and your clients are using a WINS server, then: +</para> + +<orderedlist> +<listitem> + <para> + your local master browsers will contact the WINS server and, as long as + samba has registered that it is a domain master browser with the WINS + server, your local master browser will receive samba's ip address + as its domain master browser. + </para> +</listitem> + +<listitem> + <para> + when a client receives a domain-wide browse list, and a user attempts + to access a host in that list, it will contact the WINS server to + resolve the NetBIOS name of that host. as long as that host has + registered its NetBIOS name with the same WINS server, the user will + be able to see that host. + </para> +</listitem> +</orderedlist> + +</sect1> + +<sect1> +<title>Note about broadcast addresses</title> + +<para> +If your network uses a "0" based broadcast address (for example if it +ends in a 0) then you will strike problems. Windows for Workgroups +does not seem to support a 0's broadcast and you will probably find +that browsing and name lookups won't work. +</para> +</sect1> + +<sect1> +<title>Multiple interfaces</title> + +<para> +Samba now supports machines with multiple network interfaces. If you +have multiple interfaces then you will need to use the "interfaces" +option in smb.conf to configure them. See smb.conf(5) for details. +</para> +</sect1> +</chapter> diff --git a/docs/docbook/projdoc/Bugs.sgml b/docs/docbook/projdoc/Bugs.sgml new file mode 100644 index 0000000000..5a24458e08 --- /dev/null +++ b/docs/docbook/projdoc/Bugs.sgml @@ -0,0 +1,202 @@ +<chapter id="bugreport"> + +<chapterinfo> + <author> + <affiliation> + <orgname>Samba Team</orgname> + </affiliation> + </author> + <pubdate> 27 June 1997 </pubdate> +</chapterinfo> + +<title>Reporting Bugs</title> + +<sect1> +<title>Introduction</title> + +<para> +The email address for bug reports is samba@samba.org +</para> + +<para> +Please take the time to read this file before you submit a bug +report. Also, please see if it has changed between releases, as we +may be changing the bug reporting mechanism at some time. +</para> + +<para> +Please also do as much as you can yourself to help track down the +bug. Samba is maintained by a dedicated group of people who volunteer +their time, skills and efforts. We receive far more mail about it than +we can possibly answer, so you have a much higher chance of an answer +and a fix if you send us a "developer friendly" bug report that lets +us fix it fast. +</para> + +<para> +Do not assume that if you post the bug to the comp.protocols.smb +newsgroup or the mailing list that we will read it. If you suspect that your +problem is not a bug but a configuration problem then it is better to send +it to the Samba mailing list, as there are (at last count) 5000 other users on +that list that may be able to help you. +</para> + +<para> +You may also like to look though the recent mailing list archives, +which are conveniently accessible on the Samba web pages +at http://samba.org/samba/ +</para> + +</sect1> + +<sect1> +<title>General info</title> + +<para> +Before submitting a bug report check your config for silly +errors. Look in your log files for obvious messages that tell you that +you've misconfigured something and run testparm to test your config +file for correct syntax. +</para> + +<para> +Have you run through the <ulink url="Diagnosis.html">diagnosis</ulink>? +This is very important. +</para> + +<para> +If you include part of a log file with your bug report then be sure to +annotate it with exactly what you were doing on the client at the +time, and exactly what the results were. +</para> + +</sect1> + +<sect1> +<title>Debug levels</title> + +<para> +If the bug has anything to do with Samba behaving incorrectly as a +server (like refusing to open a file) then the log files will probably +be very useful. Depending on the problem a log level of between 3 and +10 showing the problem may be appropriate. A higher level givesmore +detail, but may use too much disk space. +</para> + +<para> +To set the debug level use <command>log level =</command> in your +<filename>smb.conf</filename>. You may also find it useful to set the log +level higher for just one machine and keep separate logs for each machine. +To do this use: +</para> + +<para><programlisting> +log level = 10 +log file = /usr/local/samba/lib/log.%m +include = /usr/local/samba/lib/smb.conf.%m +</programlisting></para> + +<para> +then create a file +<filename>/usr/local/samba/lib/smb.conf.machine</filename> where +"machine" is the name of the client you wish to debug. In that file +put any smb.conf commands you want, for example +<command>log level=</command> may be useful. This also allows you to +experiment with different security systems, protocol levels etc on just +one machine. +</para> + +<para> +The <filename>smb.conf</filename> entry <command>log level =</command> +is synonymous with the entry <command>debuglevel =</command> that has been +used in older versions of Samba and is being retained for backwards +compatibility of smb.conf files. +</para> + +<para> +As the <command>log level =</command> value is increased you will record +a significantly increasing level of debugging information. For most +debugging operations you may not need a setting higher than 3. Nearly +all bugs can be tracked at a setting of 10, but be prepared for a VERY +large volume of log data. +</para> + +</sect1> + +<sect1> +<title>Internal errors</title> + +<para> +If you get a "INTERNAL ERROR" message in your log files it means that +Samba got an unexpected signal while running. It is probably a +segmentation fault and almost certainly means a bug in Samba (unless +you have faulty hardware or system software) +</para> + +<para> +If the message came from smbd then it will probably be accompanied by +a message which details the last SMB message received by smbd. This +info is often very useful in tracking down the problem so please +include it in your bug report. +</para> + +<para> +You should also detail how to reproduce the problem, if +possible. Please make this reasonably detailed. +</para> + +<para> +You may also find that a core file appeared in a "corefiles" +subdirectory of the directory where you keep your samba log +files. This file is the most useful tool for tracking down the bug. To +use it you do this: +</para> + +<para><command>gdb smbd core</command></para> + +<para> +adding appropriate paths to smbd and core so gdb can find them. If you +don't have gdb then try "dbx". Then within the debugger use the +command "where" to give a stack trace of where the problem +occurred. Include this in your mail. +</para> + +<para> +If you known any assembly language then do a "disass" of the routine +where the problem occurred (if its in a library routine then +disassemble the routine that called it) and try to work out exactly +where the problem is by looking at the surrounding code. Even if you +don't know assembly then incuding this info in the bug report can be +useful. +</para> +</sect1> + +<sect1> +<title>Attaching to a running process</title> + +<para> +Unfortunately some unixes (in particular some recent linux kernels) +refuse to dump a core file if the task has changed uid (which smbd +does often). To debug with this sort of system you could try to attach +to the running process using "gdb smbd PID" where you get PID from +smbstatus. Then use "c" to continue and try to cause the core dump +using the client. The debugger should catch the fault and tell you +where it occurred. +</para> + +</sect1> + +<sect1> +<title>Patches</title> + +<para> +The best sort of bug report is one that includes a fix! If you send us +patches please use <command>diff -u</command> format if your version of +diff supports it, otherwise use <command>diff -c4</command>. Make sure +your do the diff against a clean version of the source and let me know +exactly what version you used. +</para> + +</sect1> +</chapter> + diff --git a/docs/docbook/projdoc/Diagnosis.sgml b/docs/docbook/projdoc/Diagnosis.sgml new file mode 100644 index 0000000000..20b2ccee08 --- /dev/null +++ b/docs/docbook/projdoc/Diagnosis.sgml @@ -0,0 +1,509 @@ +<chapter id="diagnosis"> +<chapterinfo> + <author> + <firstname>Andrew</firstname><surname>Tridgell</surname> + <affiliation> + <orgname>Samba Team</orgname> + <address><email>tridge@samba.org</email></address> + </affiliation> + </author> + <pubdate> 1 November 1999</pubdate> +</chapterinfo> + +<title>Diagnosing your samba server</title> + +<sect1> +<title>Introduction</title> + +<para> +This file contains a list of tests you can perform to validate your +Samba server. It also tells you what the likely cause of the problem +is if it fails any one of these steps. If it passes all these tests +then it is probably working fine. +</para> + +<para> +You should do ALL the tests, in the order shown. I have tried to +carefully choose them so later tests only use capabilities verified in +the earlier tests. +</para> + +<para> +If you send me an email saying "it doesn't work" and you have not +followed this test procedure then you should not be surprised if I +ignore your email. +</para> + +</sect1> + +<sect1> +<title>Assumptions</title> + +<para> +In all of the tests I assume you have a Samba server called BIGSERVER +and a PC called ACLIENT both in workgroup TESTGROUP. I also assume the +PC is running windows for workgroups with a recent copy of the +microsoft tcp/ip stack. Alternatively, your PC may be running Windows +95 or Windows NT (Workstation or Server). +</para> + +<para> +The procedure is similar for other types of clients. +</para> + +<para> +I also assume you know the name of an available share in your +smb.conf. I will assume this share is called "tmp". You can add a +"tmp" share like by adding the following to smb.conf: +</para> + +<para><programlisting> + +[tmp] + comment = temporary files + path = /tmp + read only = yes + +</programlisting> +</para> + +<para> +THESE TESTS ASSUME VERSION 2.0.6 OR LATER OF THE SAMBA SUITE. SOME +COMMANDS SHOWN DID NOT EXIST IN EARLIER VERSIONS +</para> + +<para> +Please pay attention to the error messages you receive. If any error message +reports that your server is being unfriendly you should first check that you +IP name resolution is correctly set up. eg: Make sure your /etc/resolv.conf +file points to name servers that really do exist. +</para> + +<para> +Also, if you do not have DNS server access for name resolution please check +that the settings for your smb.conf file results in "dns proxy = no". The +best way to check this is with "testparm smb.conf" +</para> + +</sect1> + +<sect1> +<title>Tests</title> + +<sect2> +<title>Test 1</title> +<para> +In the directory in which you store your smb.conf file, run the command +"testparm smb.conf". If it reports any errors then your smb.conf +configuration file is faulty. +</para> + +<para> +Note: Your smb.conf file may be located in: <filename>/etc</filename> + Or in: <filename>/usr/local/samba/lib</filename> +</para> +</sect2> + +<sect2> +<title>Test 2</title> + +<para> +Run the command "ping BIGSERVER" from the PC and "ping ACLIENT" from +the unix box. If you don't get a valid response then your TCP/IP +software is not correctly installed. +</para> + +<para> +Note that you will need to start a "dos prompt" window on the PC to +run ping. +</para> + +<para> +If you get a message saying "host not found" or similar then your DNS +software or /etc/hosts file is not correctly setup. It is possible to +run samba without DNS entries for the server and client, but I assume +you do have correct entries for the remainder of these tests. +</para> + +<para> +Another reason why ping might fail is if your host is running firewall +software. You will need to relax the rules to let in the workstation +in question, perhaps by allowing access from another subnet (on Linux +this is done via the ipfwadm program.) +</para> +</sect2> + +<sect2> +<title>Test 3</title> + +<para> +Run the command "smbclient -L BIGSERVER" on the unix box. You +should get a list of available shares back. +</para> + +<para> +If you get a error message containing the string "Bad password" then +you probably have either an incorrect "hosts allow", "hosts deny" or +"valid users" line in your smb.conf, or your guest account is not +valid. Check what your guest account is using "testparm" and +temporarily remove any "hosts allow", "hosts deny", "valid users" or +"invalid users" lines. +</para> + +<para> +If you get a "connection refused" response then the smbd server may +not be running. If you installed it in inetd.conf then you probably edited +that file incorrectly. If you installed it as a daemon then check that +it is running, and check that the netbios-ssn port is in a LISTEN +state using "netstat -a". +</para> + +<para> +If you get a "session request failed" then the server refused the +connection. If it says "Your server software is being unfriendly" then +its probably because you have invalid command line parameters to smbd, +or a similar fatal problem with the initial startup of smbd. Also +check your config file (smb.conf) for syntax errors with "testparm" +and that the various directories where samba keeps its log and lock +files exist. +</para> + +<para> +There are a number of reasons for which smbd may refuse or decline +a session request. The most common of these involve one or more of +the following smb.conf file entries: +</para> + +<para><programlisting> + hosts deny = ALL + hosts allow = xxx.xxx.xxx.xxx/yy + bind interfaces only = Yes +</programlisting></para> + +<para> +In the above, no allowance has been made for any session requests that +will automatically translate to the loopback adaptor address 127.0.0.1. +To solve this problem change these lines to: +</para> + +<para><programlisting> + hosts deny = ALL + hosts allow = xxx.xxx.xxx.xxx/yy 127. +</programlisting></para> + +<para> +Do NOT use the "bind interfaces only" parameter where you may wish to +use the samba password change facility, or where smbclient may need to +access local service for name resolution or for local resource +connections. (Note: the "bind interfaces only" parameter deficiency +where it will not allow connections to the loopback address will be +fixed soon). +</para> + +<para> +Another common cause of these two errors is having something already running +on port 139, such as Samba (ie: smbd is running from inetd already) or +something like Digital's Pathworks. Check your inetd.conf file before trying +to start smbd as a daemon, it can avoid a lot of frustration! +</para> + +<para> +And yet another possible cause for failure of TEST 3 is when the subnet mask +and / or broadcast address settings are incorrect. Please check that the +network interface IP Address / Broadcast Address / Subnet Mask settings are +correct and that Samba has correctly noted these in the log.nmb file. +</para> + +</sect2> + +<sect2> +<title>Test 4</title> + +<para> +Run the command "nmblookup -B BIGSERVER __SAMBA__". You should get the +IP address of your Samba server back. +</para> + +<para> +If you don't then nmbd is incorrectly installed. Check your inetd.conf +if you run it from there, or that the daemon is running and listening +to udp port 137. +</para> + +<para> +One common problem is that many inetd implementations can't take many +parameters on the command line. If this is the case then create a +one-line script that contains the right parameters and run that from +inetd. +</para> + +</sect2> + +<sect2> +<title>Test 5</title> + +<para>run the command <command>nmblookup -B ACLIENT '*'</command></para> + +<para> +You should get the PCs IP address back. If you don't then the client +software on the PC isn't installed correctly, or isn't started, or you +got the name of the PC wrong. +</para> + +<para> +If ACLIENT doesn't resolve via DNS then use the IP address of the +client in the above test. +</para> + +</sect2> + +<sect2> +<title>Test 6</title> + +<para> +Run the command <command>nmblookup -d 2 '*'</command> +</para> + +<para> +This time we are trying the same as the previous test but are trying +it via a broadcast to the default broadcast address. A number of +Netbios/TCPIP hosts on the network should respond, although Samba may +not catch all of the responses in the short time it listens. You +should see "got a positive name query response" messages from several +hosts. +</para> + +<para> +If this doesn't give a similar result to the previous test then +nmblookup isn't correctly getting your broadcast address through its +automatic mechanism. In this case you should experiment use the +"interfaces" option in smb.conf to manually configure your IP +address, broadcast and netmask. +</para> + +<para> +If your PC and server aren't on the same subnet then you will need to +use the -B option to set the broadcast address to the that of the PCs +subnet. +</para> + +<para> +This test will probably fail if your subnet mask and broadcast address are +not correct. (Refer to TEST 3 notes above). +</para> + +</sect2> + +<sect2> +<title>Test 7</title> + +<para> +Run the command <command>smbclient //BIGSERVER/TMP</command>. You should +then be prompted for a password. You should use the password of the account +you are logged into the unix box with. If you want to test with +another account then add the -U >accountname< option to the end of +the command line. eg: +<command>smbclient //bigserver/tmp -Ujohndoe</command> +</para> + +<para> +Note: It is possible to specify the password along with the username +as follows: +<command>smbclient //bigserver/tmp -Ujohndoe%secret</command> +</para> + +<para> +Once you enter the password you should get the "smb>" prompt. If you +don't then look at the error message. If it says "invalid network +name" then the service "tmp" is not correctly setup in your smb.conf. +</para> + +<para> +If it says "bad password" then the likely causes are: +</para> + +<orderedlist> +<listitem> + <para> + you have shadow passords (or some other password system) but didn't + compile in support for them in smbd + </para> +</listitem> + +<listitem> + <para> + your "valid users" configuration is incorrect + </para> +</listitem> + +<listitem> + <para> + you have a mixed case password and you haven't enabled the "password + level" option at a high enough level + </para> +</listitem> + +<listitem> + <para> + the "path =" line in smb.conf is incorrect. Check it with testparm + </para> +</listitem> + +<listitem> + <para> + you enabled password encryption but didn't create the SMB encrypted + password file + </para> +</listitem> +</orderedlist> + +<para> +Once connected you should be able to use the commands +<command>dir</command> <command>get</command> <command>put</command> etc. +Type <command>help >command<</command> for instructions. You should +especially check that the amount of free disk space shown is correct +when you type <command>dir</command>. +</para> + +</sect2> + +<sect2> +<title>Test 8</title> + +<para> +On the PC type the command <command>net view \\BIGSERVER</command>. You will +need to do this from within a "dos prompt" window. You should get back a +list of available shares on the server. +</para> + +<para> +If you get a "network name not found" or similar error then netbios +name resolution is not working. This is usually caused by a problem in +nmbd. To overcome it you could do one of the following (you only need +to choose one of them): +</para> + +<orderedlist> +<listitem><para> + fixup the nmbd installation +</para></listitem> + +<listitem><para> + add the IP address of BIGSERVER to the "wins server" box in the + advanced tcp/ip setup on the PC. +</para></listitem> + +<listitem><para> + enable windows name resolution via DNS in the advanced section of + the tcp/ip setup +</para></listitem> + +<listitem><para> + add BIGSERVER to your lmhosts file on the PC. +</para></listitem> +</orderedlist> + +<para> +If you get a "invalid network name" or "bad password error" then the +same fixes apply as they did for the "smbclient -L" test above. In +particular, make sure your "hosts allow" line is correct (see the man +pages) +</para> + +<para> +Also, do not overlook that fact that when the workstation requests the +connection to the samba server it will attempt to connect using the +name with which you logged onto your Windows machine. You need to make +sure that an account exists on your Samba server with that exact same +name and password. +</para> + +<para> +If you get "specified computer is not receiving requests" or similar +it probably means that the host is not contactable via tcp services. +Check to see if the host is running tcp wrappers, and if so add an entry in +the hosts.allow file for your client (or subnet, etc.) +</para> + +</sect2> + +<sect2> +<title>Test 9</title> + +<para> +Run the command <command>net use x: \\BIGSERVER\TMP</command>. You should +be prompted for a password then you should get a "command completed +successfully" message. If not then your PC software is incorrectly +installed or your smb.conf is incorrect. make sure your "hosts allow" +and other config lines in smb.conf are correct. +</para> + +<para> +It's also possible that the server can't work out what user name to +connect you as. To see if this is the problem add the line "user = +USERNAME" to the [tmp] section of smb.conf where "USERNAME" is the +username corresponding to the password you typed. If you find this +fixes things you may need the username mapping option. +</para> + +</sect2> + +<sect2> +<title>Test 10</title> + +<para> +Run the command <command>nmblookup -M TESTGROUP</command> where +TESTGROUP is the name of the workgroup that your Samba server and +Windows PCs belong to. You should get back the IP address of the +master browser for that workgroup. +</para> + +<para> +If you don't then the election process has failed. Wait a minute to +see if it is just being slow then try again. If it still fails after +that then look at the browsing options you have set in smb.conf. Make +sure you have <command>preferred master = yes</command> to ensure that +an election is held at startup. +</para> + +</sect2> + +<sect2> +<title>Test 11</title> + +<para> +From file manager try to browse the server. Your samba server should +appear in the browse list of your local workgroup (or the one you +specified in smb.conf). You should be able to double click on the name +of the server and get a list of shares. If you get a "invalid +password" error when you do then you are probably running WinNT and it +is refusing to browse a server that has no encrypted password +capability and is in user level security mode. In this case either set +<command>security = server</command> AND +<command>password server = Windows_NT_Machine</command> in your +smb.conf file, or enable encrypted passwords AFTER compiling in support +for encrypted passwords (refer to the Makefile). +</para> + +</sect2> +</sect1> + +<sect1> +<title>Still having troubles?</title> + +<para> +Try the mailing list or newsgroup, or use the ethereal utility to +sniff the problem. The official samba mailing list can be reached at +<ulink url="mailto:samba@samba.org">samba@samba.org</ulink>. To find +out more about samba and how to subscribe to the mailing list check +out the samba web page at +<ulink url="http://samba.org/samba">http://samba.org/samba</ulink> +</para> + +<para> +Also look at the other docs in the Samba package! +</para> + +</sect1> + +</chapter> diff --git a/docs/docbook/projdoc/Printing.sgml b/docs/docbook/projdoc/Printing.sgml new file mode 100644 index 0000000000..cb7e5cdfb7 --- /dev/null +++ b/docs/docbook/projdoc/Printing.sgml @@ -0,0 +1,398 @@ +<chapter id="printing_debug"> +<chapterinfo> + <author> + <firstname>Patrick</firstname><surname>Powell</surname> + <affiliation> + <address><email>papowell@lprng.org</email></address> + </affiliation> + </author> + <pubdate>11 August 2000</pubdate> +</chapterinfo> + +<title>Debugging Printing Problems</title> + +<sect1> +<title>Introduction</title> + +<para> +This is a short description of how to debug printing problems with +Samba. This describes how to debug problems with printing from a SMB +client to a Samba server, not the other way around. For the reverse +see the examples/printing directory. +</para> + +<para> +Ok, so you want to print to a Samba server from your PC. The first +thing you need to understand is that Samba does not actually do any +printing itself, it just acts as a middleman between your PC client +and your Unix printing subsystem. Samba receives the file from the PC +then passes the file to a external "print command". What print command +you use is up to you. +</para> + +<para> +The whole things is controlled using options in smb.conf. The most +relevant options (which you should look up in the smb.conf man page) +are: +</para> + +<para><programlisting> + [global] + print command - send a file to a spooler + lpq command - get spool queue status + lprm command - remove a job + [printers] + path = /var/spool/lpd/samba +</programlisting></para> + +<para> +The following are nice to know about: +</para> + +<para><programlisting> + queuepause command - stop a printer or print queue + queueresume command - start a printer or print queue +</programlisting></para> + +<para> +Example: +</para> + +<para><programlisting> + print command = /usr/bin/lpr -r -P%p %s + lpq command = /usr/bin/lpq -P%p %s + lprm command = /usr/bin/lprm -P%p %j + queuepause command = /usr/sbin/lpc -P%p stop + queuepause command = /usr/sbin/lpc -P%p start +</programlisting></para> + +<para> +Samba should set reasonable defaults for these depending on your +system type, but it isn't clairvoyant. It is not uncommon that you +have to tweak these for local conditions. The commands should +always have fully specified pathnames, as the smdb may not have +the correct PATH values. +</para> + +<para> +When you send a job to Samba to be printed, it will make a temporary +copy of it in the directory specified in the [printers] section. +and it should be periodically cleaned out. The lpr -r option +requests that the temporary copy be removed after printing; If +printing fails then you might find leftover files in this directory, +and it should be periodically cleaned out. Samba used the lpq +command to determine the "job number" assigned to your print job +by the spooler. +</para> + +<para> +The %>letter< are "macros" that get dynamically replaced with appropriate +values when they are used. The %s gets replaced with the name of the spool +file that Samba creates and the %p gets replaced with the name of the +printer. The %j gets replaced with the "job number" which comes from +the lpq output. +</para> + +</sect1> + +<sect1> +<title>Debugging printer problems</title> + +<para> +One way to debug printing problems is to start by replacing these +command with shell scripts that record the arguments and the contents +of the print file. A simple example of this kind of things might +be: +</para> + +<para><programlisting> + print command = /tmp/saveprint %p %s + + #!/bin/saveprint + # we make sure that we are the right user + /usr/bin/id -p >/tmp/tmp.print + # we run the command and save the error messages + # replace the command with the one appropriate for your system + /usr/bin/lpr -r -P$1 $2 2>>&/tmp/tmp.print +</programlisting></para> + +<para> +Then you print a file and try removing it. You may find that the +print queue needs to be stopped in order to see the queue status +and remove the job: +</para> + +<para><programlisting> + +h4: {42} % echo hi >/tmp/hi +h4: {43} % smbclient //localhost/lw4 +added interface ip=10.0.0.4 bcast=10.0.0.255 nmask=255.255.255.0 +Password: +Domain=[ASTART] OS=[Unix] Server=[Samba 2.0.7] +smb: \> print /tmp/hi +putting file /tmp/hi as hi-17534 (0.0 kb/s) (average 0.0 kb/s) +smb: \> queue +1049 3 hi-17534 +smb: \> cancel 1049 +Error cancelling job 1049 : code 0 +smb: \> cancel 1049 +Job 1049 cancelled +smb: \> queue +smb: \> exit +</programlisting></para> + +<para> +The 'code 0' indicates that the job was removed. The comment +by the smbclient is a bit misleading on this. +You can observe the command output and then and look at the +/tmp/tmp.print file to see what the results are. You can quickly +find out if the problem is with your printing system. Often people +have problems with their /etc/printcap file or permissions on +various print queues. +</para> +</sect1> + +<sect1> +<title>What printers do I have?</title> + +<para> +You can use the 'testprns' program to check to see if the printer +name you are using is recognized by Samba. For example, you can +use: +</para> + +<para><programlisting> + testprns printer /etc/printcap +</programlisting></para> + +<para> +Samba can get its printcap information from a file or from a program. +You can try the following to see the format of the extracted +information: +</para> + +<para><programlisting> + testprns -a printer /etc/printcap + + testprns -a printer '|/bin/cat printcap' +</programlisting></para> + +</sect1> + +<sect1> +<title>Setting up printcap and print servers</title> + +<para> +You may need to set up some printcaps for your Samba system to use. +It is strongly recommended that you use the facilities provided by +the print spooler to set up queues and printcap information. +</para> + +<para> +Samba requires either a printcap or program to deliver printcap +information. This printcap information has the format: +</para> + +<para><programlisting> + name|alias1|alias2...:option=value:... +</programlisting></para> + +<para> +For almost all printing systems, the printer 'name' must be composed +only of alphanumeric or underscore '_' characters. Some systems also +allow hyphens ('-') as well. An alias is an alternative name for the +printer, and an alias with a space in it is used as a 'comment' +about the printer. The printcap format optionally uses a \ at the end of lines +to extend the printcap to multiple lines. +</para> + +<para> +Here are some examples of printcap files: +</para> + +<para> +<orderedlist> +<listitem><para> +pr just printer name +</para></listitem> +<listitem><para> +pr|alias printer name and alias +</para></listitem> +<listitem><para> +pr|My Printer printer name, alias used as comment +</para></listitem> +<listitem><para> +pr:sh:\ Same as pr:sh:cm= testing + :cm= \ + testing +</para></listitem> +<listitem><para> +pr:sh Same as pr:sh:cm= testing + :cm= testing +</para></listitem> +</orderedlist> +</para> + +<para> +Samba reads the printcap information when first started. If you make +changes in the printcap information, then you must do the following: +</para> + +<orderedlist> + +<listitem><para> +make sure that the print spooler is aware of these changes. +The LPRng system uses the 'lpc reread' command to do this. +</para></listitem> + +<listitem><para> +make sure that the spool queues, etc., exist and have the +correct permissions. The LPRng system uses the 'checkpc -f' +command to do this. +</para></listitem> + +<listitem><para> +You now should send a SIGHUP signal to the smbd server to have +it reread the printcap information. +</para></listitem> +</orderedlist> + +</sect1> + +<sect1> +<title>Job sent, no output</title> + +<para> +This is the most frustrating part of printing. You may have sent the +job, verified that the job was forwarded, set up a wrapper around +the command to send the file, but there was no output from the printer. +</para> + +<para> +First, check to make sure that the job REALLY is getting to the +right print queue. If you are using a BSD or LPRng print spooler, +you can temporarily stop the printing of jobs. Jobs can still be +submitted, but they will not be printed. Use: +</para> + +<para><programlisting> + lpc -Pprinter stop +</programlisting></para> + +<para> +Now submit a print job and then use 'lpq -Pprinter' to see if the +job is in the print queue. If it is not in the print queue then +you will have to find out why it is not being accepted for printing. +</para> + +<para> +Next, you may want to check to see what the format of the job really +was. With the assistance of the system administrator you can view +the submitted jobs files. You may be surprised to find that these +are not in what you would expect to call a printable format. +You can use the UNIX 'file' utitily to determine what the job +format actually is: +</para> + +<para><programlisting> + cd /var/spool/lpd/printer # spool directory of print jobs + ls # find job files + file dfA001myhost +</programlisting></para> + +<para> +You should make sure that your printer supports this format OR that +your system administrator has installed a 'print filter' that will +convert the file to a format appropriate for your printer. +</para> + +</sect1> + +<sect1> +<title>Job sent, strange output</title> + +<para> +Once you have the job printing, you can then start worrying about +making it print nicely. +</para> + +<para> +The most common problem is extra pages of output: banner pages +OR blank pages at the end. +</para> + +<para> +If you are getting banner pages, check and make sure that the +printcap option or printer option is configured for no banners. +If you have a printcap, this is the :sh (suppress header or banner +page) option. You should have the following in your printer. +</para> + +<para><programlisting> + printer: ... :sh +</programlisting></para> + +<para> +If you have this option and are still getting banner pages, there +is a strong chance that your printer is generating them for you +automatically. You should make sure that banner printing is disabled +for the printer. This usually requires using the printer setup software +or procedures supplied by the printer manufacturer. +</para> + +<para> +If you get an extra page of output, this could be due to problems +with your job format, or if you are generating PostScript jobs, +incorrect setting on your printer driver on the MicroSoft client. +For example, under Win95 there is a option: +</para> + +<para><programlisting> + Printers|Printer Name|(Right Click)Properties|Postscript|Advanced| +</programlisting></para> + +<para> +that allows you to choose if a Ctrl-D is appended to all jobs. +This is a very bad thing to do, as most spooling systems will +automatically add a ^D to the end of the job if it is detected as +PostScript. The multiple ^D may cause an additional page of output. +</para> + +</sect1> + +<sect1> +<title>Raw PostScript printed</title> + +<para> +This is a problem that is usually caused by either the print spooling +system putting information at the start of the print job that makes +the printer think the job is a text file, or your printer simply +does not support PostScript. You may need to enable 'Automatic +Format Detection' on your printer. +</para> + +</sect1> + +<sect1> +<title>Advanced Printing</title> + +<para> +Note that you can do some pretty magic things by using your +imagination with the "print command" option and some shell scripts. +Doing print accounting is easy by passing the %U option to a print +command shell script. You could even make the print command detect +the type of output and its size and send it to an appropriate +printer. +</para> + +</sect1> + +<sect1> +<title>Real debugging</title> + +<para> +If the above debug tips don't help, then maybe you need to bring in +the bug guns, system tracing. See Tracing.txt in this directory. +</para> +</sect1> +</chapter> diff --git a/docs/docbook/projdoc/Speed.sgml b/docs/docbook/projdoc/Speed.sgml new file mode 100644 index 0000000000..17adf10429 --- /dev/null +++ b/docs/docbook/projdoc/Speed.sgml @@ -0,0 +1,578 @@ +<chapter id="speed"> + +<chapterinfo> + <author> + <affiliation> + <orgname>Samba Team</orgname> + <address><email>samba@samba.org</email></address> + </affiliation> + </author> + <author> + <firstname>Paul</firstname><surname>Cochrane</surname> + <affiliation> + <orgname>Dundee Limb Fitting Centre</orgname> + <address><email>paulc@dth.scot.nhs.uk</email></address> + </affiliation> + </author> +</chapterinfo> + +<title>Samba performance issues</title> + +<sect1> +<title>Comparisons</title> + +<para> +The Samba server uses TCP to talk to the client. Thus if you are +trying to see if it performs well you should really compare it to +programs that use the same protocol. The most readily available +programs for file transfer that use TCP are ftp or another TCP based +SMB server. +</para> + +<para> +If you want to test against something like a NT or WfWg server then +you will have to disable all but TCP on either the client or +server. Otherwise you may well be using a totally different protocol +(such as Netbeui) and comparisons may not be valid. +</para> + +<para> +Generally you should find that Samba performs similarly to ftp at raw +transfer speed. It should perform quite a bit faster than NFS, +although this very much depends on your system. +</para> + +<para> +Several people have done comparisons between Samba and Novell, NFS or +WinNT. In some cases Samba performed the best, in others the worst. I +suspect the biggest factor is not Samba vs some other system but the +hardware and drivers used on the various systems. Given similar +hardware Samba should certainly be competitive in speed with other +systems. +</para> + +</sect1> + +<sect1> +<title>Oplocks</title> + +<sect2> +<title>Overview</title> + +<para> +Oplocks are the way that SMB clients get permission from a server to +locally cache file operations. If a server grants an oplock +(opportunistic lock) then the client is free to assume that it is the +only one accessing the file and it will agressively cache file +data. With some oplock types the client may even cache file open/close +operations. This can give enormous performance benefits. +</para> + +<para> +With the release of Samba 1.9.18 we now correctly support opportunistic +locks. This is turned on by default, and can be turned off on a share- +by-share basis by setting the parameter : +</para> + +<para> +<command>oplocks = False</command> +</para> + +<para> +We recommend that you leave oplocks on however, as current benchmark +tests with NetBench seem to give approximately a 30% improvement in +speed with them on. This is on average however, and the actual +improvement seen can be orders of magnitude greater, depending on +what the client redirector is doing. +</para> + +<para> +Previous to Samba 1.9.18 there was a 'fake oplocks' option. This +option has been left in the code for backwards compatibility reasons +but it's use is now deprecated. A short summary of what the old +code did follows. +</para> + +</sect2> + +<sect2> +<title>Level2 Oplocks</title> + +<para> +With Samba 2.0.5 a new capability - level2 (read only) oplocks is +supported (although the option is off by default - see the smb.conf +man page for details). Turning on level2 oplocks (on a share-by-share basis) +by setting the parameter : +</para> + +<para> +<command>level2 oplocks = true</command> +</para> + +<para> +should speed concurrent access to files that are not commonly written +to, such as application serving shares (ie. shares that contain common +.EXE files - such as a Microsoft Office share) as it allows clients to +read-ahread cache copies of these files. +</para> + +</sect2> + +<sect2> +<title>Old 'fake oplocks' option - deprecated</title> + +<para> +Samba can also fake oplocks, by granting a oplock whenever a client +asks for one. This is controlled using the smb.conf option "fake +oplocks". If you set "fake oplocks = yes" then you are telling the +client that it may agressively cache the file data for all opens. +</para> + +<para> +Enabling 'fake oplocks' on all read-only shares or shares that you know +will only be accessed from one client at a time you will see a big +performance improvement on many operations. If you enable this option +on shares where multiple clients may be accessing the files read-write +at the same time you can get data corruption. +</para> + +</sect2> +</sect1> + +<sect1> +<title>Socket options</title> + +<para> +There are a number of socket options that can greatly affect the +performance of a TCP based server like Samba. +</para> + +<para> +The socket options that Samba uses are settable both on the command +line with the -O option, or in the smb.conf file. +</para> + +<para> +The "socket options" section of the smb.conf manual page describes how +to set these and gives recommendations. +</para> + +<para> +Getting the socket options right can make a big difference to your +performance, but getting them wrong can degrade it by just as +much. The correct settings are very dependent on your local network. +</para> + +<para> +The socket option TCP_NODELAY is the one that seems to make the +biggest single difference for most networks. Many people report that +adding "socket options = TCP_NODELAY" doubles the read performance of +a Samba drive. The best explanation I have seen for this is that the +Microsoft TCP/IP stack is slow in sending tcp ACKs. +</para> + +</sect1> + +<sect1> +<title>Read size</title> + +<para> +The option "read size" affects the overlap of disk reads/writes with +network reads/writes. If the amount of data being transferred in +several of the SMB commands (currently SMBwrite, SMBwriteX and +SMBreadbraw) is larger than this value then the server begins writing +the data before it has received the whole packet from the network, or +in the case of SMBreadbraw, it begins writing to the network before +all the data has been read from disk. +</para> + +<para> +This overlapping works best when the speeds of disk and network access +are similar, having very little effect when the speed of one is much +greater than the other. +</para> + +<para> +The default value is 16384, but very little experimentation has been +done yet to determine the optimal value, and it is likely that the best +value will vary greatly between systems anyway. A value over 65536 is +pointless and will cause you to allocate memory unnecessarily. +</para> + +</sect1> + +<sect1> +<title>Max xmit</title> + +<para> +At startup the client and server negotiate a "maximum transmit" size, +which limits the size of nearly all SMB commands. You can set the +maximum size that Samba will negotiate using the "max xmit = " option +in smb.conf. Note that this is the maximum size of SMB request that +Samba will accept, but not the maximum size that the *client* will accept. +The client maximum receive size is sent to Samba by the client and Samba +honours this limit. +</para> + +<para> +It defaults to 65536 bytes (the maximum), but it is possible that some +clients may perform better with a smaller transmit unit. Trying values +of less than 2048 is likely to cause severe problems. +</para> + +<para> +In most cases the default is the best option. +</para> + +</sect1> + +<sect1> +<title>Locking</title> + +<para> +By default Samba does not implement strict locking on each read/write +call (although it did in previous versions). If you enable strict +locking (using "strict locking = yes") then you may find that you +suffer a severe performance hit on some systems. +</para> + +<para> +The performance hit will probably be greater on NFS mounted +filesystems, but could be quite high even on local disks. +</para> + +</sect1> + +<sect1> +<title>Share modes</title> + +<para> +Some people find that opening files is very slow. This is often +because of the "share modes" code needed to fully implement the dos +share modes stuff. You can disable this code using "share modes = +no". This will gain you a lot in opening and closing files but will +mean that (in some cases) the system won't force a second user of a +file to open the file read-only if the first has it open +read-write. For many applications that do their own locking this +doesn't matter, but for some it may. Most Windows applications +depend heavily on "share modes" working correctly and it is +recommended that the Samba share mode support be left at the +default of "on". +</para> + +<para> +The share mode code in Samba has been re-written in the 1.9.17 +release following tests with the Ziff-Davis NetBench PC Benchmarking +tool. It is now believed that Samba 1.9.17 implements share modes +similarly to Windows NT. +</para> + +<para> +NOTE: In the most recent versions of Samba there is an option to use +shared memory via mmap() to implement the share modes. This makes +things much faster. See the Makefile for how to enable this. +</para> + +</sect1> + +<sect1> +<title>Log level</title> + +<para> +If you set the log level (also known as "debug level") higher than 2 +then you may suffer a large drop in performance. This is because the +server flushes the log file after each operation, which can be very +expensive. +</para> +</sect1> + +<sect1> +<title>Wide lines</title> + +<para> +The "wide links" option is now enabled by default, but if you disable +it (for better security) then you may suffer a performance hit in +resolving filenames. The performance loss is lessened if you have +"getwd cache = yes", which is now the default. +</para> + +</sect1> + +<sect1> +<title>Read raw</title> + +<para> +The "read raw" operation is designed to be an optimised, low-latency +file read operation. A server may choose to not support it, +however. and Samba makes support for "read raw" optional, with it +being enabled by default. +</para> + +<para> +In some cases clients don't handle "read raw" very well and actually +get lower performance using it than they get using the conventional +read operations. +</para> + +<para> +So you might like to try "read raw = no" and see what happens on your +network. It might lower, raise or not affect your performance. Only +testing can really tell. +</para> + +</sect1> + +<sect1> +<title>Write raw</title> + +<para> +The "write raw" operation is designed to be an optimised, low-latency +file write operation. A server may choose to not support it, +however. and Samba makes support for "write raw" optional, with it +being enabled by default. +</para> + +<para> +Some machines may find "write raw" slower than normal write, in which +case you may wish to change this option. +</para> + +</sect1> + +<sect1> +<title>Read prediction</title> + +<para> +Samba can do read prediction on some of the SMB commands. Read +prediction means that Samba reads some extra data on the last file it +read while waiting for the next SMB command to arrive. It can then +respond more quickly when the next read request arrives. +</para> + +<para> +This is disabled by default. You can enable it by using "read +prediction = yes". +</para> + +<para> +Note that read prediction is only used on files that were opened read +only. +</para> + +<para> +Read prediction should particularly help for those silly clients (such +as "Write" under NT) which do lots of very small reads on a file. +</para> + +<para> +Samba will not read ahead more data than the amount specified in the +"read size" option. It always reads ahead on 1k block boundaries. +</para> + +</sect1> + +<sect1> +<title>Memory mapping</title> + +<para> +Samba supports reading files via memory mapping them. One some +machines this can give a large boost to performance, on others it +makes not difference at all, and on some it may reduce performance. +</para> + +<para> +To enable you you have to recompile Samba with the -DUSE_MMAP option +on the FLAGS line of the Makefile. +</para> + +<para> +Note that memory mapping is only used on files opened read only, and +is not used by the "read raw" operation. Thus you may find memory +mapping is more effective if you disable "read raw" using "read raw = +no". +</para> + +</sect1> + +<sect1> +<title>Slow Clients</title> + +<para> +One person has reported that setting the protocol to COREPLUS rather +than LANMAN2 gave a dramatic speed improvement (from 10k/s to 150k/s). +</para> + +<para> +I suspect that his PC's (386sx16 based) were asking for more data than +they could chew. I suspect a similar speed could be had by setting +"read raw = no" and "max xmit = 2048", instead of changing the +protocol. Lowering the "read size" might also help. +</para> + +</sect1> + +<sect1> +<title>Slow Logins</title> + +<para> +Slow logins are almost always due to the password checking time. Using +the lowest practical "password level" will improve things a lot. You +could also enable the "UFC crypt" option in the Makefile. +</para> + +</sect1> + +<sect1> +<title>Client tuning</title> + +<para> +Often a speed problem can be traced to the client. The client (for +example Windows for Workgroups) can often be tuned for better TCP +performance. +</para> + +<para> +See your client docs for details. In particular, I have heard rumours +that the WfWg options TCPWINDOWSIZE and TCPSEGMENTSIZE can have a +large impact on performance. +</para> + +<para> +Also note that some people have found that setting DefaultRcvWindow in +the [MSTCP] section of the SYSTEM.INI file under WfWg to 3072 gives a +big improvement. I don't know why. +</para> + +<para> +My own experience wth DefaultRcvWindow is that I get much better +performance with a large value (16384 or larger). Other people have +reported that anything over 3072 slows things down enourmously. One +person even reported a speed drop of a factor of 30 when he went from +3072 to 8192. I don't know why. +</para> + +<para> +It probably depends a lot on your hardware, and the type of unix box +you have at the other end of the link. +</para> + +<para> +Paul Cochrane has done some testing on client side tuning and come +to the following conclusions: +</para> + +<para> +Install the W2setup.exe file from www.microsoft.com. This is an +update for the winsock stack and utilities which improve performance. +</para> + +<para> +Configure the win95 TCPIP registry settings to give better +perfomance. I use a program called MTUSPEED.exe which I got off the +net. There are various other utilities of this type freely available. +The setting which give the best performance for me are: +</para> + +<orderedlist> +<listitem><para> +MaxMTU Remove +</para></listitem> +<listitem><para> +RWIN Remove +</para></listitem> +<listitem><para> +MTUAutoDiscover Disable +</para></listitem> +<listitem><para> +MTUBlackHoleDetect Disable +</para></listitem> +<listitem><para> +Time To Live Enabled +</para></listitem> +<listitem><para> +Time To Live - HOPS 32 +</para></listitem> +<listitem><para> +NDI Cache Size 0 +</para></listitem> +</orderedlist> + +<para> +I tried virtually all of the items mentioned in the document and +the only one which made a difference to me was the socket options. It +turned out I was better off without any!!!!! +</para> + +<para> +In terms of overall speed of transfer, between various win95 clients +and a DX2-66 20MB server with a crappy NE2000 compatible and old IDE +drive (Kernel 2.0.30). The transfer rate was reasonable for 10 baseT. +</para> + +<para> +FIXME +The figures are: Put Get +P166 client 3Com card: 420-440kB/s 500-520kB/s +P100 client 3Com card: 390-410kB/s 490-510kB/s +DX4-75 client NE2000: 370-380kB/s 330-350kB/s +</para> + +<para> +I based these test on transfer two files a 4.5MB text file and a 15MB +textfile. The results arn't bad considering the hardware Samba is +running on. It's a crap machine!!!! +</para> + +<para> +The updates mentioned in 1 and 2 brought up the transfer rates from +just over 100kB/s in some clients. +</para> + +<para> +A new client is a P333 connected via a 100MB/s card and hub. The +transfer rates from this were good: 450-500kB/s on put and 600+kB/s +on get. +</para> + +<para> +Looking at standard FTP throughput, Samba is a bit slower (100kB/s +upwards). I suppose there is more going on in the samba protocol, but +if it could get up to the rate of FTP the perfomance would be quite +staggering. +</para> + +</sect1> + +<sect1> +<title>My Results</title> + +<para> +Some people want to see real numbers in a document like this, so here +they are. I have a 486sx33 client running WfWg 3.11 with the 3.11b +tcp/ip stack. It has a slow IDE drive and 20Mb of ram. It has a SMC +Elite-16 ISA bus ethernet card. The only WfWg tuning I've done is to +set DefaultRcvWindow in the [MSTCP] section of system.ini to 16384. My +server is a 486dx3-66 running Linux. It also has 20Mb of ram and a SMC +Elite-16 card. You can see my server config in the examples/tridge/ +subdirectory of the distribution. +</para> + +<para> +I get 490k/s on reading a 8Mb file with copy. +I get 441k/s writing the same file to the samba server. +</para> + +<para> +Of course, there's a lot more to benchmarks than 2 raw throughput +figures, but it gives you a ballpark figure. +</para> + +<para> +I've also tested Win95 and WinNT, and found WinNT gave me the best +speed as a samba client. The fastest client of all (for me) is +smbclient running on another linux box. Maybe I'll add those results +here someday ... +</para> + +</sect1> +</chapter> diff --git a/docs/docbook/projdoc/security_level.sgml b/docs/docbook/projdoc/security_level.sgml new file mode 100644 index 0000000000..46a2ad7fe4 --- /dev/null +++ b/docs/docbook/projdoc/security_level.sgml @@ -0,0 +1,140 @@ +<chapter id="security_levels"> +<chapterinfo> + <author> + <firstname>Andrew</firstname><surname>Tridgell</surname> + <affiliation> + <orgname>Samba Team</orgname> + <address><email>samba@samba.org</email></address> + </affiliation> + </author> +</chapterinfo> + +<title>Security levels</title> + +<sect1> +<title>Introduction</title> + +<para> +Samba supports the following options to the global smb.conf parameter +</para> + +<para><programlisting> +[global] +<ulink url="smb.conf.5.html#SECURITY"><parameter>security</parameter></ulink> = [share|user(default)|domain|ads] +</programlisting></para> + +<para> +Please refer to the smb.conf man page for usage information and to the document +<ulink url="DOMAIN_MEMBER.html">DOMAIN_MEMBER.html</ulink> for further background details +on domain mode security. The Windows 2000 Kerberos domain security model +(security = ads) is described in the <ulink url="ADS-HOWTO.html">ADS-HOWTO.html</ulink>. +</para> + +<para> +Of the above, "security = server" means that Samba reports to clients that +it is running in "user mode" but actually passes off all authentication +requests to another "user mode" server. This requires an additional +parameter "password server =" that points to the real authentication server. +That real authentication server can be another Samba server or can be a +Windows NT server, the later natively capable of encrypted password support. +</para> + +</sect1> + +<sect1> +<title>More complete description of security levels</title> + +<para> +A SMB server tells the client at startup what "security level" it is +running. There are two options "share level" and "user level". Which +of these two the client receives affects the way the client then tries +to authenticate itself. It does not directly affect (to any great +extent) the way the Samba server does security. I know this is +strange, but it fits in with the client/server approach of SMB. In SMB +everything is initiated and controlled by the client, and the server +can only tell the client what is available and whether an action is +allowed. +</para> + +<para> +I'll describe user level security first, as its simpler. In user level +security the client will send a "session setup" command directly after +the protocol negotiation. This contains a username and password. The +server can either accept or reject that username/password +combination. Note that at this stage the server has no idea what +share the client will eventually try to connect to, so it can't base +the "accept/reject" on anything other than: +</para> + +<orderedlist> +<listitem><para>the username/password</para></listitem> +<listitem><para>the machine that the client is coming from</para></listitem> +</orderedlist> + +<para> +If the server accepts the username/password then the client expects to +be able to mount any share (using a "tree connection") without +specifying a password. It expects that all access rights will be as +the username/password specified in the "session setup". +</para> + +<para> +It is also possible for a client to send multiple "session setup" +requests. When the server responds it gives the client a "uid" to use +as an authentication tag for that username/password. The client can +maintain multiple authentication contexts in this way (WinDD is an +example of an application that does this) +</para> + +<para> +Ok, now for share level security. In share level security the client +authenticates itself separately for each share. It will send a +password along with each "tree connection" (share mount). It does not +explicitly send a username with this operation. The client is +expecting a password to be associated with each share, independent of +the user. This means that samba has to work out what username the +client probably wants to use. It is never explicitly sent the +username. Some commercial SMB servers such as NT actually associate +passwords directly with shares in share level security, but samba +always uses the unix authentication scheme where it is a +username/password that is authenticated, not a "share/password". +</para> + +<para> +Many clients send a "session setup" even if the server is in share +level security. They normally send a valid username but no +password. Samba records this username in a list of "possible +usernames". When the client then does a "tree connection" it also adds +to this list the name of the share they try to connect to (useful for +home directories) and any users listed in the "user =" smb.conf +line. The password is then checked in turn against these "possible +usernames". If a match is found then the client is authenticated as +that user. +</para> + +<para> +Finally "server level" security. In server level security the samba +server reports to the client that it is in user level security. The +client then does a "session setup" as described earlier. The samba +server takes the username/password that the client sends and attempts +to login to the "password server" by sending exactly the same +username/password that it got from the client. If that server is in +user level security and accepts the password then samba accepts the +clients connection. This allows the samba server to use another SMB +server as the "password server". +</para> + +<para> +You should also note that at the very start of all this, where the +server tells the client what security level it is in, it also tells +the client if it supports encryption. If it does then it supplies the +client with a random "cryptkey". The client will then send all +passwords in encrypted form. You have to compile samba with encryption +enabled to support this feature, and you have to maintain a separate +smbpasswd file with SMB style encrypted passwords. It is +cryptographically impossible to translate from unix style encryption +to SMB style encryption, although there are some fairly simple management +schemes by which the two could be kept in sync. +</para> +</sect1> +</chapter> diff --git a/docs/htmldocs/Browsing.html b/docs/htmldocs/Browsing.html new file mode 100644 index 0000000000..5f5f71ba69 --- /dev/null +++ b/docs/htmldocs/Browsing.html @@ -0,0 +1,741 @@ +<HTML +><HEAD +><TITLE +>Improved browsing in samba</TITLE +><META +NAME="GENERATOR" +CONTENT="Modular DocBook HTML Stylesheet Version 1.57"></HEAD +><BODY +CLASS="ARTICLE" +BGCOLOR="#FFFFFF" +TEXT="#000000" +LINK="#0000FF" +VLINK="#840084" +ALINK="#0000FF" +><DIV +CLASS="ARTICLE" +><DIV +CLASS="TITLEPAGE" +><H1 +CLASS="TITLE" +><A +NAME="IMPROVED-BROWSING" +>Improved browsing in samba</A +></H1 +><HR></DIV +><DIV +CLASS="SECT1" +><H1 +CLASS="SECT1" +><A +NAME="AEN3" +>Overview of browsing</A +></H1 +><P +>SMB networking provides a mechanism by which clients can access a list +of machines in a network, a so-called "browse list". This list +contains machines that are ready to offer file and/or print services +to other machines within the network. Thus it does not include +machines which aren't currently able to do server tasks. The browse +list is heavily used by all SMB clients. Configuration of SMB +browsing has been problematic for some Samba users, hence this +document.</P +><P +>Browsing will NOT work if name resolution from NetBIOS names to IP +addresses does not function correctly. Use of a WINS server is highly +recommended to aid the resolution of NetBIOS (SMB) names to IP addresses. +WINS allows remote segment clients to obtain NetBIOS name_type information +that can NOT be provided by any other means of name resolution.</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN7" +>Browsing support in samba</A +></H1 +><P +>Samba now fully supports browsing. The browsing is supported by nmbd +and is also controlled by options in the smb.conf file (see smb.conf(5)).</P +><P +>Samba can act as a local browse master for a workgroup and the ability +for samba to support domain logons and scripts is now available. See +DOMAIN.txt for more information on domain logons.</P +><P +>Samba can also act as a domain master browser for a workgroup. This +means that it will collate lists from local browse masters into a +wide area network server list. In order for browse clients to +resolve the names they may find in this list, it is recommended that +both samba and your clients use a WINS server.</P +><P +>Note that you should NOT set Samba to be the domain master for a +workgroup that has the same name as an NT Domain: on each wide area +network, you must only ever have one domain master browser per workgroup, +regardless of whether it is NT, Samba or any other type of domain master +that is providing this service.</P +><P +>[Note that nmbd can be configured as a WINS server, but it is not +necessary to specifically use samba as your WINS server. NTAS can +be configured as your WINS server. In a mixed NT server and +samba environment on a Wide Area Network, it is recommended that +you use the NT server's WINS server capabilities. In a samba-only +environment, it is recommended that you use one and only one nmbd +as your WINS server].</P +><P +>To get browsing to work you need to run nmbd as usual, but will need +to use the "workgroup" option in smb.conf to control what workgroup +Samba becomes a part of.</P +><P +>Samba also has a useful option for a Samba server to offer itself for +browsing on another subnet. It is recommended that this option is only +used for 'unusual' purposes: announcements over the internet, for +example. See "remote announce" in the smb.conf man page. </P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN16" +>Problem resolution</A +></H1 +><P +>If something doesn't work then hopefully the log.nmb file will help +you track down the problem. Try a debug level of 2 or 3 for finding +problems. Also note that the current browse list usually gets stored +in text form in a file called browse.dat.</P +><P +>Note that if it doesn't work for you, then you should still be able to +type the server name as \\SERVER in filemanager then hit enter and +filemanager should display the list of available shares.</P +><P +>Some people find browsing fails because they don't have the global +"guest account" set to a valid account. Remember that the IPC$ +connection that lists the shares is done as guest, and thus you must +have a valid guest account.</P +><P +>Also, a lot of people are getting bitten by the problem of too many +parameters on the command line of nmbd in inetd.conf. This trick is to +not use spaces between the option and the parameter (eg: -d2 instead +of -d 2), and to not use the -B and -N options. New versions of nmbd +are now far more likely to correctly find your broadcast and network +address, so in most cases these aren't needed.</P +><P +>The other big problem people have is that their broadcast address, +netmask or IP address is wrong (specified with the "interfaces" option +in smb.conf)</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN23" +>Browsing across subnets</A +></H1 +><P +>With the release of Samba 1.9.17(alpha1 and above) Samba has been +updated to enable it to support the replication of browse lists +across subnet boundaries. New code and options have been added to +achieve this. This section describes how to set this feature up +in different settings.</P +><P +>To see browse lists that span TCP/IP subnets (ie. networks separated +by routers that don't pass broadcast traffic) you must set up at least +one WINS server. The WINS server acts as a DNS for NetBIOS names, allowing +NetBIOS name to IP address translation to be done by doing a direct +query of the WINS server. This is done via a directed UDP packet on +port 137 to the WINS server machine. The reason for a WINS server is +that by default, all NetBIOS name to IP address translation is done +by broadcasts from the querying machine. This means that machines +on one subnet will not be able to resolve the names of machines on +another subnet without using a WINS server.</P +><P +>Remember, for browsing across subnets to work correctly, all machines, +be they Windows 95, Windows NT, or Samba servers must have the IP address +of a WINS server given to them by a DHCP server, or by manual configuration +(for Win95 and WinNT, this is in the TCP/IP Properties, under Network +settings) for Samba this is in the smb.conf file.</P +><DIV +CLASS="SECT2" +><HR><H2 +CLASS="SECT2" +><A +NAME="AEN28" +>How does cross subnet browsing work ?</A +></H2 +><P +>Cross subnet browsing is a complicated dance, containing multiple +moving parts. It has taken Microsoft several years to get the code +that achieves this correct, and Samba lags behind in some areas. +However, with the 1.9.17 release, Samba is capable of cross subnet +browsing when configured correctly.</P +><P +>Consider a network set up as follows :</P +><P +><PRE +CLASS="PROGRAMLISTING" +> (DMB) + N1_A N1_B N1_C N1_D N1_E + | | | | | + ------------------------------------------------------- + | subnet 1 | + +---+ +---+ + |R1 | Router 1 Router 2 |R2 | + +---+ +---+ + | | + | subnet 2 subnet 3 | + -------------------------- ------------------------------------ + | | | | | | | | + N2_A N2_B N2_C N2_D N3_A N3_B N3_C N3_D + (WINS)</PRE +></P +><P +>Consisting of 3 subnets (1, 2, 3) connected by two routers +(R1, R2) - these do not pass broadcasts. Subnet 1 has 5 machines +on it, subnet 2 has 4 machines, subnet 3 has 4 machines. Assume +for the moment that all these machines are configured to be in the +same workgroup (for simplicities sake). Machine N1_C on subnet 1 +is configured as Domain Master Browser (ie. it will collate the +browse lists for the workgroup). Machine N2_D is configured as +WINS server and all the other machines are configured to register +their NetBIOS names with it.</P +><P +>As all these machines are booted up, elections for master browsers +will take place on each of the three subnets. Assume that machine +N1_C wins on subnet 1, N2_B wins on subnet 2, and N3_D wins on +subnet 3 - these machines are known as local master browsers for +their particular subnet. N1_C has an advantage in winning as the +local master browser on subnet 1 as it is set up as Domain Master +Browser.</P +><P +>On each of the three networks, machines that are configured to +offer sharing services will broadcast that they are offering +these services. The local master browser on each subnet will +receive these broadcasts and keep a record of the fact that +the machine is offering a service. This list of records is +the basis of the browse list. For this case, assume that +all the machines are configured to offer services so all machines +will be on the browse list.</P +><P +>For each network, the local master browser on that network is +considered 'authoritative' for all the names it receives via +local broadcast. This is because a machine seen by the local +master browser via a local broadcast must be on the same +network as the local master browser and thus is a 'trusted' +and 'verifiable' resource. Machines on other networks that +the local master browsers learn about when collating their +browse lists have not been directly seen - these records are +called 'non-authoritative'.</P +><P +>At this point the browse lists look as follows (these are +the machines you would see in your network neighborhood if +you looked in it on a particular network right now).</P +><P +><PRE +CLASS="PROGRAMLISTING" +>Subnet Browse Master List +------ ------------- ---- +Subnet1 N1_C N1_A, N1_B, N1_C, N1_D, N1_E + +Subnet2 N2_B N2_A, N2_B, N2_C, N2_D + +Subnet3 N3_D N3_A, N3_B, N3_C, N3_D</PRE +></P +><P +>Note that at this point all the subnets are separate, no +machine is seen across any of the subnets.</P +><P +>Now examine subnet 2. As soon as N2_B has become the local +master browser it looks for a Domain master browser to synchronize +its browse list with. It does this by querying the WINS server +(N2_D) for the IP address associated with the NetBIOS name +WORKGROUP>1B<. This name was registerd by the Domain master +browser (N1_C) with the WINS server as soon as it was booted.</P +><P +>Once N2_B knows the address of the Domain master browser it +tells it that is the local master browser for subnet 2 by +sending a MasterAnnouncement packet as a UDP port 138 packet. +It then synchronizes with it by doing a NetServerEnum2 call. This +tells the Domain Master Browser to send it all the server +names it knows about. Once the domain master browser receives +the MasterAnnouncement packet it schedules a synchronization +request to the sender of that packet. After both synchronizations +are done the browse lists look like :</P +><P +><PRE +CLASS="PROGRAMLISTING" +>Subnet Browse Master List +------ ------------- ---- +Subnet1 N1_C N1_A, N1_B, N1_C, N1_D, N1_E, + N2_A(*), N2_B(*), N2_C(*), N2_D(*) + +Subnet2 N2_B N2_A, N2_B, N2_C, N2_D + N1_A(*), N1_B(*), N1_C(*), N1_D(*), N1_E(*) + +Subnet3 N3_D N3_A, N3_B, N3_C, N3_D + +Servers with a (*) after them are non-authoritative names.</PRE +></P +><P +>At this point users looking in their network neighborhood on +subnets 1 or 2 will see all the servers on both, users on +subnet 3 will still only see the servers on their own subnet.</P +><P +>The same sequence of events that occured for N2_B now occurs +for the local master browser on subnet 3 (N3_D). When it +synchronizes browse lists with the domain master browser (N1_A) +it gets both the server entries on subnet 1, and those on +subnet 2. After N3_D has synchronized with N1_C and vica-versa +the browse lists look like.</P +><P +><PRE +CLASS="PROGRAMLISTING" +>Subnet Browse Master List +------ ------------- ---- +Subnet1 N1_C N1_A, N1_B, N1_C, N1_D, N1_E, + N2_A(*), N2_B(*), N2_C(*), N2_D(*), + N3_A(*), N3_B(*), N3_C(*), N3_D(*) + +Subnet2 N2_B N2_A, N2_B, N2_C, N2_D + N1_A(*), N1_B(*), N1_C(*), N1_D(*), N1_E(*) + +Subnet3 N3_D N3_A, N3_B, N3_C, N3_D + N1_A(*), N1_B(*), N1_C(*), N1_D(*), N1_E(*), + N2_A(*), N2_B(*), N2_C(*), N2_D(*) + +Servers with a (*) after them are non-authoritative names.</PRE +></P +><P +>At this point users looking in their network neighborhood on +subnets 1 or 3 will see all the servers on all sunbets, users on +subnet 2 will still only see the servers on subnets 1 and 2, but not 3.</P +><P +>Finally, the local master browser for subnet 2 (N2_B) will sync again +with the domain master browser (N1_C) and will recieve the missing +server entries. Finally - and as a steady state (if no machines +are removed or shut off) the browse lists will look like :</P +><P +><PRE +CLASS="PROGRAMLISTING" +>Subnet Browse Master List +------ ------------- ---- +Subnet1 N1_C N1_A, N1_B, N1_C, N1_D, N1_E, + N2_A(*), N2_B(*), N2_C(*), N2_D(*), + N3_A(*), N3_B(*), N3_C(*), N3_D(*) + +Subnet2 N2_B N2_A, N2_B, N2_C, N2_D + N1_A(*), N1_B(*), N1_C(*), N1_D(*), N1_E(*) + N3_A(*), N3_B(*), N3_C(*), N3_D(*) + +Subnet3 N3_D N3_A, N3_B, N3_C, N3_D + N1_A(*), N1_B(*), N1_C(*), N1_D(*), N1_E(*), + N2_A(*), N2_B(*), N2_C(*), N2_D(*) + +Servers with a (*) after them are non-authoritative names.</PRE +></P +><P +>Synchronizations between the domain master browser and local +master browsers will continue to occur, but this should be a +steady state situation.</P +><P +>If either router R1 or R2 fails the following will occur:</P +><P +></P +><OL +TYPE="1" +><LI +><P +> Names of computers on each side of the inaccessible network fragments + will be maintained for as long as 36 minutes, in the network neighbourhood + lists. + </P +></LI +><LI +><P +> Attempts to connect to these inaccessible computers will fail, but the + names will not be removed from the network neighbourhood lists. + </P +></LI +><LI +><P +> If one of the fragments is cut off from the WINS server, it will only + be able to access servers on its local subnet, by using subnet-isolated + broadcast NetBIOS name resolution. The effects are similar to that of + losing access to a DNS server. + </P +></LI +></OL +></DIV +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN63" +>Setting up a WINS server</A +></H1 +><P +>Either a Samba machine or a Windows NT Server machine may be set up +as a WINS server. To set a Samba machine to be a WINS server you must +add the following option to the smb.conf file on the selected machine : +in the [globals] section add the line </P +><P +><B +CLASS="COMMAND" +> wins support = yes</B +></P +><P +>Versions of Samba previous to 1.9.17 had this parameter default to +yes. If you have any older versions of Samba on your network it is +strongly suggested you upgrade to 1.9.17 or above, or at the very +least set the parameter to 'no' on all these machines.</P +><P +>Machines with "<B +CLASS="COMMAND" +>wins support = yes</B +>" will keep a list of +all NetBIOS names registered with them, acting as a DNS for NetBIOS names.</P +><P +>You should set up only ONE wins server. Do NOT set the +"<B +CLASS="COMMAND" +>wins support = yes</B +>" option on more than one Samba +server.</P +><P +>To set up a Windows NT Server as a WINS server you need to set up +the WINS service - see your NT documentation for details. Note that +Windows NT WINS Servers can replicate to each other, allowing more +than one to be set up in a complex subnet environment. As Microsoft +refuse to document these replication protocols Samba cannot currently +participate in these replications. It is possible in the future that +a Samba->Samba WINS replication protocol may be defined, in which +case more than one Samba machine could be set up as a WINS server +but currently only one Samba server should have the "wins support = yes" +parameter set.</P +><P +>After the WINS server has been configured you must ensure that all +machines participating on the network are configured with the address +of this WINS server. If your WINS server is a Samba machine, fill in +the Samba machine IP address in the "Primary WINS Server" field of +the "Control Panel->Network->Protocols->TCP->WINS Server" dialogs +in Windows 95 or Windows NT. To tell a Samba server the IP address +of the WINS server add the following line to the [global] section of +all smb.conf files :</P +><P +><B +CLASS="COMMAND" +> wins server = >name or IP address<</B +></P +><P +>where >name or IP address< is either the DNS name of the WINS server +machine or its IP address.</P +><P +>Note that this line MUST NOT BE SET in the smb.conf file of the Samba +server acting as the WINS server itself. If you set both the +"<B +CLASS="COMMAND" +>wins support = yes</B +>" option and the +"<B +CLASS="COMMAND" +>wins server = >name<</B +>" option then +nmbd will fail to start.</P +><P +>There are two possible scenarios for setting up cross subnet browsing. +The first details setting up cross subnet browsing on a network containing +Windows 95, Samba and Windows NT machines that are not configured as +part of a Windows NT Domain. The second details setting up cross subnet +browsing on networks that contain NT Domains.</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN82" +>Setting up Browsing in a WORKGROUP</A +></H1 +><P +>To set up cross subnet browsing on a network containing machines +in up to be in a WORKGROUP, not an NT Domain you need to set up one +Samba server to be the Domain Master Browser (note that this is *NOT* +the same as a Primary Domain Controller, although in an NT Domain the +same machine plays both roles). The role of a Domain master browser is +to collate the browse lists from local master browsers on all the +subnets that have a machine participating in the workgroup. Without +one machine configured as a domain master browser each subnet would +be an isolated workgroup, unable to see any machines on any other +subnet. It is the presense of a domain master browser that makes +cross subnet browsing possible for a workgroup.</P +><P +>In an WORKGROUP environment the domain master browser must be a +Samba server, and there must only be one domain master browser per +workgroup name. To set up a Samba server as a domain master browser, +set the following option in the [global] section of the smb.conf file :</P +><P +><B +CLASS="COMMAND" +> domain master = yes</B +></P +><P +>The domain master browser should also preferrably be the local master +browser for its own subnet. In order to achieve this set the following +options in the [global] section of the smb.conf file :</P +><P +><PRE +CLASS="PROGRAMLISTING" +> domain master = yes + local master = yes + preferred master = yes + os level = 65</PRE +></P +><P +>The domain master browser may be the same machine as the WINS +server, if you require.</P +><P +>Next, you should ensure that each of the subnets contains a +machine that can act as a local master browser for the +workgroup. Any NT machine should be able to do this, as will +Windows 95 machines (although these tend to get rebooted more +often, so it's not such a good idea to use these). To make a +Samba server a local master browser set the following +options in the [global] section of the smb.conf file :</P +><P +><PRE +CLASS="PROGRAMLISTING" +> domain master = no + local master = yes + preferred master = yes + os level = 65</PRE +></P +><P +>Do not do this for more than one Samba server on each subnet, +or they will war with each other over which is to be the local +master browser.</P +><P +>The "local master" parameter allows Samba to act as a local master +browser. The "preferred master" causes nmbd to force a browser +election on startup and the "os level" parameter sets Samba high +enough so that it should win any browser elections.</P +><P +>If you have an NT machine on the subnet that you wish to +be the local master browser then you can disable Samba from +becoming a local master browser by setting the following +options in the [global] section of the smb.conf file :</P +><P +><PRE +CLASS="PROGRAMLISTING" +> domain master = no + local master = no + preferred master = no + os level = 0</PRE +></P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN100" +>Setting up Browsing in a DOMAIN</A +></H1 +><P +>If you are adding Samba servers to a Windows NT Domain then +you must not set up a Samba server as a domain master browser. +By default, a Windows NT Primary Domain Controller for a Domain +name is also the Domain master browser for that name, and many +things will break if a Samba server registers the Domain master +browser NetBIOS name (DOMAIN>1B<) with WINS instead of the PDC.</P +><P +>For subnets other than the one containing the Windows NT PDC +you may set up Samba servers as local master browsers as +described. To make a Samba server a local master browser set +the following options in the [global] section of the smb.conf +file :</P +><P +><PRE +CLASS="PROGRAMLISTING" +> domain master = no + local master = yes + preferred master = yes + os level = 65</PRE +></P +><P +>If you wish to have a Samba server fight the election with machines +on the same subnet you may set the "os level" parameter to lower +levels. By doing this you can tune the order of machines that +will become local master browsers if they are running. For +more details on this see the section "FORCING SAMBA TO BE THE MASTER" +below.</P +><P +>If you have Windows NT machines that are members of the domain +on all subnets, and you are sure they will always be running then +you can disable Samba from taking part in browser elections and +ever becoming a local master browser by setting following options +in the [global] section of the smb.conf file :</P +><P +><B +CLASS="COMMAND" +> domain master = no + local master = no + preferred master = no + os level = 0</B +></P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN110" +>Forcing samba to be the master</A +></H1 +><P +>Who becomes the "master browser" is determined by an election process +using broadcasts. Each election packet contains a number of parameters +which determine what precedence (bias) a host should have in the +election. By default Samba uses a very low precedence and thus loses +elections to just about anyone else.</P +><P +>If you want Samba to win elections then just set the "os level" global +option in smb.conf to a higher number. It defaults to 0. Using 34 +would make it win all elections over every other system (except other +samba systems!)</P +><P +>A "os level" of 2 would make it beat WfWg and Win95, but not NTAS. A +NTAS domain controller uses level 32.</P +><P +>The maximum os level is 255</P +><P +>If you want samba to force an election on startup, then set the +"preferred master" global option in smb.conf to "yes". Samba will +then have a slight advantage over other potential master browsers +that are not preferred master browsers. Use this parameter with +care, as if you have two hosts (whether they are windows 95 or NT or +samba) on the same local subnet both set with "preferred master" to +"yes", then periodically and continually they will force an election +in order to become the local master browser.</P +><P +>If you want samba to be a "domain master browser", then it is +recommended that you also set "preferred master" to "yes", because +samba will not become a domain master browser for the whole of your +LAN or WAN if it is not also a local master browser on its own +broadcast isolated subnet.</P +><P +>It is possible to configure two samba servers to attempt to become +the domain master browser for a domain. The first server that comes +up will be the domain master browser. All other samba servers will +attempt to become the domain master browser every 5 minutes. They +will find that another samba server is already the domain master +browser and will fail. This provides automatic redundancy, should +the current domain master browser fail.</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN119" +>Making samba the domain master</A +></H1 +><P +>The domain master is responsible for collating the browse lists of +multiple subnets so that browsing can occur between subnets. You can +make samba act as the domain master by setting "domain master = yes" +in smb.conf. By default it will not be a domain master.</P +><P +>Note that you should NOT set Samba to be the domain master for a +workgroup that has the same name as an NT Domain.</P +><P +>When samba is the domain master and the master browser it will listen +for master announcements (made roughly every twelve minutes) from local +master browsers on other subnets and then contact them to synchronise +browse lists.</P +><P +>If you want samba to be the domain master then I suggest you also set +the "os level" high enough to make sure it wins elections, and set +"preferred master" to "yes", to get samba to force an election on +startup.</P +><P +>Note that all your servers (including samba) and clients should be +using a WINS server to resolve NetBIOS names. If your clients are only +using broadcasting to resolve NetBIOS names, then two things will occur:</P +><P +></P +><OL +TYPE="1" +><LI +><P +> your local master browsers will be unable to find a domain master + browser, as it will only be looking on the local subnet. + </P +></LI +><LI +><P +> if a client happens to get hold of a domain-wide browse list, and + a user attempts to access a host in that list, it will be unable to + resolve the NetBIOS name of that host. + </P +></LI +></OL +><P +>If, however, both samba and your clients are using a WINS server, then:</P +><P +></P +><OL +TYPE="1" +><LI +><P +> your local master browsers will contact the WINS server and, as long as + samba has registered that it is a domain master browser with the WINS + server, your local master browser will receive samba's ip address + as its domain master browser. + </P +></LI +><LI +><P +> when a client receives a domain-wide browse list, and a user attempts + to access a host in that list, it will contact the WINS server to + resolve the NetBIOS name of that host. as long as that host has + registered its NetBIOS name with the same WINS server, the user will + be able to see that host. + </P +></LI +></OL +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN137" +>Note about broadcast addresses</A +></H1 +><P +>If your network uses a "0" based broadcast address (for example if it +ends in a 0) then you will strike problems. Windows for Workgroups +does not seem to support a 0's broadcast and you will probably find +that browsing and name lookups won't work.</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN140" +>Multiple interfaces</A +></H1 +><P +>Samba now supports machines with multiple network interfaces. If you +have multiple interfaces then you will need to use the "interfaces" +option in smb.conf to configure them. See smb.conf(5) for details.</P +></DIV +></DIV +></BODY +></HTML +>
\ No newline at end of file diff --git a/docs/htmldocs/Bugs.html b/docs/htmldocs/Bugs.html new file mode 100644 index 0000000000..0f7fb7bd60 --- /dev/null +++ b/docs/htmldocs/Bugs.html @@ -0,0 +1,238 @@ +<HTML +><HEAD +><TITLE +>Reporting Bugs</TITLE +><META +NAME="GENERATOR" +CONTENT="Modular DocBook HTML Stylesheet Version 1.57"></HEAD +><BODY +CLASS="ARTICLE" +BGCOLOR="#FFFFFF" +TEXT="#000000" +LINK="#0000FF" +VLINK="#840084" +ALINK="#0000FF" +><DIV +CLASS="ARTICLE" +><DIV +CLASS="TITLEPAGE" +><H1 +CLASS="TITLE" +><A +NAME="BUGREPORT" +>Reporting Bugs</A +></H1 +><HR></DIV +><DIV +CLASS="SECT1" +><H1 +CLASS="SECT1" +><A +NAME="AEN3" +>Introduction</A +></H1 +><P +>The email address for bug reports is samba@samba.org</P +><P +>Please take the time to read this file before you submit a bug +report. Also, please see if it has changed between releases, as we +may be changing the bug reporting mechanism at some time.</P +><P +>Please also do as much as you can yourself to help track down the +bug. Samba is maintained by a dedicated group of people who volunteer +their time, skills and efforts. We receive far more mail about it than +we can possibly answer, so you have a much higher chance of an answer +and a fix if you send us a "developer friendly" bug report that lets +us fix it fast. </P +><P +>Do not assume that if you post the bug to the comp.protocols.smb +newsgroup or the mailing list that we will read it. If you suspect that your +problem is not a bug but a configuration problem then it is better to send +it to the Samba mailing list, as there are (at last count) 5000 other users on +that list that may be able to help you.</P +><P +>You may also like to look though the recent mailing list archives, +which are conveniently accessible on the Samba web pages +at http://samba.org/samba/ </P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN10" +>General info</A +></H1 +><P +>Before submitting a bug report check your config for silly +errors. Look in your log files for obvious messages that tell you that +you've misconfigured something and run testparm to test your config +file for correct syntax.</P +><P +>Have you run through the <A +HREF="Diagnosis.html" +TARGET="_top" +>diagnosis</A +>? +This is very important.</P +><P +>If you include part of a log file with your bug report then be sure to +annotate it with exactly what you were doing on the client at the +time, and exactly what the results were.</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN16" +>Debug levels</A +></H1 +><P +>If the bug has anything to do with Samba behaving incorrectly as a +server (like refusing to open a file) then the log files will probably +be very useful. Depending on the problem a log level of between 3 and +10 showing the problem may be appropriate. A higher level givesmore +detail, but may use too much disk space.</P +><P +>To set the debug level use <B +CLASS="COMMAND" +>log level =</B +> in your +<TT +CLASS="FILENAME" +>smb.conf</TT +>. You may also find it useful to set the log +level higher for just one machine and keep separate logs for each machine. +To do this use:</P +><P +><PRE +CLASS="PROGRAMLISTING" +>log level = 10 +log file = /usr/local/samba/lib/log.%m +include = /usr/local/samba/lib/smb.conf.%m</PRE +></P +><P +>then create a file +<TT +CLASS="FILENAME" +>/usr/local/samba/lib/smb.conf.machine</TT +> where +"machine" is the name of the client you wish to debug. In that file +put any smb.conf commands you want, for example +<B +CLASS="COMMAND" +>log level=</B +> may be useful. This also allows you to +experiment with different security systems, protocol levels etc on just +one machine.</P +><P +>The <TT +CLASS="FILENAME" +>smb.conf</TT +> entry <B +CLASS="COMMAND" +>log level =</B +> +is synonymous with the entry <B +CLASS="COMMAND" +>debuglevel =</B +> that has been +used in older versions of Samba and is being retained for backwards +compatibility of smb.conf files.</P +><P +>As the <B +CLASS="COMMAND" +>log level =</B +> value is increased you will record +a significantly increasing level of debugging information. For most +debugging operations you may not need a setting higher than 3. Nearly +all bugs can be tracked at a setting of 10, but be prepared for a VERY +large volume of log data.</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN33" +>Internal errors</A +></H1 +><P +>If you get a "INTERNAL ERROR" message in your log files it means that +Samba got an unexpected signal while running. It is probably a +segmentation fault and almost certainly means a bug in Samba (unless +you have faulty hardware or system software)</P +><P +>If the message came from smbd then it will probably be accompanied by +a message which details the last SMB message received by smbd. This +info is often very useful in tracking down the problem so please +include it in your bug report.</P +><P +>You should also detail how to reproduce the problem, if +possible. Please make this reasonably detailed.</P +><P +>You may also find that a core file appeared in a "corefiles" +subdirectory of the directory where you keep your samba log +files. This file is the most useful tool for tracking down the bug. To +use it you do this:</P +><P +><B +CLASS="COMMAND" +>gdb smbd core</B +></P +><P +>adding appropriate paths to smbd and core so gdb can find them. If you +don't have gdb then try "dbx". Then within the debugger use the +command "where" to give a stack trace of where the problem +occurred. Include this in your mail.</P +><P +>If you known any assembly language then do a "disass" of the routine +where the problem occurred (if its in a library routine then +disassemble the routine that called it) and try to work out exactly +where the problem is by looking at the surrounding code. Even if you +don't know assembly then incuding this info in the bug report can be +useful. </P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN43" +>Attaching to a running process</A +></H1 +><P +>Unfortunately some unixes (in particular some recent linux kernels) +refuse to dump a core file if the task has changed uid (which smbd +does often). To debug with this sort of system you could try to attach +to the running process using "gdb smbd PID" where you get PID from +smbstatus. Then use "c" to continue and try to cause the core dump +using the client. The debugger should catch the fault and tell you +where it occurred.</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN46" +>Patches</A +></H1 +><P +>The best sort of bug report is one that includes a fix! If you send us +patches please use <B +CLASS="COMMAND" +>diff -u</B +> format if your version of +diff supports it, otherwise use <B +CLASS="COMMAND" +>diff -c4</B +>. Make sure +your do the diff against a clean version of the source and let me know +exactly what version you used. </P +></DIV +></DIV +></BODY +></HTML +>
\ No newline at end of file diff --git a/docs/htmldocs/Diagnosis.html b/docs/htmldocs/Diagnosis.html new file mode 100644 index 0000000000..1944c37be9 --- /dev/null +++ b/docs/htmldocs/Diagnosis.html @@ -0,0 +1,548 @@ +<HTML +><HEAD +><TITLE +>Diagnosing your samba server</TITLE +><META +NAME="GENERATOR" +CONTENT="Modular DocBook HTML Stylesheet Version 1.57"></HEAD +><BODY +CLASS="ARTICLE" +BGCOLOR="#FFFFFF" +TEXT="#000000" +LINK="#0000FF" +VLINK="#840084" +ALINK="#0000FF" +><DIV +CLASS="ARTICLE" +><DIV +CLASS="TITLEPAGE" +><H1 +CLASS="TITLE" +><A +NAME="DIAGNOSIS" +>Diagnosing your samba server</A +></H1 +><HR></DIV +><DIV +CLASS="SECT1" +><H1 +CLASS="SECT1" +><A +NAME="AEN3" +>Introduction</A +></H1 +><P +>This file contains a list of tests you can perform to validate your +Samba server. It also tells you what the likely cause of the problem +is if it fails any one of these steps. If it passes all these tests +then it is probably working fine.</P +><P +>You should do ALL the tests, in the order shown. I have tried to +carefully choose them so later tests only use capabilities verified in +the earlier tests.</P +><P +>If you send me an email saying "it doesn't work" and you have not +followed this test procedure then you should not be surprised if I +ignore your email.</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN8" +>Assumptions</A +></H1 +><P +>In all of the tests I assume you have a Samba server called BIGSERVER +and a PC called ACLIENT both in workgroup TESTGROUP. I also assume the +PC is running windows for workgroups with a recent copy of the +microsoft tcp/ip stack. Alternatively, your PC may be running Windows +95 or Windows NT (Workstation or Server).</P +><P +>The procedure is similar for other types of clients.</P +><P +>I also assume you know the name of an available share in your +smb.conf. I will assume this share is called "tmp". You can add a +"tmp" share like by adding the following to smb.conf:</P +><P +><PRE +CLASS="PROGRAMLISTING" +> [tmp] + comment = temporary files + path = /tmp + read only = yes </PRE +></P +><P +>THESE TESTS ASSUME VERSION 2.0.6 OR LATER OF THE SAMBA SUITE. SOME +COMMANDS SHOWN DID NOT EXIST IN EARLIER VERSIONS</P +><P +>Please pay attention to the error messages you receive. If any error message +reports that your server is being unfriendly you should first check that you +IP name resolution is correctly set up. eg: Make sure your /etc/resolv.conf +file points to name servers that really do exist.</P +><P +>Also, if you do not have DNS server access for name resolution please check +that the settings for your smb.conf file results in "dns proxy = no". The +best way to check this is with "testparm smb.conf"</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN18" +>Tests</A +></H1 +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN20" +>Test 1</A +></H2 +><P +>In the directory in which you store your smb.conf file, run the command +"testparm smb.conf". If it reports any errors then your smb.conf +configuration file is faulty.</P +><P +>Note: Your smb.conf file may be located in: <TT +CLASS="FILENAME" +>/etc</TT +> + Or in: <TT +CLASS="FILENAME" +>/usr/local/samba/lib</TT +></P +></DIV +><DIV +CLASS="SECT2" +><HR><H2 +CLASS="SECT2" +><A +NAME="AEN26" +>Test 2</A +></H2 +><P +>Run the command "ping BIGSERVER" from the PC and "ping ACLIENT" from +the unix box. If you don't get a valid response then your TCP/IP +software is not correctly installed. </P +><P +>Note that you will need to start a "dos prompt" window on the PC to +run ping.</P +><P +>If you get a message saying "host not found" or similar then your DNS +software or /etc/hosts file is not correctly setup. It is possible to +run samba without DNS entries for the server and client, but I assume +you do have correct entries for the remainder of these tests. </P +><P +>Another reason why ping might fail is if your host is running firewall +software. You will need to relax the rules to let in the workstation +in question, perhaps by allowing access from another subnet (on Linux +this is done via the ipfwadm program.)</P +></DIV +><DIV +CLASS="SECT2" +><HR><H2 +CLASS="SECT2" +><A +NAME="AEN32" +>Test 3</A +></H2 +><P +>Run the command "smbclient -L BIGSERVER" on the unix box. You +should get a list of available shares back. </P +><P +>If you get a error message containing the string "Bad password" then +you probably have either an incorrect "hosts allow", "hosts deny" or +"valid users" line in your smb.conf, or your guest account is not +valid. Check what your guest account is using "testparm" and +temporarily remove any "hosts allow", "hosts deny", "valid users" or +"invalid users" lines.</P +><P +>If you get a "connection refused" response then the smbd server may +not be running. If you installed it in inetd.conf then you probably edited +that file incorrectly. If you installed it as a daemon then check that +it is running, and check that the netbios-ssn port is in a LISTEN +state using "netstat -a".</P +><P +>If you get a "session request failed" then the server refused the +connection. If it says "Your server software is being unfriendly" then +its probably because you have invalid command line parameters to smbd, +or a similar fatal problem with the initial startup of smbd. Also +check your config file (smb.conf) for syntax errors with "testparm" +and that the various directories where samba keeps its log and lock +files exist.</P +><P +>There are a number of reasons for which smbd may refuse or decline +a session request. The most common of these involve one or more of +the following smb.conf file entries:</P +><P +><PRE +CLASS="PROGRAMLISTING" +> hosts deny = ALL + hosts allow = xxx.xxx.xxx.xxx/yy + bind interfaces only = Yes</PRE +></P +><P +>In the above, no allowance has been made for any session requests that +will automatically translate to the loopback adaptor address 127.0.0.1. +To solve this problem change these lines to:</P +><P +><PRE +CLASS="PROGRAMLISTING" +> hosts deny = ALL + hosts allow = xxx.xxx.xxx.xxx/yy 127.</PRE +></P +><P +>Do NOT use the "bind interfaces only" parameter where you may wish to +use the samba password change facility, or where smbclient may need to +access local service for name resolution or for local resource +connections. (Note: the "bind interfaces only" parameter deficiency +where it will not allow connections to the loopback address will be +fixed soon).</P +><P +>Another common cause of these two errors is having something already running +on port 139, such as Samba (ie: smbd is running from inetd already) or +something like Digital's Pathworks. Check your inetd.conf file before trying +to start smbd as a daemon, it can avoid a lot of frustration!</P +><P +>And yet another possible cause for failure of TEST 3 is when the subnet mask +and / or broadcast address settings are incorrect. Please check that the +network interface IP Address / Broadcast Address / Subnet Mask settings are +correct and that Samba has correctly noted these in the log.nmb file.</P +></DIV +><DIV +CLASS="SECT2" +><HR><H2 +CLASS="SECT2" +><A +NAME="AEN47" +>Test 4</A +></H2 +><P +>Run the command "nmblookup -B BIGSERVER __SAMBA__". You should get the +IP address of your Samba server back.</P +><P +>If you don't then nmbd is incorrectly installed. Check your inetd.conf +if you run it from there, or that the daemon is running and listening +to udp port 137.</P +><P +>One common problem is that many inetd implementations can't take many +parameters on the command line. If this is the case then create a +one-line script that contains the right parameters and run that from +inetd.</P +></DIV +><DIV +CLASS="SECT2" +><HR><H2 +CLASS="SECT2" +><A +NAME="AEN52" +>Test 5</A +></H2 +><P +>run the command <B +CLASS="COMMAND" +>nmblookup -B ACLIENT '*'</B +></P +><P +>You should get the PCs IP address back. If you don't then the client +software on the PC isn't installed correctly, or isn't started, or you +got the name of the PC wrong. </P +><P +>If ACLIENT doesn't resolve via DNS then use the IP address of the +client in the above test.</P +></DIV +><DIV +CLASS="SECT2" +><HR><H2 +CLASS="SECT2" +><A +NAME="AEN58" +>Test 6</A +></H2 +><P +>Run the command <B +CLASS="COMMAND" +>nmblookup -d 2 '*'</B +></P +><P +>This time we are trying the same as the previous test but are trying +it via a broadcast to the default broadcast address. A number of +Netbios/TCPIP hosts on the network should respond, although Samba may +not catch all of the responses in the short time it listens. You +should see "got a positive name query response" messages from several +hosts.</P +><P +>If this doesn't give a similar result to the previous test then +nmblookup isn't correctly getting your broadcast address through its +automatic mechanism. In this case you should experiment use the +"interfaces" option in smb.conf to manually configure your IP +address, broadcast and netmask. </P +><P +>If your PC and server aren't on the same subnet then you will need to +use the -B option to set the broadcast address to the that of the PCs +subnet.</P +><P +>This test will probably fail if your subnet mask and broadcast address are +not correct. (Refer to TEST 3 notes above).</P +></DIV +><DIV +CLASS="SECT2" +><HR><H2 +CLASS="SECT2" +><A +NAME="AEN66" +>Test 7</A +></H2 +><P +>Run the command <B +CLASS="COMMAND" +>smbclient //BIGSERVER/TMP</B +>. You should +then be prompted for a password. You should use the password of the account +you are logged into the unix box with. If you want to test with +another account then add the -U >accountname< option to the end of +the command line. eg: +<B +CLASS="COMMAND" +>smbclient //bigserver/tmp -Ujohndoe</B +></P +><P +>Note: It is possible to specify the password along with the username +as follows: +<B +CLASS="COMMAND" +>smbclient //bigserver/tmp -Ujohndoe%secret</B +></P +><P +>Once you enter the password you should get the "smb>" prompt. If you +don't then look at the error message. If it says "invalid network +name" then the service "tmp" is not correctly setup in your smb.conf.</P +><P +>If it says "bad password" then the likely causes are:</P +><P +></P +><OL +TYPE="1" +><LI +><P +> you have shadow passords (or some other password system) but didn't + compile in support for them in smbd + </P +></LI +><LI +><P +> your "valid users" configuration is incorrect + </P +></LI +><LI +><P +> you have a mixed case password and you haven't enabled the "password + level" option at a high enough level + </P +></LI +><LI +><P +> the "path =" line in smb.conf is incorrect. Check it with testparm + </P +></LI +><LI +><P +> you enabled password encryption but didn't create the SMB encrypted + password file + </P +></LI +></OL +><P +>Once connected you should be able to use the commands +<B +CLASS="COMMAND" +>dir</B +> <B +CLASS="COMMAND" +>get</B +> <B +CLASS="COMMAND" +>put</B +> etc. +Type <B +CLASS="COMMAND" +>help >command<</B +> for instructions. You should +especially check that the amount of free disk space shown is correct +when you type <B +CLASS="COMMAND" +>dir</B +>.</P +></DIV +><DIV +CLASS="SECT2" +><HR><H2 +CLASS="SECT2" +><A +NAME="AEN92" +>Test 8</A +></H2 +><P +>On the PC type the command <B +CLASS="COMMAND" +>net view \\BIGSERVER</B +>. You will +need to do this from within a "dos prompt" window. You should get back a +list of available shares on the server.</P +><P +>If you get a "network name not found" or similar error then netbios +name resolution is not working. This is usually caused by a problem in +nmbd. To overcome it you could do one of the following (you only need +to choose one of them):</P +><P +></P +><OL +TYPE="1" +><LI +><P +> fixup the nmbd installation</P +></LI +><LI +><P +> add the IP address of BIGSERVER to the "wins server" box in the + advanced tcp/ip setup on the PC.</P +></LI +><LI +><P +> enable windows name resolution via DNS in the advanced section of + the tcp/ip setup</P +></LI +><LI +><P +> add BIGSERVER to your lmhosts file on the PC.</P +></LI +></OL +><P +>If you get a "invalid network name" or "bad password error" then the +same fixes apply as they did for the "smbclient -L" test above. In +particular, make sure your "hosts allow" line is correct (see the man +pages)</P +><P +>Also, do not overlook that fact that when the workstation requests the +connection to the samba server it will attempt to connect using the +name with which you logged onto your Windows machine. You need to make +sure that an account exists on your Samba server with that exact same +name and password.</P +><P +>If you get "specified computer is not receiving requests" or similar +it probably means that the host is not contactable via tcp services. +Check to see if the host is running tcp wrappers, and if so add an entry in +the hosts.allow file for your client (or subnet, etc.)</P +></DIV +><DIV +CLASS="SECT2" +><HR><H2 +CLASS="SECT2" +><A +NAME="AEN109" +>Test 9</A +></H2 +><P +>Run the command <B +CLASS="COMMAND" +>net use x: \\BIGSERVER\TMP</B +>. You should +be prompted for a password then you should get a "command completed +successfully" message. If not then your PC software is incorrectly +installed or your smb.conf is incorrect. make sure your "hosts allow" +and other config lines in smb.conf are correct.</P +><P +>It's also possible that the server can't work out what user name to +connect you as. To see if this is the problem add the line "user = +USERNAME" to the [tmp] section of smb.conf where "USERNAME" is the +username corresponding to the password you typed. If you find this +fixes things you may need the username mapping option.</P +></DIV +><DIV +CLASS="SECT2" +><HR><H2 +CLASS="SECT2" +><A +NAME="AEN114" +>Test 10</A +></H2 +><P +>Run the command <B +CLASS="COMMAND" +>nmblookup -M TESTGROUP</B +> where +TESTGROUP is the name of the workgroup that your Samba server and +Windows PCs belong to. You should get back the IP address of the +master browser for that workgroup.</P +><P +>If you don't then the election process has failed. Wait a minute to +see if it is just being slow then try again. If it still fails after +that then look at the browsing options you have set in smb.conf. Make +sure you have <B +CLASS="COMMAND" +>preferred master = yes</B +> to ensure that +an election is held at startup.</P +></DIV +><DIV +CLASS="SECT2" +><HR><H2 +CLASS="SECT2" +><A +NAME="AEN120" +>Test 11</A +></H2 +><P +>From file manager try to browse the server. Your samba server should +appear in the browse list of your local workgroup (or the one you +specified in smb.conf). You should be able to double click on the name +of the server and get a list of shares. If you get a "invalid +password" error when you do then you are probably running WinNT and it +is refusing to browse a server that has no encrypted password +capability and is in user level security mode. In this case either set +<B +CLASS="COMMAND" +>security = server</B +> AND +<B +CLASS="COMMAND" +>password server = Windows_NT_Machine</B +> in your +smb.conf file, or enable encrypted passwords AFTER compiling in support +for encrypted passwords (refer to the Makefile).</P +></DIV +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN125" +>Still having troubles?</A +></H1 +><P +>Try the mailing list or newsgroup, or use the ethereal utility to +sniff the problem. The official samba mailing list can be reached at +<A +HREF="mailto:samba@samba.org" +TARGET="_top" +>samba@samba.org</A +>. To find +out more about samba and how to subscribe to the mailing list check +out the samba web page at +<A +HREF="http://samba.org/samba" +TARGET="_top" +>http://samba.org/samba</A +></P +><P +>Also look at the other docs in the Samba package!</P +></DIV +></DIV +></BODY +></HTML +>
\ No newline at end of file diff --git a/docs/htmldocs/Printing.html b/docs/htmldocs/Printing.html new file mode 100644 index 0000000000..6c8b196240 --- /dev/null +++ b/docs/htmldocs/Printing.html @@ -0,0 +1,408 @@ +<HTML +><HEAD +><TITLE +>Debugging Printing Problems</TITLE +><META +NAME="GENERATOR" +CONTENT="Modular DocBook HTML Stylesheet Version 1.57"></HEAD +><BODY +CLASS="ARTICLE" +BGCOLOR="#FFFFFF" +TEXT="#000000" +LINK="#0000FF" +VLINK="#840084" +ALINK="#0000FF" +><DIV +CLASS="ARTICLE" +><DIV +CLASS="TITLEPAGE" +><H1 +CLASS="TITLE" +><A +NAME="PRINTING_DEBUG" +>Debugging Printing Problems</A +></H1 +><HR></DIV +><DIV +CLASS="SECT1" +><H1 +CLASS="SECT1" +><A +NAME="AEN3" +>Introduction</A +></H1 +><P +>This is a short description of how to debug printing problems with +Samba. This describes how to debug problems with printing from a SMB +client to a Samba server, not the other way around. For the reverse +see the examples/printing directory.</P +><P +>Ok, so you want to print to a Samba server from your PC. The first +thing you need to understand is that Samba does not actually do any +printing itself, it just acts as a middleman between your PC client +and your Unix printing subsystem. Samba receives the file from the PC +then passes the file to a external "print command". What print command +you use is up to you.</P +><P +>The whole things is controlled using options in smb.conf. The most +relevant options (which you should look up in the smb.conf man page) +are:</P +><P +><PRE +CLASS="PROGRAMLISTING" +> [global] + print command - send a file to a spooler + lpq command - get spool queue status + lprm command - remove a job + [printers] + path = /var/spool/lpd/samba</PRE +></P +><P +>The following are nice to know about:</P +><P +><PRE +CLASS="PROGRAMLISTING" +> queuepause command - stop a printer or print queue + queueresume command - start a printer or print queue</PRE +></P +><P +>Example:</P +><P +><PRE +CLASS="PROGRAMLISTING" +> print command = /usr/bin/lpr -r -P%p %s + lpq command = /usr/bin/lpq -P%p %s + lprm command = /usr/bin/lprm -P%p %j + queuepause command = /usr/sbin/lpc -P%p stop + queuepause command = /usr/sbin/lpc -P%p start</PRE +></P +><P +>Samba should set reasonable defaults for these depending on your +system type, but it isn't clairvoyant. It is not uncommon that you +have to tweak these for local conditions. The commands should +always have fully specified pathnames, as the smdb may not have +the correct PATH values.</P +><P +>When you send a job to Samba to be printed, it will make a temporary +copy of it in the directory specified in the [printers] section. +and it should be periodically cleaned out. The lpr -r option +requests that the temporary copy be removed after printing; If +printing fails then you might find leftover files in this directory, +and it should be periodically cleaned out. Samba used the lpq +command to determine the "job number" assigned to your print job +by the spooler.</P +><P +>The %>letter< are "macros" that get dynamically replaced with appropriate +values when they are used. The %s gets replaced with the name of the spool +file that Samba creates and the %p gets replaced with the name of the +printer. The %j gets replaced with the "job number" which comes from +the lpq output.</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN19" +>Debugging printer problems</A +></H1 +><P +>One way to debug printing problems is to start by replacing these +command with shell scripts that record the arguments and the contents +of the print file. A simple example of this kind of things might +be:</P +><P +><PRE +CLASS="PROGRAMLISTING" +> print command = /tmp/saveprint %p %s + + #!/bin/saveprint + # we make sure that we are the right user + /usr/bin/id -p >/tmp/tmp.print + # we run the command and save the error messages + # replace the command with the one appropriate for your system + /usr/bin/lpr -r -P$1 $2 2>>&/tmp/tmp.print</PRE +></P +><P +>Then you print a file and try removing it. You may find that the +print queue needs to be stopped in order to see the queue status +and remove the job:</P +><P +><PRE +CLASS="PROGRAMLISTING" +> h4: {42} % echo hi >/tmp/hi +h4: {43} % smbclient //localhost/lw4 +added interface ip=10.0.0.4 bcast=10.0.0.255 nmask=255.255.255.0 +Password: +Domain=[ASTART] OS=[Unix] Server=[Samba 2.0.7] +smb: \> print /tmp/hi +putting file /tmp/hi as hi-17534 (0.0 kb/s) (average 0.0 kb/s) +smb: \> queue +1049 3 hi-17534 +smb: \> cancel 1049 +Error cancelling job 1049 : code 0 +smb: \> cancel 1049 +Job 1049 cancelled +smb: \> queue +smb: \> exit</PRE +></P +><P +>The 'code 0' indicates that the job was removed. The comment +by the smbclient is a bit misleading on this. +You can observe the command output and then and look at the +/tmp/tmp.print file to see what the results are. You can quickly +find out if the problem is with your printing system. Often people +have problems with their /etc/printcap file or permissions on +various print queues.</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN28" +>What printers do I have?</A +></H1 +><P +>You can use the 'testprns' program to check to see if the printer +name you are using is recognized by Samba. For example, you can +use:</P +><P +><PRE +CLASS="PROGRAMLISTING" +> testprns printer /etc/printcap</PRE +></P +><P +>Samba can get its printcap information from a file or from a program. +You can try the following to see the format of the extracted +information:</P +><P +><PRE +CLASS="PROGRAMLISTING" +> testprns -a printer /etc/printcap + + testprns -a printer '|/bin/cat printcap'</PRE +></P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN36" +>Setting up printcap and print servers</A +></H1 +><P +>You may need to set up some printcaps for your Samba system to use. +It is strongly recommended that you use the facilities provided by +the print spooler to set up queues and printcap information.</P +><P +>Samba requires either a printcap or program to deliver printcap +information. This printcap information has the format:</P +><P +><PRE +CLASS="PROGRAMLISTING" +> name|alias1|alias2...:option=value:...</PRE +></P +><P +>For almost all printing systems, the printer 'name' must be composed +only of alphanumeric or underscore '_' characters. Some systems also +allow hyphens ('-') as well. An alias is an alternative name for the +printer, and an alias with a space in it is used as a 'comment' +about the printer. The printcap format optionally uses a \ at the end of lines +to extend the printcap to multiple lines.</P +><P +>Here are some examples of printcap files:</P +><P +><P +></P +><OL +TYPE="1" +><LI +><P +>pr just printer name</P +></LI +><LI +><P +>pr|alias printer name and alias</P +></LI +><LI +><P +>pr|My Printer printer name, alias used as comment</P +></LI +><LI +><P +>pr:sh:\ Same as pr:sh:cm= testing + :cm= \ + testing</P +></LI +><LI +><P +>pr:sh Same as pr:sh:cm= testing + :cm= testing</P +></LI +></OL +></P +><P +>Samba reads the printcap information when first started. If you make +changes in the printcap information, then you must do the following:</P +><P +></P +><OL +TYPE="1" +><LI +><P +>make sure that the print spooler is aware of these changes. +The LPRng system uses the 'lpc reread' command to do this.</P +></LI +><LI +><P +>make sure that the spool queues, etc., exist and have the +correct permissions. The LPRng system uses the 'checkpc -f' +command to do this.</P +></LI +><LI +><P +>You now should send a SIGHUP signal to the smbd server to have +it reread the printcap information.</P +></LI +></OL +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN64" +>Job sent, no output</A +></H1 +><P +>This is the most frustrating part of printing. You may have sent the +job, verified that the job was forwarded, set up a wrapper around +the command to send the file, but there was no output from the printer.</P +><P +>First, check to make sure that the job REALLY is getting to the +right print queue. If you are using a BSD or LPRng print spooler, +you can temporarily stop the printing of jobs. Jobs can still be +submitted, but they will not be printed. Use:</P +><P +><PRE +CLASS="PROGRAMLISTING" +> lpc -Pprinter stop</PRE +></P +><P +>Now submit a print job and then use 'lpq -Pprinter' to see if the +job is in the print queue. If it is not in the print queue then +you will have to find out why it is not being accepted for printing.</P +><P +>Next, you may want to check to see what the format of the job really +was. With the assistance of the system administrator you can view +the submitted jobs files. You may be surprised to find that these +are not in what you would expect to call a printable format. +You can use the UNIX 'file' utitily to determine what the job +format actually is:</P +><P +><PRE +CLASS="PROGRAMLISTING" +> cd /var/spool/lpd/printer # spool directory of print jobs + ls # find job files + file dfA001myhost</PRE +></P +><P +>You should make sure that your printer supports this format OR that +your system administrator has installed a 'print filter' that will +convert the file to a format appropriate for your printer.</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN75" +>Job sent, strange output</A +></H1 +><P +>Once you have the job printing, you can then start worrying about +making it print nicely.</P +><P +>The most common problem is extra pages of output: banner pages +OR blank pages at the end.</P +><P +>If you are getting banner pages, check and make sure that the +printcap option or printer option is configured for no banners. +If you have a printcap, this is the :sh (suppress header or banner +page) option. You should have the following in your printer.</P +><P +><PRE +CLASS="PROGRAMLISTING" +> printer: ... :sh</PRE +></P +><P +>If you have this option and are still getting banner pages, there +is a strong chance that your printer is generating them for you +automatically. You should make sure that banner printing is disabled +for the printer. This usually requires using the printer setup software +or procedures supplied by the printer manufacturer.</P +><P +>If you get an extra page of output, this could be due to problems +with your job format, or if you are generating PostScript jobs, +incorrect setting on your printer driver on the MicroSoft client. +For example, under Win95 there is a option:</P +><P +><PRE +CLASS="PROGRAMLISTING" +> Printers|Printer Name|(Right Click)Properties|Postscript|Advanced|</PRE +></P +><P +>that allows you to choose if a Ctrl-D is appended to all jobs. +This is a very bad thing to do, as most spooling systems will +automatically add a ^D to the end of the job if it is detected as +PostScript. The multiple ^D may cause an additional page of output.</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN87" +>Raw PostScript printed</A +></H1 +><P +>This is a problem that is usually caused by either the print spooling +system putting information at the start of the print job that makes +the printer think the job is a text file, or your printer simply +does not support PostScript. You may need to enable 'Automatic +Format Detection' on your printer.</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN90" +>Advanced Printing</A +></H1 +><P +>Note that you can do some pretty magic things by using your +imagination with the "print command" option and some shell scripts. +Doing print accounting is easy by passing the %U option to a print +command shell script. You could even make the print command detect +the type of output and its size and send it to an appropriate +printer.</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN93" +>Real debugging</A +></H1 +><P +>If the above debug tips don't help, then maybe you need to bring in +the bug guns, system tracing. See Tracing.txt in this directory.</P +></DIV +></DIV +></BODY +></HTML +>
\ No newline at end of file diff --git a/docs/htmldocs/Speed.html b/docs/htmldocs/Speed.html new file mode 100644 index 0000000000..47a8c885b6 --- /dev/null +++ b/docs/htmldocs/Speed.html @@ -0,0 +1,550 @@ +<HTML +><HEAD +><TITLE +>Samba performance issues</TITLE +><META +NAME="GENERATOR" +CONTENT="Modular DocBook HTML Stylesheet Version 1.57"></HEAD +><BODY +CLASS="ARTICLE" +BGCOLOR="#FFFFFF" +TEXT="#000000" +LINK="#0000FF" +VLINK="#840084" +ALINK="#0000FF" +><DIV +CLASS="ARTICLE" +><DIV +CLASS="TITLEPAGE" +><H1 +CLASS="TITLE" +><A +NAME="SPEED" +>Samba performance issues</A +></H1 +><HR></DIV +><DIV +CLASS="SECT1" +><H1 +CLASS="SECT1" +><A +NAME="AEN3" +>Comparisons</A +></H1 +><P +>The Samba server uses TCP to talk to the client. Thus if you are +trying to see if it performs well you should really compare it to +programs that use the same protocol. The most readily available +programs for file transfer that use TCP are ftp or another TCP based +SMB server.</P +><P +>If you want to test against something like a NT or WfWg server then +you will have to disable all but TCP on either the client or +server. Otherwise you may well be using a totally different protocol +(such as Netbeui) and comparisons may not be valid.</P +><P +>Generally you should find that Samba performs similarly to ftp at raw +transfer speed. It should perform quite a bit faster than NFS, +although this very much depends on your system.</P +><P +>Several people have done comparisons between Samba and Novell, NFS or +WinNT. In some cases Samba performed the best, in others the worst. I +suspect the biggest factor is not Samba vs some other system but the +hardware and drivers used on the various systems. Given similar +hardware Samba should certainly be competitive in speed with other +systems.</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN9" +>Oplocks</A +></H1 +><DIV +CLASS="SECT2" +><H2 +CLASS="SECT2" +><A +NAME="AEN11" +>Overview</A +></H2 +><P +>Oplocks are the way that SMB clients get permission from a server to +locally cache file operations. If a server grants an oplock +(opportunistic lock) then the client is free to assume that it is the +only one accessing the file and it will agressively cache file +data. With some oplock types the client may even cache file open/close +operations. This can give enormous performance benefits.</P +><P +>With the release of Samba 1.9.18 we now correctly support opportunistic +locks. This is turned on by default, and can be turned off on a share- +by-share basis by setting the parameter :</P +><P +><B +CLASS="COMMAND" +>oplocks = False</B +></P +><P +>We recommend that you leave oplocks on however, as current benchmark +tests with NetBench seem to give approximately a 30% improvement in +speed with them on. This is on average however, and the actual +improvement seen can be orders of magnitude greater, depending on +what the client redirector is doing.</P +><P +>Previous to Samba 1.9.18 there was a 'fake oplocks' option. This +option has been left in the code for backwards compatibility reasons +but it's use is now deprecated. A short summary of what the old +code did follows.</P +></DIV +><DIV +CLASS="SECT2" +><HR><H2 +CLASS="SECT2" +><A +NAME="AEN19" +>Level2 Oplocks</A +></H2 +><P +>With Samba 2.0.5 a new capability - level2 (read only) oplocks is +supported (although the option is off by default - see the smb.conf +man page for details). Turning on level2 oplocks (on a share-by-share basis) +by setting the parameter :</P +><P +><B +CLASS="COMMAND" +>level2 oplocks = true</B +></P +><P +>should speed concurrent access to files that are not commonly written +to, such as application serving shares (ie. shares that contain common +.EXE files - such as a Microsoft Office share) as it allows clients to +read-ahread cache copies of these files.</P +></DIV +><DIV +CLASS="SECT2" +><HR><H2 +CLASS="SECT2" +><A +NAME="AEN25" +>Old 'fake oplocks' option - deprecated</A +></H2 +><P +>Samba can also fake oplocks, by granting a oplock whenever a client +asks for one. This is controlled using the smb.conf option "fake +oplocks". If you set "fake oplocks = yes" then you are telling the +client that it may agressively cache the file data for all opens.</P +><P +>Enabling 'fake oplocks' on all read-only shares or shares that you know +will only be accessed from one client at a time you will see a big +performance improvement on many operations. If you enable this option +on shares where multiple clients may be accessing the files read-write +at the same time you can get data corruption.</P +></DIV +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN29" +>Socket options</A +></H1 +><P +>There are a number of socket options that can greatly affect the +performance of a TCP based server like Samba.</P +><P +>The socket options that Samba uses are settable both on the command +line with the -O option, or in the smb.conf file.</P +><P +>The "socket options" section of the smb.conf manual page describes how +to set these and gives recommendations.</P +><P +>Getting the socket options right can make a big difference to your +performance, but getting them wrong can degrade it by just as +much. The correct settings are very dependent on your local network.</P +><P +>The socket option TCP_NODELAY is the one that seems to make the +biggest single difference for most networks. Many people report that +adding "socket options = TCP_NODELAY" doubles the read performance of +a Samba drive. The best explanation I have seen for this is that the +Microsoft TCP/IP stack is slow in sending tcp ACKs.</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN36" +>Read size</A +></H1 +><P +>The option "read size" affects the overlap of disk reads/writes with +network reads/writes. If the amount of data being transferred in +several of the SMB commands (currently SMBwrite, SMBwriteX and +SMBreadbraw) is larger than this value then the server begins writing +the data before it has received the whole packet from the network, or +in the case of SMBreadbraw, it begins writing to the network before +all the data has been read from disk.</P +><P +>This overlapping works best when the speeds of disk and network access +are similar, having very little effect when the speed of one is much +greater than the other.</P +><P +>The default value is 16384, but very little experimentation has been +done yet to determine the optimal value, and it is likely that the best +value will vary greatly between systems anyway. A value over 65536 is +pointless and will cause you to allocate memory unnecessarily.</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN41" +>Max xmit</A +></H1 +><P +>At startup the client and server negotiate a "maximum transmit" size, +which limits the size of nearly all SMB commands. You can set the +maximum size that Samba will negotiate using the "max xmit = " option +in smb.conf. Note that this is the maximum size of SMB request that +Samba will accept, but not the maximum size that the *client* will accept. +The client maximum receive size is sent to Samba by the client and Samba +honours this limit.</P +><P +>It defaults to 65536 bytes (the maximum), but it is possible that some +clients may perform better with a smaller transmit unit. Trying values +of less than 2048 is likely to cause severe problems.</P +><P +>In most cases the default is the best option.</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN46" +>Locking</A +></H1 +><P +>By default Samba does not implement strict locking on each read/write +call (although it did in previous versions). If you enable strict +locking (using "strict locking = yes") then you may find that you +suffer a severe performance hit on some systems.</P +><P +>The performance hit will probably be greater on NFS mounted +filesystems, but could be quite high even on local disks.</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN50" +>Share modes</A +></H1 +><P +>Some people find that opening files is very slow. This is often +because of the "share modes" code needed to fully implement the dos +share modes stuff. You can disable this code using "share modes = +no". This will gain you a lot in opening and closing files but will +mean that (in some cases) the system won't force a second user of a +file to open the file read-only if the first has it open +read-write. For many applications that do their own locking this +doesn't matter, but for some it may. Most Windows applications +depend heavily on "share modes" working correctly and it is +recommended that the Samba share mode support be left at the +default of "on".</P +><P +>The share mode code in Samba has been re-written in the 1.9.17 +release following tests with the Ziff-Davis NetBench PC Benchmarking +tool. It is now believed that Samba 1.9.17 implements share modes +similarly to Windows NT.</P +><P +>NOTE: In the most recent versions of Samba there is an option to use +shared memory via mmap() to implement the share modes. This makes +things much faster. See the Makefile for how to enable this.</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN55" +>Log level</A +></H1 +><P +>If you set the log level (also known as "debug level") higher than 2 +then you may suffer a large drop in performance. This is because the +server flushes the log file after each operation, which can be very +expensive. </P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN58" +>Wide lines</A +></H1 +><P +>The "wide links" option is now enabled by default, but if you disable +it (for better security) then you may suffer a performance hit in +resolving filenames. The performance loss is lessened if you have +"getwd cache = yes", which is now the default.</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN61" +>Read raw</A +></H1 +><P +>The "read raw" operation is designed to be an optimised, low-latency +file read operation. A server may choose to not support it, +however. and Samba makes support for "read raw" optional, with it +being enabled by default.</P +><P +>In some cases clients don't handle "read raw" very well and actually +get lower performance using it than they get using the conventional +read operations. </P +><P +>So you might like to try "read raw = no" and see what happens on your +network. It might lower, raise or not affect your performance. Only +testing can really tell.</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN66" +>Write raw</A +></H1 +><P +>The "write raw" operation is designed to be an optimised, low-latency +file write operation. A server may choose to not support it, +however. and Samba makes support for "write raw" optional, with it +being enabled by default.</P +><P +>Some machines may find "write raw" slower than normal write, in which +case you may wish to change this option.</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN70" +>Read prediction</A +></H1 +><P +>Samba can do read prediction on some of the SMB commands. Read +prediction means that Samba reads some extra data on the last file it +read while waiting for the next SMB command to arrive. It can then +respond more quickly when the next read request arrives.</P +><P +>This is disabled by default. You can enable it by using "read +prediction = yes".</P +><P +>Note that read prediction is only used on files that were opened read +only.</P +><P +>Read prediction should particularly help for those silly clients (such +as "Write" under NT) which do lots of very small reads on a file.</P +><P +>Samba will not read ahead more data than the amount specified in the +"read size" option. It always reads ahead on 1k block boundaries.</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN77" +>Memory mapping</A +></H1 +><P +>Samba supports reading files via memory mapping them. One some +machines this can give a large boost to performance, on others it +makes not difference at all, and on some it may reduce performance.</P +><P +>To enable you you have to recompile Samba with the -DUSE_MMAP option +on the FLAGS line of the Makefile.</P +><P +>Note that memory mapping is only used on files opened read only, and +is not used by the "read raw" operation. Thus you may find memory +mapping is more effective if you disable "read raw" using "read raw = +no".</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN82" +>Slow Clients</A +></H1 +><P +>One person has reported that setting the protocol to COREPLUS rather +than LANMAN2 gave a dramatic speed improvement (from 10k/s to 150k/s).</P +><P +>I suspect that his PC's (386sx16 based) were asking for more data than +they could chew. I suspect a similar speed could be had by setting +"read raw = no" and "max xmit = 2048", instead of changing the +protocol. Lowering the "read size" might also help.</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN86" +>Slow Logins</A +></H1 +><P +>Slow logins are almost always due to the password checking time. Using +the lowest practical "password level" will improve things a lot. You +could also enable the "UFC crypt" option in the Makefile.</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN89" +>Client tuning</A +></H1 +><P +>Often a speed problem can be traced to the client. The client (for +example Windows for Workgroups) can often be tuned for better TCP +performance.</P +><P +>See your client docs for details. In particular, I have heard rumours +that the WfWg options TCPWINDOWSIZE and TCPSEGMENTSIZE can have a +large impact on performance.</P +><P +>Also note that some people have found that setting DefaultRcvWindow in +the [MSTCP] section of the SYSTEM.INI file under WfWg to 3072 gives a +big improvement. I don't know why.</P +><P +>My own experience wth DefaultRcvWindow is that I get much better +performance with a large value (16384 or larger). Other people have +reported that anything over 3072 slows things down enourmously. One +person even reported a speed drop of a factor of 30 when he went from +3072 to 8192. I don't know why.</P +><P +>It probably depends a lot on your hardware, and the type of unix box +you have at the other end of the link.</P +><P +>Paul Cochrane has done some testing on client side tuning and come +to the following conclusions:</P +><P +>Install the W2setup.exe file from www.microsoft.com. This is an +update for the winsock stack and utilities which improve performance.</P +><P +>Configure the win95 TCPIP registry settings to give better +perfomance. I use a program called MTUSPEED.exe which I got off the +net. There are various other utilities of this type freely available. +The setting which give the best performance for me are:</P +><P +></P +><OL +TYPE="1" +><LI +><P +>MaxMTU Remove</P +></LI +><LI +><P +>RWIN Remove</P +></LI +><LI +><P +>MTUAutoDiscover Disable</P +></LI +><LI +><P +>MTUBlackHoleDetect Disable</P +></LI +><LI +><P +>Time To Live Enabled</P +></LI +><LI +><P +>Time To Live - HOPS 32</P +></LI +><LI +><P +>NDI Cache Size 0</P +></LI +></OL +><P +>I tried virtually all of the items mentioned in the document and +the only one which made a difference to me was the socket options. It +turned out I was better off without any!!!!!</P +><P +>In terms of overall speed of transfer, between various win95 clients +and a DX2-66 20MB server with a crappy NE2000 compatible and old IDE +drive (Kernel 2.0.30). The transfer rate was reasonable for 10 baseT.</P +><P +>FIXME +The figures are: Put Get +P166 client 3Com card: 420-440kB/s 500-520kB/s +P100 client 3Com card: 390-410kB/s 490-510kB/s +DX4-75 client NE2000: 370-380kB/s 330-350kB/s</P +><P +>I based these test on transfer two files a 4.5MB text file and a 15MB +textfile. The results arn't bad considering the hardware Samba is +running on. It's a crap machine!!!!</P +><P +>The updates mentioned in 1 and 2 brought up the transfer rates from +just over 100kB/s in some clients.</P +><P +>A new client is a P333 connected via a 100MB/s card and hub. The +transfer rates from this were good: 450-500kB/s on put and 600+kB/s +on get.</P +><P +>Looking at standard FTP throughput, Samba is a bit slower (100kB/s +upwards). I suppose there is more going on in the samba protocol, but +if it could get up to the rate of FTP the perfomance would be quite +staggering.</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN121" +>My Results</A +></H1 +><P +>Some people want to see real numbers in a document like this, so here +they are. I have a 486sx33 client running WfWg 3.11 with the 3.11b +tcp/ip stack. It has a slow IDE drive and 20Mb of ram. It has a SMC +Elite-16 ISA bus ethernet card. The only WfWg tuning I've done is to +set DefaultRcvWindow in the [MSTCP] section of system.ini to 16384. My +server is a 486dx3-66 running Linux. It also has 20Mb of ram and a SMC +Elite-16 card. You can see my server config in the examples/tridge/ +subdirectory of the distribution.</P +><P +>I get 490k/s on reading a 8Mb file with copy. +I get 441k/s writing the same file to the samba server.</P +><P +>Of course, there's a lot more to benchmarks than 2 raw throughput +figures, but it gives you a ballpark figure.</P +><P +>I've also tested Win95 and WinNT, and found WinNT gave me the best +speed as a samba client. The fastest client of all (for me) is +smbclient running on another linux box. Maybe I'll add those results +here someday ...</P +></DIV +></DIV +></BODY +></HTML +>
\ No newline at end of file diff --git a/docs/htmldocs/security_level.html b/docs/htmldocs/security_level.html new file mode 100644 index 0000000000..e26e1ea78b --- /dev/null +++ b/docs/htmldocs/security_level.html @@ -0,0 +1,169 @@ +<HTML +><HEAD +><TITLE +>Security levels</TITLE +><META +NAME="GENERATOR" +CONTENT="Modular DocBook HTML Stylesheet Version 1.57"></HEAD +><BODY +CLASS="ARTICLE" +BGCOLOR="#FFFFFF" +TEXT="#000000" +LINK="#0000FF" +VLINK="#840084" +ALINK="#0000FF" +><DIV +CLASS="ARTICLE" +><DIV +CLASS="TITLEPAGE" +><H1 +CLASS="TITLE" +><A +NAME="SECURITY_LEVELS" +>Security levels</A +></H1 +><HR></DIV +><DIV +CLASS="SECT1" +><H1 +CLASS="SECT1" +><A +NAME="AEN3" +>Introduction</A +></H1 +><P +>Samba supports the following options to the global smb.conf parameter</P +><P +><PRE +CLASS="PROGRAMLISTING" +>[global] +<A +HREF="smb.conf.5.html#SECURITY" +TARGET="_top" +><TT +CLASS="PARAMETER" +><I +>security</I +></TT +></A +> = [share|user(default)|domain|ads]</PRE +></P +><P +>Please refer to the smb.conf man page for usage information and to the document +<A +HREF="DOMAIN_MEMBER.html" +TARGET="_top" +>DOMAIN_MEMBER.html</A +> for further background details +on domain mode security. The Windows 2000 Kerberos domain security model +(security = ads) is described in the <A +HREF="ADS-HOWTO.html" +TARGET="_top" +>ADS-HOWTO.html</A +>.</P +><P +>Of the above, "security = server" means that Samba reports to clients that +it is running in "user mode" but actually passes off all authentication +requests to another "user mode" server. This requires an additional +parameter "password server =" that points to the real authentication server. +That real authentication server can be another Samba server or can be a +Windows NT server, the later natively capable of encrypted password support.</P +></DIV +><DIV +CLASS="SECT1" +><HR><H1 +CLASS="SECT1" +><A +NAME="AEN14" +>More complete description of security levels</A +></H1 +><P +>A SMB server tells the client at startup what "security level" it is +running. There are two options "share level" and "user level". Which +of these two the client receives affects the way the client then tries +to authenticate itself. It does not directly affect (to any great +extent) the way the Samba server does security. I know this is +strange, but it fits in with the client/server approach of SMB. In SMB +everything is initiated and controlled by the client, and the server +can only tell the client what is available and whether an action is +allowed. </P +><P +>I'll describe user level security first, as its simpler. In user level +security the client will send a "session setup" command directly after +the protocol negotiation. This contains a username and password. The +server can either accept or reject that username/password +combination. Note that at this stage the server has no idea what +share the client will eventually try to connect to, so it can't base +the "accept/reject" on anything other than:</P +><P +></P +><OL +TYPE="1" +><LI +><P +>the username/password</P +></LI +><LI +><P +>the machine that the client is coming from</P +></LI +></OL +><P +>If the server accepts the username/password then the client expects to +be able to mount any share (using a "tree connection") without +specifying a password. It expects that all access rights will be as +the username/password specified in the "session setup". </P +><P +>It is also possible for a client to send multiple "session setup" +requests. When the server responds it gives the client a "uid" to use +as an authentication tag for that username/password. The client can +maintain multiple authentication contexts in this way (WinDD is an +example of an application that does this)</P +><P +>Ok, now for share level security. In share level security the client +authenticates itself separately for each share. It will send a +password along with each "tree connection" (share mount). It does not +explicitly send a username with this operation. The client is +expecting a password to be associated with each share, independent of +the user. This means that samba has to work out what username the +client probably wants to use. It is never explicitly sent the +username. Some commercial SMB servers such as NT actually associate +passwords directly with shares in share level security, but samba +always uses the unix authentication scheme where it is a +username/password that is authenticated, not a "share/password".</P +><P +>Many clients send a "session setup" even if the server is in share +level security. They normally send a valid username but no +password. Samba records this username in a list of "possible +usernames". When the client then does a "tree connection" it also adds +to this list the name of the share they try to connect to (useful for +home directories) and any users listed in the "user =" smb.conf +line. The password is then checked in turn against these "possible +usernames". If a match is found then the client is authenticated as +that user.</P +><P +>Finally "server level" security. In server level security the samba +server reports to the client that it is in user level security. The +client then does a "session setup" as described earlier. The samba +server takes the username/password that the client sends and attempts +to login to the "password server" by sending exactly the same +username/password that it got from the client. If that server is in +user level security and accepts the password then samba accepts the +clients connection. This allows the samba server to use another SMB +server as the "password server". </P +><P +>You should also note that at the very start of all this, where the +server tells the client what security level it is in, it also tells +the client if it supports encryption. If it does then it supplies the +client with a random "cryptkey". The client will then send all +passwords in encrypted form. You have to compile samba with encryption +enabled to support this feature, and you have to maintain a separate +smbpasswd file with SMB style encrypted passwords. It is +cryptographically impossible to translate from unix style encryption +to SMB style encryption, although there are some fairly simple management +schemes by which the two could be kept in sync.</P +></DIV +></DIV +></BODY +></HTML +>
\ No newline at end of file diff --git a/examples/VFS/Makefile.in b/examples/VFS/Makefile.in new file mode 100644 index 0000000000..3126dfa3b8 --- /dev/null +++ b/examples/VFS/Makefile.in @@ -0,0 +1,42 @@ +MAKEFILE = Makefile.vfs + +include $(MAKEFILE) + +CC = @CC@ +LIBTOOL = libtool +CFLAGS = @CFLAGS@ $(VFS_CFLAGS) +CPPFLAGS = @CPPFLAGS@ $(VFS_CPPFLAGS) +LDFLAGS = @LDFLAGS@ $(VFS_LDFLAGS) +LDSHFLAGS = -shared +srcdir = @builddir@ +FLAGS = $(CFLAGS) -Iinclude -I$(srcdir)/include -I$(srcdir)/ubiqx -I$(srcdir)/smbwrapper -I. $(CPPFLAGS) -I$(srcdir) + +# Default target + +default: $(VFS_OBJS) + +# if file doesn't exist try to create one; +# it is possible that some variables will be +# defined correctly +Makefile.vfs: + @echo -ne "VFS_OBJS\t= " > $(MAKEFILE); \ + for i in *.c; do \ + echo -n $$i" " | sed -e 's/\(.*\)\.c\(.*\)/\1\.so\2/g' >> $(MAKEFILE); \ + done; \ + echo -ne "\nVFS_CFLAGS\t= \nVFS_CPPFLAGS\t= \nVFS_LDFLAGS\t= \n" >> $(MAKEFILE) + make + +# Pattern rules + +%.so: %.lo + $(LIBTOOL) $(CC) $(LDSHFLAGS) $(LDFLAGS) -o $@ $< + +%.lo: %.c + $(LIBTOOL) $(CC) $(FLAGS) -c $< + +# Misc targets + +clean: + rm -rf .libs + rm -f core *~ *% *.bak \ + $(VFS_OBJS) $(VFS_OBJS:.so=.o) $(VFS_OBJS:.so=.lo) diff --git a/examples/VFS/block/Makefile.in b/examples/VFS/block/Makefile.in new file mode 100644 index 0000000000..3126dfa3b8 --- /dev/null +++ b/examples/VFS/block/Makefile.in @@ -0,0 +1,42 @@ +MAKEFILE = Makefile.vfs + +include $(MAKEFILE) + +CC = @CC@ +LIBTOOL = libtool +CFLAGS = @CFLAGS@ $(VFS_CFLAGS) +CPPFLAGS = @CPPFLAGS@ $(VFS_CPPFLAGS) +LDFLAGS = @LDFLAGS@ $(VFS_LDFLAGS) +LDSHFLAGS = -shared +srcdir = @builddir@ +FLAGS = $(CFLAGS) -Iinclude -I$(srcdir)/include -I$(srcdir)/ubiqx -I$(srcdir)/smbwrapper -I. $(CPPFLAGS) -I$(srcdir) + +# Default target + +default: $(VFS_OBJS) + +# if file doesn't exist try to create one; +# it is possible that some variables will be +# defined correctly +Makefile.vfs: + @echo -ne "VFS_OBJS\t= " > $(MAKEFILE); \ + for i in *.c; do \ + echo -n $$i" " | sed -e 's/\(.*\)\.c\(.*\)/\1\.so\2/g' >> $(MAKEFILE); \ + done; \ + echo -ne "\nVFS_CFLAGS\t= \nVFS_CPPFLAGS\t= \nVFS_LDFLAGS\t= \n" >> $(MAKEFILE) + make + +# Pattern rules + +%.so: %.lo + $(LIBTOOL) $(CC) $(LDSHFLAGS) $(LDFLAGS) -o $@ $< + +%.lo: %.c + $(LIBTOOL) $(CC) $(FLAGS) -c $< + +# Misc targets + +clean: + rm -rf .libs + rm -f core *~ *% *.bak \ + $(VFS_OBJS) $(VFS_OBJS:.so=.o) $(VFS_OBJS:.so=.lo) diff --git a/examples/misc/modify_samba_config.pl b/examples/misc/modify_samba_config.pl new file mode 100755 index 0000000000..eb997f9b0c --- /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 (/=/, $_); + $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/pdb/mysql/ChangeLog b/examples/pdb/mysql/ChangeLog new file mode 100644 index 0000000000..5aeeb66268 --- /dev/null +++ b/examples/pdb/mysql/ChangeLog @@ -0,0 +1,41 @@ +** This file is now deprecated, use CVS' log featues ** + +2002-06-13 Jelmer Vernooij <jelmer@samba.org> + * Converted to using SID's like samba HEAD does now + * Fixed some FIXME's + +2002-05-28 Jelmer Vernooij <jelmer@samba.org> + * Updated docs, after some testing by Vance Lankhaar + +2002-05-25 Jelmer Vernooij <jelmer@samba.org> + * Added support for dynamic debug classes + * Fixed nt/lanman password support + * Released 1.2 + +2002-05-06 Jelmer Vernooij <jelmer@samba.org> + * Added support for multiple instances of pdb_mysql + * Added identifiers + * Updated documentation + * Released 1.1 + +2002-04-27 Jelmer Vernooij <jelmer@samba.org> + * Updated documentation + * Released 1.0! + +2002-04-27 Jelmer Vernooij <jelmer@samba.org> + * Added update/add sam account support + * Released 0.4 + +2002-04-13 Jelmer Vernooij <jelmer@samba.org> + * Support for multiple instances of pdb_mysql + * Released 0.3 + +2002-04-12 Jelmer Vernooij <jelmer@samba.org> + * Now using lp_parm_string to retrieve configuration values (instead of + our configuration files) + * Updated documentation + * Released 0.2 + +2002-04-10 Jelmer Vernooij <jelmer@samba.org> + * Released 0.1 + * Initial release. Not supporting adding and updating data of users diff --git a/examples/pdb/mysql/Makefile.in b/examples/pdb/mysql/Makefile.in new file mode 100644 index 0000000000..1da6ea789e --- /dev/null +++ b/examples/pdb/mysql/Makefile.in @@ -0,0 +1,33 @@ +PDB_OBJS = pdb_mysql.so +PDB_LDFLAGS = -lmysqlclient +MAKEFILE = Makefile.pdb + +include $(MAKEFILE) + +CC = @CC@ +LIBTOOL = libtool +CFLAGS = @CFLAGS@ $(PDB_CFLAGS) +CPPFLAGS = @CPPFLAGS@ $(PDB_CPPFLAGS) +LDFLAGS = @LDFLAGS@ $(PDB_LDFLAGS) +LDSHFLAGS = -shared +srcdir = @builddir@ +FLAGS = $(CFLAGS) -Iinclude -I$(srcdir)/include -I$(srcdir)/ubiqx -I$(srcdir)/smbwrapper -I. $(CPPFLAGS) -I$(srcdir) + +# Default target + +default: $(PDB_OBJS) + +# Pattern rules + +%.so: %.lo + $(LIBTOOL) $(CC) $(LDSHFLAGS) $(LDFLAGS) -o $@ $< + +%.lo: %.c + $(LIBTOOL) $(CC) $(FLAGS) -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/mysql/README b/examples/pdb/mysql/README new file mode 100644 index 0000000000..e3cbcab8cf --- /dev/null +++ b/examples/pdb/mysql/README @@ -0,0 +1,92 @@ +PDB MySQL plugin for samba v1.1 +-- + +Building +========= +Before you can build the plugin, set the variable SAMBA_SRC in Makefile to the +path containing the samba sources. This is usually the 'source' directory in +the samba tarball or CVS. + +Next, type make, and then copy pdb_mysql.so to any location you want. I +strongly recommend installing it in $PREFIX/lib or /usr/lib/samba/ + +Configuring +============ +This plugin lacks some good documentation, but here is some short info: + +Add a the following to the 'passdb backend' variable in your smb.conf: + +passdb backend = [other-plugins] plugin:/location/to/pdb_mysql.so:identifier [other-plugins] + +The identifier can be any string you like, as long as it doesn't collide with +the identifiers of other plugins or other instances of pdb_mysql. If you +specify multiple pdb_mysql.so entries in 'passdb backend', you also need to +use different identifiers! + +Additional options can be given thru the smb.conf file in the [global] section. + +identifier:mysql host - host name, defaults to 'localhost' +identifier:mysql password +identifier:mysql user - defaults to 'samba' +identifier:mysql database - defaults to 'samba' +identifier:mysql port - defaults to 3306 +identifier:table - Name of the table containing users + +Names of the columns in this table(I've added column types those columns + should have first): +identifier:logon time column - int(9) +identifier:logoff time column - int(9) +identifier:kickoff time column - int(9) +identifier:pass last set time column - int(9) +identifier:pass can change time column - int(9) +identifier:pass must change time column - int(9) +identifier:username column - varchar(255) - unix username +identifier:domain column - varchar(255) - NT domain user is part of +identifier:nt username column - varchar(255) - NT username +identifier:fullname column - varchar(255) - Full name of user +identifier:home dir column - varchar(255) - Unix homedir path +identifier:dir drive column - varchar(2) - Directory drive path (eg: 'H:') +identifier:logon script column - varchar(255) - Batch file to run on client side when logging on +identifier:profile path column - varchar(255) - Path of profile +identifier:acct desc column - varchar(255) - Some ASCII NT user data +identifier:workstations column - varchar(255) - Workstations user can logon to (or NULL for all) +identifier:unknown string column - varchar(255) - unknown string +identifier:munged dial column - varchar(255) - ? +identifier:uid column - int(9) - Unix user ID (uid) +identifier:gid column - int(9) - Unix user group (gid) +identifier:user sid column - varchar(255) - NT user SID +identifier:group sid column - varchar(255) - NT group ID +identifier:lanman pass column - varchar(255) - encrypted lanman password +identifier:nt pass column - varchar(255) - encrypted nt passwd +identifier:plaintext pass column - varchar(255) - plaintext password +identifier:acct control column - int(9) - nt user data +identifier:unknown 3 column - int(9) - unknown +identifier:logon divs column - int(9) - ? +identifier:hours len column - int(9) - ? +identifier:unknown 5 column - int(9) - unknown +identifier:unknown 6 column - int(9) - unknown + +Eventually, you can put a colon (:) after the name of each column, which +should specify the column to update when updating the table. You can also +specify nothing behind the colon - then the data from the field will not be +updated. + +Using plaintext passwords or encrypted password +=============================================== +I strongly discourage the use of plaintext passwords, however, you can use them: + +If you would like to use plaintext passwords, set 'identifier:lanman pass column' and 'identifier:nt pass column' to 'NULL' (without the quotes) and 'identifier:plaintext pass column' to the name of the column containing the plaintext passwords. + +If you use encrypted passwords, set the 'identifier:plaintext pass column' to 'NULL' (without the quotes). This is the default. + +Getting non-column data from the table +====================================== +It is possible to have not all data in the database and making some 'constant'. + +For example, you can set 'identifier:fullname column' to : + CONCAT(First_name,' ',Sur_name) + +Or, set 'identifier:workstations column' to : + NULL + +See the MySQL documentation for more language constructs. diff --git a/examples/pdb/mysql/pdb_mysql.c b/examples/pdb/mysql/pdb_mysql.c new file mode 100644 index 0000000000..c7e9e781c3 --- /dev/null +++ b/examples/pdb/mysql/pdb_mysql.c @@ -0,0 +1,983 @@ + +/* + * MySQL password backend for samba + * Copyright (C) Jelmer Vernooij 2002 + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 675 + * Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "includes.h" +#include <mysql/mysql.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_UID_DEFAULT "uid" +#define CONFIG_GID_DEFAULT "gid" +#define CONFIG_USER_SID_DEFAULT "user_sid" +#define CONFIG_GROUP_SID_DEFAULT "group_sid" +#define CONFIG_LM_PW_DEFAULT "lm_pw" +#define CONFIG_NT_PW_DEFAULT "nt_pw" +#define CONFIG_PLAIN_PW_DEFAULT "NULL" +#define CONFIG_ACCT_CTRL_DEFAULT "acct_ctrl" +#define CONFIG_UNKNOWN_3_DEFAULT "unknown_3" +#define CONFIG_LOGON_DIVS_DEFAULT "logon_divs" +#define CONFIG_HOURS_LEN_DEFAULT "hours_len" +#define CONFIG_UNKNOWN_5_DEFAULT "unknown_5" +#define CONFIG_UNKNOWN_6_DEFAULT "unknown_6" +#define CONFIG_HOST_DEFAULT "localhost" +#define CONFIG_USER_DEFAULT "samba" +#define CONFIG_PASS_DEFAULT "" +#define CONFIG_PORT_DEFAULT "3306" +#define CONFIG_DB_DEFAULT "samba" + +static int mysqlsam_debug_level = DBGC_ALL; + +#undef DBGC_CLASS +#define DBGC_CLASS mysqlsam_debug_level + +PDB_MODULE_VERSIONING_MAGIC + +typedef struct pdb_mysql_data { + MYSQL *handle; + MYSQL_RES *pwent; + char *location; +} pdb_mysql_data; + +/* Used to construct insert and update queries */ + +typedef struct pdb_mysql_query { + char update; + TALLOC_CTX *mem_ctx; + char *part1; + char *part2; +} pdb_mysql_query; + +#define SET_DATA(data,methods) { \ + if(!methods){ \ + DEBUG(0, ("invalid methods!\n")); \ + return False; \ + } \ + data = (struct pdb_mysql_data *)methods->private_data; \ + if(!data || !(data->handle)){ \ + DEBUG(0, ("invalid handle!\n")); \ + return False; \ + } \ +} +void +pdb_mysql_int_field(struct pdb_methods *m, + struct pdb_mysql_query *q, char *name, int value) +{ + if (!name || strchr(name, '\'')) + return; /* This field shouldn't be set by us */ + + if (q->update) { + q->part1 = + talloc_asprintf_append(q->mem_ctx, q->part1, + "%s = %d,", name, value); + } else { + q->part1 = + talloc_asprintf_append(q->mem_ctx, q->part1, "%s,", name); + q->part2 = + talloc_asprintf_append(q->mem_ctx, q->part2, "%d,", value); + } +} + +static BOOL +pdb_mysql_string_field(struct pdb_methods *methods, + struct pdb_mysql_query *q, + char *name, const char *value) +{ + char *esc_value; + struct pdb_mysql_data *data; + + SET_DATA(data, methods); + + if (!name || !value || !strcmp(value, "") || strchr(name, '\'')) + return False; /* This field shouldn't be set by module */ + + esc_value = malloc(strlen(value) * 2 + 1); + mysql_real_escape_string(data->handle, esc_value, (char *) value, + strlen(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 True; +} + +static char * +config_value(pdb_mysql_data * data, char *name, char *default_value) +{ + if (lp_parm_string(NULL, data->location, name)) + return lp_parm_string(NULL, data->location, name); + + return default_value; +} + +static char * +config_value_write(pdb_mysql_data * data, char *name, char *default_value) +{ + char *v = config_value(data, name, NULL); + char *write; + + if (!v) + return default_value; + + write = strchr(v, ':'); + + /* Default to the same field as read field */ + if (!write) + return v; + + write++; + + /* If the field is 0 chars long, we shouldn't write to it */ + if (!strlen(write) || !strcmp(write, "NULL")) + return NULL; + + /* Otherwise, use the additionally specified */ + return write; +} + +static const char * +config_value_read(pdb_mysql_data * data, char *name, char *default_value) +{ + char *v = config_value(data, name, NULL); + char *write; + + if (!v) + return default_value; + + write = strchr(v, ':'); + + /* If no write is specified, there are no problems */ + if (!write) { + if (strlen(v) == 0) + return "NULL"; + return v; + } + + /* Otherwise, we have to cut the ':write_part' */ + *write = '\0'; + if (strlen(v) == 0) + return "NULL"; + + return v; +} + +/* Wrapper for atol that returns 0 if 'a' points to NULL */ +static long +xatol(char *a) +{ + long ret = 0; + + if (a != NULL) + ret = atol(a); + + return ret; +} + +static BOOL +row_to_sam_account(MYSQL_RES * r, SAM_ACCOUNT * u) +{ + MYSQL_ROW row; + pstring temp; + unsigned int num_fields; + unsigned long *lengths; + DOM_SID sid; + + num_fields = mysql_num_fields(r); + row = mysql_fetch_row(r); + if (!row) + return False; + + pdb_set_logon_time(u, xatol(row[0]), FALSE); + pdb_set_logoff_time(u, xatol(row[1]), FALSE); + pdb_set_kickoff_time(u, xatol(row[2]), FALSE); + pdb_set_pass_last_set_time(u, xatol(row[3])); + pdb_set_pass_can_change_time(u, xatol(row[4]), FALSE); + pdb_set_pass_must_change_time(u, xatol(row[5]), FALSE); + pdb_set_username(u, row[6]); + pdb_set_domain(u, row[7]); + pdb_set_nt_username(u, row[8]); + pdb_set_fullname(u, row[9]); + pdb_set_homedir(u, row[10], True); + pdb_set_dir_drive(u, row[11], True); + pdb_set_logon_script(u, row[12], True); + pdb_set_profile_path(u, row[13], True); + pdb_set_acct_desc(u, row[14]); + pdb_set_workstations(u, row[15]); + pdb_set_unknown_str(u, row[16]); + pdb_set_munged_dial(u, row[17]); + + if (row[18]) + pdb_set_uid(u, xatol(row[18])); + if (row[19]) + pdb_set_gid(u, xatol(row[19])); + + string_to_sid(&sid, row[20]); + pdb_set_user_sid(u, &sid); + string_to_sid(&sid, row[21]); + pdb_set_group_sid(u, &sid); + + if (pdb_gethexpwd(row[22], temp)) + pdb_set_lanman_passwd(u, temp); + if (pdb_gethexpwd(row[23], temp)) + pdb_set_nt_passwd(u, temp); + + /* Only use plaintext password storage when lanman and nt are + * NOT used */ + if (!row[22] || !row[23]) + pdb_set_plaintext_passwd(u, row[24]); + + pdb_set_acct_ctrl(u, xatol(row[25])); + pdb_set_unknown_3(u, xatol(row[26])); + pdb_set_logon_divs(u, xatol(row[27])); + pdb_set_hours_len(u, xatol(row[28])); + pdb_set_unknown_5(u, xatol(row[29])); + pdb_set_unknown_6(u, xatol(row[30])); + + return True; +} + +static BOOL +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 False; + } + + asprintf(&query, + "SELECT %s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s FROM %s", + config_value_read(data, "logon time column", + CONFIG_LOGON_TIME_DEFAULT), + config_value_read(data, "logoff time column", + CONFIG_LOGOFF_TIME_DEFAULT), + config_value_read(data, "kickoff time column", + CONFIG_KICKOFF_TIME_DEFAULT), + config_value_read(data, "pass last set time column", + CONFIG_PASS_LAST_SET_TIME_DEFAULT), + config_value_read(data, "pass can change time column", + CONFIG_PASS_CAN_CHANGE_TIME_DEFAULT), + config_value_read(data, "pass must change time column", + CONFIG_PASS_MUST_CHANGE_TIME_DEFAULT), + config_value_read(data, "username column", + CONFIG_USERNAME_DEFAULT), + config_value_read(data, "domain column", + CONFIG_DOMAIN_DEFAULT), + config_value_read(data, "nt username column", + CONFIG_NT_USERNAME_DEFAULT), + config_value_read(data, "fullname column", + CONFIG_FULLNAME_DEFAULT), + config_value_read(data, "home dir column", + CONFIG_HOME_DIR_DEFAULT), + config_value_read(data, "dir drive column", + CONFIG_DIR_DRIVE_DEFAULT), + config_value_read(data, "logon script column", + CONFIG_LOGON_SCRIPT_DEFAULT), + config_value_read(data, "profile path column", + CONFIG_PROFILE_PATH_DEFAULT), + config_value_read(data, "acct desc column", + CONFIG_ACCT_DESC_DEFAULT), + config_value_read(data, "workstations column", + CONFIG_WORKSTATIONS_DEFAULT), + config_value_read(data, "unknown string column", + CONFIG_UNKNOWN_STR_DEFAULT), + config_value_read(data, "munged dial column", + CONFIG_MUNGED_DIAL_DEFAULT), + config_value_read(data, "uid column", CONFIG_UID_DEFAULT), + config_value_read(data, "gid column", CONFIG_GID_DEFAULT), + config_value_read(data, "user sid column", + CONFIG_USER_SID_DEFAULT), + config_value_read(data, "group sid column", + CONFIG_GROUP_SID_DEFAULT), + config_value_read(data, "lanman pass column", + CONFIG_LM_PW_DEFAULT), + config_value_read(data, "nt pass column", + CONFIG_NT_PW_DEFAULT), + config_value_read(data, "plain pass column", + CONFIG_PLAIN_PW_DEFAULT), + config_value_read(data, "acct ctrl column", + CONFIG_ACCT_CTRL_DEFAULT), + config_value_read(data, "unknown 3 column", + CONFIG_UNKNOWN_3_DEFAULT), + config_value_read(data, "logon divs column", + CONFIG_LOGON_DIVS_DEFAULT), + config_value_read(data, "hours len column", + CONFIG_HOURS_LEN_DEFAULT), + config_value_read(data, "unknown 5 column", + CONFIG_UNKNOWN_5_DEFAULT), + config_value_read(data, "unknown 6 column", + CONFIG_UNKNOWN_6_DEFAULT), + config_value(data, "table", CONFIG_TABLE_DEFAULT) + ); + + ret = mysql_query(data->handle, query); + SAFE_FREE(query); + + if (ret) { + DEBUG(0, + ("Error executing query: %s\n", mysql_error(data->handle))); + return False; + } + + data->pwent = mysql_store_result(data->handle); + + if (data->pwent == NULL) { + DEBUG(0, + ("Error storing results: %s\n", mysql_error(data->handle))); + return False; + } + + DEBUG(5, + ("mysqlsam_setsampwent succeeded(%d results)!\n", + mysql_num_fields(data->pwent))); + + return True; +} + +/*************************************************************** + 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 BOOL +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 False; + } + + return row_to_sam_account(data->pwent, user); +} + +BOOL +mysqlsam_select_by_field(struct pdb_methods * methods, SAM_ACCOUNT * user, + const char *field, const char *sname) +{ + char *esc_sname; + char *query; + int ret; + MYSQL_RES *res; + struct pdb_mysql_data *data; + + SET_DATA(data, methods); + + esc_sname = malloc(strlen(sname) * 2 + 1); + if (!esc_sname) { + DEBUG(0, ("Not enough memory available!\n")); + return False; + } + + DEBUG(5, + ("mysqlsam_select_by_field: getting data where %s = %s(nonescaped)\n", + field, sname)); + + /* Escape sname */ + mysql_real_escape_string(data->handle, esc_sname, (char *) sname, + strlen(sname)); + + if (user == NULL) { + DEBUG(0, ("pdb_getsampwnam: SAM_ACCOUNT is NULL.\n")); + SAFE_FREE(esc_sname); + return False; + } + + asprintf(&query, + "SELECT %s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s FROM %s WHERE %s = '%s'", + config_value_read(data, "logon time column", + CONFIG_LOGON_TIME_DEFAULT), + config_value_read(data, "logoff time column", + CONFIG_LOGOFF_TIME_DEFAULT), + config_value_read(data, "kickoff time column", + CONFIG_KICKOFF_TIME_DEFAULT), + config_value_read(data, "pass last set time column", + CONFIG_PASS_LAST_SET_TIME_DEFAULT), + config_value_read(data, "pass can change time column", + CONFIG_PASS_CAN_CHANGE_TIME_DEFAULT), + config_value_read(data, "pass must change time column", + CONFIG_PASS_MUST_CHANGE_TIME_DEFAULT), + config_value_read(data, "username column", + CONFIG_USERNAME_DEFAULT), + config_value_read(data, "domain column", + CONFIG_DOMAIN_DEFAULT), + config_value_read(data, "nt username column", + CONFIG_NT_USERNAME_DEFAULT), + config_value_read(data, "fullname column", + CONFIG_FULLNAME_DEFAULT), + config_value_read(data, "home dir column", + CONFIG_HOME_DIR_DEFAULT), + config_value_read(data, "dir drive column", + CONFIG_DIR_DRIVE_DEFAULT), + config_value_read(data, "logon script column", + CONFIG_LOGON_SCRIPT_DEFAULT), + config_value_read(data, "profile path column", + CONFIG_PROFILE_PATH_DEFAULT), + config_value_read(data, "acct desc column", + CONFIG_ACCT_DESC_DEFAULT), + config_value_read(data, "workstations column", + CONFIG_WORKSTATIONS_DEFAULT), + config_value_read(data, "unknown string column", + CONFIG_UNKNOWN_STR_DEFAULT), + config_value_read(data, "munged dial column", + CONFIG_MUNGED_DIAL_DEFAULT), + config_value_read(data, "uid column", CONFIG_UID_DEFAULT), + config_value_read(data, "gid column", CONFIG_GID_DEFAULT), + config_value_read(data, "user sid column", + CONFIG_USER_SID_DEFAULT), + config_value_read(data, "group sid column", + CONFIG_GROUP_SID_DEFAULT), + config_value_read(data, "lanman pass column", + CONFIG_LM_PW_DEFAULT), + config_value_read(data, "nt pass column", + CONFIG_NT_PW_DEFAULT), + config_value_read(data, "plain pass column", + CONFIG_PLAIN_PW_DEFAULT), + config_value_read(data, "acct ctrl column", + CONFIG_ACCT_CTRL_DEFAULT), + config_value_read(data, "unknown 3 column", + CONFIG_UNKNOWN_3_DEFAULT), + config_value_read(data, "logon divs column", + CONFIG_LOGON_DIVS_DEFAULT), + config_value_read(data, "hours len column", + CONFIG_HOURS_LEN_DEFAULT), + config_value_read(data, "unknown 5 column", + CONFIG_UNKNOWN_5_DEFAULT), + config_value_read(data, "unknown 6 column", + CONFIG_UNKNOWN_6_DEFAULT), + config_value(data, "table", CONFIG_TABLE_DEFAULT), field, + esc_sname); + + SAFE_FREE(esc_sname); + + ret = mysql_query(data->handle, query); + + SAFE_FREE(query); + + if (ret) { + DEBUG(0, + ("Error while executing MySQL query: %s\n", + mysql_error(data->handle))); + return False; + } + + res = mysql_store_result(data->handle); + if (res == NULL) { + DEBUG(0, + ("Error storing results: %s\n", mysql_error(data->handle))); + return False; + } + + ret = row_to_sam_account(res, user); + mysql_free_result(res); + + return ret; +} + +/****************************************************************** + Lookup a name in the SAM database + ******************************************************************/ + +static BOOL +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 False; + } + return mysqlsam_select_by_field(methods, user, + config_value_read(data, "username column", + CONFIG_USERNAME_DEFAULT), sname); +} + + +/*************************************************************************** + Search by sid + **************************************************************************/ + +static BOOL +mysqlsam_getsampwsid(struct pdb_methods *methods, SAM_ACCOUNT * user, + const DOM_SID * sid) +{ + BOOL ret = False; + struct pdb_mysql_data *data; + fstring sid_str; + + SET_DATA(data, methods); + + sid_to_string(sid_str, sid); + + ret = + mysqlsam_select_by_field(methods, user, + config_value_read(data, "user sid column", + CONFIG_USER_SID_DEFAULT), sid_str); + + return ret; +} + +/*************************************************************************** + Delete a SAM_ACCOUNT + ****************************************************************************/ + +static BOOL +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; + + SET_DATA(data, methods); + + if (!methods) { + DEBUG(0, ("invalid methods!\n")); + return False; + } + + data = (struct pdb_mysql_data *) methods->private_data; + if (!data || !(data->handle)) { + DEBUG(0, ("invalid handle!\n")); + return False; + } + + if (!sname) { + DEBUG(0, ("invalid name specified\n")); + return False; + } + + /* Escape sname */ + esc = malloc(strlen(sname) * 2 + 1); + if (!esc) { + DEBUG(0, ("Can't allocate memory to store escaped name\n")); + return False; + } + mysql_real_escape_string(data->handle, esc, (char *) sname, + strlen(sname)); + + asprintf(&query, "DELETE FROM %s WHERE %s = '%s'", + config_value(data, "table", CONFIG_TABLE_DEFAULT), + config_value_read(data, "username column", + CONFIG_USERNAME_DEFAULT), esc); + + SAFE_FREE(esc); + + ret = mysql_query(data->handle, query); + + SAFE_FREE(query); + + if (ret) { + DEBUG(0, + ("Error while executing query: %s\n", + mysql_error(data->handle))); + return False; + } + + DEBUG(5, ("User '%s' deleted\n", sname)); + return True; +} + +static BOOL +mysqlsam_replace_sam_account(struct pdb_methods *methods, + const SAM_ACCOUNT * newpwd, char isupdate) +{ + pstring temp; + uint32 store = pdb_get_init_flag(newpwd); + struct pdb_mysql_data *data; + pdb_mysql_query query; + fstring sid_str; + + if (!methods) { + DEBUG(0, ("invalid methods!\n")); + return False; + } + + data = (struct pdb_mysql_data *) methods->private_data; + if (data == NULL || data->handle == NULL) { + DEBUG(0, ("invalid handle!\n")); + return False; + } + 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(); + query.part2 = talloc_asprintf(query.mem_ctx, "%s", ""); + if (query.update) { + query.part1 = + talloc_asprintf(query.mem_ctx, "UPDATE %s SET ", + config_value(data, "table", + CONFIG_TABLE_DEFAULT)); + } else { + query.part1 = + talloc_asprintf(query.mem_ctx, "INSERT INTO %s (", + config_value(data, "table", + CONFIG_TABLE_DEFAULT)); + } + + pdb_mysql_int_field(methods, &query, + config_value_write(data, "acct ctrl column", + CONFIG_ACCT_CTRL_DEFAULT), + pdb_get_acct_ctrl(newpwd)); + + if (store & FLAG_SAM_LOGONTIME) { + pdb_mysql_int_field(methods, &query, + config_value_write(data, + "logon time column", + CONFIG_LOGON_TIME_DEFAULT), + pdb_get_logon_time(newpwd)); + } + + if (store & FLAG_SAM_LOGOFFTIME) { + pdb_mysql_int_field(methods, &query, + config_value_write(data, + "logoff time column", + CONFIG_LOGOFF_TIME_DEFAULT), + pdb_get_logoff_time(newpwd)); + } + + if (store & FLAG_SAM_KICKOFFTIME) { + pdb_mysql_int_field(methods, &query, + config_value_write(data, + "kickoff time column", + CONFIG_KICKOFF_TIME_DEFAULT), + pdb_get_kickoff_time(newpwd)); + } + + if (store & FLAG_SAM_CANCHANGETIME) { + pdb_mysql_int_field(methods, &query, + config_value_write(data, + "pass can change time column", + CONFIG_PASS_CAN_CHANGE_TIME_DEFAULT), + pdb_get_pass_can_change_time(newpwd)); + } + + if (store & FLAG_SAM_MUSTCHANGETIME) { + pdb_mysql_int_field(methods, &query, + config_value_write(data, + "pass must change time column", + CONFIG_PASS_MUST_CHANGE_TIME_DEFAULT), + pdb_get_pass_must_change_time(newpwd)); + } + + if (pdb_get_pass_last_set_time(newpwd)) { + pdb_mysql_int_field(methods, &query, + config_value_write(data, + "pass must change time column", + CONFIG_PASS_LAST_SET_TIME_DEFAULT), + pdb_get_pass_last_set_time(newpwd)); + } + + if (pdb_get_hours_len(newpwd)) { + pdb_mysql_int_field(methods, &query, + config_value_write(data, + "hours len column", + CONFIG_HOURS_LEN_DEFAULT), + pdb_get_hours_len(newpwd)); + } + + if (pdb_get_logon_divs(newpwd)) { + pdb_mysql_int_field(methods, &query, + config_value_write(data, + "logon divs column", + CONFIG_LOGON_DIVS_DEFAULT), + pdb_get_logon_divs(newpwd)); + } + + if (store & FLAG_SAM_UID) { + pdb_mysql_int_field(methods, &query, + config_value_write(data, "uid column", + CONFIG_UID_DEFAULT), + pdb_get_uid(newpwd)); + } + + if (store & FLAG_SAM_GID) { + pdb_mysql_int_field(methods, &query, + config_value_write(data, "gid column", + CONFIG_GID_DEFAULT), + pdb_get_gid(newpwd)); + } + + pdb_mysql_string_field(methods, &query, + config_value_write(data, "user sid column", + CONFIG_USER_SID_DEFAULT), + sid_to_string(sid_str, (DOM_SID *) + pdb_get_user_sid(newpwd))); + + pdb_mysql_string_field(methods, &query, + config_value_write(data, "group sid column", + CONFIG_GROUP_SID_DEFAULT), + sid_to_string(sid_str, (DOM_SID *) + pdb_get_group_sid(newpwd))); + + pdb_mysql_string_field(methods, &query, + config_value_write(data, "username column", + CONFIG_USERNAME_DEFAULT), + pdb_get_username(newpwd)); + + pdb_mysql_string_field(methods, &query, + config_value_write(data, "domain column", + CONFIG_DOMAIN_DEFAULT), + pdb_get_domain(newpwd)); + + pdb_mysql_string_field(methods, &query, + config_value_write(data, + "nt username column", + CONFIG_NT_USERNAME_DEFAULT), + pdb_get_nt_username(newpwd)); + + pdb_mysql_string_field(methods, &query, + config_value_write(data, "fullname column", + CONFIG_FULLNAME_DEFAULT), + pdb_get_fullname(newpwd)); + + pdb_mysql_string_field(methods, &query, + config_value_write(data, + "logon script column", + CONFIG_LOGON_SCRIPT_DEFAULT), + pdb_get_logon_script(newpwd)); + + pdb_mysql_string_field(methods, &query, + config_value_write(data, + "profile path column", + CONFIG_PROFILE_PATH_DEFAULT), + pdb_get_profile_path(newpwd)); + + pdb_mysql_string_field(methods, &query, + config_value_write(data, "dir drive column", + CONFIG_DIR_DRIVE_DEFAULT), + pdb_get_dir_drive(newpwd)); + + pdb_mysql_string_field(methods, &query, + config_value_write(data, "home dir column", + CONFIG_HOME_DIR_DEFAULT), + pdb_get_homedir(newpwd)); + + pdb_mysql_string_field(methods, &query, + config_value_write(data, + "workstations column", + CONFIG_WORKSTATIONS_DEFAULT), + pdb_get_workstations(newpwd)); + + pdb_mysql_string_field(methods, &query, + config_value_write(data, + "unknown string column", + CONFIG_UNKNOWN_STR_DEFAULT), + pdb_get_workstations(newpwd)); + + pdb_sethexpwd(temp, pdb_get_lanman_passwd(newpwd), + pdb_get_acct_ctrl(newpwd)); + pdb_mysql_string_field(methods, &query, + config_value_write(data, + "lanman pass column", + CONFIG_LM_PW_DEFAULT), temp); + + pdb_sethexpwd(temp, pdb_get_nt_passwd(newpwd), + pdb_get_acct_ctrl(newpwd)); + pdb_mysql_string_field(methods, &query, + config_value_write(data, "nt pass column", + CONFIG_NT_PW_DEFAULT), temp); + + if (query.update) { + query.part1[strlen(query.part1) - 1] = '\0'; + query.part1 = + talloc_asprintf_append(query.mem_ctx, query.part1, + " WHERE %s = '%s'", + config_value_read(data, + "user sid column", + CONFIG_USER_SID_DEFAULT), + sid_to_string(sid_str, (DOM_SID *) + pdb_get_user_sid + (newpwd))); + } else { + query.part2[strlen(query.part2) - 1] = ')'; + query.part1[strlen(query.part1) - 1] = ')'; + query.part1 = + talloc_asprintf_append(query.mem_ctx, query.part1, + " VALUES (%s", query.part2); + } + + DEBUG(0, ("%s\n", query.part1)); + /* Execute the query */ + if (mysql_query(data->handle, query.part1)) { + DEBUG(0, + ("Error executing %s, %s\n", query.part1, + mysql_error(data->handle))); + return False; + } + talloc_destroy(query.mem_ctx); + return True; +} + +static BOOL +mysqlsam_add_sam_account(struct pdb_methods *methods, SAM_ACCOUNT * newpwd) +{ + return mysqlsam_replace_sam_account(methods, newpwd, 0); +} + +static BOOL +mysqlsam_update_sam_account(struct pdb_methods *methods, + SAM_ACCOUNT * newpwd) +{ + return mysqlsam_replace_sam_account(methods, newpwd, 1); +} + +NTSTATUS +pdb_init(PDB_CONTEXT * pdb_context, PDB_METHODS ** pdb_method, + 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. See README for details\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + data->location = smb_xstrdup(location); + + DEBUG(1, + ("Connecting to database server, host: %s, user: %s, password: %s, database: %s, port: %d\n", + config_value(data, "mysql host", CONFIG_HOST_DEFAULT), + config_value(data, "mysql user", CONFIG_USER_DEFAULT), + config_value(data, "mysql password", CONFIG_PASS_DEFAULT), + config_value(data, "mysql database", CONFIG_DB_DEFAULT), + xatol(config_value(data, "mysql port", CONFIG_PORT_DEFAULT)))); + + /* Do the mysql initialization */ + data->handle = mysql_init(NULL); + if (!data->handle) { + DEBUG(0, ("Failed to connect to server\n")); + return NT_STATUS_UNSUCCESSFUL; + } + /* Process correct entry in $HOME/.my.conf */ + if (!mysql_real_connect(data->handle, + config_value(data, "mysql host", CONFIG_HOST_DEFAULT), + config_value(data, "mysql user", CONFIG_USER_DEFAULT), + config_value(data, "mysql password", CONFIG_PASS_DEFAULT), + config_value(data, "mysql database", CONFIG_DB_DEFAULT), + xatol(config_value (data, "mysql port", CONFIG_PORT_DEFAULT)), + NULL, 0)) { + DEBUG(0, + ("Failed to connect to mysql database: error: %s\n", + mysql_error(data->handle))); + return NT_STATUS_UNSUCCESSFUL; + } + + DEBUG(5, ("Connected to mysql db\n")); + + return NT_STATUS_OK; +} diff --git a/examples/pdb/xml/ChangeLog b/examples/pdb/xml/ChangeLog new file mode 100644 index 0000000000..e44fa3bd30 --- /dev/null +++ b/examples/pdb/xml/ChangeLog @@ -0,0 +1,13 @@ +** This file is now deprecated - use CVS' log features ** + +2002-06-13 Jelmer Vernooij <jelmer@samba.org> + * Use SID's instead of RID's (just like samba-HEAD CVS) + * Released 1.1 + +2002-05-26 Jelmer Vernooij <jelmer@samba.org> + * Update read support (didn't support all elements yet) + * Released 1.0 + +2002-05-26 Jelmer Vernooij <jelmer@samba.org> + * Initial release + * Released 0.5 diff --git a/examples/pdb/xml/Makefile.in b/examples/pdb/xml/Makefile.in new file mode 100644 index 0000000000..87d4546972 --- /dev/null +++ b/examples/pdb/xml/Makefile.in @@ -0,0 +1,33 @@ +PDB_OBJS = pdb_xml.so +PDB_CFLAGS = `xml2-config --cflags` +PDB_LDFLAGS = `xml2-config --libs` + +include $(MAKEFILE) + +CC = @CC@ +LIBTOOL = libtool +CFLAGS = @CFLAGS@ $(PDB_CFLAGS) +CPPFLAGS = @CPPFLAGS@ $(PDB_CPPFLAGS) +LDFLAGS = @LDFLAGS@ $(PDB_LDFLAGS) +LDSHFLAGS = -shared +srcdir = @builddir@ +FLAGS = $(CFLAGS) -Iinclude -I$(srcdir)/include -I$(srcdir)/ubiqx -I$(srcdir)/smbwrapper -I. $(CPPFLAGS) -I$(srcdir) + +# Default target + +default: $(PDB_OBJS) + +# Pattern rules + +%.so: %.lo + $(LIBTOOL) $(CC) $(LDSHFLAGS) $(LDFLAGS) -o $@ $< + +%.lo: %.c + $(LIBTOOL) $(CC) $(FLAGS) -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/xml/README b/examples/pdb/xml/README new file mode 100644 index 0000000000..afb08fdb4f --- /dev/null +++ b/examples/pdb/xml/README @@ -0,0 +1,14 @@ +Readme for samba pdb xml 0.5 +-- +This module requires libxml2 to be installed. + +The usage of pdb_xml is pretty straightforward. To export data, use: + +pdbedit -e plugin:/usr/lib/samba/pdb_xml.so:filename + +(where filename is the name of the file to put the data in) +To import data, use: + +pdbedit -i plugin:/usr/lib/samba/pdb_xml.so:filename -e <current-pdb> + +Where filename is the name to read the data from and <current-pdb> to put it in. diff --git a/examples/pdb/xml/TODO b/examples/pdb/xml/TODO new file mode 100644 index 0000000000..3947bb68be --- /dev/null +++ b/examples/pdb/xml/TODO @@ -0,0 +1,6 @@ +- 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! +- Support stdin ? diff --git a/examples/pdb/xml/pdb_xml.c b/examples/pdb/xml/pdb_xml.c new file mode 100644 index 0000000000..f237a7da9d --- /dev/null +++ b/examples/pdb/xml/pdb_xml.c @@ -0,0 +1,561 @@ + +/* + * 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 '-' */ + +#define XML_URL "http://www.samba.org/ns" + +#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 + +PDB_MODULE_VERSIONING_MAGIC + +static char * iota(int a) { + static char tmp[10]; + + snprintf(tmp, 9, "%d", a); + return tmp; +} + +BOOL +parsePass(xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur, SAM_ACCOUNT * u) +{ + pstring temp; + + cur = cur->xmlChildrenNode; + while (cur != NULL) { + if (strcmp(cur->name, "crypt")) + DEBUG(0, ("Unknown element %s\n", cur->name)); + else { + if (!strcmp(xmlGetProp(cur, "type"), "nt") + && + pdb_gethexpwd(xmlNodeListGetString + (doc, cur->xmlChildrenNode, 1), temp)) + pdb_set_nt_passwd(u, temp); + else if (!strcmp(xmlGetProp(cur, "type"), "lanman") + && + pdb_gethexpwd(xmlNodeListGetString + (doc, cur->xmlChildrenNode, 1), temp)) + pdb_set_lanman_passwd(u, temp); + else + DEBUG(0, + ("Unknown crypt type: %s\n", + xmlGetProp(cur, "type"))); + } + cur = cur->next; + } + return True; +} + +BOOL +parseUser(xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur, SAM_ACCOUNT * u) +{ + char *tmp; + DOM_SID sid; + + tmp = xmlGetProp(cur, "sid"); + if (tmp){ + string_to_sid(&sid, tmp); + pdb_set_user_sid(u, &sid); + } + tmp = xmlGetProp(cur, "uid"); + if (tmp) + pdb_set_uid(u, atol(tmp)); + pdb_set_username(u, xmlGetProp(cur, "name")); + /* We don't care what the top level element name is */ + cur = cur->xmlChildrenNode; + while (cur != NULL) { + if ((!strcmp(cur->name, "group")) && (cur->ns == ns)) { + tmp = xmlGetProp(cur, "gid"); + if (tmp) + pdb_set_gid(u, atol(tmp)); + tmp = xmlGetProp(cur, "sid"); + if (tmp){ + string_to_sid(&sid, tmp); + pdb_set_group_sid(u, &sid); + } + } + + else if ((!strcmp(cur->name, "domain")) && (cur->ns == ns)) + pdb_set_domain(u, + xmlNodeListGetString(doc, cur->xmlChildrenNode, + 1)); + + else if (!strcmp(cur->name, "fullname") && cur->ns == ns) + pdb_set_fullname(u, + xmlNodeListGetString(doc, + cur->xmlChildrenNode, + 1)); + + else if (!strcmp(cur->name, "nt_username") && cur->ns == ns) + pdb_set_nt_username(u, + xmlNodeListGetString(doc, + cur->xmlChildrenNode, + 1)); + + else if (!strcmp(cur->name, "logon_script") && cur->ns == ns) + pdb_set_logon_script(u, + xmlNodeListGetString(doc, + cur->xmlChildrenNode, + 1), True); + + else if (!strcmp(cur->name, "profile_path") && cur->ns == ns) + pdb_set_profile_path(u, + xmlNodeListGetString(doc, + cur->xmlChildrenNode, + 1), True); + + else if (!strcmp(cur->name, "logon_time") && cur->ns == ns) + pdb_set_logon_time(u, + atol(xmlNodeListGetString + (doc, cur->xmlChildrenNode, 1)), True); + + else if (!strcmp(cur->name, "logoff_time") && cur->ns == ns) + pdb_set_logoff_time(u, + atol(xmlNodeListGetString + (doc, cur->xmlChildrenNode, 1)), + True); + + else if (!strcmp(cur->name, "kickoff_time") && cur->ns == ns) + pdb_set_kickoff_time(u, + atol(xmlNodeListGetString + (doc, cur->xmlChildrenNode, 1)), + True); + + else if (!strcmp(cur->name, "logon_divs") && cur->ns == ns) + pdb_set_logon_divs(u, + atol(xmlNodeListGetString + (doc, cur->xmlChildrenNode, 1))); + + else if (!strcmp(cur->name, "hours_len") && cur->ns == ns) + pdb_set_hours_len(u, + atol(xmlNodeListGetString + (doc, cur->xmlChildrenNode, 1))); + + else if (!strcmp(cur->name, "unknown_3") && cur->ns == ns) + pdb_set_unknown_3(u, + atol(xmlNodeListGetString + (doc, cur->xmlChildrenNode, 1))); + + else if (!strcmp(cur->name, "unknown_5") && cur->ns == ns) + pdb_set_unknown_5(u, + atol(xmlNodeListGetString + (doc, cur->xmlChildrenNode, 1))); + + else if (!strcmp(cur->name, "unknown_6") && cur->ns == ns) + pdb_set_unknown_6(u, + atol(xmlNodeListGetString + (doc, cur->xmlChildrenNode, 1))); + + else if (!strcmp(cur->name, "homedir") && cur->ns == ns) + pdb_set_homedir(u, + xmlNodeListGetString(doc, cur->xmlChildrenNode, + 1), True); + + else if (!strcmp(cur->name, "unknown_str") && cur->ns == ns) + pdb_set_unknown_str(u, + xmlNodeListGetString(doc, + cur->xmlChildrenNode, + 1)); + + else if (!strcmp(cur->name, "dir_drive") && cur->ns == ns) + pdb_set_dir_drive(u, + xmlNodeListGetString(doc, + cur->xmlChildrenNode, + 1), True); + + else if (!strcmp(cur->name, "munged_dial") && cur->ns == ns) + pdb_set_munged_dial(u, + xmlNodeListGetString(doc, + cur->xmlChildrenNode, + 1)); + + else if (!strcmp(cur->name, "acct_desc") && cur->ns == ns) + pdb_set_acct_desc(u, + xmlNodeListGetString(doc, + cur->xmlChildrenNode, + 1)); + + else if (!strcmp(cur->name, "acct_ctrl") && cur->ns == ns) + pdb_set_acct_ctrl(u, + atol(xmlNodeListGetString + (doc, cur->xmlChildrenNode, 1))); + + else if (!strcmp(cur->name, "workstations") && cur->ns == ns) + pdb_set_workstations(u, + xmlNodeListGetString(doc, + cur->xmlChildrenNode, + 1)); + + 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)); + tmp = xmlGetProp(cur, "must_change"); + if (tmp) + pdb_set_pass_must_change_time(u, atol(tmp), True); + tmp = xmlGetProp(cur, "can_change"); + if (tmp) + pdb_set_pass_can_change_time(u, atol(tmp), True); + parsePass(doc, ns, cur, u); + } + + else + DEBUG(0, ("Unknown element %s\n", cur->name)); + cur = cur->next; + } + + return True; +} + +typedef struct pdb_xml { + char *location; + char written; + xmlDocPtr doc; + xmlNodePtr users; + xmlNodePtr pwent; + xmlNsPtr ns; +} pdb_xml; + +xmlNodePtr +parseSambaXMLFile(struct pdb_xml *data) +{ + xmlNodePtr cur; + + data->doc = xmlParseFile(data->location); + if (data->doc == NULL) + return NULL; + + cur = xmlDocGetRootElement(data->doc); + if (!cur) { + DEBUG(0, ("empty document\n")); + xmlFreeDoc(data->doc); + return NULL; + } + data->ns = xmlSearchNsByHref(data->doc, cur, XML_URL); + if (!data->ns) { + DEBUG(0, + ("document of the wrong type, samba user namespace not found\n")); + xmlFreeDoc(data->doc); + return NULL; + } + if (strcmp(cur->name, "samba")) { + DEBUG(0, ("document of the wrong type, root node != samba")); + xmlFreeDoc(data->doc); + return NULL; + } + + cur = cur->xmlChildrenNode; + while (cur && xmlIsBlankNode(cur)) { + cur = cur->next; + } + if (!cur) + return NULL; + if ((strcmp(cur->name, "users")) || (cur->ns != data->ns)) { + DEBUG(0, ("document of the wrong type, was '%s', users expected", + cur->name)); + DEBUG(0, ("xmlDocDump follows\n")); + xmlDocDump(stderr, data->doc); + DEBUG(0, ("xmlDocDump finished\n")); + xmlFreeDoc(data->doc); + return NULL; + } + data->users = cur; + cur = cur->xmlChildrenNode; + return cur; +} + +static BOOL +xmlsam_setsampwent(struct pdb_methods *methods, BOOL update) +{ + pdb_xml *data; + + if (!methods) { + DEBUG(0, ("Invalid methods\n")); + return False; + } + data = (pdb_xml *) methods->private_data; + if (!data) { + DEBUG(0, ("Invalid pdb_xml_data\n")); + return False; + } + data->pwent = parseSambaXMLFile(data); + if (!data->pwent) + return False; + return True; +} + +/*************************************************************** + 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 BOOL +xmlsam_getsampwent(struct pdb_methods *methods, SAM_ACCOUNT * user) +{ + pdb_xml *data; + + if (!methods) { + DEBUG(0, ("Invalid methods\n")); + return False; + } + data = (pdb_xml *) methods->private_data; + + if (!data) { + DEBUG(0, ("Invalid pdb_xml_data\n")); + return False; + } + + 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 True; + } + data->pwent = data->pwent->next; + } + return False; +} + +/*************************************************************************** + Adds an existing SAM_ACCOUNT + ****************************************************************************/ + +static BOOL +xmlsam_add_sam_account(struct pdb_methods *methods, SAM_ACCOUNT * u) +{ + pstring temp; + fstring sid_str; + xmlNodePtr cur, user, pass, root; + pdb_xml *data; + uint32 store = pdb_get_init_flag(u); + + DEBUG(10, ("xmlsam_add_sam_account called!\n")); + + if (!methods) { + DEBUG(0, ("Invalid methods\n")); + return False; + } + + data = (pdb_xml *) methods->private_data; + if (!data) { + DEBUG(0, ("Invalid pdb_xml_data\n")); + return False; + } + + /* 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 (store & FLAG_SAM_UID) + xmlNewProp(user, "uid", iota(pdb_get_uid(u))); + + if (pdb_get_username(u) && strcmp(pdb_get_username(u), "")) + xmlNewProp(user, "name", pdb_get_username(u)); + + cur = xmlNewChild(user, data->ns, "group", NULL); + + xmlNewProp(cur, "sid", + sid_to_string(sid_str, pdb_get_group_sid(u))); + if (store & FLAG_SAM_GID) + xmlNewProp(cur, "gid", iota(pdb_get_gid(u))); + + if (store & FLAG_SAM_LOGONTIME) + xmlNewChild(user, data->ns, "login_time", + iota(pdb_get_logon_time(u))); + + if (store & FLAG_SAM_LOGOFFTIME) + xmlNewChild(user, data->ns, "logoff_time", + iota(pdb_get_logoff_time(u))); + + if (store & FLAG_SAM_KICKOFFTIME) + 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 (store & FLAG_SAM_CANCHANGETIME) + xmlNewProp(pass, "can_change", + iota(pdb_get_pass_can_change_time(u))); + + if (store & FLAG_SAM_MUSTCHANGETIME) + xmlNewProp(pass, "must_change", + iota(pdb_get_pass_must_change_time(u))); + + + if (pdb_get_lanman_passwd(u)) { + pdb_sethexpwd(temp, pdb_get_lanman_passwd(u), + pdb_get_acct_ctrl(u)); + cur = xmlNewChild(pass, data->ns, "crypt", temp); + xmlNewProp(cur, "type", "lanman"); + } + + if (pdb_get_nt_passwd(u)) { + pdb_sethexpwd(temp, pdb_get_nt_passwd(u), pdb_get_acct_ctrl(u)); + cur = xmlNewChild(pass, data->ns, "crypt", temp); + xmlNewProp(cur, "type", "nt"); + } + + xmlNewChild(user, data->ns, "acct_ctrl", iota(pdb_get_acct_ctrl(u))); + xmlNewChild(user, data->ns, "unknown_3", iota(pdb_get_unknown3(u))); + + if (pdb_get_logon_divs(u)) + xmlNewChild(user, data->ns, "logon_divs", + iota(pdb_get_logon_divs(u))); + + if (pdb_get_hours_len(u)) + xmlNewChild(user, data->ns, "hours_len", + iota(pdb_get_hours_len(u))); + + xmlNewChild(user, data->ns, "unknown_5", iota(pdb_get_unknown5(u))); + xmlNewChild(user, data->ns, "unknown_6", iota(pdb_get_unknown6(u))); + xmlSaveFile(data->location, data->doc); + + return True; +} + +NTSTATUS +pdb_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; + + data = talloc(pdb_context->mem_ctx, sizeof(pdb_xml)); + data->location = + (location ? talloc_strdup(pdb_context->mem_ctx, location) : "-"); + data->pwent = NULL; + data->written = 0; + (*pdb_method)->private_data = data; + + LIBXML_TEST_VERSION xmlKeepBlanksDefault(0); + + return NT_STATUS_OK; +} diff --git a/source3/include/adt_tree.h b/source3/include/adt_tree.h new file mode 100644 index 0000000000..b1bf7ad85d --- /dev/null +++ b/source3/include/adt_tree.h @@ -0,0 +1,38 @@ +/* + * Unix SMB/CIFS implementation. + * Generic Abstract Data Types + * Copyright (C) Gerald Carter 2002. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef ADT_TREE_H +#define ADT_TREE_H + +typedef struct _tree_node { + struct _tree_node *parent; + struct _tree_node **children; + int num_children; + char *key; + void *data_p; +} TREE_NODE; + +typedef struct _tree_root { + TREE_NODE *root; + int (*compare)(void* x, void *y); + void (*free)(void *p); +} SORTED_TREE; + +#endif diff --git a/source3/include/ntioctl.h b/source3/include/ntioctl.h new file mode 100644 index 0000000000..4749842ddc --- /dev/null +++ b/source3/include/ntioctl.h @@ -0,0 +1,26 @@ +/* + 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 +*/ + +#define NTIOCTL_SET_SPARSE 0x900c4 diff --git a/source3/lib/adt_tree.c b/source3/lib/adt_tree.c new file mode 100644 index 0000000000..2c18bb1198 --- /dev/null +++ b/source3/lib/adt_tree.c @@ -0,0 +1,464 @@ +/* + * Unix SMB/CIFS implementation. + * Generic Abstract Data Types + * Copyright (C) Gerald Carter 2002. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "includes.h" + + +/************************************************************************** + Initialize the tree's root. The cmp_fn is a callback function used + for comparision of two children + *************************************************************************/ + +static BOOL trim_tree_keypath( char *path, char **base, char **new_path ) +{ + char *p; + + *new_path = *base = NULL; + + if ( !path ) + return False; + + *base = path; + + p = strchr( path, '/' ); + + if ( p ) { + *p = '\0'; + *new_path = p+1; + } + + return True; +} + + +/************************************************************************** + Initialize the tree's root. The cmp_fn is a callback function used + for comparision of two children + *************************************************************************/ + +SORTED_TREE* sorted_tree_init( void *data_p, + int (cmp_fn)(void*, void*), + void (free_fn)(void*) ) +{ + SORTED_TREE *tree = NULL; + + if ( !(tree = (SORTED_TREE*)malloc( sizeof(SORTED_TREE) )) ) + return NULL; + + ZERO_STRUCTP( tree ); + + tree->compare = cmp_fn; + tree->free = free_fn; + + if ( !(tree->root = (TREE_NODE*)malloc( sizeof(TREE_NODE) )) ) { + SAFE_FREE( tree ); + return NULL; + } + + ZERO_STRUCTP( tree->root ); + tree->root->data_p = data_p; + + return tree; +} + + +/************************************************************************** + Delete a tree and free all allocated memory + *************************************************************************/ + +static void sorted_tree_destroy_children( TREE_NODE *root ) +{ + int i; + + if ( !root ) + return; + + for ( i=0; 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 ) + tree->free( tree->root ); + + SAFE_FREE( tree ); +} + +/************************************************************************** + Find the next child given a key string + *************************************************************************/ + +static TREE_NODE* sorted_tree_birth_child( TREE_NODE *node, char* key ) +{ + TREE_NODE *infant = NULL; + TREE_NODE **siblings; + int i; + + if ( !(infant = (TREE_NODE*)malloc( sizeof(TREE_NODE) )) ) + return NULL; + + ZERO_STRUCTP( infant ); + + infant->key = strdup( key ); + infant->parent = node; + + siblings = Realloc( node->children, sizeof(TREE_NODE*)*(node->num_children+1) ); + + if ( siblings ) + node->children = siblings; + + node->num_children++; + + /* first child */ + + if ( node->num_children == 1 ) { + DEBUG(11,("sorted_tree_birth_child: First child of node [%s]! [%s]\n", + node->key ? node->key : "NULL", infant->key )); + node->children[0] = infant; + } + else + { + /* + * multiple siblings .... (at least 2 children) + * + * work from the end of the list forward + * The last child is not set at this point + * Insert the new infanct in ascending order + * from left to right + */ + + for ( i = node->num_children-1; i>=1; i-- ) + { + DEBUG(11,("sorted_tree_birth_child: Looking for crib; infant -> [%s], child -> [%s]\n", + infant->key, node->children[i-1]->key)); + + /* the strings should never match assuming that we + have called sorted_tree_find_child() first */ + + if ( StrCaseCmp( infant->key, node->children[i-1]->key ) > 0 ) { + DEBUG(11,("sorted_tree_birth_child: storing infant in i == [%d]\n", + i)); + node->children[i] = infant; + break; + } + + /* bump everything towards the end on slot */ + + node->children[i] = node->children[i-1]; + } + + DEBUG(11,("sorted_tree_birth_child: Exiting loop (i == [%d])\n", i )); + + /* if we haven't found the correct slot yet, the child + will be first in the list */ + + if ( i == 0 ) + node->children[0] = infant; + } + + return infant; +} + +/************************************************************************** + Find the next child given a key string + *************************************************************************/ + +static TREE_NODE* sorted_tree_find_child( TREE_NODE *node, char* key ) +{ + TREE_NODE *next = NULL; + int i, result; + + if ( !node ) { + DEBUG(0,("sorted_tree_find_child: NULL node passed into function!\n")); + return NULL; + } + + if ( !key ) { + DEBUG(0,("sorted_tree_find_child: NULL key string passed into function!\n")); + return NULL; + } + + for ( i=0; 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, 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/source3/lib/popt_common.c b/source3/lib/popt_common.c new file mode 100644 index 0000000000..a3d6af4fbc --- /dev/null +++ b/source3/lib/popt_common.c @@ -0,0 +1,49 @@ +/* + Unix SMB/CIFS implementation. + Common popt routines + + 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" + +/* Handle -d,--debuglevel command line option */ + +static void debug_callback(poptContext con, + enum poptCallbackReason reason, + const struct poptOption *opt, + const char *arg, const void *data) +{ + extern BOOL AllowDebugChange; + + switch(opt->val) { + case 'd': + if (arg) { + DEBUGLEVEL = atoi(arg); + AllowDebugChange = False; + } + + break; + } +} + +struct poptOption popt_common_debug[] = { + { NULL, 0, POPT_ARG_CALLBACK, debug_callback }, + { "debuglevel", 'd', POPT_ARG_INT, NULL, 'd', "Set debug level", + "DEBUGLEVEL" }, + { 0 } +}; diff --git a/source3/lib/system_smbd.c b/source3/lib/system_smbd.c new file mode 100644 index 0000000000..28ceaf3939 --- /dev/null +++ b/source3/lib/system_smbd.c @@ -0,0 +1,105 @@ +/* + 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; + + /* 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) { + free(gids_saved); + /* very strange! */ + return -1; + } + + if (initgroups(user, gid) != 0) { + 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); + + ret = getgroups(*grpcnt, groups); + if (ret >= 0) { + *grpcnt = ret; + } + + restore_re_gid(); + + if (setgroups(ngrp_saved, gids_saved) != 0) { + /* yikes! */ + DEBUG(0,("ERROR: getgrouplist: failed to reset group list!\n")); + smb_panic("getgrouplist: failed to reset group list!\n"); + free(gids_saved); + return -1; + } + + free(gids_saved); + return ret; +} +#endif + +int sys_getgrouplist(const char *user, gid_t gid, gid_t *groups, int *grpcnt) +{ +#ifdef HAVE_GETGROUPLIST + return getgrouplist(user, gid, groups, grpcnt); +#else + int retval; + become_root(); + retval = getgrouplist_internals(user, gid, groups, grpcnt); + unbecome_root(); +#endif +} diff --git a/source3/lib/util_smbd.c b/source3/lib/util_smbd.c new file mode 100644 index 0000000000..071f20b416 --- /dev/null +++ b/source3/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/source3/lib/util_uuid.c b/source3/lib/util_uuid.c new file mode 100644 index 0000000000..63e2504982 --- /dev/null +++ b/source3/lib/util_uuid.c @@ -0,0 +1,72 @@ +/* + * Unix SMB/CIFS implementation. + * UUID server routines + * Copyright (C) Theodore Ts'o 1996, 1997, + * Copyright (C) Jim McDonough 2002. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "includes.h" + +/* + * Offset between 15-Oct-1582 and 1-Jan-70 + */ +#define TIME_OFFSET_HIGH 0x01B21DD2 +#define TIME_OFFSET_LOW 0x13814000 + +struct uuid { + uint32 time_low; + uint16 time_mid; + uint16 time_hi_and_version; + uint16 clock_seq; + uint8 node[6]; +}; + + +static void uuid_pack(const struct uuid *uu, GUID *ptr) +{ + uint8 *out = ptr->info; + + SIVAL(out, 0, uu->time_low); + SSVAL(out, 4, uu->time_mid); + SSVAL(out, 6, uu->time_hi_and_version); + SSVAL(out, 8, uu->clock_seq); + memcpy(out+10, uu->node, 6); +} + +static void uuid_unpack(const GUID in, struct uuid *uu) +{ + const uint8 *ptr = in.info; + + uu->time_low = IVAL(ptr, 0); + uu->time_mid = SVAL(ptr, 4); + uu->time_hi_and_version = SVAL(ptr, 6); + uu->clock_seq = SVAL(ptr, 8); + memcpy(uu->node, ptr+10, 6); +} + +void uuid_generate_random(GUID *out) +{ + GUID tmp; + struct uuid uu; + + generate_random_buffer(tmp.info, sizeof(tmp.info), True); + uuid_unpack(tmp, &uu); + + uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000; + uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) | 0x4000; + uuid_pack(&uu, out); +} diff --git a/source3/libsmb/namecache.c b/source3/libsmb/namecache.c new file mode 100644 index 0000000000..fc09d8eac2 --- /dev/null +++ b/source3/libsmb/namecache.c @@ -0,0 +1,252 @@ +/* + Unix SMB/CIFS implementation. + + NetBIOS name cache module. + + Copyright (C) Tim Potter, 2002 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +static BOOL done_namecache_init; +static BOOL enable_namecache; +static TDB_CONTEXT *namecache_tdb; + +struct nc_value { + time_t expiry; /* When entry expires */ + int count; /* Number of addresses */ + struct in_addr ip_list[0]; /* Address list */ +}; + +/* Initialise namecache system */ + +void namecache_enable(void) +{ + /* Check if we have been here before, or name caching disabled + by setting the name cache timeout to zero. */ + + if (done_namecache_init) + return; + + done_namecache_init = True; + + if (lp_name_cache_timeout() == 0) { + DEBUG(5, ("namecache_init: disabling netbios name cache\n")); + return; + } + + /* Open namecache tdb in read/write or readonly mode */ + + namecache_tdb = tdb_open_log( + lock_path("namecache.tdb"), 0, + TDB_DEFAULT, O_RDWR | O_CREAT, 0644); + + if (!namecache_tdb) { + DEBUG(5, ("namecache_init: could not open %s\n", + lock_path("namecache.tdb"))); + return; + } + + DEBUG(5, ("namecache_init: enabling netbios namecache, timeout %d " + "seconds\n", lp_name_cache_timeout())); + + enable_namecache = True; +} + +/* Return a key for a name and name type. The caller must free + retval.dptr when finished. */ + +static TDB_DATA namecache_key(const char *name, int name_type) +{ + TDB_DATA retval; + char *keystr; + + asprintf(&keystr, "%s#%02X", strupper_static(name), name_type); + + retval.dsize = strlen(keystr) + 1; + retval.dptr = keystr; + + return retval; +} + +/* Return a data value for an IP list. The caller must free + retval.dptr when finished. */ + +static TDB_DATA namecache_value(struct in_addr *ip_list, int num_names, + time_t expiry) +{ + TDB_DATA retval; + struct nc_value *value; + int size; + + size = sizeof(struct nc_value) + sizeof(struct in_addr) * + num_names; + + value = (struct nc_value *)malloc(size); + + value->expiry = expiry; + value->count = num_names; + + memcpy(value->ip_list, ip_list, num_names * sizeof(struct in_addr)); + + retval.dptr = (char *)value; + retval.dsize = size; + + return retval; +} + +/* Store a name in the name cache */ + +void namecache_store(const char *name, int name_type, + int num_names, struct in_addr *ip_list) +{ + TDB_DATA key, value; + time_t expiry; + int i; + + if (!enable_namecache) + return; + + DEBUG(5, ("namecache_store: storing %d address%s for %s#%02x: ", + num_names, num_names == 1 ? "": "es", name, name_type)); + + for (i = 0; i < num_names; i++) + DEBUGADD(5, ("%s%s", inet_ntoa(ip_list[i]), + i == (num_names - 1) ? "" : ", ")); + + DEBUGADD(5, ("\n")); + + key = namecache_key(name, name_type); + + /* Cache pdc location or dc lists for only a little while + otherwise if we lock on to a bad DC we can potentially be + out of action for the entire cache timeout time! */ + + if (name_type != 0x1b || name_type != 0x1c) + expiry = time(NULL) + 10; + else + expiry = time(NULL) + lp_name_cache_timeout(); + + value = namecache_value(ip_list, num_names, expiry); + + tdb_store(namecache_tdb, key, value, TDB_REPLACE); + + free(key.dptr); + free(value.dptr); +} + +/* Look up a name in the name cache. Return a mallocated list of IP + addresses if the name is contained in the cache. */ + +BOOL namecache_fetch(const char *name, int name_type, struct in_addr **ip_list, + int *num_names) +{ + TDB_DATA key, value; + struct nc_value *data; + time_t now; + int i; + + if (!enable_namecache) + return False; + + /* Read value */ + + key = namecache_key(name, name_type); + + value = tdb_fetch(namecache_tdb, key); + + if (!value.dptr) { + DEBUG(5, ("namecache_fetch: %s#%02x not found\n", + name, name_type)); + goto done; + } + + data = (struct nc_value *)value.dptr; + + /* Check expiry time */ + + now = time(NULL); + + if (now > data->expiry) { + + DEBUG(5, ("namecache_fetch: entry for %s#%02x expired\n", + name, name_type)); + + tdb_delete(namecache_tdb, key); + + value = tdb_null; + + goto done; + } + + if ((data->expiry - now) > lp_name_cache_timeout()) { + + /* Someone may have changed the system time on us */ + + DEBUG(5, ("namecache_fetch: entry for %s#%02x has bad expiry\n", + name, name_type)); + + tdb_delete(namecache_tdb, key); + + value = tdb_null; + + goto done; + } + + /* Extract and return namelist */ + + *ip_list = (struct in_addr *)malloc( + sizeof(struct in_addr) * data->count); + + memcpy(*ip_list, data->ip_list, sizeof(struct in_addr) * + data->count); + + *num_names = data->count; + + DEBUG(5, ("namecache_fetch: returning %d address%s for %s#%02x: ", + *num_names, *num_names == 1 ? "" : "es", name, name_type)); + + for (i = 0; i < *num_names; i++) + DEBUGADD(5, ("%s%s", inet_ntoa((*ip_list)[i]), + i == (*num_names - 1) ? "" : ", ")); + + DEBUGADD(5, ("\n")); + +done: + SAFE_FREE(key.dptr); + SAFE_FREE(value.dptr); + + return value.dsize > 0; +} + +/* Flush all names from the name cache */ + +void namecache_flush(void) +{ + int result; + + if (!namecache_tdb) + return; + + result = tdb_traverse(namecache_tdb, tdb_traverse_delete_fn, NULL); + + if (result == -1) + DEBUG(5, ("namecache_flush: error deleting cache entries\n")); + else + DEBUG(5, ("namecache_flush: deleted %d cache entr%s\n", + result, result == 1 ? "y" : "ies")); +} diff --git a/source3/python/py_common_proto.h b/source3/python/py_common_proto.h new file mode 100644 index 0000000000..89f0f35fc9 --- /dev/null +++ b/source3/python/py_common_proto.h @@ -0,0 +1,32 @@ +#ifndef _PY_COMMON_PROTO_H +#define _PY_COMMON_PROTO_H + +/* This file is automatically generated with "make proto". DO NOT EDIT */ + + +/* 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, + char *pipe_name, 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_PROTO_H */ diff --git a/source3/python/py_lsa.c b/source3/python/py_lsa.c new file mode 100644 index 0000000000..0584cf716b --- /dev/null +++ b/source3/python/py_lsa.c @@ -0,0 +1,462 @@ +/* + 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, PIPE_LSARPC, &errstr))) { + PyErr_SetString(lsa_error, errstr); + free(errstr); + return NULL; + } + + if (!(mem_ctx = talloc_init())) { + PyErr_SetString(lsa_error, "unable to init talloc context\n"); + goto done; + } + + ntstatus = cli_lsa_open_policy(cli, mem_ctx, True, + SEC_RIGHTS_MAXIMUM_ALLOWED, &hnd); + + if (!NT_STATUS_IS_OK(ntstatus)) { + PyErr_SetObject(lsa_ntstatus, py_ntstatus_tuple(ntstatus)); + goto done; + } + + result = new_lsa_policy_hnd_object(cli, mem_ctx, &hnd); + +done: + if (!result) { + if (cli) + cli_shutdown(cli); + + if (mem_ctx) + talloc_destroy(mem_ctx); + } + + return result; +} + +static PyObject *lsa_close(PyObject *self, PyObject *args, PyObject *kw) +{ + PyObject *po; + lsa_policy_hnd_object *hnd; + NTSTATUS result; + + /* Parse parameters */ + + if (!PyArg_ParseTuple(args, "O!", &lsa_policy_hnd_type, &po)) + return NULL; + + hnd = (lsa_policy_hnd_object *)po; + + /* Call rpc function */ + + result = cli_lsa_close(hnd->cli, hnd->mem_ctx, &hnd->pol); + + /* Cleanup samba stuff */ + + cli_shutdown(hnd->cli); + talloc_destroy(hnd->mem_ctx); + + /* Return value */ + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *lsa_lookup_names(PyObject *self, PyObject *args) +{ + PyObject *py_names, *result; + NTSTATUS ntstatus; + lsa_policy_hnd_object *hnd = (lsa_policy_hnd_object *)self; + int num_names, i; + const char **names; + DOM_SID *sids; + uint32 *name_types; + + if (!PyArg_ParseTuple(args, "O", &py_names)) + return NULL; + + if (!PyList_Check(py_names) && !PyString_Check(py_names)) { + PyErr_SetString(PyExc_TypeError, "must be list or string"); + return NULL; + } + + if (PyList_Check(py_names)) { + + /* Convert list to char ** array */ + + num_names = PyList_Size(py_names); + names = (const char **)talloc( + hnd->mem_ctx, num_names * sizeof(char *)); + + for (i = 0; i < num_names; i++) { + PyObject *obj = PyList_GetItem(py_names, i); + + names[i] = talloc_strdup(hnd->mem_ctx, PyString_AsString(obj)); + } + + } else { + + /* Just a single element */ + + num_names = 1; + names = (const char **)talloc(hnd->mem_ctx, sizeof(char *)); + + names[0] = PyString_AsString(py_names); + } + + ntstatus = cli_lsa_lookup_names(hnd->cli, hnd->mem_ctx, &hnd->pol, + num_names, names, &sids, &name_types); + + if (!NT_STATUS_IS_OK(ntstatus) && NT_STATUS_V(ntstatus) != 0x107) { + PyErr_SetObject(lsa_ntstatus, py_ntstatus_tuple(ntstatus)); + return NULL; + } + + result = PyList_New(num_names); + + for (i = 0; i < num_names; i++) { + PyObject *sid_obj, *obj; + + py_from_SID(&sid_obj, &sids[i]); + + obj = Py_BuildValue("(Oi)", sid_obj, name_types[i]); + + PyList_SetItem(result, i, obj); + } + + return result; +} + +static PyObject *lsa_lookup_sids(PyObject *self, PyObject *args, + PyObject *kw) +{ + PyObject *py_sids, *result; + NTSTATUS ntstatus; + int num_sids, i; + char **domains, **names; + uint32 *types; + lsa_policy_hnd_object *hnd = (lsa_policy_hnd_object *)self; + DOM_SID *sids; + + if (!PyArg_ParseTuple(args, "O", &py_sids)) + return NULL; + + if (!PyList_Check(py_sids) && !PyString_Check(py_sids)) { + PyErr_SetString(PyExc_TypeError, "must be list or string"); + return NULL; + } + + if (PyList_Check(py_sids)) { + + /* Convert dictionary to char ** array */ + + num_sids = PyList_Size(py_sids); + sids = (DOM_SID *)talloc(hnd->mem_ctx, num_sids * sizeof(DOM_SID)); + + memset(sids, 0, num_sids * sizeof(DOM_SID)); + + for (i = 0; i < num_sids; i++) { + PyObject *obj = PyList_GetItem(py_sids, i); + + string_to_sid(&sids[i], PyString_AsString(obj)); + } + + } else { + + /* Just a single element */ + + num_sids = 1; + sids = (DOM_SID *)talloc(hnd->mem_ctx, sizeof(DOM_SID)); + + string_to_sid(&sids[0], PyString_AsString(py_sids)); + } + + ntstatus = cli_lsa_lookup_sids(hnd->cli, hnd->mem_ctx, &hnd->pol, + num_sids, sids, &domains, &names, + &types); + + if (!NT_STATUS_IS_OK(ntstatus)) { + PyErr_SetObject(lsa_ntstatus, py_ntstatus_tuple(ntstatus)); + return NULL; + } + + result = PyList_New(num_sids); + + for (i = 0; i < num_sids; i++) { + PyObject *obj; + + obj = Py_BuildValue("{sssssi}", "username", names[i], + "domain", domains[i], "name_type", + types[i]); + + PyList_SetItem(result, i, obj); + } + + return result; +} + +static PyObject *lsa_enum_trust_dom(PyObject *self, PyObject *args) +{ + lsa_policy_hnd_object *hnd = (lsa_policy_hnd_object *)self; + NTSTATUS ntstatus; + uint32 enum_ctx = 0, num_domains, i; + char **domain_names; + DOM_SID *domain_sids; + PyObject *result; + + if (!PyArg_ParseTuple(args, "")) + return NULL; + + ntstatus = cli_lsa_enum_trust_dom( + hnd->cli, hnd->mem_ctx, &hnd->pol, &enum_ctx, + &num_domains, &domain_names, &domain_sids); + + if (!NT_STATUS_IS_OK(ntstatus)) { + PyErr_SetObject(lsa_ntstatus, py_ntstatus_tuple(ntstatus)); + return NULL; + } + + result = PyList_New(num_domains); + + for (i = 0; i < num_domains; i++) { + fstring sid_str; + + sid_to_string(sid_str, &domain_sids[i]); + PyList_SetItem( + result, i, + Py_BuildValue("(ss)", domain_names[i], sid_str)); + } + + return result; +} + +/* + * Method dispatch tables + */ + +static PyMethodDef lsa_hnd_methods[] = { + + /* SIDs<->names */ + + { "lookup_sids", (PyCFunction)lsa_lookup_sids, + METH_VARARGS | METH_KEYWORDS, + "Convert sids to names." }, + + { "lookup_names", (PyCFunction)lsa_lookup_names, + METH_VARARGS | METH_KEYWORDS, + "Convert names to sids." }, + + /* Trusted domains */ + + { "enum_trusted_domains", (PyCFunction)lsa_enum_trust_dom, + METH_VARARGS, + "Enumerate trusted domains." }, + + { NULL } +}; + +static void py_lsa_policy_hnd_dealloc(PyObject* self) +{ + PyObject_Del(self); +} + +static PyObject *py_lsa_policy_hnd_getattr(PyObject *self, char *attrname) +{ + return Py_FindMethod(lsa_hnd_methods, self, attrname); +} + +PyTypeObject lsa_policy_hnd_type = { + PyObject_HEAD_INIT(NULL) + 0, + "LSA Policy Handle", + sizeof(lsa_policy_hnd_object), + 0, + py_lsa_policy_hnd_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + py_lsa_policy_hnd_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ +}; + +static PyMethodDef lsa_methods[] = { + + /* Open/close lsa handles */ + + { "open_policy", (PyCFunction)lsa_open_policy, + METH_VARARGS | METH_KEYWORDS, + "Open a policy handle" }, + + { "close", (PyCFunction)lsa_close, + METH_VARARGS, + "Close a policy handle" }, + + /* Other stuff - this should really go into a samba config module + but for the moment let's leave it here. */ + + { "setup_logging", (PyCFunction)py_setup_logging, + METH_VARARGS | METH_KEYWORDS, + "Set up debug logging. + +Initialises Samba's debug logging system. One argument is expected which +is a boolean specifying whether debugging is interactive and sent to stdout +or logged to a file. + +Example: + +>>> spoolss.setup_logging(interactive = 1)" }, + + { "get_debuglevel", (PyCFunction)get_debuglevel, + METH_VARARGS, + "Set the current debug level. + +Example: + +>>> spoolss.get_debuglevel() +0" }, + + { "set_debuglevel", (PyCFunction)set_debuglevel, + METH_VARARGS, + "Get the current debug level. + +Example: + +>>> spoolss.set_debuglevel(10)" }, + + { NULL } +}; + +static struct const_vals { + char *name; + uint32 value; +} module_const_vals[] = { + { NULL } +}; + +static void const_init(PyObject *dict) +{ + struct const_vals *tmp; + PyObject *obj; + + for (tmp = module_const_vals; tmp->name; tmp++) { + obj = PyInt_FromLong(tmp->value); + PyDict_SetItemString(dict, tmp->name, obj); + Py_DECREF(obj); + } +} + +/* + * Module initialisation + */ + +void initlsa(void) +{ + PyObject *module, *dict; + + /* Initialise module */ + + module = Py_InitModule("lsa", lsa_methods); + dict = PyModule_GetDict(module); + + lsa_error = PyErr_NewException("lsa.error", NULL, NULL); + PyDict_SetItemString(dict, "error", lsa_error); + + lsa_ntstatus = PyErr_NewException("lsa.ntstatus", NULL, NULL); + PyDict_SetItemString(dict, "ntstatus", lsa_ntstatus); + + /* Initialise policy handle object */ + + lsa_policy_hnd_type.ob_type = &PyType_Type; + + /* Initialise constants */ + + const_init(dict); + + /* Do samba initialisation */ + + py_samba_init(); + + setup_logging("lsa", True); + DEBUGLEVEL = 10; +} diff --git a/source3/python/py_smb.c b/source3/python/py_smb.c new file mode 100644 index 0000000000..77d7bb32fc --- /dev/null +++ b/source3/python/py_smb.c @@ -0,0 +1,224 @@ +/* + 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; + extern pstring global_myname; + 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" }; + 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); + + 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", "creds" }; + PyObject *creds; + char *service, *username, *domain, *password, *errstr; + BOOL result; + + if (!PyArg_ParseTupleAndKeywords(args, kw, "sO", kwlist, &service, + &creds)) + return NULL; + + if (!py_parse_creds(creds, &username, &domain, &password, &errstr)) { + free(errstr); + return NULL; + } + + result = cli_send_tconX( + cli->cli, service, strequal(service, "IPC$") ? "IPC" : "?????", + password, strlen(password) + 1); + + return Py_BuildValue("i", result); +} + +static PyMethodDef smb_hnd_methods[] = { + + { "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" }, + + { NULL } +}; + +/* + * Method dispatch tables + */ + +static PyMethodDef smb_methods[] = { + + { "connect", (PyCFunction)py_smb_connect, METH_VARARGS | METH_KEYWORDS, + "Connect to a host" }, + + { NULL } +}; + +static void py_cli_state_dealloc(PyObject* self) +{ + PyObject_Del(self); +} + +static PyObject *py_cli_state_getattr(PyObject *self, char *attrname) +{ + return Py_FindMethod(smb_hnd_methods, self, attrname); +} + +PyTypeObject cli_state_type = { + PyObject_HEAD_INIT(NULL) + 0, + "SMB client connection", + sizeof(cli_state_object), + 0, + py_cli_state_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + py_cli_state_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ +}; + +/* + * Module initialisation + */ + +void initsmb(void) +{ + PyObject *module, *dict; + + /* Initialise module */ + + module = Py_InitModule("smb", smb_methods); + dict = PyModule_GetDict(module); + + /* Initialise policy handle object */ + + cli_state_type.ob_type = &PyType_Type; + + /* Do samba initialisation */ + + py_samba_init(); + + setup_logging("smb", True); + DEBUGLEVEL = 10; +} diff --git a/source3/python/py_smb.h b/source3/python/py_smb.h new file mode 100644 index 0000000000..18677b4905 --- /dev/null +++ b/source3/python/py_smb.h @@ -0,0 +1,42 @@ +/* + 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 "includes.h" +#include "Python.h" + +#include "python/py_common_proto.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/source3/registry/reg_cachehook.c b/source3/registry/reg_cachehook.c new file mode 100644 index 0000000000..547eed392d --- /dev/null +++ b/source3/registry/reg_cachehook.c @@ -0,0 +1,112 @@ +/* + * Unix SMB/CIFS implementation. + * RPC Pipe client / server routines + * Copyright (C) Gerald Carter 2002. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Implementation of registry hook cache tree */ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_RPC_SRV + +static SORTED_TREE *cache_tree; +extern REGISTRY_OPS regdb_ops; /* these are the default */ +static REGISTRY_HOOK default_hook = { KEY_TREE_ROOT, ®db_ops }; + +/********************************************************************** + Initialize the cache tree + *********************************************************************/ + +BOOL reghook_cache_init( void ) +{ + cache_tree = sorted_tree_init( &default_hook, NULL, NULL ); + + return ( cache_tree == NULL ); +} + +/********************************************************************** + Add a new REGISTRY_HOOK to the cache. Note that the keyname + is not in the exact format that a SORTED_TREE expects. + *********************************************************************/ + +BOOL reghook_cache_add( REGISTRY_HOOK *hook ) +{ + pstring key; + + if ( !hook ) + return False; + + pstrcpy( key, "\\"); + pstrcat( key, hook->keyname ); + + pstring_sub( key, "\\", "/" ); + + DEBUG(10,("reghook_cache_add: Adding key [%s]\n", key)); + + return sorted_tree_add( cache_tree, key, hook ); +} + +/********************************************************************** + Initialize the cache tree + *********************************************************************/ + +REGISTRY_HOOK* reghook_cache_find( char *keyname ) +{ + char *key; + int len; + REGISTRY_HOOK *hook; + + if ( !keyname ) + return NULL; + + /* prepend the string with a '\' character */ + + len = strlen( keyname ); + if ( !(key = malloc( len + 2 )) ) { + DEBUG(0,("reghook_cache_find: malloc failed for string [%s] !?!?!\n", + keyname)); + return NULL; + } + + *key = '\\'; + strncpy( key+1, keyname, len+1); + + /* swap to a form understood by the SORTED_TREE */ + + string_sub( key, "\\", "/", 0 ); + + DEBUG(10,("reghook_cache_find: Searching for keyname [%s]\n", key)); + + hook = sorted_tree_find( cache_tree, key ) ; + + SAFE_FREE( key ); + + return hook; +} + +/********************************************************************** + Initialize the cache tree + *********************************************************************/ + +void reghook_dump_cache( int debuglevel ) +{ + DEBUG(debuglevel,("reghook_dump_cache: Starting cache dump now...\n")); + + sorted_tree_print_keys( cache_tree, debuglevel ); +} diff --git a/source3/registry/reg_db.c b/source3/registry/reg_db.c new file mode 100644 index 0000000000..b0917c8f60 --- /dev/null +++ b/source3/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_by_string( tdb_reg, path ); + + buf = dbuf.dptr; + buflen = dbuf.dsize; + + if ( !buf ) { + DEBUG(5,("regdb_fetch_reg_keys: tdb lookup failed to locate key [%s]\n", key)); + return -1; + } + + len = tdb_unpack( buf, buflen, "d", &num_items); + + for (i=0; i<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/source3/registry/reg_frontend.c b/source3/registry/reg_frontend.c new file mode 100644 index 0000000000..45c1f24001 --- /dev/null +++ b/source3/registry/reg_frontend.c @@ -0,0 +1,575 @@ +/* + * 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 } +}; + + +/* + * Utility functions for REGSUBKEY_CTR + */ + +/*********************************************************************** + Init the talloc context held by a REGSUBKEY_CTR structure + **********************************************************************/ + +void regsubkey_ctr_init( REGSUBKEY_CTR *ctr ) +{ + if ( !ctr->ctx ) + ctr->ctx = talloc_init(); +} + +/*********************************************************************** + Add a new key to the array + **********************************************************************/ + +int regsubkey_ctr_addkey( REGSUBKEY_CTR *ctr, 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(); +} + +/*********************************************************************** + 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, char *name, uint16 type, + char *data_p, size_t size ) +{ + REGISTRY_VALUE **ppreg; + uint16 len; + + if ( name ) + { + len = strlen( 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; +} + +/*********************************************************************** + 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, char *name ) +{ + int i; + + /* search for the value */ + + 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, char *name ) +{ + int i; + + /* search for the value */ + + for ( i=0; i<ctr->num_values; i++ ) { + if ( strcmp( ctr->values[i]->valuename, name ) == 0) + 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 ); + } +} + +/*********************************************************************** + 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(®_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/source3/registry/reg_printing.c b/source3/registry/reg_printing.c new file mode 100644 index 0000000000..8f53fe9ea5 --- /dev/null +++ b/source3/registry/reg_printing.c @@ -0,0 +1,814 @@ +/* + * Unix SMB/CIFS implementation. + * RPC Pipe client / server routines + * Copyright (C) Gerald Carter 2002. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Implementation of registry virtual views for printing information */ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_RPC_SRV + +#define MAX_TOP_LEVEL_KEYS 3 + +/* some symbolic indexes into the top_level_keys */ + +#define KEY_INDEX_ENVIR 0 +#define KEY_INDEX_FORMS 1 +#define KEY_INDEX_PRINTER 2 + +static char *top_level_keys[MAX_TOP_LEVEL_KEYS] = { + "Environments", + "Forms", + "Printers" +}; + + +/********************************************************************** + It is safe to assume that every registry path passed into on of + the exported functions here begins with KEY_PRINTING else + these functions would have never been called. This is a small utility + function to strip the beginning of the path and make a copy that the + caller can modify. Note that the caller is responsible for releasing + the memory allocated here. + **********************************************************************/ + +static char* trim_reg_path( char *path ) +{ + char *p; + uint16 key_len = strlen(KEY_PRINTING); + + /* + * sanity check...this really should never be True. + * It is only here to prevent us from accessing outside + * the path buffer in the extreme case. + */ + + if ( strlen(path) < key_len ) { + DEBUG(0,("trim_reg_path: Registry path too short! [%s]\n", path)); + DEBUG(0,("trim_reg_path: KEY_PRINTING => [%s]!\n", KEY_PRINTING)); + return NULL; + } + + + p = path + strlen( KEY_PRINTING ); + + if ( *p == '\\' ) + p++; + + if ( *p ) + return strdup(p); + else + return NULL; +} + +/********************************************************************** + handle enumeration of subkeys below KEY_PRINTING\Environments + *********************************************************************/ + +static int print_subpath_environments( char *key, REGSUBKEY_CTR *subkeys ) +{ + 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; + + 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 ); + regval_ctr_addvalue( val, "Driver", REG_SZ, filename, strlen(filename)+1 ); + filename = dos_basename( info3->configfile ); + regval_ctr_addvalue( val, "Configuration File", REG_SZ, filename, strlen(filename)+1 ); + filename = dos_basename( info3->datafile ); + regval_ctr_addvalue( val, "Data File", REG_SZ, filename, strlen(filename)+1 ); + filename = dos_basename( info3->helpfile ); + regval_ctr_addvalue( val, "Help File", REG_SZ, filename, strlen(filename)+1 ); + + regval_ctr_addvalue( val, "Data Type", REG_SZ, info3->defaultdatatype, strlen(info3->defaultdatatype)+1 ); + + 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 ); + if ( !buffer2 ) + break; + buffer = buffer2; + + memcpy( buffer+buffer_size, filename, length+1 ); + + buffer_size += length + 1; + } + + /* terminated by double NULL. Add the final one here */ + + buffer2 = Realloc( buffer, buffer_size + 1 ); + if ( !buffer2 ) { + SAFE_FREE( buffer ); + buffer_size = 0; + } + else { + buffer = buffer2; + 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 num_subkeys = 0; + char *keystr, *key2 = NULL; + char *base, *new_path; + NT_PRINTER_INFO_LEVEL *printer = 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 ( !new_path ) { + /* sanity check on the printer name */ + if ( !W_ERROR_IS_OK( get_a_printer(&printer, 2, base) ) ) + goto done; + + free_a_printer( &printer, 2 ); + + regsubkey_ctr_addkey( subkeys, SPOOL_PRINTERDATA_KEY ); + } + + /* no other subkeys below here */ + +done: + SAFE_FREE( key2 ); + 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; + int i; + fstring valuename; + uint8 *data; + uint32 type, data_len; + fstring printername; + + /* + * There 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(&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) ); + + regval_ctr_addvalue( val, "Name", REG_SZ, info2->printername, sizeof(info2->printername)+1 ); + regval_ctr_addvalue( val, "Location", REG_SZ, info2->location, sizeof(info2->location)+1 ); + regval_ctr_addvalue( val, "Comment", REG_SZ, info2->comment, sizeof(info2->comment)+1 ); + regval_ctr_addvalue( val, "Parameters", REG_SZ, info2->parameters, sizeof(info2->parameters)+1 ); + regval_ctr_addvalue( val, "Port", REG_SZ, info2->portname, sizeof(info2->portname)+1 ); + regval_ctr_addvalue( val, "Server", REG_SZ, info2->servername, sizeof(info2->servername)+1 ); + regval_ctr_addvalue( val, "Share", REG_SZ, info2->sharename, sizeof(info2->sharename)+1 ); + regval_ctr_addvalue( val, "Driver", REG_SZ, info2->drivername, sizeof(info2->drivername)+1 ); + regval_ctr_addvalue( val, "Separator File", REG_SZ, info2->sepfile, sizeof(info2->sepfile)+1 ); + regval_ctr_addvalue( val, "Print Processor", REG_SZ, "winprint", sizeof("winprint")+1 ); + + + /* 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 ); + free_a_printer( &printer, 2 ); + + num_values = regval_ctr_numvals( val ); + goto done; + + } + + + keystr = new_path; + reg_split_path( keystr, &base, &new_path ); + + /* here should be no more path components here */ + + if ( new_path || strcmp(base, SPOOL_PRINTERDATA_KEY) ) + goto done; + + /* now enumerate the PrinterDriverData key */ + if ( !W_ERROR_IS_OK( get_a_printer(&printer, 2, printername) ) ) + goto done; + + info2 = printer->info_2; + + + /* iterate over all printer data and fill the regval container */ + +#if 0 /* JERRY */ + for ( i=0; get_specific_param_by_index(*printer, 2, i, valuename, &data, &type, &data_len); i++ ) + { + regval_ctr_addvalue( val, valuename, type, data, data_len ); + } +#endif + + free_a_printer( &printer, 2 ); + + num_values = regval_ctr_numvals( val ); + +done: + 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/source3/rpc_client/cli_dfs.c b/source3/rpc_client/cli_dfs.c new file mode 100644 index 0000000000..7fc27b9c3b --- /dev/null +++ b/source3/rpc_client/cli_dfs.c @@ -0,0 +1,245 @@ +/* + 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, + char *entrypath, char *servername, char *sharename, + 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, + char *entrypath, char *servername, 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, + char *entrypath, char *servername, 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/source3/rpc_client/cli_lsarpc.c b/source3/rpc_client/cli_lsarpc.c new file mode 100644 index 0000000000..5555f4bd52 --- /dev/null +++ b/source3/rpc_client/cli_lsarpc.c @@ -0,0 +1,1256 @@ +/* + 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, DOM_SID *sids, + char ***domains, char ***names, uint32 **types) +{ + prs_struct qbuf, rbuf; + LSA_Q_LOOKUP_SIDS q; + LSA_R_LOOKUP_SIDS r; + DOM_R_REF ref; + LSA_TRANS_NAME_ENUM t_names; + NTSTATUS result; + int i; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_q_lookup_sids(mem_ctx, &q, pol, num_sids, sids, 1); + + if (!lsa_io_q_lookup_sids("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, LSA_LOOKUPSIDS, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + ZERO_STRUCT(ref); + ZERO_STRUCT(t_names); + + r.dom_ref = &ref; + r.names = &t_names; + + if (!lsa_io_r_lookup_sids("", &r, &rbuf, 0)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + result = r.status; + + if (!NT_STATUS_IS_OK(result) && + NT_STATUS_V(result) != NT_STATUS_V(STATUS_SOME_UNMAPPED)) { + + /* An actual error occured */ + + goto done; + } + + /* Return output parameters */ + + if (r.mapped_count == 0) { + result = NT_STATUS_NONE_MAPPED; + goto done; + } + + if (!((*domains) = (char **)talloc(mem_ctx, sizeof(char *) * + num_sids))) { + DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n")); + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (!((*names) = (char **)talloc(mem_ctx, sizeof(char *) * + num_sids))) { + DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n")); + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (!((*types) = (uint32 *)talloc(mem_ctx, sizeof(uint32) * + num_sids))) { + DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n")); + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + for (i = 0; i < num_sids; i++) { + fstring name, dom_name; + uint32 dom_idx = t_names.name[i].domain_idx; + + /* Translate optimised name through domain index array */ + + if (dom_idx != 0xffffffff) { + + rpcstr_pull_unistr2_fstring( + dom_name, &ref.ref_dom[dom_idx].uni_dom_name); + rpcstr_pull_unistr2_fstring( + name, &t_names.uni_name[i]); + + (*names)[i] = talloc_strdup(mem_ctx, name); + (*domains)[i] = talloc_strdup(mem_ctx, dom_name); + (*types)[i] = t_names.name[i].sid_name_use; + + if (((*names)[i] == NULL) || ((*domains)[i] == NULL)) { + DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n")); + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + } else { + (*names)[i] = NULL; + (*types)[i] = SID_NAME_UNKNOWN; + } + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/** Lookup a list of names */ + +NTSTATUS cli_lsa_lookup_names(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *pol, int num_names, + const char **names, DOM_SID **sids, + uint32 **types) +{ + prs_struct qbuf, rbuf; + LSA_Q_LOOKUP_NAMES q; + LSA_R_LOOKUP_NAMES r; + DOM_R_REF ref; + NTSTATUS result; + int i; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_q_lookup_names(mem_ctx, &q, pol, num_names, names); + + if (!lsa_io_q_lookup_names("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, LSA_LOOKUPNAMES, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + ZERO_STRUCT(ref); + r.dom_ref = &ref; + + if (!lsa_io_r_lookup_names("", &r, &rbuf, 0)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + result = r.status; + + if (!NT_STATUS_IS_OK(result) && NT_STATUS_V(result) != + NT_STATUS_V(STATUS_SOME_UNMAPPED)) { + + /* An actual error occured */ + + goto done; + } + + /* Return output parameters */ + + if (r.mapped_count == 0) { + result = NT_STATUS_NONE_MAPPED; + goto done; + } + + if (!((*sids = (DOM_SID *)talloc(mem_ctx, sizeof(DOM_SID) * + num_names)))) { + DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n")); + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (!((*types = (uint32 *)talloc(mem_ctx, sizeof(uint32) * + num_names)))) { + DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n")); + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + for (i = 0; i < num_names; i++) { + DOM_RID2 *t_rids = r.dom_rid; + uint32 dom_idx = t_rids[i].rid_idx; + uint32 dom_rid = t_rids[i].rid; + DOM_SID *sid = &(*sids)[i]; + + /* Translate optimised sid through domain index array */ + + if (dom_idx != 0xffffffff) { + + sid_copy(sid, &ref.ref_dom[dom_idx].ref_dom.sid); + + if (dom_rid != 0xffffffff) { + sid_append_rid(sid, dom_rid); + } + + (*types)[i] = t_rids[i].type; + } else { + ZERO_STRUCTP(sid); + (*types)[i] = SID_NAME_UNKNOWN; + } + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/** Query info policy + * + * @param domain_sid - returned remote server's domain sid */ + +NTSTATUS cli_lsa_query_info_policy(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *pol, uint16 info_class, + fstring domain_name, DOM_SID *domain_sid) +{ + prs_struct qbuf, rbuf; + LSA_Q_QUERY_INFO q; + LSA_R_QUERY_INFO r; + NTSTATUS result; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_q_query(&q, pol, info_class); + + if (!lsa_io_q_query("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, LSA_QUERYINFOPOLICY, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + if (!lsa_io_r_query("", &r, &rbuf, 0)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (!NT_STATUS_IS_OK(result = r.status)) { + goto done; + } + + /* Return output parameters */ + + ZERO_STRUCTP(domain_sid); + domain_name[0] = '\0'; + + switch (info_class) { + + case 3: + if (r.dom.id3.buffer_dom_name != 0) { + unistr2_to_ascii(domain_name, + &r.dom.id3. + uni_domain_name, + sizeof (fstring) - 1); + } + + if (r.dom.id3.buffer_dom_sid != 0) { + *domain_sid = r.dom.id3.dom_sid.sid; + } + + break; + + case 5: + + if (r.dom.id5.buffer_dom_name != 0) { + unistr2_to_ascii(domain_name, &r.dom.id5. + uni_domain_name, + sizeof (fstring) - 1); + } + + if (r.dom.id5.buffer_dom_sid != 0) { + *domain_sid = r.dom.id5.dom_sid.sid; + } + + break; + + default: + DEBUG(3, ("unknown info class %d\n", info_class)); + break; + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/** Query info policy2 + * + * @param domain_name - returned remote server's domain name + * @param dns_name - returned remote server's dns domain name + * @param forest_name - returned remote server's forest name + * @param domain_guid - returned remote server's domain guid + * @param domain_sid - returned remote server's domain sid */ + +NTSTATUS cli_lsa_query_info_policy2(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *pol, uint16 info_class, + fstring domain_name, fstring dns_name, + fstring forest_name, GUID *domain_guid, + DOM_SID *domain_sid) +{ + prs_struct qbuf, rbuf; + LSA_Q_QUERY_INFO2 q; + LSA_R_QUERY_INFO2 r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + if (info_class != 12) + goto done; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_q_query2(&q, pol, info_class); + + if (!lsa_io_q_query_info2("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, LSA_QUERYINFO2, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + if (!lsa_io_r_query_info2("", &r, &rbuf, 0)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (!NT_STATUS_IS_OK(result = r.status)) { + goto done; + } + + /* Return output parameters */ + + ZERO_STRUCTP(domain_sid); + ZERO_STRUCTP(domain_guid); + domain_name[0] = '\0'; + + if (r.info.dns_dom_info.hdr_nb_dom_name.buffer) { + unistr2_to_ascii(domain_name, + &r.info.dns_dom_info.uni_nb_dom_name, + sizeof(fstring) - 1); + } + if (r.info.dns_dom_info.hdr_dns_dom_name.buffer) { + unistr2_to_ascii(dns_name, + &r.info.dns_dom_info.uni_dns_dom_name, + sizeof(fstring) - 1); + } + if (r.info.dns_dom_info.hdr_forest_name.buffer) { + unistr2_to_ascii(forest_name, + &r.info.dns_dom_info.uni_forest_name, + sizeof(fstring) - 1); + } + + memcpy(domain_guid, &r.info.dns_dom_info.dom_guid, sizeof(GUID)); + + if (r.info.dns_dom_info.ptr_dom_sid != 0) { + *domain_sid = r.info.dns_dom_info.dom_sid.sid; + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/** + * Enumerate list of trusted domains + * + * @param cli client state (cli_state) structure of the connection + * @param mem_ctx memory context + * @param pol opened lsa policy handle + * @param enum_ctx enumeration context ie. index of first returned domain entry + * @param pref_num_domains preferred max number of entries returned in one response + * @param num_domains total number of trusted domains returned by response + * @param domain_names returned trusted domain names + * @param domain_sids returned trusted domain sids + * + * @return nt status code of response + **/ + +NTSTATUS cli_lsa_enum_trust_dom(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *pol, uint32 *enum_ctx, + uint32 *num_domains, + char ***domain_names, DOM_SID **domain_sids) +{ + prs_struct qbuf, rbuf; + LSA_Q_ENUM_TRUST_DOM q; + LSA_R_ENUM_TRUST_DOM r; + NTSTATUS result; + int i; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + /* 64k is enough for about 2000 trusted domains */ + init_q_enum_trust_dom(&q, pol, *enum_ctx, 0x10000); + + if (!lsa_io_q_enum_trust_dom("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, LSA_ENUMTRUSTDOM, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + if (!lsa_io_r_enum_trust_dom("", &r, &rbuf, 0)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + result = r.status; + + if (!NT_STATUS_IS_OK(result) && + !NT_STATUS_EQUAL(result, NT_STATUS_NO_MORE_ENTRIES) && + !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) { + + /* An actual error ocured */ + + goto done; + } + + /* Return output parameters */ + + if (r.num_domains) { + + /* Allocate memory for trusted domain names and sids */ + + *domain_names = (char **)talloc(mem_ctx, sizeof(char *) * + r.num_domains); + + if (!*domain_names) { + DEBUG(0, ("cli_lsa_enum_trust_dom(): out of memory\n")); + result = NT_STATUS_NO_MEMORY; + goto done; + } + + *domain_sids = (DOM_SID *)talloc(mem_ctx, sizeof(DOM_SID) * + r.num_domains); + if (!domain_sids) { + DEBUG(0, ("cli_lsa_enum_trust_dom(): out of memory\n")); + result = NT_STATUS_NO_MEMORY; + goto done; + } + + /* Copy across names and sids */ + + for (i = 0; i < r.num_domains; i++) { + fstring tmp; + + unistr2_to_ascii(tmp, &r.uni_domain_name[i], + sizeof(tmp) - 1); + (*domain_names)[i] = talloc_strdup(mem_ctx, tmp); + sid_copy(&(*domain_sids)[i], &r.domain_sid[i].sid); + } + } + + *num_domains = r.num_domains; + *enum_ctx = r.enum_context; + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + + +/** Enumerate privileges*/ + +NTSTATUS cli_lsa_enum_privilege(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *pol, uint32 *enum_context, uint32 pref_max_length, + uint32 *count, char ***privs_name, uint32 **privs_high, uint32 **privs_low) +{ + prs_struct qbuf, rbuf; + LSA_Q_ENUM_PRIVS q; + LSA_R_ENUM_PRIVS r; + NTSTATUS result; + int i; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_q_enum_privs(&q, pol, *enum_context, pref_max_length); + + if (!lsa_io_q_enum_privs("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, LSA_ENUM_PRIVS, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + if (!lsa_io_r_enum_privs("", &r, &rbuf, 0)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (!NT_STATUS_IS_OK(result = r.status)) { + goto done; + } + + /* Return output parameters */ + + *enum_context = r.enum_context; + *count = r.count; + + if (!((*privs_name = (char **)talloc(mem_ctx, sizeof(char *) * r.count)))) { + DEBUG(0, ("(cli_lsa_enum_privilege): out of memory\n")); + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (!((*privs_high = (uint32 *)talloc(mem_ctx, sizeof(uint32) * r.count)))) { + DEBUG(0, ("(cli_lsa_enum_privilege): out of memory\n")); + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (!((*privs_low = (uint32 *)talloc(mem_ctx, sizeof(uint32) * r.count)))) { + DEBUG(0, ("(cli_lsa_enum_privilege): out of memory\n")); + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + for (i = 0; i < r.count; i++) { + fstring name; + + rpcstr_pull_unistr2_fstring( name, &r.privs[i].name); + + (*privs_name)[i] = talloc_strdup(mem_ctx, name); + + (*privs_high)[i] = r.privs[i].luid_high; + (*privs_low)[i] = r.privs[i].luid_low; + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/** Get privilege name */ + +NTSTATUS cli_lsa_get_dispname(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *pol, char *name, uint16 lang_id, uint16 lang_id_sys, + fstring description, uint16 *lang_id_desc) +{ + prs_struct qbuf, rbuf; + LSA_Q_PRIV_GET_DISPNAME q; + LSA_R_PRIV_GET_DISPNAME r; + NTSTATUS result; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_lsa_priv_get_dispname(&q, pol, name, lang_id, lang_id_sys); + + if (!lsa_io_q_priv_get_dispname("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, LSA_PRIV_GET_DISPNAME, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + if (!lsa_io_r_priv_get_dispname("", &r, &rbuf, 0)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (!NT_STATUS_IS_OK(result = r.status)) { + goto done; + } + + /* Return output parameters */ + + rpcstr_pull_unistr2_fstring(description , &r.desc); + *lang_id_desc = r.lang_id; + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/** Enumerate list of SIDs */ + +NTSTATUS cli_lsa_enum_sids(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *pol, uint32 *enum_ctx, uint32 pref_max_length, + uint32 *num_sids, DOM_SID **sids) +{ + prs_struct qbuf, rbuf; + LSA_Q_ENUM_ACCOUNTS q; + LSA_R_ENUM_ACCOUNTS r; + NTSTATUS result; + int i; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_lsa_q_enum_accounts(&q, pol, *enum_ctx, pref_max_length); + + if (!lsa_io_q_enum_accounts("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, LSA_ENUM_ACCOUNTS, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + if (!lsa_io_r_enum_accounts("", &r, &rbuf, 0)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + result = r.status; + + if (!NT_STATUS_IS_OK(result = r.status)) { + goto done; + } + + if (r.sids.num_entries==0) + goto done; + + /* Return output parameters */ + + *sids = (DOM_SID *)talloc(mem_ctx, sizeof(DOM_SID) * r.sids.num_entries); + if (!*sids) { + DEBUG(0, ("(cli_lsa_enum_sids): out of memory\n")); + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Copy across names and sids */ + + for (i = 0; i < r.sids.num_entries; i++) { + sid_copy(&(*sids)[i], &r.sids.sid[i].sid); + } + + *num_sids= r.sids.num_entries; + *enum_ctx = r.enum_context; + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/** Open a LSA user handle + * + * @param cli Handle on an initialised SMB connection */ + +NTSTATUS cli_lsa_open_account(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *dom_pol, DOM_SID *sid, uint32 des_access, + POLICY_HND *user_pol) +{ + prs_struct qbuf, rbuf; + LSA_Q_OPENACCOUNT q; + LSA_R_OPENACCOUNT r; + NTSTATUS result; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Initialise input parameters */ + + init_lsa_q_open_account(&q, dom_pol, sid, des_access); + + /* Marshall data and send request */ + + if (!lsa_io_q_open_account("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, LSA_OPENACCOUNT, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + if (!lsa_io_r_open_account("", &r, &rbuf, 0)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Return output parameters */ + + if (NT_STATUS_IS_OK(result = r.status)) { + *user_pol = r.pol; + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/** Enumerate user privileges + * + * @param cli Handle on an initialised SMB connection */ + +NTSTATUS cli_lsa_enum_privsaccount(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *pol, uint32 *count, LUID_ATTR **set) +{ + prs_struct qbuf, rbuf; + LSA_Q_ENUMPRIVSACCOUNT q; + LSA_R_ENUMPRIVSACCOUNT r; + NTSTATUS result; + int i; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Initialise input parameters */ + + init_lsa_q_enum_privsaccount(&q, pol); + + /* Marshall data and send request */ + + if (!lsa_io_q_enum_privsaccount("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, LSA_ENUMPRIVSACCOUNT, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + if (!lsa_io_r_enum_privsaccount("", &r, &rbuf, 0)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Return output parameters */ + + if (!NT_STATUS_IS_OK(result = r.status)) { + goto done; + } + + if (r.count == 0) + goto done; + + if (!((*set = (LUID_ATTR *)talloc(mem_ctx, sizeof(LUID_ATTR) * r.count)))) { + DEBUG(0, ("(cli_lsa_enum_privsaccount): out of memory\n")); + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + for (i=0; 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, 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; +} + +#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, PIPE_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/source3/sam/SAM-interface_handles.txt b/source3/sam/SAM-interface_handles.txt new file mode 100644 index 0000000000..1c164bd198 --- /dev/null +++ b/source3/sam/SAM-interface_handles.txt @@ -0,0 +1,123 @@ +SAM API
+
+NTSTATUS sam_get_sec_obj(NT_USER_TOKEN *access, DOM_SID *sid, SEC_DESC **sd)
+NTSTATUS sam_set_sec_obj(NT_USER_TOKEN *access, DOM_SID *sid, SEC_DESC *sd)
+
+NTSTATUS sam_lookup_name(NT_USER_TOKEN *access, DOM_SID *domain, char *name, DOM_SID **sid, uint32 *type)
+NTSTATUS sam_lookup_sid(NT_USER_TOKEN *access, DOM_SID *sid, char **name, uint32 *type)
+
+
+Domain API
+
+NTSTATUS sam_update_domain(SAM_DOMAIN_HANDLE *domain)
+
+NTSTATUS sam_enum_domains(NT_USER_TOKEN *access, int32 *domain_count, DOM_SID **domains, char **domain_names)
+NTSTATUS sam_lookup_domain(NT_USER_TOKEN *access, char *domain, DOM_SID **domainsid)
+
+NTSTATUS sam_get_domain_by_sid(NT_USER_TOKEN *access, uint32 access_desired, DOM_SID *domainsid, SAM_DOMAIN_HANDLE **domain)
+
+
+User API
+
+NTSTATUS sam_create_user(NT_USER_TOKEN *access, uint32 access_desired, SAM_USER_HANDLE **user)
+NTSTATUS sam_add_user(SAM_USER_HANDLE *user)
+NTSTATUS sam_update_user(SAM_USER_HANDLE *user)
+NTSTATUS sam_delete_user(SAM_USER_HANDLE * user)
+
+NTSTATUS sam_enum_users(NT_USER_TOKEN *access, DOM_SID *domain, int32 *user_count, SAM_USER_ENUM **users)
+
+NTSTATUS sam_get_user_by_sid(NT_USER_TOKEN *access, uint32 access_desired, DOM_SID *usersid, SAM_USER_HANDLE **user)
+NTSTATUS sam_get_user_by_name(NT_USER_TOKEN *access, uint32 access_desired, char *domain, char *name, SAM_USER_HANDLE **user)
+
+
+Group API
+
+NTSTATUS sam_create_group(NT_USER_TOKEN *access, uint32 access_desired, uint32 typ, SAM_GROUP_HANDLE **group)
+NTSTATUS sam_add_group(SAM_GROUP_HANDLE *samgroup)
+NTSTATUS sam_update_group(SAM_GROUP_HANDLE *samgroup)
+NTSTATUS sam_delete_group(SAM_GROUP_HANDLE *groupsid)
+
+NTSTATUS sam_enum_groups(NT_USER_TOKEN *access, DOM_SID *domainsid, uint32 typ, uint32 *groups_count, SAM_GROUP_ENUM **groups)
+
+NTSTATUS sam_get_group_by_sid(NT_USER_TOKEN *access, uint32 access_desired, DOM_SID *groupsid, SAM_GROUP_HANDLE **group)
+NTSTATUS sam_get_group_by_name(NT_USER_TOKEN *access, uint32 access_desired, char *domain, char *name, SAM_GROUP_HANDLE **group)
+
+NTSTATUS sam_add_member_to_group(SAM_GROUP_HANDLE *group, SAM_GROUP_MEMBER *member)
+NTSTATUS sam_delete_member_from_group(SAM_GROUP_HANDLE *group, SAM_GROUP_MEMBER *member)
+NTSTATUS sam_enum_groupmembers(SAM_GROUP_HANLDE *group, uint32 *members_count, SAM_GROUP_MEMBER **members)
+
+NTSTATUS sam_get_groups_of_user(SAM_USER_HANDLE *user, uint32 typ, uint32 *group_count, SAM_GROUP_ENUM **groups)
+
+
+
+structures
+
+typedef _SAM_GROUP_MEMBER {
+ DOM_SID sid;
+ BOOL group; /* specifies if it is a group or a user */
+
+} SAM_GROUP_MEMBER
+
+typedef struct sam_user_enum {
+ DOM_SID sid;
+ char *username;
+ char *full_name;
+ char *user_desc;
+ uint16 acc_ctrl;
+} SAM_USER_ENUM;
+
+typedef struct sam_group_enum {
+ DOM_SID sid;
+ char *groupname;
+ char *comment;
+} SAM_GROUP_ENUM
+
+NTSTATUS sam_get_domain_sid(SAM_DOMAIN_HANDLE *domain, DOM_SID **sid)
+NTSTATUS sam_get_domain_num_users(SAM_DOMAIN_HANDLE *domain, uint32 *num_users)
+NTSTATUS sam_get_domain_num_groups(SAM_DOMAIN_HANDLE *domain, uint32 *num_groups)
+NTSTATUS sam_get_domain_num_aliases(SAM_DOMAIN_HANDLE *domain, uint32 *num_aliases)
+NTSTATUS sam_{get,set}_domain_name(SAM_DOMAIN_HANDLE *domain, char **domain_name)
+NTSTATUS sam_{get,set}_domain_server(SAM_DOMAIN_HANDLE *domain, char **server_name)
+NTSTATUS sam_{get,set}_domain_max_pwdage(SAM_DOMAIN_HANDLE *domain, NTTIME *max_passwordage)
+NTSTATUS sam_{get,set}_domain_min_pwdage(SAM_DOMAIN_HANDLE *domain, NTTIME *min_passwordage)
+NTSTATUS sam_{get,set}_domain_lockout_duration(SAM_DOMAIN_HANDLE *domain, NTTIME *lockout_duration)
+NTSTATUS sam_{get,set}_domain_reset_count(SAM_DOMAIN_HANDLE *domain, NTTIME *reset_lockout_count)
+NTSTATUS sam_{get,set}_domain_min_pwdlength(SAM_DOMAIN_HANDLE *domain, uint16 *min_passwordlength)
+NTSTATUS sam_{get,set}_domain_pwd_history(SAM_DOMAIN_HANDLE *domain, uin16 *password_history)
+NTSTATUS sam_{get,set}_domain_lockout_count(SAM_DOMAIN_HANDLE *domain, uint16 *lockout_count)
+NTSTATUS sam_{get,set}_domain_force_logoff(SAM_DOMAIN_HANDLE *domain, BOOL *force_logoff)
+NTSTATUS sam_{get,set}_domain_login_pwdchange(SAM_DOMAIN_HANDLE *domain, BOOL *login_pwdchange)
+
+NTSTATUS sam_get_user_sid(SAM_USER_HANDLE *user, DOM_SID **sid)
+NTSTATUS sam_{get,set}_user_pgroup(SAM_USER_HANDLE *user, DOM_SID **pgroup)
+NTSTATUS sam_{get,set}_user_name(SAM_USER_HANDLE *user, char **username)
+NTSTATUS sam_{get,set}_user_fullname(SAM_USER_HANDLE *user, char** fullname)
+NTSTATUS sam_{get,set}_user_description(SAM_USER_HANDLE *user, char **description)
+NTSTATUS sam_{get,set}_user_home_dir(SAM_USER_HANDLE *user, char **home_dir)
+NTSTATUS sam_{get,set}_user_dir_drive(SAM_USER_HANDLE *user, char **dir_drive)
+NTSTATUS sam_{get,set}_user_logon_script(SAM_USER_HANDLE *user, char **logon_script)
+NTSTATUS sam_{get,set}_user_profile_path(SAM_USER_HANDLE *user, char **profile_path)
+NTSTATUS sam_{get,set}_user_workstations(SAM_USER_HANDLE *user, char **workstations)
+NTSTATUS sam_{get,set}_user_munged_dial(SAM_USER_HANDLE *user, char **munged_dial)
+NTSTATUS sam_{get,set}_user_lm_pwd(SAM_USER_HANDLE *user, DATA_BLOB *lm_pwd)
+NTSTATUS sam_{get,set}_user_nt_pwd(SAM_USER_HANDLE *user, DATA_BLOB *nt_pwd)
+NTSTATUS sam_{get,set}_user_plain_pwd(SAM_USER_HANDLE *user, DATA_BLOB *plaintext_pwd)
+NTSTATUS sam_{get,set}_user_acct_ctrl(SAM_USER_HANDLE *user, uint16 *acct_ctrl)
+NTSTATUS sam_{get,set}_user_logon_divs(SAM_USER_HANDLE *user, uint16 *logon_divs)
+NTSTATUS sam_{get,set}_user_hours(SAM_USER_HANDLE *user, uint32 *hours_len, uint8 **hours)
+NTSTATUS sam_{get,set}_user_logon_time(SAM_USER_HANDLE *user, NTTIME *logon_time)
+NTSTATUS sam_{get,set}_user_logoff_time(SAM_USER_HANDLE *user, NTTIME *logoff_time)
+NTSTATUS sam_{get,set}_user_kickoff_time(SAM_USER_HANDLE *user, NTTIME kickoff_time)
+NTSTATUS sam_{get,set}_user_pwd_last_set(SAM_USER_HANDLE *user, NTTIME pwd_last_set)
+NTSTATUS sam_{get,set}_user_pwd_can_change(SAM_USER_HANDLE *user, NTTIME pwd_can_change)
+NTSTATUS sam_{get,set}_user_pwd_must_change(SAM_USER_HANDLE *user, NTTIME pwd_must_change)
+NTSTATUS sam_{get,set}_user_unknown_1(SAM_USER_HANDLE *user, char **unknown_1)
+NTSTATUS sam_{get,set}_user_unknown_2(SAM_USER_HANDLE *user, uint32 *unknown_2)
+NTSTATUS sam_{get,set}_user_unknown_3(SAM_USER_HANDLE *user, uint32 *unknown_3)
+NTSTATUS sam_{get,set}_user_unknown_4(SAM_USER_HANDLE *user, uint32 *unknown_4)
+
+NTSTATUS sam_get_group_sid(SAM_GROUP_HANDLE *group, DOM_SID **sid)
+NTSTATUS sam_get_group_typ(SAM_GROUP_HANDLE *group, uint32 *typ)
+NTSTATUS sam_{get,set}_group_name(SAM_GROUP_HANDLE *group, char **group_name)
+NTSTATUS sam_{get,set}_group_comment(SAM_GROUP_HANDLE *group, char **comment)
+NTSTATUS sam_{get,set}_group_priv_set(SAM_GROUP_HANDLE *group, PRIVILEGE_SET *priv_set)
\ No newline at end of file diff --git a/source3/script/cvslog.pl b/source3/script/cvslog.pl new file mode 100755 index 0000000000..f3d020aa72 --- /dev/null +++ b/source3/script/cvslog.pl @@ -0,0 +1,102 @@ +#!/usr/bin/perl -w + +my ( $tag, $filename, $date ); +my ( $tmp, $change_flag ); + +if ( $#ARGV != 2 ) { + + print "Usage: ", $0, " cvstag date file\n"; + exit 1; +} + +$tag = $ARGV[0]; +$date = $ARGV[1]; +$filename = $ARGV[2]; + +print STDERR "$filename\n"; + +open ( CVSLOG, "cvs log -d\"$date\" $filename |" ) || die $!; + +## +## First get the branch revision number +## +undef $revision; +while ( !defined($revision) ) { + if ( eof( \*CVSLOG ) ) { + print STDERR "Premature end of cvs log output!\n"; + exit (1); + } + + $string = <CVSLOG>; + chomp( $string ); + + if ( $string =~ /$tag:/ ) { + ( $tmp, $revision ) = split( /:/, $string ); + $revision =~ s/\s+//g; + $revision =~ s/\.0\./\./g; + } +} + +## +## Setup the beginning of the first record +## +$string = ""; +while ( $string !~ /^-+/ ) { + $string = <CVSLOG>; + exit(0) if ( eof(\*CVSLOG) ); +} + +## +## Loop starting at the revision number for the entry +## + +while ( $string = <CVSLOG> ) { + + ($tmp, $entry_rev) = split( /\s+/, $string ); + if ( equal_revision( $revision, $entry_rev ) ) { + if ( ! defined($change_flag) ) { + print "++++++++++++++++++++++++++++++++++++++++++++++++++\n"; + print "## $filename\n"; + print "++\n"; + $change_flag = 1; + } + + while ( $string !~ /^-+/ && !eof(CVSLOG) ) { + print "$string"; + $string = <CVSLOG>; + } + } + else { + while ( ($string !~ /^-+/) && !eof(CVSLOG) ) { + $string = <CVSLOG>; + } + } +} + +close( CVSLOG ); +exit 0; + +############################################################## +## +sub equal_revision { + my ( $branch, $newfile ) = @_; + my ( $indx ); + my ( @branch_rev, @file_rev ); + + @branch_rev = split( /\./, $branch ); + @file_rev = split( /\./, $newfile ); + + return 0 if ( $#branch_rev != ($#file_rev - 1) ); + + $indx = 0; + while( $indx <= $#branch_rev ) { + if ( $branch_rev[$indx] != $file_rev[$indx] ) { + return 0; + } + $indx++; + } + + return 1; +} + + |