This commit was manufactured by cvs2svn to create branch 'SAMBA_3_0'.
authorcvs2svn Import User <samba-bugs@samba.org>
Tue, 18 Mar 2003 07:09:24 +0000 (07:09 +0000)
committercvs2svn Import User <samba-bugs@samba.org>
Tue, 18 Mar 2003 07:09:24 +0000 (07:09 +0000)
16 files changed:
docs/docbook/projdoc/securing-samba.sgml [new file with mode: 0644]
source/client/mount.cifs.c [new file with mode: 0755]
source/include/srvstr.h [new file with mode: 0644]
source/lib/clobber.c [new file with mode: 0644]
source/libsmb/ntlmssp_sign.c [new file with mode: 0644]
source/libsmb/smb_signing.c [new file with mode: 0644]
source/rpc_client/.cvsignore [new file with mode: 0644]
source/stf/.cvsignore [new file with mode: 0644]
source/stf/README.stf [new file with mode: 0644]
source/stf/info3cache.py [new file with mode: 0755]
source/stf/notes.txt [new file with mode: 0644]
source/stf/osver.py [new file with mode: 0755]
source/stf/spoolss.py [new file with mode: 0755]
source/stf/stf.py [new file with mode: 0755]
source/stf/test.py [new file with mode: 0755]
source/torture/t_stringoverflow.c [new file with mode: 0644]

