Unix SMB/Netbios implementation.
Version 1.9.
SMB torture tester
- Copyright (C) Andrew Tridgell 1997
+ Copyright (C) Andrew Tridgell 1997-1998
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#ifdef SYSLOG
-#undef SYSLOG
-#endif
+#define NO_SYSLOG
#include "includes.h"
static struct timeval tp1,tp2;
-static void start_timer()
+static void start_timer(void)
{
gettimeofday(&tp1,NULL);
}
-static double end_timer()
+static double end_timer(void)
{
gettimeofday(&tp2,NULL);
return((tp2.tv_sec - tp1.tv_sec) +
static BOOL open_connection(struct cli_state *c)
{
+ struct nmb_name called, calling;
+
+ ZERO_STRUCTP(c);
+
+ make_nmb_name(&calling, myname, 0x0, "");
+ make_nmb_name(&called , host, 0x20, "");
+
if (!cli_initialise(c) || !cli_connect(c, host, NULL)) {
printf("Failed to connect with %s\n", host);
return False;
}
- if (!cli_session_request(c, host, 0x20, myname)) {
- printf("%s rejected the session\n",host);
+ if (!cli_session_request(c, &calling, &called)) {
cli_shutdown(c);
+ printf("%s rejected the session\n",host);
return False;
}
- c->protocol = max_protocol;
-
if (!cli_negprot(c)) {
printf("%s rejected the negprot (%s)\n",host, cli_errstr(c));
cli_shutdown(c);
return False;
}
- if (!cli_session_setup(c, username, password, strlen(password),
- "", 0, workgroup)) {
- printf("%s rejected the sessionsetup (%s)\n", host, cli_errstr(c));
+ if (!cli_session_setup(c, username,
+ password, strlen(password),
+ password, strlen(password),
+ workgroup)) {
cli_shutdown(c);
+ printf("%s rejected the sessionsetup (%s)\n", host, cli_errstr(c));
return False;
}
- if (!cli_send_tconX(c, share, "A:", password, strlen(password)+1)) {
+ if (!cli_send_tconX(c, share, "?????",
+ password, strlen(password)+1)) {
printf("%s refused tree connect (%s)\n", host, cli_errstr(c));
cli_shutdown(c);
return False;
}
+/* check if the server produced the expected error code */
+static BOOL check_error(struct cli_state *c,
+ uint8 eclass, uint32 ecode, uint32 nterr)
+{
+ uint8 class;
+ uint32 num;
+ int eno;
+ eno = cli_error(c, &class, &num);
+ if ((eclass != class || ecode != num) &&
+ num != (nterr&0xFFFFFF)) {
+ printf("unexpected error code class=%d code=%d\n",
+ (int)class, (int)num);
+ printf(" expected %d/%d %d\n",
+ (int)eclass, (int)ecode, (int)nterr);
+ return False;
+ }
+ return True;
+}
+
+
static BOOL wait_lock(struct cli_state *c, int fnum, uint32 offset, uint32 len)
{
while (!cli_lock(c, fnum, offset, len, -1)) {
- int eclass, num;
- cli_error(c, &eclass, &num);
- if (eclass != ERRDOS || num != ERRlock) {
- printf("lock failed (%s)\n",
- cli_errstr(c));
- return False;
- }
+ if (!check_error(c, ERRDOS, ERRlock, 0)) return False;
}
return True;
}
int fnum;
int fnum2;
int pid2, pid = getpid();
- int i;
+ int i, j;
+ char buf[1024];
fnum2 = cli_open(c, lockfname, O_RDWR | O_CREAT | O_EXCL,
DENY_NONE);
for (i=0;i<numops;i++) {
- unsigned n = (unsigned)random()%10;
+ unsigned n = (unsigned)sys_random()%10;
if (i % 10 == 0) {
printf("%d\r", i); fflush(stdout);
}
- sprintf(fname,"\\torture.%u", n);
+ slprintf(fname, sizeof(fstring) - 1, "\\torture.%u", n);
if (!wait_lock(c, fnum2, n*sizeof(int), sizeof(int))) {
return False;
break;
}
- if (cli_write(c, fnum, (char *)&pid, 0, sizeof(pid)) != sizeof(pid)) {
+ if (cli_write(c, fnum, 0, (char *)&pid, 0, sizeof(pid)) != sizeof(pid)) {
printf("write failed (%s)\n", cli_errstr(c));
}
+ for (j=0;j<50;j++) {
+ if (cli_write(c, fnum, 0, (char *)buf,
+ sizeof(pid)+(j*sizeof(buf)),
+ sizeof(buf)) != sizeof(buf)) {
+ printf("write failed (%s)\n", cli_errstr(c));
+ }
+ }
+
pid2 = 0;
if (cli_read(c, fnum, (char *)&pid2, 0, sizeof(pid)) != sizeof(pid)) {
}
}
+ cli_close(c, fnum2);
+ cli_unlink(c, lockfname);
+
printf("%d\n", i);
return True;
static void usage(void)
{
- printf("Usage: smbtorture \\\\server\\share <options>\n");
+ printf("Usage: smbtorture //server/share <options>\n");
printf("\t-U user%%pass\n");
printf("\t-N numprocs\n");
static void run_locktest1(void)
{
static struct cli_state cli1, cli2;
- char *fname = "\\locktest.lck";
+ char *fname = "\\lockt1.lck";
int fnum1, fnum2, fnum3;
time_t t1, t2;
printf("lock2 succeeded! This is a locking bug\n");
return;
} else {
- int eclass, num;
- cli_error(&cli2, &eclass, &num);
- if (eclass != ERRDOS || num != ERRlock) {
- printf("error should have been ERRDOS/ERRlock (%s)\n",
- cli_errstr(&cli2));
- return;
- }
+ if (!check_error(&cli2, ERRDOS, ERRlock, 0)) return;
}
printf("lock3 succeeded! This is a locking bug\n");
return;
} else {
- int eclass, num;
- cli_error(&cli2, &eclass, &num);
- if (eclass != ERRDOS || num != ERRlock) {
- printf("error should have been ERRDOS/ERRlock (%s)\n",
- cli_errstr(&cli2));
- return;
- }
+ if (!check_error(&cli2, ERRDOS, ERRlock, 0)) return;
}
t2 = time(NULL);
printf("lock4 succeeded! This is a locking bug\n");
return;
} else {
- int eclass, num;
- cli_error(&cli2, &eclass, &num);
- if (eclass != ERRDOS || num != ERRlock) {
- printf("error should have been ERRDOS/ERRlock (%s)\n",
- cli_errstr(&cli2));
- return;
- }
+ if (!check_error(&cli2, ERRDOS, ERRlock, 0)) return;
}
if (!cli_close(&cli1, fnum1)) {
static void run_locktest2(void)
{
static struct cli_state cli;
- char *fname = "\\locktest.lck";
+ char *fname = "\\lockt2.lck";
int fnum1, fnum2, fnum3;
if (!open_connection(&cli)) {
if (cli_lock(&cli, fnum2, 0, 4, 0)) {
printf("lock2 succeeded! This is a locking bug\n");
} else {
- int eclass, num;
- cli_error(&cli, &eclass, &num);
- if (eclass != ERRDOS || num != ERRlock) {
- printf("error should have been ERRDOS/ERRlock (%s)\n",
- cli_errstr(&cli));
- return;
- }
+ if (!check_error(&cli, ERRDOS, ERRlock, 0)) return;
}
cli_setpid(&cli, 2);
if (cli_lock(&cli, fnum3, 0, 4, 0)) {
printf("lock3 succeeded! This is a locking bug\n");
} else {
- int eclass, num;
- cli_error(&cli, &eclass, &num);
- if (eclass != ERRDOS || num != ERRlock) {
- printf("error should have been ERRDOS/ERRlock (%s)\n",
- cli_errstr(&cli));
- return;
- }
+ if (!check_error(&cli, ERRDOS, ERRlock, 0)) return;
}
cli_setpid(&cli, 1);
static void run_locktest3(int numops)
{
static struct cli_state cli1, cli2;
- char *fname = "\\locktest.lck";
+ char *fname = "\\lockt3.lck";
int fnum1, fnum2, i;
uint32 offset;
}
+/*
+test whether fnums and tids open on one VC are available on another (a major
+security hole)
+*/
+static void run_fdpasstest(void)
+{
+ static struct cli_state cli1, cli2;
+ char *fname = "\\fdpass.tst";
+ int fnum1;
+ pstring buf;
+
+ if (!open_connection(&cli1) || !open_connection(&cli2)) {
+ return;
+ }
+ cli_sockopt(&cli1, sockops);
+ cli_sockopt(&cli2, sockops);
+
+ printf("starting fdpasstest\n");
+
+ cli_unlink(&cli1, fname);
+
+ fnum1 = cli_open(&cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("open of %s failed (%s)\n", fname, cli_errstr(&cli1));
+ return;
+ }
+
+ if (cli_write(&cli1, fnum1, 0, "hello world\n", 0, 13) != 13) {
+ printf("write failed (%s)\n", cli_errstr(&cli1));
+ return;
+ }
+
+ cli2.vuid = cli1.vuid;
+ cli2.cnum = cli1.cnum;
+ cli2.pid = cli1.pid;
+
+
+ if (cli_read(&cli2, fnum1, buf, 0, 13) == 13) {
+ printf("read succeeded! nasty security hole [%s]\n",
+ buf);
+ return;
+ }
+
+ cli_close(&cli1, fnum1);
+ cli_unlink(&cli1, fname);
+
+ close_connection(&cli1);
+ close_connection(&cli2);
+
+ printf("finished fdpasstest\n");
+}
+
+
/*
This test checks that
printf("error: server allowed unlink on an open file\n");
}
+ cli_close(&cli, fnum);
+ cli_unlink(&cli, fname);
+
close_connection(&cli);
printf("unlink test finished\n");
}
+/*
+test how many open files this server supports on the one socket
+*/
+static void run_maxfidtest(int n)
+{
+ static struct cli_state cli;
+ char *template = "\\maxfid.%d.%d";
+ fstring fname;
+ int fnum;
+ int retries=4;
+
+ srandom(getpid());
+
+ while (!open_connection(&cli) && retries--) msleep(random() % 2000);
+
+ if (retries <= 0) {
+ printf("failed to connect\n");
+ return;
+ }
+
+ cli_sockopt(&cli, sockops);
+
+ printf("starting maxfid test\n");
+
+ fnum = 0;
+ while (1) {
+ slprintf(fname,sizeof(fname)-1,template, fnum,getpid());
+ if (cli_open(&cli, fname,
+ O_RDWR|O_CREAT|O_TRUNC, DENY_NONE) ==
+ -1) {
+ printf("open of %s failed (%s)\n",
+ fname, cli_errstr(&cli));
+ printf("maximum fnum is %d\n", fnum);
+ break;
+ }
+ fnum++;
+ }
+
+ printf("cleaning up\n");
+ while (fnum > n) {
+ fnum--;
+ slprintf(fname,sizeof(fname)-1,template, fnum,getpid());
+ if (cli_unlink(&cli, fname)) {
+ printf("unlink of %s failed (%s)\n",
+ fname, cli_errstr(&cli));
+ }
+ }
+
+ printf("maxfid test finished\n");
+ close_connection(&cli);
+}
+
+/* generate a random buffer */
+static void rand_buf(char *buf, int len)
+{
+ while (len--) {
+ *buf = sys_random();
+ buf++;
+ }
+}
+
+/* send random IPC commands */
+static void run_randomipc(void)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ int rdrcnt,rprcnt;
+ pstring param;
+ int api, param_len, i;
+ static struct cli_state cli;
+
+ printf("starting random ipc test\n");
+
+ if (!open_connection(&cli)) {
+ return;
+ }
-static void browse_callback(char *sname, uint32 stype, char *comment)
+ for (i=0;i<50000;i++) {
+ api = sys_random() % 500;
+ param_len = (sys_random() % 64);
+
+ rand_buf(param, param_len);
+
+ SSVAL(param,0,api);
+
+ cli_api(&cli,
+ param, param_len, 8,
+ NULL, 0, BUFFER_SIZE,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt);
+ }
+
+ close_connection(&cli);
+
+ printf("finished random ipc test\n");
+}
+
+
+
+static void browse_callback(const char *sname, uint32 stype,
+ const char *comment)
{
printf("\t%20.20s %08x %s\n", sname, stype, comment);
}
+
/*
This test checks the browse list code
{
static struct cli_state cli;
- printf("staring browse test\n");
+ printf("starting browse test\n");
if (!open_connection(&cli)) {
return;
time_t t, t2;
char *fname = "\\attrib.tst";
- printf("staring attrib test\n");
+ printf("starting attrib test\n");
if (!open_connection(&cli)) {
return;
printf("%s", ctime(&t2));
}
+ cli_unlink(&cli, fname);
+
close_connection(&cli);
printf("attrib test finished\n");
{
static struct cli_state cli;
int fnum;
- uint32 size;
- time_t c_time, a_time, m_time, w_time;
+ size_t size;
+ time_t c_time, a_time, m_time, w_time, m_time2;
char *fname = "\\trans2.tst";
+ char *dname = "\\trans2";
+ char *fname2 = "\\trans2\\trans2.tst";
- printf("staring trans2 test\n");
+ printf("starting trans2 test\n");
if (!open_connection(&cli)) {
return;
cli_unlink(&cli, fname);
fnum = cli_open(&cli, fname,
O_RDWR | O_CREAT | O_TRUNC, DENY_NONE);
- if (!cli_qfileinfo(&cli, fnum, &c_time, &a_time, &m_time, &size)) {
+ if (!cli_qfileinfo(&cli, fnum, NULL, &size, &c_time, &a_time, &m_time)) {
printf("ERROR: qfileinfo failed (%s)\n", cli_errstr(&cli));
}
cli_close(&cli, fnum);
O_RDWR | O_CREAT | O_TRUNC, DENY_NONE);
cli_close(&cli, fnum);
- if (!cli_qpathinfo(&cli, fname, &c_time, &a_time, &m_time, &size)) {
+ if (!cli_qpathinfo(&cli, fname, &c_time, &a_time, &m_time, &size, NULL)) {
printf("ERROR: qpathinfo failed (%s)\n", cli_errstr(&cli));
} else {
if (c_time != m_time) {
printf("This system appears to set a midnight access time\n");
}
- if (abs(m_time - time(NULL)) > 60) {
+ if (abs(m_time - time(NULL)) > 60*60*24*7) {
printf("ERROR: totally incorrect times - maybe word reversed?\n");
}
}
O_RDWR | O_CREAT | O_TRUNC, DENY_NONE);
cli_close(&cli, fnum);
if (!cli_qpathinfo2(&cli, fname, &c_time, &a_time, &m_time,
- &w_time, &size)) {
+ &w_time, &size, NULL)) {
printf("ERROR: qpathinfo2 failed (%s)\n", cli_errstr(&cli));
} else {
if (w_time < 60*60*24*2) {
}
}
+ cli_unlink(&cli, fname);
+
+
+ /* check if the server updates the directory modification time
+ when creating a new file */
+ if (!cli_mkdir(&cli, dname)) {
+ printf("ERROR: mkdir failed (%s)\n", cli_errstr(&cli));
+ }
+ sleep(3);
+ if (!cli_qpathinfo2(&cli, "\\trans2\\", &c_time, &a_time, &m_time,
+ &w_time, &size, NULL)) {
+ printf("ERROR: qpathinfo2 failed (%s)\n", cli_errstr(&cli));
+ }
+
+ fnum = cli_open(&cli, fname2,
+ O_RDWR | O_CREAT | O_TRUNC, DENY_NONE);
+ cli_write(&cli, fnum, 0, (char *)&fnum, 0, sizeof(fnum));
+ cli_close(&cli, fnum);
+ if (!cli_qpathinfo2(&cli, "\\trans2\\", &c_time, &a_time, &m_time2,
+ &w_time, &size, NULL)) {
+ printf("ERROR: qpathinfo2 failed (%s)\n", cli_errstr(&cli));
+ } else {
+ if (m_time2 == m_time)
+ printf("This system does not update directory modification times\n");
+ }
+ cli_unlink(&cli, fname2);
+ cli_rmdir(&cli, dname);
+
+
close_connection(&cli);
printf("trans2 test finished\n");
}
-static void create_procs(int nprocs, int numops)
+static void create_procs(int nprocs, int numops, void (*fn)(int ))
{
int i, status;
for (i=0;i<nprocs;i++) {
if (fork() == 0) {
int mypid = getpid();
- srandom(mypid ^ time(NULL));
- run_torture(numops);
+ sys_srandom(mypid ^ time(NULL));
+ fn(numops);
_exit(0);
}
}
usage();
}
- if (strncmp(argv[1], "\\\\", 2)) {
+ for(p = argv[1]; *p; p++)
+ if(*p == '\\')
+ *p = '/';
+
+ if (strncmp(argv[1], "//", 2)) {
usage();
}
fstrcpy(host, &argv[1][2]);
- p = strchr(&host[2],'\\');
+ p = strchr(&host[2],'/');
if (!p) {
usage();
}
get_myname(myname,NULL);
if (*username == 0 && getenv("LOGNAME")) {
- strcpy(username,getenv("LOGNAME"));
+ pstrcpy(username,getenv("LOGNAME"));
}
argc--;
fstrcpy(myname, optarg);
break;
case 'U':
- strcpy(username,optarg);
+ pstrcpy(username,optarg);
p = strchr(username,'%');
if (p) {
*p = 0;
- strcpy(password, p+1);
+ pstrcpy(password, p+1);
gotpass = 1;
}
break;
while (!gotpass) {
p = getpass("Password:");
if (p) {
- strcpy(password, p);
+ pstrcpy(password, p);
gotpass = 1;
}
}
printf("host=%s share=%s user=%s myname=%s\n",
host, share, username, myname);
- start_timer();
- create_procs(nprocs, numops);
- printf("rw_torture: %g secs\n", end_timer());
-
+ run_fdpasstest();
run_locktest1();
run_locktest2();
run_locktest3(numops);
run_attrtest();
run_trans2test();
+ create_procs(nprocs, numops, run_maxfidtest);
+
+ start_timer();
+ create_procs(nprocs, numops, run_torture);
+ printf("rw_torture: %g secs\n", end_timer());
+
+ run_randomipc();
+
return(0);
}