diff options
-rw-r--r-- | docs/docbook/projdoc/securing-samba.sgml | 181 | ||||
-rwxr-xr-x | source3/client/mount.cifs.c | 557 | ||||
-rw-r--r-- | source3/include/srvstr.h | 36 | ||||
-rw-r--r-- | source3/lib/clobber.c | 60 | ||||
-rw-r--r-- | source3/libsmb/ntlmssp_sign.c | 208 | ||||
-rw-r--r-- | source3/libsmb/smb_signing.c | 461 | ||||
-rw-r--r-- | source3/rpc_client/.cvsignore | 3 | ||||
-rw-r--r-- | source3/stf/.cvsignore | 1 | ||||
-rw-r--r-- | source3/stf/README.stf | 3 | ||||
-rwxr-xr-x | source3/stf/info3cache.py | 54 | ||||
-rw-r--r-- | source3/stf/notes.txt | 175 | ||||
-rwxr-xr-x | source3/stf/osver.py | 55 | ||||
-rwxr-xr-x | source3/stf/spoolss.py | 288 | ||||
-rwxr-xr-x | source3/stf/stf.py | 101 | ||||
-rwxr-xr-x | source3/stf/test.py | 33 | ||||
-rw-r--r-- | source3/torture/t_stringoverflow.c | 23 |
16 files changed, 2239 insertions, 0 deletions
diff --git a/docs/docbook/projdoc/securing-samba.sgml b/docs/docbook/projdoc/securing-samba.sgml new file mode 100644 index 0000000000..bfedc5456f --- /dev/null +++ b/docs/docbook/projdoc/securing-samba.sgml @@ -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/source3/client/mount.cifs.c b/source3/client/mount.cifs.c new file mode 100755 index 0000000000..7167859d7b --- /dev/null +++ b/source3/client/mount.cifs.c @@ -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/source3/include/srvstr.h b/source3/include/srvstr.h new file mode 100644 index 0000000000..a433e0e3f9 --- /dev/null +++ b/source3/include/srvstr.h @@ -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/source3/lib/clobber.c b/source3/lib/clobber.c new file mode 100644 index 0000000000..fb3a0dc281 --- /dev/null +++ b/source3/lib/clobber.c @@ -0,0 +1,60 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Martin Pool 2003 + Copyright (C) Andrew Bartlett 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +#ifdef DEVELOPER +const char *global_clobber_region_function; +unsigned int global_clobber_region_line; +#endif + +/** + * In developer builds, clobber a region of memory. + * + * If we think a string buffer is longer than it really is, this ought + * to make the failure obvious, by segfaulting (if in the heap) or by + * killing the return address (on the stack), or by trapping under a + * memory debugger. + * + * This is meant to catch possible string overflows, even if the + * actual string copied is not big enough to cause an overflow. + * + * In addition, under Valgrind the buffer is marked as uninitialized. + **/ +void clobber_region(const char *fn, unsigned int line, char *dest, size_t len) +{ +#ifdef DEVELOPER + global_clobber_region_function = fn; + global_clobber_region_line = line; + + /* F1 is odd and 0xf1f1f1f1 shouldn't be a valid pointer */ + memset(dest, 0xF1, len); +#ifdef VALGRIND + /* Even though we just wrote to this, from the application's + * point of view it is not initialized. + * + * (This is not redundant with the clobbering above. The + * marking might not actually take effect if we're not running + * under valgrind.) */ + VALGRIND_MAKE_WRITABLE(dest, len); +#endif /* VALGRIND */ +#endif /* DEVELOPER */ +} diff --git a/source3/libsmb/ntlmssp_sign.c b/source3/libsmb/ntlmssp_sign.c new file mode 100644 index 0000000000..f51d532319 --- /dev/null +++ b/source3/libsmb/ntlmssp_sign.c @@ -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/source3/libsmb/smb_signing.c b/source3/libsmb/smb_signing.c new file mode 100644 index 0000000000..c3538ee9fd --- /dev/null +++ b/source3/libsmb/smb_signing.c @@ -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/source3/rpc_client/.cvsignore b/source3/rpc_client/.cvsignore new file mode 100644 index 0000000000..07da2225c7 --- /dev/null +++ b/source3/rpc_client/.cvsignore @@ -0,0 +1,3 @@ +*.po +*.po32 + diff --git a/source3/stf/.cvsignore b/source3/stf/.cvsignore new file mode 100644 index 0000000000..7e99e367f8 --- /dev/null +++ b/source3/stf/.cvsignore @@ -0,0 +1 @@ +*.pyc
\ No newline at end of file diff --git a/source3/stf/README.stf b/source3/stf/README.stf new file mode 100644 index 0000000000..3fbd33cb6c --- /dev/null +++ b/source3/stf/README.stf @@ -0,0 +1,3 @@ +This directory contains the Samba Testing Framework, a Python-based +system for exercising Samba in various ways. It is quite small at the +moment. diff --git a/source3/stf/info3cache.py b/source3/stf/info3cache.py new file mode 100755 index 0000000000..96d5a1d459 --- /dev/null +++ b/source3/stf/info3cache.py @@ -0,0 +1,54 @@ +#!/usr/bin/python +# +# Upon a winbindd authentication, test that an info3 record is cached in +# netsamlogon_cache.tdb and cache records are removed from winbindd_cache.tdb +# + +import comfychair, stf +from samba import tdb, winbind + +# +# We want to implement the following test on a win2k native mode domain. +# +# 1. trash netsamlogon_cache.tdb +# 2. wbinfo -r DOMAIN\Administrator [FAIL] +# 3. wbinfo --auth-crap DOMAIN\Administrator%password [PASS] +# 4. wbinfo -r DOMAIN\Administrator [PASS] +# +# Also for step 3 we want to try 'wbinfo --auth-smbd' and +# 'wbinfo --auth-plaintext' +# + +# +# TODO: To implement this test we need to be able to +# +# - pass username%password combination for an invidivual winbindd request +# (so we can get the administrator SID so we can clear the info3 cache) +# +# - start/restart winbindd (to trash the winbind cache) +# +# - from samba import dynconfig (to find location of info3 cache) +# +# - be able to modify the winbindd cache (to set/reset individual winbind +# cache entries) +# +# - have --auth-crap present in HEAD +# + +class WinbindAuthCrap(comfychair.TestCase): + def runtest(self): + raise comfychair.NotRunError, "not implemented" + +class WinbindAuthSmbd(comfychair.TestCase): + def runtest(self): + # Grr - winbindd in HEAD doesn't contain the auth_smbd function + raise comfychair.NotRunError, "no auth_smbd in HEAD" + +class WinbindAuthPlaintext(comfychair.TestCase): + def runtest(self): + raise comfychair.NotRunError, "not implemented" + +tests = [WinbindAuthCrap, WinbindAuthSmbd, WinbindAuthPlaintext] + +if __name__ == "__main__": + comfychair.main(tests) diff --git a/source3/stf/notes.txt b/source3/stf/notes.txt new file mode 100644 index 0000000000..68aca63c23 --- /dev/null +++ b/source3/stf/notes.txt @@ -0,0 +1,175 @@ + -*- indented-text -*- + +(set lotus no) + + + +Notes on using comfychair with Samba (samba testing framework units): + +The tests need to rely on some external resources, such as + +If suitable resources are not available, need to skip particular +tests. Must include a message indicating what resources would be +needed to run that test. (e.g. must be root.) + +We want to be able to select and run particular subsets of tests, such +as "all winbind tests". + +We want to keep the number of configurable parameters down as much as +possible, to make it easy on people running the tests. + +Wherever possible, the tests should set up their preconditions, but a +few basic resources need to be provided by the people running the +tests. So for example, rather than asking the user for the name of a +non-root user, we should give the tests the administrator name and +password, and it can create a new user to use. + +This makes it simpler to get the tests running, and possible also +makes them more reproducible. + +In the future, rather than using NT machines provided by the test +person, we might have a way to drive VMWare non-persistent sessions, +to make tests even more tightly controlled. + + +Another design question is how to communicate this information to the +tests. If there's a lot of settings, then it might need to be stored +in a configuration file. + +However, if we succeed in cutting down the number of parameters, then +it might be straightforward to pass the information on the command +line or in an environment variable. + +Environment variables are probably better because they can't be seen +by other users, and they are more easily passed down through an +invocation of "make check". + + + +Notes on Samba Testing Framework for Unittests +---------------------------------------------- + +This is to be read after reading the notes.txt from comfychair. I'm +proposing a slightly more concrete description of what's described +there. + +The model of having tests require named resources looks useful for +incorporation into a framework that can be run by many people in +widely different environments. + +Some possible environments for running the test framework in are: + + - Casual downloader of Samba compiling from source and just wants + to run 'make check'. May only have one Unix machine and a + handful of clients. + + - Samba team member with access to a small number of other + machines or VMware sessions. + + - PSA developer who may not have intimate knowledge of Samba + internals and is only interested in testing against the PSA. + + - Non-team hacker wanting to run test suite after making small + hacks. + + - Build farm environment (loaner machine with no physical access + or root privilege). + + - HP BAT. + +Developers in most of these environments are also potential test case +authors. It should be easy for people unfamiliar with the framework +to write new tests and have them work. We should provide examples and +the existing tests should well written and understandable. + +Different types of tests: + + - Tests that check Samba internals and link against + libbigballofmud.so. For example: + + - Upper/lowercase string functions + - user_in_list() for large lists + + - Tests that use the Samba Python extensions. + + - Tests that execute Samba command line programs, for example + smbpasswd. + + - Tests that require other resources on the network such as domain + controllers or PSAs. + + - Tests that are performed on the documentation or the source code + such as: + + - grep for common spelling mistakes made by abartlet (-: + - grep for company copyright (IBM, HP) + + - Link to other existing testing frameworks (smbtorture, + abartlet's bash based build farm tests) + +I propose a TestResourceManager which would be instantiated by a test +case. The test case would require("resourcename") as part of its +constructor and raise a comfychair.NotRun exception if the resource +was not present. A TestResource class could be defined which could +read a configuration file or examine a environment variable and +register a resource only if some condition was satisfied. + +It would be nice to be able to completely separate the PSA testing +from the test framework. This would entail being able to define test +resources dynamically, possibly with a plugin type system. + +class TestResourceManager: + def __init__(self, name): + self.resources = {} + + def register(self, resource): + name = resource.name() + if self.resources.has_key(name): + raise "Test manager already has resource %s" % name + self.resources[name] = resource + + def require(self, resource_name): + if not self.resources.has_key(resource_name): + raise "Test manager does not have resources %s" % resource_name + +class TestResource: + def __init__(self, name): + self.name = name + + def name(self): + return self.name + +import os + +trm = TestResourceManager() + +if os.getuid() == 0: + trm.register(TestResource("root")) + +A config-o-matic Python module can take a list of machines and +administrator%password entries and classify them by operating system +version and service pack. These resources would be registered with +the TestResourceManager. + +Some random thoughts about named resources for network servers: + +require("nt4.sp3") +require("nt4.domaincontroller") +require("psa") + +Some kind of format for location of passwords, libraries: + +require("exec(smbpasswd)") +require("lib(bigballofmud)") + +maybe require("exec.smbpasswd") looks nicer... + +The require() function could return a dictionary of configuration +information or some handle to fetch dynamic information on. We may +need to create and destroy extra users or print queues. How to manage +cleanup of dynamic resources? + +Requirements for running stf: + + - Python, obviously + - Samba python extensions diff --git a/source3/stf/osver.py b/source3/stf/osver.py new file mode 100755 index 0000000000..68601fa7bb --- /dev/null +++ b/source3/stf/osver.py @@ -0,0 +1,55 @@ +#!/usr/bin/python +# +# Utilities for determining the Windows operating system version remotely. +# + +from samba import srvsvc + +# Constants + +PLATFORM_UNKNOWN = 0 +PLATFORM_WIN9X = 1 +PLATFORM_NT4 = 2 +PLATFORM_NT5 = 3 # Windows 2000 + +def platform_name(platform_type): + + platform_names = { PLATFORM_UNKNOWN: "Unknown", + PLATFORM_WIN9X: "Windows 9x", + PLATFORM_NT4: "Windows NT", + PLATFORM_NT5: "Windows 2000" } + + if platform_names.has_key(platform_type): + return platform_names[platform_type] + + return "Unknown" + +def platform_type(info101): + """Determine the operating system type from a SRV_INFO_101.""" + + if info101['major_version'] == 4 and info101['minor_version'] == 0: + return PLATFORM_NT4 + + if info101['major_version'] == 5 and info101['minor_version'] == 0: + return PLATFORM_NT5 + + return PLATFORM_UNKNOWN + +def is_domain_controller(info101): + """Return true if the server_type field from a SRV_INFO_101 + indicates a domain controller.""" + return info101['server_type'] & srvsvc.SV_TYPE_DOMAIN_CTRL + +def os_version(name): + info = srvsvc.netservergetinfo("\\\\%s" % name, 101) + return platform_type(info) + +if __name__ == "__main__": + import sys + if len(sys.argv) != 2: + print "Usage: osver.py server" + sys.exit(0) + info = srvsvc.netservergetinfo("\\\\%s" % sys.argv[1], 101) + print "platform type = %d" % platform_type(info) + if is_domain_controller(info): + print "%s is a domain controller" % sys.argv[1] diff --git a/source3/stf/spoolss.py b/source3/stf/spoolss.py new file mode 100755 index 0000000000..735291508b --- /dev/null +++ b/source3/stf/spoolss.py @@ -0,0 +1,288 @@ +#!/usr/bin/python + +import re +import comfychair, stf +from samba import spoolss + +class PrintServerTest(comfychair.TestCase): + """An abstract class requiring a print server.""" + def setUp(self): + # TODO: create a test printer + self.server = stf.get_server(platform = "nt") + self.require(self.server != None, "print server required") + # TODO: remove hardcoded printer name + self.printername = "p" + self.uncname = "\\\\%s\\%s" % \ + (self.server["hostname"], self.printername) + +class W2kPrintServerTest(comfychair.TestCase): + """An abstract class requiring a print server.""" + def setUp(self): + # TODO: create a test printer + self.server = stf.get_server(platform = "nt5") + self.require(self.server != None, "print server required") + # TODO: remove hardcoded printer name + self.printername = "p" + self.uncname = "\\\\%s\\%s" % \ + (self.server["hostname"], self.printername) + +class CredentialTest(PrintServerTest): + """An class that calls a function with various sets of credentials.""" + def runTest(self): + + bad_user_creds = {"username": "spotty", + "domain": "dog", + "password": "bone"} + + cases = ((self.server["administrator"], "Admin credentials", 1), + (bad_user_creds, "Bad credentials", 0)) + + # TODO: add unpriv user case + + for creds, testname, result in cases: + try: + self.runTestArg(creds) + except: + if result: + import traceback + traceback.print_exc() + self.fail("rpc with creds %s failed when it " + "should have suceeded" % creds) + return + + if not result: + self.fail("rpc with creds %s suceeded when it should " + "have failed" % creds) + +class ArgTestServer(PrintServerTest): + """Test a RPC that takes a UNC print server name.""" + def runTest(self): + + # List of test cases, %s substituted for server name + + cases = (("", "No server name", 0), + ("\\\\%s", "Valid server name", 1), + ("\\%s", "Invalid unc server name", 0), + ("\\\\%s__", "Invalid unc server name", 0)) + + for unc, testname, result in cases: + unc = re.sub("%s", self.server["hostname"], unc) + try: + self.runTestArg(unc) + except: + if result: + self.fail("rpc(\"%s\") failed when it should have " + "suceeded" % unc) + return + + if not result: + # Suceeded when we should have failed + self.fail("rpc(\"%s\") suceeded when it should have " + "failed" % unc) + +class ArgTestServerAndPrinter(ArgTestServer): + """Test a RPC that takes a UNC print server or UNC printer name.""" + def runTest(self): + + ArgTestServer.runTest(self) + + # List of test cases, %s substituted for server name, %p substituted + # for printer name. + + cases = (("\\\\%s\\%p", "Valid server and printer name", 1), + ("\\\\%s\\%p__", "Valid server, invalid printer name", 0), + ("\\\\%s__\\%p", "Invalid server, valid printer name", 0)) + + for unc, testname, result in cases: + unc = re.sub("%s", self.server["hostname"], unc) + unc = re.sub("%p", self.printername, unc) + try: + self.runTestArg(unc) + except: + if result: + self.fail("openprinter(\"%s\") failed when it should have " + "suceeded" % unc) + return + + if not result: + # Suceeded when we should have failed + self.fail("openprinter(\"%s\") suceeded when it should have " + "failed" % unc) + +class OpenPrinterArg(ArgTestServerAndPrinter): + """Test the OpenPrinter RPC with combinations of valid and invalid + server and printer names.""" + def runTestArg(self, unc): + spoolss.openprinter(unc) + +class OpenPrinterCred(CredentialTest): + """Test opening printer with good and bad credentials.""" + def runTestArg(self, creds): + spoolss.openprinter(self.uncname, creds = creds) + +class ClosePrinter(PrintServerTest): + """Test the ClosePrinter RPC on a printer handle.""" + def runTest(self): + hnd = spoolss.openprinter(self.uncname) + spoolss.closeprinter(hnd) + +class ClosePrinterServer(PrintServerTest): + """Test the ClosePrinter RPC on a print server handle.""" + def runTest(self): + hnd = spoolss.openprinter("\\\\%s" % self.server["hostname"]) + spoolss.closeprinter(hnd) + +class GetPrinterInfo(PrintServerTest): + """Retrieve printer info at various levels.""" + + # Sample printer data + + sample_info = { + 0: {'printer_errors': 0, 'unknown18': 0, 'unknown13': 0, 'unknown26': 0, 'cjobs': 0, 'unknown11': 0, 'server_name': '\\\\win2kdc1', 'total_pages': 0, 'unknown15': 586, 'unknown16': 0, 'month': 2, 'unknown20': 0, 'second': 23, 'unknown22': 983040, 'unknown25': 0, 'total_bytes': 0, 'unknown27': 0, 'year': 2003, 'build_version': 2195, 'unknown28': 0, 'global_counter': 4, 'day': 13, 'minute': 53, 'total_jobs': 0, 'unknown29': 1114112, 'name': '\\\\win2kdc1\\p', 'hour': 2, 'level': 0, 'c_setprinter': 0, 'change_id': 522454169, 'major_version': 5, 'unknown23': 15, 'day_of_week': 4, 'unknown14': 1, 'session_counter': 2, 'status': 1, 'unknown7': 1, 'unknown8': 0, 'unknown9': 0, 'milliseconds': 421, 'unknown24': 0}, + 1: {'comment': "I'm a teapot!", 'level': 1, 'flags': 8388608, 'name': '\\\\win2kdc1\\p', 'description': '\\\\win2kdc1\\p,HP LaserJet 4,Canberra office'}, + 2: {'comment': "I'm a teapot!", 'status': 1, 'print_processor': 'WinPrint', 'until_time': 0, 'share_name': 'p', 'start_time': 0, 'device_mode': {'icm_method': 1, 'bits_per_pel': 0, 'log_pixels': 0, 'orientation': 1, 'panning_width': 0, 'color': 2, 'pels_width': 0, 'print_quality': 600, 'driver_version': 24, 'display_flags': 0, 'y_resolution': 600, 'media_type': 0, 'display_frequency': 0, 'icm_intent': 0, 'pels_height': 0, 'reserved1': 0, 'size': 220, 'scale': 100, 'dither_type': 0, 'panning_height': 0, 'default_source': 7, 'duplex': 1, 'fields': 16131, 'spec_version': 1025, 'copies': 1, 'device_name': '\\\\win2kdc1\\p', 'paper_size': 1, 'paper_length': 0, 'private': 'private', 'collate': 0, 'paper_width': 0, 'form_name': 'Letter', 'reserved2': 0, 'tt_option': 0}, 'port_name': 'LPT1:', 'sepfile': '', 'parameters': '', 'security_descriptor': {'group_sid': 'S-1-5-21-1606980848-1677128483-854245398-513', 'sacl': None, 'dacl': {'ace_list': [{'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-544'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-544'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1121'}, {'flags': 10, 'type': 0, 'mask': 131072, 'trustee': 'S-1-3-0'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-3-0'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1124'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-1-0'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-550'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-550'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-549'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-549'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1106'}], 'revision': 2}, 'owner_sid': 'S-1-5-32-544', 'revision': 1}, 'name': '\\\\win2kdc1\\p', 'server_name': '\\\\win2kdc1', 'level': 2, 'datatype': 'RAW', 'cjobs': 0, 'average_ppm': 0, 'priority': 1, 'driver_name': 'HP LaserJet 4', 'location': 'Canberra office', 'attributes': 8776, 'default_priority': 0}, + 3: {'flags': 4, 'security_descriptor': {'group_sid': 'S-1-5-21-1606980848-1677128483-854245398-513', 'sacl': None, 'dacl': {'ace_list': [{'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-544'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-544'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1121'}, {'flags': 10, 'type': 0, 'mask': 131072, 'trustee': 'S-1-3-0'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-3-0'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1124'}, {'flags': 0, 'type': 0, 'mask': 131080, 'trustee': 'S-1-1-0'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-550'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-550'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-32-549'}, {'flags': 9, 'type': 0, 'mask': 983056, 'trustee': 'S-1-5-32-549'}, {'flags': 0, 'type': 0, 'mask': 983052, 'trustee': 'S-1-5-21-1606980848-1677128483-854245398-1106'}], 'revision': 2}, 'owner_sid': 'S-1-5-32-544', 'revision': 1}, 'level': 3} + } + + def runTest(self): + self.hnd = spoolss.openprinter(self.uncname) + + # Everyone should have getprinter levels 0-3 + + for i in (0, 1, 2, 3): + info = self.hnd.getprinter(level = i) + try: + stf.dict_check(self.sample_info[i], info) + except ValueError, msg: + raise "info%d: %s" % (i, msg) + +class EnumPrinters(PrintServerTest): + """Enumerate print info at various levels.""" + + sample_info = { + + 0: {'q': {'printer_errors': 0, 'unknown18': 0, 'unknown13': 0, 'unknown26': 0, 'cjobs': 0, 'unknown11': 0, 'server_name': '', 'total_pages': 0, 'unknown15': 586, 'unknown16': 0, 'month': 2, 'unknown20': 0, 'second': 23, 'unknown22': 983040, 'unknown25': 0, 'total_bytes': 0, 'unknown27': 0, 'year': 2003, 'build_version': 2195, 'unknown28': 0, 'global_counter': 4, 'day': 13, 'minute': 53, 'total_jobs': 0, 'unknown29': -1833435136, 'name': 'q', 'hour': 2, 'level': 0, 'c_setprinter': 0, 'change_id': 522454169, 'major_version': 5, 'unknown23': 15, 'day_of_week': 4, 'unknown14': 1, 'session_counter': 1, 'status': 0, 'unknown7': 1, 'unknown8': 0, 'unknown9': 0, 'milliseconds': 421, 'unknown24': 0}, 'p': {'printer_errors': 0, 'unknown18': 0, 'unknown13': 0, 'unknown26': 0, 'cjobs': 0, 'unknown11': 0, 'server_name': '', 'total_pages': 0, 'unknown15': 586, 'unknown16': 0, 'month': 2, 'unknown20': 0, 'second': 23, 'unknown22': 983040, 'unknown25': 0, 'total_bytes': 0, 'unknown27': 0, 'year': 2003, 'build_version': 2195, 'unknown28': 0, 'global_counter': 4, 'day': 13, 'minute': 53, 'total_jobs': 0, 'unknown29': -1831337984, 'name': 'p', 'hour': 2, 'level': 0, 'c_setprinter': 0, 'change_id': 522454169, 'major_version': 5, 'unknown23': 15, 'day_of_week': 4, 'unknown14': 1, 'session_counter': 1, 'status': 1, 'unknown7': 1, 'unknown8': 0, 'unknown9': 0, 'milliseconds': 421, 'unknown24': 0}, 'magpie': {'printer_errors': 0, 'unknown18': 0, 'unknown13': 0, 'unknown26': 0, 'cjobs': 0, 'unknown11': 0, 'server_name': '', 'total_pages': 0, 'unknown15': 586, 'unknown16': 0, 'month': 2, 'unknown20': 0, 'second': 23, 'unknown22': 983040, 'unknown25': 0, 'total_bytes': 0, 'unknown27': 0, 'year': 2003, 'build_version': 2195, 'unknown28': 0, 'global_counter': 4, 'day': 13, 'minute': 53, 'total_jobs': 0, 'unknown29': 1114112, 'name': 'magpie', 'hour': 2, 'level': 0, 'c_setprinter': 0, 'change_id': 522454169, 'major_version': 5, 'unknown23': 15, 'day_of_week': 4, 'unknown14': 1, 'session_counter': 1, 'status': 0, 'unknown7': 1, 'unknown8': 0, 'unknown9': 0, 'milliseconds': 421, 'unknown24': 0}}, + + 1: {'q': {'comment': 'cheepy birds', 'level': 1, 'flags': 8388608, 'name': 'q', 'description': 'q,HP LaserJet 4,'}, 'p': {'comment': "I'm a teapot!", 'level': 1, 'flags': 8388608, 'name': 'p', 'description': 'p,HP LaserJet 4,Canberra office'}, 'magpie': {'comment': '', 'level': 1, 'flags': 8388608, 'name': 'magpie', 'description': 'magpie,Generic / Text Only,'}} + } + + def runTest(self): + for i in (0, 1): + info = spoolss.enumprinters( + "\\\\%s" % self.server["hostname"], level = i) + try: + stf.dict_check(self.sample_info[i], info) + except ValueError, msg: + raise "info%d: %s" % (i, msg) + +class EnumPrintersArg(ArgTestServer): + def runTestArg(self, unc): + spoolss.enumprinters(unc) + +class EnumPrintersCred(CredentialTest): + """Test opening printer with good and bad credentials.""" + def runTestArg(self, creds): + spoolss.enumprinters( + "\\\\%s" % self.server["hostname"], creds = creds) + +class EnumPrinterdrivers(PrintServerTest): + + sample_info = { + 1: {'Okipage 10ex (PCL5E) : STANDARD': {'name': 'Okipage 10ex (PCL5E) : STANDARD', 'level': 1}, 'Generic / Text Only': {'name': 'Generic / Text Only', 'level': 1}, 'Brother HL-1030 series': {'name': 'Brother HL-1030 series', 'level': 1}, 'Brother HL-1240 series': {'name': 'Brother HL-1240 series', 'level': 1}, 'HP DeskJet 1220C Printer': {'name': 'HP DeskJet 1220C Printer', 'level': 1}, 'HP LaserJet 4100 PCL 6': {'name': 'HP LaserJet 4100 PCL 6', 'level': 1}, 'HP LaserJet 4': {'name': 'HP LaserJet 4', 'level': 1}}, + 2: {'Okipage 10ex (PCL5E) : STANDARD': {'version': 2, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\2\\RASDDUI.DLL', 'name': 'Okipage 10ex (PCL5E) : STANDARD', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\2\\RASDD.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\2\\OKIPAGE.DLL', 'level': 2, 'architecture': 'Windows NT x86'}, 'Generic / Text Only': {'version': 3, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\UNIDRVUI.DLL', 'name': 'Generic / Text Only', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\3\\UNIDRV.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\TTY.GPD', 'level': 2, 'architecture': 'Windows NT x86'}, 'Brother HL-1030 series': {'version': 3, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BRUHL99A.DLL', 'name': 'Brother HL-1030 series', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BROHL99A.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BROHL103.PPD', 'level': 2, 'architecture': 'Windows NT x86'}, 'Brother HL-1240 series': {'version': 3, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BRUHL99A.DLL', 'name': 'Brother HL-1240 series', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BROHL99A.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\BROHL124.PPD', 'level': 2, 'architecture': 'Windows NT x86'}, 'HP DeskJet 1220C Printer': {'version': 3, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPW8KMD.DLL', 'name': 'HP DeskJet 1220C Printer', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPW8KMD.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPW8KMD.DLL', 'level': 2, 'architecture': 'Windows NT x86'}, 'HP LaserJet 4100 PCL 6': {'version': 3, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPBF042E.DLL', 'name': 'HP LaserJet 4100 PCL 6', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPBF042G.DLL', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\3\\HPBF042I.PMD', 'level': 2, 'architecture': 'Windows NT x86'}, 'HP LaserJet 4': {'version': 2, 'config_file': '\\\\WIN2KDC1\\print$\\W32X86\\2\\hpblff0.dll', 'name': 'HP LaserJet 4', 'driver_path': '\\\\WIN2KDC1\\print$\\W32X86\\2\\hpblff2.dll', 'data_file': '\\\\WIN2KDC1\\print$\\W32X86\\2\\hpblff39.pmd', 'level': 2, 'architecture': 'Windows NT x86'}} + } + + def runTest(self): + for i in (1, 2): + info = spoolss.enumprinterdrivers( + "\\\\%s" % self.server["hostname"], level = i) + try: + if not self.sample_info.has_key(i): + self.log("%s" % info) + self.fail() + stf.dict_check(self.sample_info[i], info) + except ValueError, msg: + raise "info%d: %s" % (i, msg) + +class EnumPrinterdriversArg(ArgTestServer): + def runTestArg(self, unc): + spoolss.enumprinterdrivers(unc) + +class EnumPrinterdriversCred(CredentialTest): + """Test opening printer with good and bad credentials.""" + def runTestArg(self, creds): + spoolss.enumprinterdrivers( + "\\\\%s" % self.server["hostname"], creds = creds) + +def usage(): + print "Usage: spoolss.py [options] [test1[,test2...]]" + print "\t -v/--verbose Display debugging information" + print "\t -l/--list-tests List available tests" + print + print "A list of comma separated test names or regular expressions" + print "can be used to filter the tests performed." + +def test_match(subtest_list, test_name): + """Return true if a test matches a comma separated list of regular + expression of test names.""" + # re.match does an implicit ^ at the start of the pattern. + # Explicitly anchor to end to avoid matching substrings. + for s in string.split(subtest_list, ","): + if re.match(s + "$", test_name): + return 1 + return 0 + +if __name__ == "__main__": + import os, sys, string + import getopt + + try: + opts, args = getopt.getopt(sys.argv[1:], "vl", \ + ["verbose", "list-tests"]) + except getopt.GetoptError: + usage() + sys.exit(0) + + verbose = 0 + list_tests = 0 + + for opt, arg in opts: + if opt in ("-v", "--verbose"): + verbose = 1 + if opt in ("-l", "--list-tests"): + list_tests = 1 + + if len(args) > 1: + usage() + sys.exit(0) + + test_list = [ + OpenPrinterArg, + OpenPrinterCred, + ClosePrinter, + ClosePrinterServer, + GetPrinterInfo, + EnumPrinters, + EnumPrintersCred, + EnumPrintersArg, + EnumPrinterdrivers, + EnumPrinterdriversCred, + EnumPrinterdriversArg, + ] + + if len(args): + t = [] + for test in test_list: + if test_match(args[0], test.__name__): + t.append(test) + test_list = t + + if os.environ.has_key("SAMBA_DEBUG"): + spoolss.setup_logging(interactive = 1) + spoolss.set_debuglevel(10) + + if list_tests: + for test in test_list: + print test.__name__ + else: + comfychair.runtests(test_list, verbose = verbose) diff --git a/source3/stf/stf.py b/source3/stf/stf.py new file mode 100755 index 0000000000..ee0ff73561 --- /dev/null +++ b/source3/stf/stf.py @@ -0,0 +1,101 @@ +#!/usr/bin/python +# +# Samba Testing Framework for Unit-testing +# + +import os, string, re +import osver + +def get_server_list_from_string(s): + + server_list = [] + + # Format is a list of server:domain\username%password separated + # by commas. + + for entry in string.split(s, ","): + + # Parse entry + + m = re.match("(.*):(.*)(\\\\|/)(.*)%(.*)", entry) + if not m: + raise "badly formed server list entry '%s'" % entry + + server = m.group(1) + domain = m.group(2) + username = m.group(4) + password = m.group(5) + + # Categorise servers + + server_list.append({"platform": osver.os_version(server), + "hostname": server, + "administrator": {"username": username, + "domain": domain, + "password" : password}}) + + return server_list + +def get_server_list(): + """Iterate through all sources of server info and append them all + in one big list.""" + + server_list = [] + + # The $STF_SERVERS environment variable + + if os.environ.has_key("STF_SERVERS"): + server_list = server_list + \ + get_server_list_from_string(os.environ["STF_SERVERS"]) + + return server_list + +def get_server(platform = None): + """Return configuration information for a server. The platform + argument can be a string either 'nt4' or 'nt5' for Windows NT or + Windows 2000 servers, or just 'nt' for Windows NT and higher.""" + + server_list = get_server_list() + + for server in server_list: + if platform: + p = server["platform"] + if platform == "nt": + if (p == osver.PLATFORM_NT4 or p == osver.PLATFORM_NT5): + return server + if platform == "nt4" and p == osver.PLATFORM_NT4: + return server + if platform == "nt5" and p == osver.PLATFORM_NT5: + return server + else: + # No filter defined, return first in list + return server + + return None + +def dict_check(sample_dict, real_dict): + """Check that real_dict contains all the keys present in sample_dict + and no extras. Also check that common keys are of them same type.""" + tmp = real_dict.copy() + for key in sample_dict.keys(): + # Check existing key and type + if not real_dict.has_key(key): + raise ValueError, "dict does not contain key '%s'" % key + if type(sample_dict[key]) != type(real_dict[key]): + raise ValueError, "dict has differing types (%s vs %s) for key " \ + "'%s'" % (type(sample_dict[key]), type(real_dict[key]), key) + # Check dictionaries recursively + if type(sample_dict[key]) == dict: + dict_check(sample_dict[key], real_dict[key]) + # Delete visited keys from copy + del(tmp[key]) + # Any keys leftover are present in the real dict but not the sample + if len(tmp) == 0: + return + result = "dict has extra keys: " + for key in tmp.keys(): + result = result + key + " " + raise ValueError, result + +if __name__ == "__main__": + print get_server(platform = "nt") diff --git a/source3/stf/test.py b/source3/stf/test.py new file mode 100755 index 0000000000..fb57926cc3 --- /dev/null +++ b/source3/stf/test.py @@ -0,0 +1,33 @@ +#!/usr/bin/python + +# meta-test-case / example for comfychair. Should demonstrate +# different kinds of failure. + +import comfychair + +class NormalTest(comfychair.TestCase): + def runtest(self): + pass + +class RootTest(comfychair.TestCase): + def setup(self): + self.require_root() + + def runTest(self): + pass + +class GoodExecTest(comfychair.TestCase): + def runtest(self): + stdout = self.runcmd("ls -l") + +class BadExecTest(comfychair.TestCase): + def setup(self): + exit, stdout = self.runcmd_unchecked("spottyfoot --slobber", + skip_on_noexec = 1) + + +tests = [NormalTest, RootTest, GoodExecTest, BadExecTest] + +if __name__ == '__main__': + comfychair.main(tests) + diff --git a/source3/torture/t_stringoverflow.c b/source3/torture/t_stringoverflow.c new file mode 100644 index 0000000000..ec14d81189 --- /dev/null +++ b/source3/torture/t_stringoverflow.c @@ -0,0 +1,23 @@ +#include "includes.h" + + int main(void) +{ + fstring dest; + char *ptr = dest; + + printf("running on valgrind? %d\n", RUNNING_ON_VALGRIND); + + /* Try copying a string into an fstring buffer. The string + * will actually fit, but this is still wrong because you + * can't pstrcpy into an fstring. This should trap in a + * developer build. */ + +#if 0 + /* As of CVS 20030318, this will be trapped at compile time! */ + pstrcpy(dest, "hello"); +#endif /* 0 */ + + pstrcpy(ptr, "hello!"); + + return 0; +} |