diff --git a/docs/docbook/projdoc/securing-samba.sgml b/docs/docbook/projdoc/securing-samba.sgml
new file mode 100644 (file)
index 0000000..bfedc54
--- /dev/null
@@ -0,0 +1,181 @@
+<chapter id="securing-samba">
+
+<chapterinfo>
+       <author>
+               <firstname>Andrew</firstname><surname>Tridgell</surname>
+               <affiliation><orgname>Samba Team</orgname></affiliation>
+       </author>
+       <pubdate>17 March 2003</pubdate>
+</chapterinfo>
+
+<title>Securing Samba</title>
+
+<sect1>
+<title>Introduction</title>
+<para>
+This note was attached to the Samba 2.2.8 release notes as it contained an
+important security fix.  The information contained here applies to Samba
+installations in general.
+</para>
+
+</sect1>
+
+<sect1>
+<title>Using host based protection</title>
+
+<para>
+In many installations of Samba the greatest threat comes for outside
+your immediate network. By default Samba will accept connections from
+any host, which means that if you run an insecure version of Samba on
+a host that is directly connected to the Internet you can be
+especially vulnerable.
+</para>
+
+<para>
+One of the simplest fixes in this case is to use the 'hosts allow' and
+'hosts deny' options in the Samba smb.conf configuration file to only
+allow access to your server from a specific range of hosts. An example
+might be:
+</para>
+
+<para><programlisting>
+  hosts allow = 127.0.0.1 192.168.2.0/24 192.168.3.0/24
+  hosts deny = 0.0.0.0/0
+</programlisting></para>
+
+<para>
+The above will only allow SMB connections from 'localhost' (your own
+computer) and from the two private networks 192.168.2 and
+192.168.3. All other connections will be refused connections as soon
+as the client sends its first packet. The refusal will be marked as a
+'not listening on called name' error.
+</para>
+
+</sect1>
+
+<sect1>
+
+<title>Using interface protection</title>
+
+<para>
+By default Samba will accept connections on any network interface that
+it finds on your system. That means if you have a ISDN line or a PPP
+connection to the Internet then Samba will accept connections on those
+links. This may not be what you want.
+</para>
+
+<para>
+You can change this behaviour using options like the following:
+</para>
+
+<para><programlisting>
+  interfaces = eth* lo
+  bind interfaces only = yes
+</programlisting><para>
+
+<para>
+This tells Samba to only listen for connections on interfaces with a
+name starting with 'eth' such as eth0, eth1, plus on the loopback
+interface called 'lo'. The name you will need to use depends on what
+OS you are using, in the above I used the common name for Ethernet
+adapters on Linux.
+</para>
+
+<para>
+If you use the above and someone tries to make a SMB connection to
+your host over a PPP interface called 'ppp0' then they will get a TCP
+connection refused reply. In that case no Samba code is run at all as
+the operating system has been told not to pass connections from that
+interface to any process.
+</para>
+
+</sect1>
+
+<sect1>
+<title>Using a firewall</title>
+
+<para>
+Many people use a firewall to deny access to services that they don't
+want exposed outside their network. This can be a very good idea,
+although I would recommend using it in conjunction with the above
+methods so that you are protected even if your firewall is not active
+for some reason.
+</para>
+
+<para>
+If you are setting up a firewall then you need to know what TCP and
+UDP ports to allow and block. Samba uses the following:
+</para>
+
+<para><programlisting>
+UDP/137    - used by nmbd
+UDP/138    - used by nmbd
+TCP/139    - used by smbd
+TCP/445    - used by smbd
+</programlisting></para>
+
+<para>
+The last one is important as many older firewall setups may not be
+aware of it, given that this port was only added to the protocol in
+recent years. 
+</para>
+
+</sect1>
+
+<sect1>
+<title>Using a IPC$ share deny</title>
+
+<para>
+If the above methods are not suitable, then you could also place a
+more specific deny on the IPC$ share that is used in the recently
+discovered security hole. This allows you to offer access to other
+shares while denying access to IPC$ from potentially untrustworthy
+hosts.
+</para>
+
+<para>
+To do that you could use:
+</para>
+
+<para><programlisting>
+  [ipc$]
+     hosts allow = 192.168.115.0/24 127.0.0.1
+     hosts deny = 0.0.0.0/0
+</programlisting></para>
+
+<para>
+this would tell Samba that IPC$ connections are not allowed from
+anywhere but the two listed places (localhost and a local
+subnet). Connections to other shares would still be allowed. As the
+IPC$ share is the only share that is always accessible anonymously
+this provides some level of protection against attackers that do not
+know a username/password for your host.
+</para>
+
+<para>
+If you use this method then clients will be given a 'access denied'
+reply when they try to access the IPC$ share. That means that those
+clients will not be able to browse shares, and may also be unable to
+access some other resources. 
+</para>
+
+<para>
+This is not recommended unless you cannot use one of the other
+methods listed above for some reason.
+</para>
+
+</sect1>
+
+<sect1>
+<title>Upgrading Samba</title>
+
+<para>
+Please check regularly on http://www.samba.org/ for updates and
+important announcements.  Occasionally security releases are made and 
+it is highly recommended to upgrade Samba when a security vulnerability
+is discovered.
+</para>
+
+</sect1>
+
+</chapter>
diff --git a/source/client/mount.cifs.c b/source/client/mount.cifs.c
new file mode 100755 (executable)
index 0000000..7167859
--- /dev/null
@@ -0,0 +1,557 @@
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <getopt.h>
+#include <errno.h>
+#include <netdb.h>
+#include <string.h>
+#include <mntent.h>
+
+#define MOUNT_CIFS_VERSION "1"
+
+extern char *getusername(void);
+
+char * thisprogram;
+int verboseflag = 0;
+static int got_password = 0;
+static int got_user = 0;
+static int got_domain = 0;
+static int got_ip = 0;
+static int got_unc = 0;
+static int got_uid = 0;
+static int got_gid = 0;
+static char * user_name = NULL;
+char * mountpassword = NULL;
+
+
+void mount_cifs_usage()
+{
+       printf("\nUsage:  %s remotetarget dir\n", thisprogram);
+       printf("\nMount the remotetarget, specified as either a UNC name or ");
+       printf(" CIFS URL, to the local directory, dir.\n");
+
+       exit(1);
+}
+
+/* caller frees username if necessary */
+char * getusername() {
+       char *username = NULL;
+       struct passwd *password = getpwuid(getuid());
+
+       if (password) {
+               username = password->pw_name;
+       }
+       return username;
+}
+
+char * parse_cifs_url(unc_name)
+{
+       printf("\ncifs url %s\n",unc_name);
+}
+
+int parse_options(char * options)
+{
+       char * data;
+       char * value = 0;
+
+       if (!options)
+               return 1;
+
+       while ((data = strsep(&options, ",")) != NULL) {
+               if (!*data)
+                       continue;
+               if ((value = strchr(data, '=')) != NULL) {
+                       *value++ = '\0';
+               }
+               if (strncmp(data, "user", 4) == 0) {
+                       if (!value || !*value) {
+                               printf("invalid or missing username\n");
+                               return 1;       /* needs_arg; */
+                       }
+                       if (strnlen(value, 260) < 260) {
+                               got_user=1;
+                               /* BB add check for format user%pass */
+                               /* if(strchr(username%passw) got_password = 1) */
+                       } else {
+                               printf("username too long\n");
+                               return 1;
+                       }
+       } else if (strncmp(data, "pass", 4) == 0) {
+               if (!value || !*value) {
+                       if(got_password) {
+                               printf("password specified twice, ignoring second\n");
+                       } else
+                               got_password = 1;
+               } else if (strnlen(value, 17) < 17) {
+                       got_password = 1;
+               } else {
+                       printf("password too long\n");
+                       return 1;
+               }
+       } else if (strncmp(data, "ip", 2) == 0) {
+               if (!value || !*value) {
+                       printf("target ip address argument missing");
+               } else if (strnlen(value, 35) < 35) {
+                       got_ip = 1;
+               } else {
+                       printf("ip address too long\n");
+                       return 1;
+               }
+       } else if ((strncmp(data, "unc", 3) == 0)
+                  || (strncmp(data, "target", 6) == 0)
+                  || (strncmp(data, "path", 4) == 0)) {
+               if (!value || !*value) {
+                       printf("invalid path to network resource\n");
+                       return 1;  /* needs_arg; */
+               } else if(strnlen(value,5) < 5) {
+                       printf("UNC name too short");
+               }
+
+               if (strnlen(value, 300) < 300) {
+                       got_unc = 1;
+                       if (strncmp(value, "//", 2) == 0) {
+                               if(got_unc)
+                                       printf("unc name specified twice, ignoring second\n");
+                               else
+                                       got_unc = 1;
+                       } else if (strncmp(value, "\\\\", 2) != 0) {                       
+                               printf("UNC Path does not begin with // or \\\\ \n");
+                               return 1;
+                       } else {
+                               if(got_unc)
+                                       printf("unc name specified twice, ignoring second\n");
+                               else
+                                       got_unc = 1;
+                       }
+               } else {
+                       printf("CIFS: UNC name too long\n");
+                       return 1;
+               }
+       } else if ((strncmp(data, "domain", 3) == 0)
+                  || (strncmp(data, "workgroup", 5) == 0)) {
+               if (!value || !*value) {
+                       printf("CIFS: invalid domain name\n");
+                       return 1;       /* needs_arg; */
+               }
+               if (strnlen(value, 65) < 65) {
+                       got_domain = 1;
+               } else {
+                       printf("domain name too long\n");
+                       return 1;
+               }
+       } else if (strncmp(data, "uid", 3) == 0) {
+               if (value && *value) {
+                       got_uid = 1;
+               }
+       } else if (strncmp(data, "gid", 3) == 0) {
+               if (value && *value) {
+                       got_gid = 1;
+               }
+       } /* else if (strnicmp(data, "file_mode", 4) == 0) {
+               if (value && *value) {
+                       vol->file_mode =
+                               simple_strtoul(value, &value, 0);
+               }
+       } else if (strnicmp(data, "dir_mode", 3) == 0) {
+               if (value && *value) {
+                       vol->dir_mode =
+                               simple_strtoul(value, &value, 0);
+               }
+       } else if (strnicmp(data, "port", 4) == 0) {
+               if (value && *value) {
+                       vol->port =
+                               simple_strtoul(value, &value, 0);
+               }
+       } else if (strnicmp(data, "rsize", 5) == 0) {
+               if (value && *value) {
+                       vol->rsize =
+                               simple_strtoul(value, &value, 0);
+               }
+       } else if (strnicmp(data, "wsize", 5) == 0) {
+               if (value && *value) {
+                       vol->wsize =
+                               simple_strtoul(value, &value, 0);
+               }
+       } else if (strnicmp(data, "version", 3) == 0) {
+               
+       } else if (strnicmp(data, "rw", 2) == 0) {
+               
+       } else
+               printf("CIFS: Unknown mount option %s\n",data); */
+       }
+       return 0;
+}
+
+/* Note that caller frees the returned buffer if necessary */
+char * parse_server(char * unc_name)
+{
+       int length = strnlen(unc_name,1024);
+       char * share;
+       char * ipaddress_string = NULL;
+       struct hostent * host_entry;
+       struct in_addr server_ipaddr;
+       int rc,j;
+       char temp[64];
+
+       if(length > 1023) {
+               printf("mount error: UNC name too long");
+               return 0;
+       }
+       if (strncasecmp("cifs://",unc_name,7) == 0)
+               return parse_cifs_url(unc_name+7);
+       if (strncasecmp("smb://",unc_name,6) == 0) {
+               return parse_cifs_url(unc_name+6);
+       }
+
+       if(length < 3) {
+               /* BB add code to find DFS root here */
+               printf("\nMounting the DFS root for domain not implemented yet");
+               return 0;
+       } else {
+               /* BB add support for \\\\ not just // */
+               if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
+                       printf("mount error: improperly formatted UNC name.");
+                       printf(" %s does not begin with \\\\ or //\n",unc_name);
+                       return 0;
+               } else {
+                       unc_name[0] = '\\';
+                       unc_name[1] = '\\';
+                       unc_name += 2;
+                       if ((share = strchr(unc_name, '/')) || 
+                               (share = strchr(unc_name,'\\'))) {
+                               *share = 0;  /* temporarily terminate the string */
+                               share += 1;
+                               host_entry = gethostbyname(unc_name);
+                               *(share - 1) = '\\'; /* put the slash back */
+/*                             rc = getipnodebyname(unc_name, AF_INET, AT_ADDRCONFIG ,&rc);*/
+                               if(host_entry == NULL) {
+                                       printf("mount error: could not find target server. TCP name %s not found ", unc_name);
+                                       printf(" rc = %d\n",rc);
+                                       return 0;
+                               }
+                               else {
+                                       /* BB should we pass an alternate version of the share name as Unicode */
+                                       /* BB what about ipv6? BB */
+                                       /* BB add retries with alternate servers in list */
+
+                                       memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
+
+                                       ipaddress_string = inet_ntoa(server_ipaddr);                                                                                     
+                                       if(ipaddress_string == NULL) {
+                                               printf("mount error: could not get valid ip address for target server\n");
+                                               return 0;
+                                       }
+                                       return ipaddress_string; 
+                               }
+                       } else {
+                               /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
+                               printf("Mounting the DFS root for a particular server not implemented yet\n");
+                               return 0;
+                       }
+               }
+       }
+}
+
+static struct option longopts[] = {
+       { "all", 0, 0, 'a' },
+       { "help", 0, 0, 'h' },
+       { "read-only", 0, 0, 'r' },
+       { "ro", 0, 0, 'r' },
+       { "verbose", 0, 0, 'v' },
+       { "version", 0, 0, 'V' },
+       { "read-write", 0, 0, 'w' },
+       { "rw", 0, 0, 'w' },
+       { "options", 1, 0, 'o' },
+       { "types", 1, 0, 't' },
+       { "replace", 0, 0, 129 },
+       { "after", 0, 0, 130 },
+       { "before", 0, 0, 131 },
+       { "over", 0, 0, 132 },
+       { "move", 0, 0, 133 },
+       { "rsize",1, 0, 136 },
+       { "wsize",1, 0, 137 },
+       { "uid", 1, 0, 138},
+       { "gid", 1, 0, 139},
+       { "uuid",1,0,'U' },
+       { "user",1,0,140},
+       { "username",1,0,140},
+       { "dom",1,0,141},
+       { "domain",1,0,141},
+       { "password",1,0,142},
+       { NULL, 0, 0, 0 }
+};
+
+int main(int argc, char ** argv)
+{
+       int c;
+       int flags = MS_MANDLOCK | MS_MGC_VAL;
+       char * orgoptions = NULL;
+       char * share_name = NULL;
+       char * domain_name = NULL;
+       char * ipaddr = NULL;
+       char * uuid = NULL;
+       char * mountpoint;
+       char * options;
+       int rc,i;
+       int rsize = 0;
+       int wsize = 0;
+       int nomtab = 0;
+       int uid = 0;
+       int gid = 0;
+       int optlen = 0;
+       struct stat statbuf;
+       struct utsname sysinfo;
+       struct mntent mountent;
+       FILE * pmntfile;
+
+       /* setlocale(LC_ALL, "");
+       bindtextdomain(PACKAGE, LOCALEDIR);
+       textdomain(PACKAGE); */
+
+       if(argc && argv) {
+               thisprogram = argv[0];
+       }
+       if(thisprogram == NULL)
+               thisprogram = "mount.cifs";
+
+       uname(&sysinfo);
+       /* BB add workstation name and domain and pass down */
+/*#ifdef _GNU_SOURCE
+       printf(" node: %s machine: %s\n", sysinfo.nodename,sysinfo.machine);
+#endif*/
+       if(argc < 3)
+               mount_cifs_usage();
+       share_name = argv[1];
+       mountpoint = argv[2];
+       /* add sharename in opts string as unc= parm */
+
+       while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsU:vVwt:",
+                        longopts, NULL)) != -1) {
+               switch (c) {
+/*     case 'a':              
+               ++mount_all;
+               break;
+       case 'f':              
+               ++fake;
+               break;
+       case 'F':
+               ++optfork;
+               break; */
+               case 'h':        /* help */
+                       mount_cifs_usage ();
+                       break;
+/*     case 'i':
+               external_allowed = 0;
+               break;
+       case 'l':
+               list_with_volumelabel = 1;
+               break;
+       case 'L':
+               volumelabel = optarg;
+               break; */
+       case 'n':
+               ++nomtab;
+               break;
+       case 'o':
+               if (orgoptions) {
+                       orgoptions = strcat(orgoptions, ",");
+                       orgoptions = strcat(orgoptions,optarg);
+               } else
+                       orgoptions = strdup(optarg);
+               break;
+
+/*     case 'O':
+               if (test_opts)
+                       test_opts = xstrconcat3(test_opts, ",", optarg);
+               else
+                       test_opts = xstrdup(optarg);
+               break;*/
+               case 'r':  /* mount readonly */
+                       flags |= MS_RDONLY;;
+                       break;
+               case 'U':
+                       uuid = optarg;
+                       break;
+               case 'v':
+                       ++verboseflag;
+                       break;
+/*     case 'V':          
+               printf ("mount: %s\n", version);
+               exit (0);*/
+               case 'w':
+                       flags &= ~MS_RDONLY;;
+                       break;
+/*     case 0:
+               break;
+
+       case 128: 
+               mounttype = MS_BIND;
+               break;
+       case 129: 
+               mounttype = MS_REPLACE;
+               break;
+       case 130: 
+               mounttype = MS_AFTER;
+               break;
+       case 131: 
+               mounttype = MS_BEFORE;
+               break;
+       case 132: 
+               mounttype = MS_OVER;
+               break;
+       case 133: 
+               mounttype = MS_MOVE;
+               break;
+       case 135:
+               mounttype = (MS_BIND | MS_REC);
+               break; */
+               case 136:
+                       rsize = atoi(optarg) ;
+                       break;
+               case 137:
+                       wsize = atoi(optarg);
+                       break;
+               case 138:
+                       uid = atoi(optarg);
+                       break;
+               case 139:
+                       gid = atoi(optarg);
+                       break;
+               case 140:
+                       got_user = 1;
+                       user_name = optarg;
+                       break;
+               case 141:
+                       domain_name = optarg;
+                       break;
+               case 142:
+                       got_password = 1;
+                        mountpassword = optarg;
+                       break;
+               case '?':
+               default:
+                       mount_cifs_usage ();
+               }
+       }
+
+       /* canonicalize the path in argv[1]? */
+
+       if(stat (mountpoint, &statbuf)) {
+               printf("mount error: mount point %s does not exist\n",mountpoint);
+               return -1;
+       }
+       if (S_ISDIR(statbuf.st_mode) == 0) {
+               printf("mount error: mount point %s is not a directory\n",mountpoint);
+               return -1;
+       }
+
+       if(geteuid()) {
+               printf("mount error: permission denied, not superuser and cifs.mount not installed SUID\n"); 
+               return -1;
+       }
+
+       ipaddr = parse_server(share_name);
+/*     if(share_name == NULL)
+               return 1; */
+       if (parse_options(strdup(orgoptions)))
+               return 1;
+
+       if(got_user == 0)
+               user_name = getusername();
+       
+/*     check username for user%password format */
+
+       if(got_password == 0) {
+               if (getenv("PASSWD")) {
+                       mountpassword = malloc(33);
+                       if(mountpassword) {
+                               strncpy(mountpassword,getenv("PASSWD"),32);
+                               got_password = 1;
+                       }
+/*             } else if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
+                       get_password_file();
+                       got_password = 1;*/ /* BB add missing function */
+               } else {
+                       mountpassword = getpass("Password: "); /* BB obsolete */
+                       got_password = 1;
+               }
+       }
+       /* FIXME launch daemon (handles dfs name resolution and credential change) 
+          remember to clear parms and overwrite password field before launching */
+       if(orgoptions) {
+               optlen = strlen(orgoptions);
+       } else
+               optlen = 0;
+       if(share_name)
+               optlen += strlen(share_name) + 4;
+       if(user_name)
+               optlen += strlen(user_name) + 6;
+       if(ipaddr)
+               optlen += strlen(ipaddr) + 4;
+       if(mountpassword)
+               optlen += strlen(mountpassword) + 6;
+       options = malloc(optlen + 10);
+
+    options[0] = 0;
+       strncat(options,"unc=",4);
+       strcat(options,share_name);
+       if(ipaddr) {
+               strncat(options,",ip=",4);
+               strcat(options,ipaddr);
+       } 
+       if(user_name) {
+               strncat(options,",user=",6);
+               strcat(options,user_name);
+       } 
+       if(mountpassword) {
+               strncat(options,",pass=",6);
+               strcat(options,mountpassword);
+       }
+       strncat(options,",ver=",5);
+       strcat(options,MOUNT_CIFS_VERSION);
+
+       if(orgoptions) {
+               strcat(options,",");
+               strcat(options,orgoptions);
+       }
+       /* printf("\noptions %s \n",options);*/
+       if(mount(share_name, mountpoint, "cifs", flags, options)) {
+       /* remember to kill daemon on error */
+               switch (errno) {
+               case 0:
+                       printf("mount failed but no error number set\n");
+                       return 0;
+               case ENODEV:
+                       printf("mount error: cifs filesystem not supported by the system\n");
+                       break;
+               default:
+                       printf("mount error %d = %s",errno,strerror(errno));
+               }
+               printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
+               return -1;
+       } else {
+               pmntfile = setmntent(MOUNTED, "a+");
+               if(pmntfile) {
+                       mountent.mnt_fsname = share_name;
+                       mountent.mnt_dir = mountpoint; 
+                       mountent.mnt_type = "cifs"; 
+                       mountent.mnt_opts = "";
+                       mountent.mnt_freq = 0;
+                       mountent.mnt_passno = 0;
+                       rc = addmntent(pmntfile,&mountent);
+                       endmntent(pmntfile);
+               } else {
+                   printf("could not update mount table\n");
+               }
+       }
+       return 0;
+}
+
diff --git a/source/include/srvstr.h b/source/include/srvstr.h
new file mode 100644 (file)
index 0000000..a433e0e
--- /dev/null
@@ -0,0 +1,36 @@
+/* 
+   Unix SMB/CIFS implementation.
+   server specific string routines
+   Copyright (C) Andrew Tridgell 2001
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#define srvstr_push(base_ptr, dest, src, dest_len, flags) \
+    push_string(base_ptr, dest, src, dest_len, flags)
+
+#define srvstr_pull(base_ptr, dest, src, dest_len, src_len, flags) \
+    pull_string(base_ptr, dest, src, dest_len, src_len, flags)
+
+/* pull a string from the smb_buf part of a packet. In this case the
+   string can either be null terminated or it can be terminated by the
+   end of the smbbuf area 
+*/
+
+#define srvstr_pull_buf(inbuf, dest, src, dest_len, flags) \
+    pull_string(inbuf, dest, src, dest_len, smb_bufrem(inbuf, src), flags)
+
diff --git a/source/lib/clobber.c b/source/lib/clobber.c
new file mode 100644 (file)
index 0000000..fb3a0dc
--- /dev/null
@@ -0,0 +1,60 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba utility functions
+   Copyright (C) Martin Pool     2003
+   Copyright (C) Andrew Bartlett 2003
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#ifdef DEVELOPER
+const char *global_clobber_region_function;
+unsigned int global_clobber_region_line;
+#endif
+
+/**
+ * In developer builds, clobber a region of memory.
+ *
+ * If we think a string buffer is longer than it really is, this ought
+ * to make the failure obvious, by segfaulting (if in the heap) or by
+ * killing the return address (on the stack), or by trapping under a
+ * memory debugger.
+ *
+ * This is meant to catch possible string overflows, even if the
+ * actual string copied is not big enough to cause an overflow.
+ *
+ * In addition, under Valgrind the buffer is marked as uninitialized.
+ **/
+void clobber_region(const char *fn, unsigned int line, char *dest, size_t len)
+{
+#ifdef DEVELOPER
+       global_clobber_region_function = fn;
+       global_clobber_region_line = line;
+
+       /* F1 is odd and 0xf1f1f1f1 shouldn't be a valid pointer */
+       memset(dest, 0xF1, len);
+#ifdef VALGRIND
+       /* Even though we just wrote to this, from the application's
+        * point of view it is not initialized.
+        *
+        * (This is not redundant with the clobbering above.  The
+        * marking might not actually take effect if we're not running
+        * under valgrind.) */
+       VALGRIND_MAKE_WRITABLE(dest, len);
+#endif /* VALGRIND */
+#endif /* DEVELOPER */
+}
diff --git a/source/libsmb/ntlmssp_sign.c b/source/libsmb/ntlmssp_sign.c
new file mode 100644 (file)
index 0000000..f51d532
--- /dev/null
@@ -0,0 +1,208 @@
+/* 
+ *  Unix SMB/CIFS implementation.
+ *  Version 3.0
+ *  NTLMSSP Signing routines
+ *  Copyright (C) Luke Kenneth Casson Leighton 1996-2001
+ *  Copyright (C) Andrew Bartlett 2003
+ *  
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software Foundation,
+ *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "includes.h"
+
+#define CLI_SIGN "session key to client-to-server signing key magic constant"
+#define CLI_SEAL "session key to client-to-server sealing key magic constant"
+#define SRV_SIGN "session key to server-to-client signing key magic constant"
+#define SRV_SEAL "session key to server-to-client sealing key magic constant"
+
+static void NTLMSSPcalc_ap( unsigned char *hash, unsigned char *data, int len)
+{
+    unsigned char index_i = hash[256];
+    unsigned char index_j = hash[257];
+    int ind;
+
+    for (ind = 0; ind < len; ind++)
+    {
+        unsigned char tc;
+        unsigned char t;
+
+        index_i++;
+        index_j += hash[index_i];
+
+        tc = hash[index_i];
+        hash[index_i] = hash[index_j];
+        hash[index_j] = tc;
+
+        t = hash[index_i] + hash[index_j];
+        data[ind] = data[ind] ^ hash[t];
+    }
+
+    hash[256] = index_i;
+    hash[257] = index_j;
+}
+
+static void calc_hash(unsigned char *hash, const char *k2, int k2l)
+{
+       unsigned char j = 0;
+       int ind;
+
+       for (ind = 0; ind < 256; ind++)
+       {
+               hash[ind] = (unsigned char)ind;
+       }
+
+       for (ind = 0; ind < 256; ind++)
+       {
+               unsigned char tc;
+
+               j += (hash[ind] + k2[ind%k2l]);
+
+               tc = hash[ind];
+               hash[ind] = hash[j];
+               hash[j] = tc;
+       }
+
+       hash[256] = 0;
+       hash[257] = 0;
+}
+
+static void calc_ntlmv2_hash(unsigned char hash[16], char digest[16],
+                            const char encrypted_response[16], 
+                            const char *constant)
+{
+       struct MD5Context ctx3;
+
+       MD5Init(&ctx3);
+       MD5Update(&ctx3, encrypted_response, 5);
+       MD5Update(&ctx3, constant, strlen(constant));
+       MD5Final(digest, &ctx3);
+
+       calc_hash(hash, digest, 16);
+}
+
+static NTSTATUS ntlmssp_make_packet_signiture(NTLMSSP_CLIENT_STATE *ntlmssp_state,
+                                             const uchar *data, size_t length, 
+                                             DATA_BLOB *sig) 
+{
+       if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+               HMACMD5Context ctx;
+               char seq_num[4];
+               uchar digest[16];
+               SIVAL(seq_num, 0, &ntlmssp_state->ntlmssp_seq_num);
+
+               hmac_md5_init_limK_to_64(ntlmssp_state->cli_sign_const, 16, &ctx);
+               hmac_md5_update(seq_num, 4, &ctx);
+               hmac_md5_update(data, length, &ctx);
+               hmac_md5_final(digest, &ctx);
+
+               if (!msrpc_gen(sig, "Bd", digest, sizeof(digest), ntlmssp_state->ntlmssp_seq_num)) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+              
+               NTLMSSPcalc_ap(ntlmssp_state->cli_seal_hash,  sig->data, sig->length);
+       } else {
+               uint32 crc;
+               crc = crc32_calc_buffer(data, length);
+               if (!msrpc_gen(sig, "ddd", 0, crc, ntlmssp_state->ntlmssp_seq_num)) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+               
+               NTLMSSPcalc_ap(ntlmssp_state->ntlmssp_hash, sig->data, sig->length);
+       }
+       return NT_STATUS_OK;
+}
+
+NTSTATUS ntlmssp_client_sign_packet(NTLMSSP_CLIENT_STATE *ntlmssp_state,
+                                          const uchar *data, size_t length, 
+                                          DATA_BLOB *sig) 
+{
+       ntlmssp_state->ntlmssp_seq_num++;
+       return ntlmssp_make_packet_signiture(ntlmssp_state, data, length, sig);
+}
+
+/**
+ * Check the signature of an incoming packet 
+ * @note caller *must* check that the signature is the size it expects 
+ *
+ */
+
+NTSTATUS ntlmssp_client_check_packet(NTLMSSP_CLIENT_STATE *ntlmssp_state,
+                                          const uchar *data, size_t length, 
+                                          const DATA_BLOB *sig) 
+{
+       DATA_BLOB local_sig;
+       NTSTATUS nt_status;
+
+       if (sig->length < 8) {
+               DEBUG(0, ("NTLMSSP packet check failed due to short signiture (%u bytes)!\n", 
+                         sig->length));
+       }
+
+       nt_status = ntlmssp_make_packet_signiture(ntlmssp_state, data, 
+                                                 length, &local_sig);
+       
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DEBUG(0, ("NTLMSSP packet check failed with %s\n", nt_errstr(nt_status)));
+               return nt_status;
+       }
+       
+       if (memcmp(sig->data, local_sig.data, MIN(sig->length, local_sig.length)) == 0) {
+               return NT_STATUS_OK;
+       } else {
+               DEBUG(0, ("NTLMSSP packet check failed due to invalid signiture!\n"));
+               return NT_STATUS_ACCESS_DENIED;
+       }
+}
+
+/**
+   Initialise the state for NTLMSSP signing.
+*/
+NTSTATUS ntlmssp_client_sign_init(NTLMSSP_CLIENT_STATE *ntlmssp_state)
+{
+       unsigned char p24[24];
+       unsigned char lm_hash[16];
+
+       if (!ntlmssp_state->lm_resp.data) {
+               /* can't sign or check signitures yet */ 
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+                           
+       E_deshash(ntlmssp_state->password, lm_hash);
+               
+       NTLMSSPOWFencrypt(lm_hash, ntlmssp_state->lm_resp.data, p24);
+       
+       if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2)
+       {
+               calc_ntlmv2_hash(ntlmssp_state->cli_sign_hash, ntlmssp_state->cli_sign_const, p24, CLI_SIGN);
+               calc_ntlmv2_hash(ntlmssp_state->cli_seal_hash, ntlmssp_state->cli_seal_const, p24, CLI_SEAL);
+               calc_ntlmv2_hash(ntlmssp_state->srv_sign_hash, ntlmssp_state->srv_sign_const, p24, SRV_SIGN);
+               calc_ntlmv2_hash(ntlmssp_state->srv_seal_hash, ntlmssp_state->srv_seal_const, p24, SRV_SEAL);
+       }
+       else
+       {
+               char k2[8];
+               memcpy(k2, p24, 5);
+               k2[5] = 0xe5;
+               k2[6] = 0x38;
+               k2[7] = 0xb0;
+               
+               calc_hash(ntlmssp_state->ntlmssp_hash, k2, 8);
+       }
+
+       ntlmssp_state->ntlmssp_seq_num = 0;
+
+       ZERO_STRUCT(lm_hash);
+       return NT_STATUS_OK;
+}
diff --git a/source/libsmb/smb_signing.c b/source/libsmb/smb_signing.c
new file mode 100644 (file)
index 0000000..c3538ee
--- /dev/null
@@ -0,0 +1,461 @@
+/* 
+   Unix SMB/CIFS implementation.
+   SMB Signing Code
+   Copyright (C) Jeremy Allison 2002.
+   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2002-2003
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+struct smb_basic_signing_context {
+       DATA_BLOB mac_key;
+       uint32 send_seq_num;
+       uint32 reply_seq_num;
+};
+
+/***********************************************************
+ SMB signing - Common code before we set a new signing implementation
+************************************************************/
+
+static BOOL set_smb_signing_common(struct cli_state *cli) 
+{
+       if (!cli->sign_info.negotiated_smb_signing 
+           && !cli->sign_info.mandetory_signing) {
+               return False;
+       }
+
+       if (cli->sign_info.doing_signing) {
+               return False;
+       }
+       
+       if (cli->sign_info.free_signing_context)
+               cli->sign_info.free_signing_context(cli);
+
+       /* These calls are INCOMPATIBLE with SMB signing */
+       cli->readbraw_supported = False;
+       cli->writebraw_supported = False;
+       
+       return True;
+}
+
+/***********************************************************
+ SMB signing - Common code for 'real' implementations
+************************************************************/
+
+static BOOL set_smb_signing_real_common(struct cli_state *cli) 
+{
+       if (cli->sign_info.mandetory_signing) {
+               DEBUG(5, ("Mandatory SMB signing enabled!\n"));
+               cli->sign_info.doing_signing = True;
+       }
+
+       DEBUG(5, ("SMB signing enabled!\n"));
+
+       return True;
+}
+
+static void mark_packet_signed(struct cli_state *cli) 
+{
+       uint16 flags2;
+       flags2 = SVAL(cli->outbuf,smb_flg2);
+       flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES;
+       SSVAL(cli->outbuf,smb_flg2, flags2);
+}
+
+static BOOL signing_good(struct cli_state *cli, BOOL good) 
+{
+       DEBUG(10, ("got SMB signature of\n"));
+       dump_data(10,&cli->outbuf[smb_ss_field] , 8);
+
+       if (good && !cli->sign_info.doing_signing) {
+               cli->sign_info.doing_signing = True;
+       }
+
+       if (!good) {
+               if (cli->sign_info.doing_signing) {
+                       DEBUG(1, ("SMB signature check failed!\n"));
+                       return False;
+               } else {
+                       DEBUG(3, ("Server did not sign reply correctly\n"));
+                       cli_free_signing_context(cli);
+                       return False;
+               }
+       }
+       return True;
+}      
+
+/***********************************************************
+ SMB signing - Simple implementation - calculate a MAC to send.
+************************************************************/
+
+static void cli_simple_sign_outgoing_message(struct cli_state *cli)
+{
+       unsigned char calc_md5_mac[16];
+       struct MD5Context md5_ctx;
+       struct smb_basic_signing_context *data = cli->sign_info.signing_context;
+
+       /*
+        * Firstly put the sequence number into the first 4 bytes.
+        * and zero out the next 4 bytes.
+        */
+       SIVAL(cli->outbuf, smb_ss_field, 
+             data->send_seq_num);
+       SIVAL(cli->outbuf, smb_ss_field + 4, 0);
+
+       /* mark the packet as signed - BEFORE we sign it...*/
+       mark_packet_signed(cli);
+
+       /* Calculate the 16 byte MAC and place first 8 bytes into the field. */
+       MD5Init(&md5_ctx);
+       MD5Update(&md5_ctx, data->mac_key.data, 
+                 data->mac_key.length); 
+       MD5Update(&md5_ctx, cli->outbuf + 4, smb_len(cli->outbuf));
+       MD5Final(calc_md5_mac, &md5_ctx);
+
+       DEBUG(10, ("sent SMB signature of\n"));
+       dump_data(10, calc_md5_mac, 8);
+
+       memcpy(&cli->outbuf[smb_ss_field], calc_md5_mac, 8);
+
+/*     cli->outbuf[smb_ss_field+2]=0; 
+       Uncomment this to test if the remote server actually verifies signitures...*/
+       data->send_seq_num++;
+       data->reply_seq_num = data->send_seq_num;
+       data->send_seq_num++;
+}
+
+/***********************************************************
+ SMB signing - Simple implementation - check a MAC sent by server.
+************************************************************/
+
+static BOOL cli_simple_check_incoming_message(struct cli_state *cli)
+{
+       BOOL good;
+       unsigned char calc_md5_mac[16];
+       unsigned char server_sent_mac[8];
+       struct MD5Context md5_ctx;
+       struct smb_basic_signing_context *data = cli->sign_info.signing_context;
+
+       /*
+        * Firstly put the sequence number into the first 4 bytes.
+        * and zero out the next 4 bytes.
+        */
+
+       memcpy(server_sent_mac, &cli->inbuf[smb_ss_field], sizeof(server_sent_mac));
+
+       DEBUG(10, ("got SMB signature of\n"));
+       dump_data(10, server_sent_mac, 8);
+
+       SIVAL(cli->inbuf, smb_ss_field, data->reply_seq_num);
+       SIVAL(cli->inbuf, smb_ss_field + 4, 0);
+
+       /* Calculate the 16 byte MAC and place first 8 bytes into the field. */
+       MD5Init(&md5_ctx);
+       MD5Update(&md5_ctx, data->mac_key.data, 
+                 data->mac_key.length); 
+       MD5Update(&md5_ctx, cli->inbuf + 4, smb_len(cli->inbuf));
+       MD5Final(calc_md5_mac, &md5_ctx);
+
+       good = (memcmp(server_sent_mac, calc_md5_mac, 8) == 0);
+       
+       return signing_good(cli, good);
+}
+
+/***********************************************************
+ SMB signing - Simple implementation - free signing context
+************************************************************/
+
+static void cli_simple_free_signing_context(struct cli_state *cli)
+{
+       struct smb_basic_signing_context *data = cli->sign_info.signing_context;
+
+       data_blob_free(&data->mac_key);
+       SAFE_FREE(cli->sign_info.signing_context);
+
+       return;
+}
+
+/***********************************************************
+ SMB signing - Simple implementation - setup the MAC key.
+************************************************************/
+
+BOOL cli_simple_set_signing(struct cli_state *cli, const uchar user_session_key[16], const DATA_BLOB response)
+{
+       struct smb_basic_signing_context *data;
+
+       if (!set_smb_signing_common(cli)) {
+               return False;
+       }
+
+       if (!set_smb_signing_real_common(cli)) {
+               return False;
+       }
+
+       data = smb_xmalloc(sizeof(*data));
+       cli->sign_info.signing_context = data;
+       
+       data->mac_key = data_blob(NULL, MIN(response.length + 16, 40));
+
+       memcpy(&data->mac_key.data[0], user_session_key, 16);
+       memcpy(&data->mac_key.data[16],response.data, MIN(response.length, 40 - 16));
+
+       /* Initialize the sequence number */
+       data->send_seq_num = 0;
+
+       cli->sign_info.sign_outgoing_message = cli_simple_sign_outgoing_message;
+       cli->sign_info.check_incoming_message = cli_simple_check_incoming_message;
+       cli->sign_info.free_signing_context = cli_simple_free_signing_context;
+
+       return True;
+}
+
+/***********************************************************
+ SMB signing - NTLMSSP implementation - calculate a MAC to send.
+************************************************************/
+
+static void cli_ntlmssp_sign_outgoing_message(struct cli_state *cli)
+{
+       NTSTATUS nt_status;
+       DATA_BLOB sig;
+       NTLMSSP_CLIENT_STATE *ntlmssp_state = cli->sign_info.signing_context;
+
+       /* mark the packet as signed - BEFORE we sign it...*/
+       mark_packet_signed(cli);
+       
+       nt_status = ntlmssp_client_sign_packet(ntlmssp_state, cli->outbuf + 4, 
+                                              smb_len(cli->outbuf), &sig);
+       
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DEBUG(0, ("NTLMSSP signing failed with %s\n", nt_errstr(nt_status)));
+               return;
+       }
+
+       DEBUG(10, ("sent SMB signature of\n"));
+       dump_data(10, sig.data, MIN(sig.length, 8));
+       memcpy(&cli->outbuf[smb_ss_field], sig.data, MIN(sig.length, 8));
+       
+       data_blob_free(&sig);
+}
+
+/***********************************************************
+ SMB signing - NTLMSSP implementation - check a MAC sent by server.
+************************************************************/
+
+static BOOL cli_ntlmssp_check_incoming_message(struct cli_state *cli)
+{
+       BOOL good;
+       NTSTATUS nt_status;
+       DATA_BLOB sig = data_blob(&cli->outbuf[smb_ss_field], 8);
+
+       NTLMSSP_CLIENT_STATE *ntlmssp_state = cli->sign_info.signing_context;
+
+       nt_status = ntlmssp_client_check_packet(ntlmssp_state, cli->outbuf + 4, 
+                                               smb_len(cli->outbuf), &sig);
+       
+       data_blob_free(&sig);
+       
+       good = NT_STATUS_IS_OK(nt_status);
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DEBUG(5, ("NTLMSSP signing failed with %s\n", nt_errstr(nt_status)));
+       }
+
+       return signing_good(cli, good);
+}
+
+/***********************************************************
+ SMB signing - NTLMSSP implementation - free signing context
+************************************************************/
+
+static void cli_ntlmssp_free_signing_context(struct cli_state *cli)
+{
+       ntlmssp_client_end((NTLMSSP_CLIENT_STATE **)&cli->sign_info.signing_context);
+}
+
+/***********************************************************
+ SMB signing - NTLMSSP implementation - setup the MAC key.
+************************************************************/
+
+BOOL cli_ntlmssp_set_signing(struct cli_state *cli,
+                            NTLMSSP_CLIENT_STATE *ntlmssp_state)
+{
+       if (!set_smb_signing_common(cli)) {
+               return False;
+       }
+
+       if (!NT_STATUS_IS_OK(ntlmssp_client_sign_init(ntlmssp_state))) {
+               return False;
+       }
+
+       if (!set_smb_signing_real_common(cli)) {
+               return False;
+       }
+
+       cli->sign_info.signing_context = ntlmssp_state;
+       ntlmssp_state->ref_count++;
+
+       cli->sign_info.sign_outgoing_message = cli_ntlmssp_sign_outgoing_message;
+       cli->sign_info.check_incoming_message = cli_ntlmssp_check_incoming_message;
+       cli->sign_info.free_signing_context = cli_ntlmssp_free_signing_context;
+
+       return True;
+}
+
+/***********************************************************
+ SMB signing - NULL implementation - calculate a MAC to send.
+************************************************************/
+
+static void cli_null_sign_outgoing_message(struct cli_state *cli)
+{
+       /* we can't zero out the sig, as we might be trying to send a
+          session request - which is NBT-level, not SMB level and doesn't
+          have the field */
+       return;
+}
+
+/***********************************************************
+ SMB signing - NULL implementation - check a MAC sent by server.
+************************************************************/
+
+static BOOL cli_null_check_incoming_message(struct cli_state *cli)
+{
+       return True;
+}
+
+/***********************************************************
+ SMB signing - NULL implementation - free signing context
+************************************************************/
+
+static void cli_null_free_signing_context(struct cli_state *cli)
+{
+       return;
+}
+
+/**
+ SMB signing - NULL implementation - setup the MAC key.
+
+ @note Used as an initialisation only - it will not correctly
+       shut down a real signing mechinism
+*/
+
+BOOL cli_null_set_signing(struct cli_state *cli)
+{
+       struct smb_basic_sign_data *data;
+
+       cli->sign_info.signing_context = NULL;
+       
+       cli->sign_info.sign_outgoing_message = cli_null_sign_outgoing_message;
+       cli->sign_info.check_incoming_message = cli_null_check_incoming_message;
+       cli->sign_info.free_signing_context = cli_null_free_signing_context;
+
+       return True;
+}
+
+/***********************************************************
+ SMB signing - TEMP implementation - calculate a MAC to send.
+************************************************************/
+
+static void cli_temp_sign_outgoing_message(struct cli_state *cli)
+{
+       /* mark the packet as signed - BEFORE we sign it...*/
+       mark_packet_signed(cli);
+
+       /* I wonder what BSRSPYL stands for - but this is what MS 
+          actually sends! */
+       memcpy(&cli->outbuf[smb_ss_field], "BSRSPYL ", 8);
+       return;
+}
+
+/***********************************************************
+ SMB signing - TEMP implementation - check a MAC sent by server.
+************************************************************/
+
+static BOOL cli_temp_check_incoming_message(struct cli_state *cli)
+{
+       return True;
+}
+
+/***********************************************************
+ SMB signing - TEMP implementation - free signing context
+************************************************************/
+
+static void cli_temp_free_signing_context(struct cli_state *cli)
+{
+       return;
+}
+
+/***********************************************************
+ SMB signing - NULL implementation - setup the MAC key.
+************************************************************/
+
+BOOL cli_temp_set_signing(struct cli_state *cli)
+{
+       if (!set_smb_signing_common(cli)) {
+               return False;
+       }
+
+       cli->sign_info.signing_context = NULL;
+       
+       cli->sign_info.sign_outgoing_message = cli_temp_sign_outgoing_message;
+       cli->sign_info.check_incoming_message = cli_temp_check_incoming_message;
+       cli->sign_info.free_signing_context = cli_temp_free_signing_context;
+
+       return True;
+}
+
+/**
+ * Free the signing context
+ */
+void cli_free_signing_context(struct cli_state *cli) 
+{
+       if (cli->sign_info.free_signing_context) 
+               cli->sign_info.free_signing_context(cli);
+
+       cli_null_set_signing(cli);
+}
+
+/**
+ * Sign a packet with the current mechanism
+ */
+void cli_caclulate_sign_mac(struct cli_state *cli)
+{
+       cli->sign_info.sign_outgoing_message(cli);
+}
+
+/**
+ * Check a packet with the current mechanism
+ * @return False if we had an established signing connection
+ *         which had a back checksum, True otherwise
+ */
+BOOL cli_check_sign_mac(struct cli_state *cli) 
+{
+       BOOL good;
+       good = cli->sign_info.check_incoming_message(cli);
+       
+       if (!good) {
+               if (cli->sign_info.doing_signing) {
+                       return False;
+               } else {
+                       cli_free_signing_context(cli);  
+               }
+       }
+
+       return True;
+}
+
diff --git a/source/rpc_client/.cvsignore b/source/rpc_client/.cvsignore
new file mode 100644 (file)
index 0000000..07da222
--- /dev/null
@@ -0,0 +1,3 @@
+*.po
+*.po32
+
diff --git a/source/stf/.cvsignore b/source/stf/.cvsignore
new file mode 100644 (file)
index 0000000..7e99e36
--- /dev/null
@@ -0,0 +1 @@
+*.pyc
\ No newline at end of file
diff --git a/source/stf/README.stf b/source/stf/README.stf
new file mode 100644 (file)
index 0000000..3fbd33c
--- /dev/null
@@ -0,0 +1,3 @@
+This directory contains the Samba Testing Framework, a Python-based
+system for exercising Samba in various ways.  It is quite small at the
+moment.
diff --git a/source/stf/info3cache.py b/source/stf/info3cache.py
new file mode 100755 (executable)
index 0000000..96d5a1d
--- /dev/null
@@ -0,0 +1,54 @@
+#!/usr/bin/python
+#
+# Upon a winbindd authentication, test that an info3 record is cached in
+# netsamlogon_cache.tdb and cache records are removed from winbindd_cache.tdb
+#
+
+import comfychair, stf
+from samba import tdb, winbind
+
+#
+# We want to implement the following test on a win2k native mode domain.
+#
+# 1. trash netsamlogon_cache.tdb
+# 2. wbinfo -r DOMAIN\Administrator                    [FAIL]
+# 3. wbinfo --auth-crap DOMAIN\Administrator%password  [PASS]
+# 4. wbinfo -r DOMAIN\Administrator                    [PASS]
+#
+# Also for step 3 we want to try 'wbinfo --auth-smbd' and
+# 'wbinfo --auth-plaintext'
+#
+
+#
+# TODO: To implement this test we need to be able to
+#
+#  - pass username%password combination for an invidivual winbindd request
+#    (so we can get the administrator SID so we can clear the info3 cache)
+#
+#  - start/restart winbindd (to trash the winbind cache)
+#
+#  - from samba import dynconfig (to find location of info3 cache)
+#
+#  - be able to modify the winbindd cache (to set/reset individual winbind
+#    cache entries)
+#
+#  - have --auth-crap present in HEAD
+#
+
+class WinbindAuthCrap(comfychair.TestCase):
+    def runtest(self):
+        raise comfychair.NotRunError, "not implemented"
+        
+class WinbindAuthSmbd(comfychair.TestCase):
+    def runtest(self):
+        # Grr - winbindd in HEAD doesn't contain the auth_smbd function
+        raise comfychair.NotRunError, "no auth_smbd in HEAD"
+
+class WinbindAuthPlaintext(comfychair.TestCase):
+    def runtest(self):
+        raise comfychair.NotRunError, "not implemented"
+
+tests = [WinbindAuthCrap, WinbindAuthSmbd, WinbindAuthPlaintext]
+
+if __name__ == "__main__":
+    comfychair.main(tests)
diff --git a/source/stf/notes.txt b/source/stf/notes.txt
new file mode 100644 (file)
index 0000000..68aca63
--- /dev/null
@@ -0,0 +1,175 @@
+               -*- indented-text -*-
+
+(set lotus no)
+
+
+
+Notes on using comfychair with Samba (samba testing framework units):
+
+The tests need to rely on some external resources, such as 
+
+If suitable resources are not available, need to skip particular
+tests.  Must include a message indicating what resources would be
+needed to run that test.  (e.g. must be root.)
+
+We want to be able to select and run particular subsets of tests, such
+as "all winbind tests".
+
+We want to keep the number of configurable parameters down as much as
+possible, to make it easy on people running the tests.
+
+Wherever possible, the tests should set up their preconditions, but a
+few basic resources need to be provided by the people running the
+tests.  So for example, rather than asking the user for the name of a
+non-root user, we should give the tests the administrator name and
+password, and it can create a new user to use.
+
+This makes it simpler to get the tests running, and possible also
+makes them more reproducible.
+
+In the future, rather than using NT machines provided by the test
+person, we might have a way to drive VMWare non-persistent sessions,
+to make tests even more tightly controlled.
+
+
+Another design question is how to communicate this information to the
+tests.  If there's a lot of settings, then it might need to be stored
+in a configuration file.  
+
+However, if we succeed in cutting down the number of parameters, then
+it might be straightforward to pass the information on the command
+line or in an environment variable.  
+
+Environment variables are probably better because they can't be seen
+by other users, and they are more easily passed down through an
+invocation of "make check".
+
+
+\f
+Notes on Samba Testing Framework for Unittests
+----------------------------------------------
+
+This is to be read after reading the notes.txt from comfychair.  I'm
+proposing a slightly more concrete description of what's described
+there.
+
+The model of having tests require named resources looks useful for
+incorporation into a framework that can be run by many people in
+widely different environments.
+
+Some possible environments for running the test framework in are:
+
+    - Casual downloader of Samba compiling from source and just wants
+      to run 'make check'.  May only have one Unix machine and a
+      handful of clients.
+
+    - Samba team member with access to a small number of other
+      machines or VMware sessions.
+
+    - PSA developer who may not have intimate knowledge of Samba
+      internals and is only interested in testing against the PSA.
+
+    - Non-team hacker wanting to run test suite after making small
+      hacks. 
+
+    - Build farm environment (loaner machine with no physical access
+      or root privilege).
+
+    - HP BAT.
+
+Developers in most of these environments are also potential test case
+authors.  It should be easy for people unfamiliar with the framework
+to write new tests and have them work.  We should provide examples and
+the existing tests should well written and understandable.
+
+Different types of tests:
+
+    - Tests that check Samba internals and link against
+      libbigballofmud.so.  For example:
+
+        - Upper/lowercase string functions
+        - user_in_list() for large lists
+
+    - Tests that use the Samba Python extensions.
+
+    - Tests that execute Samba command line programs, for example
+      smbpasswd. 
+
+    - Tests that require other resources on the network such as domain
+      controllers or PSAs.
+
+    - Tests that are performed on the documentation or the source code
+      such as:
+
+        - grep for common spelling mistakes made by abartlet (-:
+        - grep for company copyright (IBM, HP)
+                         
+    - Link to other existing testing frameworks (smbtorture,
+      abartlet's bash based build farm tests)
+
+I propose a TestResourceManager which would be instantiated by a test
+case.  The test case would require("resourcename") as part of its
+constructor and raise a comfychair.NotRun exception if the resource
+was not present.  A TestResource class could be defined which could
+read a configuration file or examine a environment variable and
+register a resource only if some condition was satisfied.
+
+It would be nice to be able to completely separate the PSA testing
+from the test framework.  This would entail being able to define test
+resources dynamically, possibly with a plugin type system.
+
+class TestResourceManager:
+    def __init__(self, name):
+        self.resources = {}
+
+    def register(self, resource):
+        name = resource.name()
+        if self.resources.has_key(name):
+            raise "Test manager already has resource %s" % name
+        self.resources[name] = resource
+
+    def require(self, resource_name):
+        if not self.resources.has_key(resource_name):
+            raise "Test manager does not have resources %s" % resource_name
+
+class TestResource:
+    def __init__(self, name):
+        self.name = name
+
+    def name(self):
+        return self.name
+
+import os
+
+trm = TestResourceManager()
+
+if os.getuid() == 0:
+    trm.register(TestResource("root"))
+
+A config-o-matic Python module can take a list of machines and
+administrator%password entries and classify them by operating system
+version and service pack.  These resources would be registered with
+the TestResourceManager.
+
+Some random thoughts about named resources for network servers:
+
+require("nt4.sp3")
+require("nt4.domaincontroller")
+require("psa")
+
+Some kind of format for location of passwords, libraries:
+
+require("exec(smbpasswd)")
+require("lib(bigballofmud)")
+
+maybe require("exec.smbpasswd") looks nicer...
+
+The require() function could return a dictionary of configuration
+information or some handle to fetch dynamic information on.  We may
+need to create and destroy extra users or print queues.  How to manage
+cleanup of dynamic resources?
+
+Requirements for running stf:
+
+    - Python, obviously
+    - Samba python extensions
diff --git a/source/stf/osver.py b/source/stf/osver.py
new file mode 100755 (executable)
index 0000000..68601fa
--- /dev/null
@@ -0,0 +1,55 @@
+#!/usr/bin/python
+#
+# Utilities for determining the Windows operating system version remotely.
+#
+
+from samba import srvsvc
+
+# Constants
+
+PLATFORM_UNKNOWN = 0
+PLATFORM_WIN9X = 1
+PLATFORM_NT4 = 2
+PLATFORM_NT5 = 3                        # Windows 2000
+
+def platform_name(platform_type):
+
+    platform_names = { PLATFORM_UNKNOWN: "Unknown",
+                       PLATFORM_WIN9X: "Windows 9x",
+                       PLATFORM_NT4: "Windows NT",
+                       PLATFORM_NT5: "Windows 2000" }
+
+    if platform_names.has_key(platform_type):
+        return platform_names[platform_type]
+
+    return "Unknown"
+
+def platform_type(info101):
+    """Determine the operating system type from a SRV_INFO_101."""
+
+    if info101['major_version'] == 4 and info101['minor_version'] == 0:
+        return PLATFORM_NT4
+
+    if info101['major_version'] == 5 and info101['minor_version'] == 0:
+        return PLATFORM_NT5
+
+    return PLATFORM_UNKNOWN
+
+def is_domain_controller(info101):
+    """Return true if the server_type field from a  SRV_INFO_101
+    indicates a domain controller."""
+    return info101['server_type'] & srvsvc.SV_TYPE_DOMAIN_CTRL
+
+def os_version(name):
+    info = srvsvc.netservergetinfo("\\\\%s" % name, 101)
+    return platform_type(info)
+
+if __name__ == "__main__":
+    import sys
+    if len(sys.argv) != 2:
+        print "Usage: osver.py server"
+        sys.exit(0)
+    info = srvsvc.netservergetinfo("\\\\%s" % sys.argv[1], 101)
+    print "platform type = %d" % platform_type(info)
+    if is_domain_controller(info):
+        print "%s is a domain controller" % sys.argv[1]
diff --git a/source/stf/spoolss.py b/source/stf/spoolss.py
new file mode 100755 (executable)
index 0000000..7352915
--- /dev/null
@@ -0,0 +1,288 @@
+#!/usr/bin/python
+
+import re
+import comfychair, stf
+from samba import spoolss
+
+class PrintServerTest(comfychair.TestCase):
+    """An abstract class requiring a print server."""
+    def setUp(self):
+        # TODO: create a test printer
+        self.server = stf.get_server(platform = "nt")
+        self.require(self.server != None, "print server required")
+        # TODO: remove hardcoded printer name
+        self.printername = "p"
+        self.uncname = "\\\\%s\\%s" % \
+                           (self.server["hostname"], self.printername)
+
+class W2kPrintServerTest(comfychair.TestCase):
+    """An abstract class requiring a print server."""
+    def setUp(self):
+        # TODO: create a test printer
+        self.server = stf.get_server(platform = "nt5")
+        self.require(self.server != None, "print server required")
+        # TODO: remove hardcoded printer name
+        self.printername = "p"
+        self.uncname = "\\\\%s\\%s" % \
+                           (self.server["hostname"], self.printername)
+
+class CredentialTest(PrintServerTest):
+    """An class that calls a function with various sets of credentials."""
+    def runTest(self):
+
+        bad_user_creds = {"username": "spotty",
+                          "domain": "dog",
+                          "password": "bone"}
+                        
+        cases = ((self.server["administrator"], "Admin credentials", 1),
+                 (bad_user_creds,               "Bad credentials",   0))
+
+        # TODO: add unpriv user case
+
+        for creds, testname, result in cases:
+            try:
+                self.runTestArg(creds)
+            except:
+                if result:
+                    import traceback
+                    traceback.print_exc()
+                    self.fail("rpc with creds %s failed when it "
+                              "should have suceeded" % creds)
+                return
+
+            if not result:
+                self.fail("rpc with creds %s suceeded when it should "
+                          "have failed" % creds)
+        
+class ArgTestServer(PrintServerTest):
+    """Test a RPC that takes a UNC print server name."""
+    def runTest(self):
+
+        # List of test cases, %s substituted for server name
+
+        cases = (("",             "No server name",          0),
+                 ("\\\\%s",       "Valid server name",       1),
+                 ("\\%s",         "Invalid unc server name", 0),
+                 ("\\\\%s__",     "Invalid unc server name", 0))
+
+        for unc, testname, result in cases:
+            unc = re.sub("%s", self.server["hostname"], unc)
+            try:
+                self.runTestArg(unc)
+            except:
+                if result:
+                    self.fail("rpc(\"%s\") failed when it should have "
+                              "suceeded" % unc)
+                return
+
+            if not result:
+                # Suceeded when we should have failed
+                self.fail("rpc(\"%s\") suceeded when it should have "
+                          "failed" % unc)
+
+class ArgTestServerAndPrinter(ArgTestServer):
+    """Test a RPC that takes a UNC print server or UNC printer name."""
+    def runTest(self):
+
+        ArgTestServer.runTest(self)
+        
+        # List of test cases, %s substituted for server name, %p substituted
+        # for printer name.
+
+        cases = (("\\\\%s\\%p",   "Valid server and printer name",      1),
+                 ("\\\\%s\\%p__", "Valid server, invalid printer name", 0),
+                 ("\\\\%s__\\%p", "Invalid server, valid printer name", 0))
+
+        for unc, testname, result in cases:
+            unc = re.sub("%s", self.server["hostname"], unc)
+            unc = re.sub("%p", self.printername, unc)
+            try:
+                self.runTestArg(unc)
+            except:
+                if result:
+                    self.fail("openprinter(\"%s\") failed when it should have "
+                              "suceeded" % unc)
+                return
+
+            if not result:
+                # Suceeded when we should have failed
+                self.fail("openprinter(\"%s\") suceeded when it should have "
+                          "failed" % unc)
+
+class OpenPrinterArg(ArgTestServerAndPrinter):
+    """Test the OpenPrinter RPC with combinations of valid and invalid
+    server and printer names."""
+    def runTestArg(self, unc):
+        spoolss.openprinter(unc)
+
+class OpenPrinterCred(CredentialTest):
+    """Test opening printer with good and bad credentials."""
+    def runTestArg(self, creds):
+        spoolss.openprinter(self.uncname, creds = creds)
+
+class ClosePrinter(PrintServerTest):
+    """Test the ClosePrinter RPC on a printer handle."""
+    def runTest(self):
+        hnd = spoolss.openprinter(self.uncname)
+        spoolss.closeprinter(hnd)
+        
+class ClosePrinterServer(PrintServerTest):
+    """Test the ClosePrinter RPC on a print server handle."""
+    def runTest(self):
+        hnd = spoolss.openprinter("\\\\%s" % self.server["hostname"])
+        spoolss.closeprinter(hnd)
+        
+class GetPrinterInfo(PrintServerTest):
+    """Retrieve printer info at various levels."""
+
+    # Sample printer data
+
+    sample_info = {
+        0: {'printer_errors': 0, 'unknown18': 0, 'unknown13': 0, 'unknown26': 0, 'cjobs': 0, 'unknown11': 0, 'server_name': '\\\\win2kdc1', 'total_pages': 0, 'unknown15': 586, 'unknown16': 0, 'month': 2, 'unknown20': 0, 'second': 23, 'unknown22': 983040, 'unknown25': 0, 'total_bytes': 0, 'unknown27': 0, 'year': 2003, 'build_version': 2195, 'unknown28': 0, 'global_counter': 4, 'day': 13, 'minute': 53, 'total_jobs': 0, 'unknown29': 1114112, 'name': '\\\\win2kdc1\\p', 'hour': 2, 'level': 0, 'c_setprinter': 0, 'change_id': 522454169, 'major_version': 5, 'unknown23': 15, 'day_of_week': 4, 'unknown14': 1, 'session_counter': 2, 'status': 1, 'unknown7': 1, 'unknown8': 0, 'unknown9': 0, 'milliseconds': 421, 'unknown24': 0},
+        1: {'comment': "I'm a teapot!", 'level': 1, 'flags': 8388608, 'name': '\\\\win2kdc1\\p', 'description': '\\\\win2kdc1\\p,HP LaserJet 4,Canberra office'},
+        2: {'comment': "I'm a teapot!", 'status': 1, 'print_processor': 'WinPrint', 'until_time': 0, 'share_name': 'p', 'start_time': 0, 'device_mode': {'icm_method': 1, 'bits_per_pel': 0, 'log_pixels': 0, 'orientation': 1, 'panning_width': 0, 'color': 2, 'pels_width': 0, 'print_quality': 600, 'driver_version': 24, 'display_flags': 0, 'y_resolution': 600, 'media_type': 0, 'display_frequency': 0, 'icm_intent': 0, 'pels_height': 0, 'reserved1': 0, 'size': 220, 'scale': 100, 'dither_type': 0, 'panning_height': 0, 'default_source': 7, 'duplex': 1, 'fields': 16131, 'spec_version': 1025, 'copies': 1, 'device_name': '\\\\win2kdc1\\p', 'paper_size': 1, 'paper_length': 0, 'private': 'private', 'collate': 0, 'paper_width': 0, 'form_name': 'Letter', 'reserved2': 0, 'tt_option': 0}, 'port_name': 'LPT1:', 'sepfile': '', 'parameters': '', 'security_descriptor': {'group_sid': 'S-1-5-21-1606980848-1677128483-854245398-513', 'sacl': None, 'dacl': {'ace_list': [{'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-544'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-544'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1121'}, {'flags': 10, 'type': 0, 'mask': 131072, 'trustee': 'S-1-3-0'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-3-0'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1124'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-1-0'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-550'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-550'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-549'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-549'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1106'}], 'revision': 2}, 'owner_sid': 'S-1-5-32-544', 'revision': 1}, 'name': '\\\\win2kdc1\\p', 'server_name': '\\\\win2kdc1', 'level': 2, 'datatype': 'RAW', 'cjobs': 0, 'average_ppm': 0, 'priority': 1, 'driver_name': 'HP LaserJet 4', 'location': 'Canberra office', 'attributes': 8776, 'default_priority': 0},
+        3: {'flags': 4, 'security_descriptor': {'group_sid': 'S-1-5-21-1606980848-1677128483-854245398-513', 'sacl': None, 'dacl': {'ace_list': [{'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-544'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-544'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1121'}, {'flags': 10, 'type': 0, 'mask': 131072, 'trustee': 'S-1-3-0'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-3-0'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1124'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-1-0'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-550'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-550'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-549'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-549'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1106'}], 'revision': 2}, 'owner_sid': 'S-1-5-32-544', 'revision': 1}, 'level': 3}
+        }
+
+    def runTest(self):
+        self.hnd = spoolss.openprinter(self.uncname)
+
+        # Everyone should have getprinter levels 0-3
+
+        for i in (0, 1, 2, 3):
+            info = self.hnd.getprinter(level = i)
+            try:
+                stf.dict_check(self.sample_info[i], info)
+            except ValueError, msg:
+                raise "info%d: %s" % (i, msg)
+
+class EnumPrinters(PrintServerTest):
+    """Enumerate print info at various levels."""
+
+    sample_info = {
+
+        0: {'q': {'printer_errors': 0, 'unknown18': 0, 'unknown13': 0, 'unknown26': 0, 'cjobs': 0, 'unknown11': 0, 'server_name': '', 'total_pages': 0, 'unknown15': 586, 'unknown16': 0, 'month': 2, 'unknown20': 0, 'second': 23, 'unknown22': 983040, 'unknown25': 0, 'total_bytes': 0, 'unknown27': 0, 'year': 2003, 'build_version': 2195, 'unknown28': 0, 'global_counter': 4, 'day': 13, 'minute': 53, 'total_jobs': 0, 'unknown29': -1833435136, 'name': 'q', 'hour': 2, 'level': 0, 'c_setprinter': 0, 'change_id': 522454169, 'major_version': 5, 'unknown23': 15, 'day_of_week': 4, 'unknown14': 1, 'session_counter': 1, 'status': 0, 'unknown7': 1, 'unknown8': 0, 'unknown9': 0, 'milliseconds': 421, 'unknown24': 0}, 'p': {'printer_errors': 0, 'unknown18': 0, 'unknown13': 0, 'unknown26': 0, 'cjobs': 0, 'unknown11': 0, 'server_name': '', 'total_pages': 0, 'unknown15': 586, 'unknown16': 0, 'month': 2, 'unknown20': 0, 'second': 23, 'unknown22': 983040, 'unknown25': 0, 'total_bytes': 0, 'unknown27': 0, 'year': 2003, 'build_version': 2195, 'unknown28': 0, 'global_counter': 4, 'day': 13, 'minute': 53, 'total_jobs': 0, 'unknown29': -1831337984, 'name': 'p', 'hour': 2, 'level': 0, 'c_setprinter': 0, 'change_id': 522454169, 'major_version': 5, 'unknown23': 15, 'day_of_week': 4, 'unknown14': 1, 'session_counter': 1, 'status': 1, 'unknown7': 1, 'unknown8': 0, 'unknown9': 0, 'milliseconds': 421, 'unknown24': 0}, 'magpie': {'printer_errors': 0, 'unknown18': 0, 'unknown13': 0, 'unknown26': 0, 'cjobs': 0, 'unknown11': 0, 'server_name': '', 'total_pages': 0, 'unknown15': 586, 'unknown16': 0, 'month': 2, 'unknown20': 0, 'second': 23, 'unknown22': 983040, 'unknown25': 0, 'total_bytes': 0, 'unknown27': 0, 'year': 2003, 'build_version': 2195, 'unknown28': 0, 'global_counter': 4, 'day': 13, 'minute': 53, 'total_jobs': 0, 'unknown29': 1114112, 'name': 'magpie', 'hour': 2, 'level': 0, 'c_setprinter': 0, 'change_id': 522454169, 'major_version': 5, 'unknown23': 15, 'day_of_week': 4, 'unknown14': 1, 'session_counter': 1, 'status': 0, 'unknown7': 1, 'unknown8': 0, 'unknown9': 0, 'milliseconds': 421, 'unknown24': 0}},
+
+        1: {'q': {'comment': 'cheepy birds', 'level': 1, 'flags': 8388608, 'name': 'q', 'description': 'q,HP LaserJet 4,'}, 'p': {'comment': "I'm a teapot!", 'level': 1, 'flags': 8388608, 'name': 'p', 'description': 'p,HP LaserJet 4,Canberra office'}, 'magpie': {'comment': '', 'level': 1, 'flags': 8388608, 'name': 'magpie', 'description': 'magpie,Generic / Text Only,'}}
+        }
+
+    def runTest(self):
+        for i in (0, 1):
+            info = spoolss.enumprinters(
+                "\\\\%s" % self.server["hostname"], level = i)
+            try:
+                stf.dict_check(self.sample_info[i], info)
+            except ValueError, msg:
+                raise "info%d: %s" % (i, msg)
+
+class EnumPrintersArg(ArgTestServer):
+    def runTestArg(self, unc):
+        spoolss.enumprinters(unc)
+
+class EnumPrintersCred(CredentialTest):
+    """Test opening printer with good and bad credentials."""
+    def runTestArg(self, creds):
+        spoolss.enumprinters(
+            "\\\\%s" % self.server["hostname"], creds = creds)
+
+class EnumPrinterdrivers(PrintServerTest):
+
+    sample_info = {
+        1: {'Okipage 10ex (PCL5E) : STANDARD': {'name': 'Okipage 10ex (PCL5E) : STANDARD', 'level': 1}, 'Generic / Text Only': {'name': 'Generic / Text Only', 'level': 1}, 'Brother HL-1030 series': {'name': 'Brother HL-1030 series', 'level': 1}, 'Brother HL-1240 series': {'name': 'Brother HL-1240 series', 'level': 1}, 'HP DeskJet 1220C Printer': {'name': 'HP DeskJet 1220C Printer', 'level': 1}, 'HP LaserJet 4100 PCL 6': {'name': 'HP LaserJet 4100 PCL 6', 'level': 1}, 'HP LaserJet 4': {'name': 'HP LaserJet 4', 'level': 1}},
+        2: {'Okipage 10ex (PCL5E) : STANDARD': {'version': 2, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\2\\RASDDUI.DLL', 'name': 'Okipage 10ex (PCL5E) : STANDARD', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\2\\RASDD.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\2\\OKIPAGE.DLL', 'level': 2, 'architecture': 'Windows NT x86'}, 'Generic / Text Only': {'version': 3, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\UNIDRVUI.DLL', 'name': 'Generic / Text Only', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\3\\UNIDRV.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\TTY.GPD', 'level': 2, 'architecture': 'Windows NT x86'}, 'Brother HL-1030 series': {'version': 3, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BRUHL99A.DLL', 'name': 'Brother HL-1030 series', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BROHL99A.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BROHL103.PPD', 'level': 2, 'architecture': 'Windows NT x86'}, 'Brother HL-1240 series': {'version': 3, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BRUHL99A.DLL', 'name': 'Brother HL-1240 series', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BROHL99A.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BROHL124.PPD', 'level': 2, 'architecture': 'Windows NT x86'}, 'HP DeskJet 1220C Printer': {'version': 3, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPW8KMD.DLL', 'name': 'HP DeskJet 1220C Printer', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPW8KMD.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPW8KMD.DLL', 'level': 2, 'architecture': 'Windows NT x86'}, 'HP LaserJet 4100 PCL 6': {'version': 3, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPBF042E.DLL', 'name': 'HP LaserJet 4100 PCL 6', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPBF042G.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPBF042I.PMD', 'level': 2, 'architecture': 'Windows NT x86'}, 'HP LaserJet 4': {'version': 2, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\2\\hpblff0.dll', 'name': 'HP LaserJet 4', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\2\\hpblff2.dll', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\2\\hpblff39.pmd', 'level': 2, 'architecture': 'Windows NT x86'}}
+        }
+
+    def runTest(self):
+        for i in (1, 2):
+            info = spoolss.enumprinterdrivers(
+                "\\\\%s" % self.server["hostname"], level = i)
+            try:
+                if not self.sample_info.has_key(i):
+                    self.log("%s" % info)
+                    self.fail()
+                stf.dict_check(self.sample_info[i], info)
+            except ValueError, msg:
+                raise "info%d: %s" % (i, msg)
+
+class EnumPrinterdriversArg(ArgTestServer):
+    def runTestArg(self, unc):
+        spoolss.enumprinterdrivers(unc)
+
+class EnumPrinterdriversCred(CredentialTest):
+    """Test opening printer with good and bad credentials."""
+    def runTestArg(self, creds):
+        spoolss.enumprinterdrivers(
+            "\\\\%s" % self.server["hostname"], creds = creds)
+
+def usage():
+    print "Usage: spoolss.py [options] [test1[,test2...]]"
+    print "\t -v/--verbose     Display debugging information"
+    print "\t -l/--list-tests  List available tests"
+    print
+    print "A list of comma separated test names or regular expressions"
+    print "can be used to filter the tests performed."
+            
+def test_match(subtest_list, test_name):
+    """Return true if a test matches a comma separated list of regular
+    expression of test names."""
+    # re.match does an implicit ^ at the start of the pattern.
+    # Explicitly anchor to end to avoid matching substrings.
+    for s in string.split(subtest_list, ","):
+        if re.match(s + "$", test_name):
+            return 1
+    return 0
+           
+if __name__ == "__main__":
+    import os, sys, string
+    import getopt
+    
+    try:
+        opts, args = getopt.getopt(sys.argv[1:], "vl", \
+                                   ["verbose", "list-tests"])
+    except getopt.GetoptError:
+        usage()
+        sys.exit(0)
+
+    verbose = 0
+    list_tests = 0
+
+    for opt, arg in opts:
+        if opt in ("-v", "--verbose"):
+            verbose = 1
+        if opt in ("-l", "--list-tests"):
+            list_tests = 1
+
+    if len(args) > 1:
+        usage()
+        sys.exit(0)
+
+    test_list = [
+        OpenPrinterArg,
+        OpenPrinterCred,
+        ClosePrinter,
+        ClosePrinterServer,
+        GetPrinterInfo,
+        EnumPrinters,
+        EnumPrintersCred,
+        EnumPrintersArg,
+        EnumPrinterdrivers,
+        EnumPrinterdriversCred,
+        EnumPrinterdriversArg,
+        ]
+
+    if len(args):
+        t = []
+        for test in test_list:
+            if test_match(args[0], test.__name__):
+                t.append(test)
+        test_list = t
+
+    if os.environ.has_key("SAMBA_DEBUG"):
+        spoolss.setup_logging(interactive = 1)
+        spoolss.set_debuglevel(10)
+
+    if list_tests:
+        for test in test_list:
+            print test.__name__
+    else:
+        comfychair.runtests(test_list, verbose = verbose)
diff --git a/source/stf/stf.py b/source/stf/stf.py
new file mode 100755 (executable)
index 0000000..ee0ff73
--- /dev/null
@@ -0,0 +1,101 @@
+#!/usr/bin/python
+#
+# Samba Testing Framework for Unit-testing
+#
+
+import os, string, re
+import osver
+
+def get_server_list_from_string(s):
+
+    server_list = []
+    
+    # Format is a list of server:domain\username%password separated
+    # by commas.
+
+    for entry in string.split(s, ","):
+
+        # Parse entry 
+
+        m = re.match("(.*):(.*)(\\\\|/)(.*)%(.*)", entry)
+        if not m:
+            raise "badly formed server list entry '%s'" % entry
+
+        server = m.group(1)
+        domain = m.group(2)
+        username = m.group(4)
+        password = m.group(5)
+
+        # Categorise servers
+
+        server_list.append({"platform": osver.os_version(server),
+                            "hostname": server,
+                            "administrator": {"username": username,
+                                              "domain": domain,
+                                              "password" : password}})
+
+    return server_list
+
+def get_server_list():
+    """Iterate through all sources of server info and append them all
+    in one big list."""
+    
+    server_list = []
+
+    # The $STF_SERVERS environment variable
+
+    if os.environ.has_key("STF_SERVERS"):
+        server_list = server_list + \
+                      get_server_list_from_string(os.environ["STF_SERVERS"])
+
+    return server_list
+
+def get_server(platform = None):
+    """Return configuration information for a server.  The platform
+    argument can be a string either 'nt4' or 'nt5' for Windows NT or
+    Windows 2000 servers, or just 'nt' for Windows NT and higher."""
+    
+    server_list = get_server_list()
+
+    for server in server_list:
+        if platform:
+            p = server["platform"]
+            if platform == "nt":
+                if (p == osver.PLATFORM_NT4 or p == osver.PLATFORM_NT5):
+                    return server
+            if platform == "nt4" and p == osver.PLATFORM_NT4:
+                return server
+            if platform == "nt5" and p == osver.PLATFORM_NT5:
+                return server
+        else:
+            # No filter defined, return first in list
+            return server
+        
+    return None
+
+def dict_check(sample_dict, real_dict):
+    """Check that real_dict contains all the keys present in sample_dict
+    and no extras.  Also check that common keys are of them same type."""
+    tmp = real_dict.copy()
+    for key in sample_dict.keys():
+        # Check existing key and type
+        if not real_dict.has_key(key):
+            raise ValueError, "dict does not contain key '%s'" % key
+        if type(sample_dict[key]) != type(real_dict[key]):
+            raise ValueError, "dict has differing types (%s vs %s) for key " \
+                  "'%s'" % (type(sample_dict[key]), type(real_dict[key]), key)
+        # Check dictionaries recursively
+        if type(sample_dict[key]) == dict:
+            dict_check(sample_dict[key], real_dict[key])
+        # Delete visited keys from copy
+        del(tmp[key])
+    # Any keys leftover are present in the real dict but not the sample
+    if len(tmp) == 0:
+        return
+    result = "dict has extra keys: "
+    for key in tmp.keys():
+        result = result + key + " "
+    raise ValueError, result
+
+if __name__ == "__main__":
+    print get_server(platform = "nt")
diff --git a/source/stf/test.py b/source/stf/test.py
new file mode 100755 (executable)
index 0000000..fb57926
--- /dev/null
@@ -0,0 +1,33 @@
+#!/usr/bin/python
+
+# meta-test-case / example for comfychair.  Should demonstrate
+# different kinds of failure.
+
+import comfychair
+
+class NormalTest(comfychair.TestCase):
+    def runtest(self):
+        pass
+
+class RootTest(comfychair.TestCase):
+    def setup(self):
+        self.require_root()
+            
+    def runTest(self):
+        pass
+
+class GoodExecTest(comfychair.TestCase):
+    def runtest(self):
+        stdout = self.runcmd("ls -l")
+
+class BadExecTest(comfychair.TestCase):
+    def setup(self):
+        exit, stdout = self.runcmd_unchecked("spottyfoot --slobber",
+                                             skip_on_noexec = 1)
+
+
+tests = [NormalTest, RootTest, GoodExecTest, BadExecTest]
+
+if __name__ == '__main__':
+    comfychair.main(tests)
+    
diff --git a/source/torture/t_stringoverflow.c b/source/torture/t_stringoverflow.c
new file mode 100644 (file)
index 0000000..ec14d81
--- /dev/null
@@ -0,0 +1,23 @@
+#include "includes.h"
+
+ int main(void)
+{
+       fstring dest;
+       char *ptr = dest;
+
+       printf("running on valgrind? %d\n", RUNNING_ON_VALGRIND);
+
+       /* Try copying a string into an fstring buffer.  The string
+        * will actually fit, but this is still wrong because you
+        * can't pstrcpy into an fstring.  This should trap in a
+        * developer build. */
+
+#if 0
+       /* As of CVS 20030318, this will be trapped at compile time! */
+       pstrcpy(dest, "hello");
+#endif /* 0 */
+
+       pstrcpy(ptr, "hello!");
+
+       return 0;
+}