summaryrefslogtreecommitdiff
path: root/source/smbd
diff options
context:
space:
mode:
Diffstat (limited to 'source/smbd')
-rw-r--r--source/smbd/chgpasswd.c376
-rw-r--r--source/smbd/dir.c955
-rw-r--r--source/smbd/ipc.c2779
-rw-r--r--source/smbd/mangle.c610
-rw-r--r--source/smbd/message.c204
-rw-r--r--source/smbd/password.c1416
-rw-r--r--source/smbd/reply.c3210
-rw-r--r--source/smbd/server.c4300
-rw-r--r--source/smbd/smbrun.c96
-rw-r--r--source/smbd/trans2.c1646
-rw-r--r--source/smbd/vt_mode.c496
11 files changed, 16088 insertions, 0 deletions
diff --git a/source/smbd/chgpasswd.c b/source/smbd/chgpasswd.c
new file mode 100644
index 00000000000..dc0514c1ed7
--- /dev/null
+++ b/source/smbd/chgpasswd.c
@@ -0,0 +1,376 @@
+/* fork a child process to exec passwd and write to its
+* tty to change a users password. This is running as the
+* user who is attempting to change the password.
+*/
+
+/*
+ * This code was copied/borrowed and stolen from various sources.
+ * The primary source was the poppasswd.c from the authors of POPMail. This software
+ * was included as a client to change passwords using the 'passwd' program
+ * on the remote machine.
+ *
+ * This routine is called by set_user_password() in password.c only if ALLOW_PASSWORD_CHANGE
+ * is defined in the compiler directives located in the Makefile.
+ *
+ * This code has been hacked by Bob Nance (nance@niehs.nih.gov) and Evan Patterson
+ * (patters2@niehs.nih.gov) at the National Institute of Environmental Health Sciences
+ * and rights to modify, distribute or incorporate this change to the CAP suite or
+ * using it for any other reason are granted, so long as this disclaimer is left intact.
+ */
+
+/*
+ This code was hacked considerably for inclusion in Samba, primarily
+ by Andrew.Tridgell@anu.edu.au. The biggest change was the addition
+ of the "password chat" option, which allows the easy runtime
+ specification of the expected sequence of events to change a
+ password.
+ */
+
+#include "includes.h"
+#include "loadparm.h"
+
+extern int DEBUGLEVEL;
+
+#ifdef ALLOW_CHANGE_PASSWORD
+
+#define MINPASSWDLENGTH 5
+#define BUFSIZE 512
+
+static int findpty(char **slave)
+{
+ int master;
+#ifdef SVR4
+ extern char *ptsname();
+#else
+ static char line[12] = "/dev/ptyXX";
+ void *dirp;
+ char *dpname;
+#endif
+
+#ifdef SVR4
+ if ((master = open("/dev/ptmx", O_RDWR)) >= 1) {
+ grantpt(master);
+ unlockpt(master);
+ *slave = ptsname(master);
+ return (master);
+ }
+#else
+ dirp = OpenDir("/dev");
+ if (!dirp) return(-1);
+ while ((dpname = ReadDirName(dirp)) != NULL) {
+ if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5) {
+ line[8] = dpname[3];
+ line[9] = dpname[4];
+ if ((master = open(line, O_RDWR)) >= 0) {
+ line[5] = 't';
+ *slave = line;
+ CloseDir(dirp);
+ return (master);
+ }
+ }
+ }
+ CloseDir(dirp);
+#endif
+ return (-1);
+}
+
+static int dochild(int master,char *slavedev, char *name, char *passwordprogram)
+{
+ int slave;
+ struct termios stermios;
+ struct passwd *pass = Get_Pwnam(name,True);
+ int gid = pass->pw_gid;
+ int uid = pass->pw_uid;
+
+#ifdef USE_SETRES
+ setresuid(0,0,0);
+#else
+ setuid(0);
+#endif
+
+ /* Start new session - gets rid of controlling terminal. */
+ if (setsid() < 0) {
+ DEBUG(3,("Weirdness, couldn't let go of controlling terminal\n"));
+ return(False);
+ }
+
+ /* Open slave pty and acquire as new controlling terminal. */
+ if ((slave = open(slavedev, O_RDWR)) < 0) {
+ DEBUG(3,("More weirdness, could not open %s\n",
+ slavedev));
+ return(False);
+ }
+#ifdef SVR4
+ ioctl(slave, I_PUSH, "ptem");
+ ioctl(slave, I_PUSH, "ldterm");
+#else
+ if (ioctl(slave,TIOCSCTTY,0) <0) {
+ DEBUG(3,("Error in ioctl call for slave pty\n"));
+ /* return(False); */
+ }
+#endif
+
+ /* Close master. */
+ close(master);
+
+ /* Make slave stdin/out/err of child. */
+
+ if (dup2(slave, STDIN_FILENO) != STDIN_FILENO) {
+ DEBUG(3,("Could not re-direct stdin\n"));
+ return(False);
+ }
+ if (dup2(slave, STDOUT_FILENO) != STDOUT_FILENO) {
+ DEBUG(3,("Could not re-direct stdout\n"));
+ return(False);
+ }
+ if (dup2(slave, STDERR_FILENO) != STDERR_FILENO) {
+ DEBUG(3,("Could not re-direct stderr\n"));
+ return(False);
+ }
+ if (slave > 2) close(slave);
+
+ /* Set proper terminal attributes - no echo, canonical input processing,
+ no map NL to CR/NL on output. */
+
+ if (tcgetattr(0, &stermios) < 0) {
+ DEBUG(3,("could not read default terminal attributes on pty\n"));
+ return(False);
+ }
+ stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
+ stermios.c_lflag |= ICANON;
+ stermios.c_oflag &= ~(ONLCR);
+ if (tcsetattr(0, TCSANOW, &stermios) < 0) {
+ DEBUG(3,("could not set attributes of pty\n"));
+ return(False);
+ }
+
+ /* make us completely into the right uid */
+#ifdef USE_SETRES
+ setresgid(0,0,0);
+ setresuid(0,0,0);
+ setresgid(gid,gid,gid);
+ setresuid(uid,uid,uid);
+#else
+ setuid(0);
+ seteuid(0);
+ setgid(gid);
+ setegid(gid);
+ setuid(uid);
+ seteuid(uid);
+#endif
+
+ /* execl() password-change application */
+ if (execl("/bin/sh","sh","-c",passwordprogram,NULL) < 0) {
+ DEBUG(3,("Bad status returned from %s\n",passwordprogram));
+ return(False);
+ }
+ return(True);
+}
+
+static int expect(int master,char *expected,char *buf)
+{
+ int n, m;
+
+ n = 0;
+ buf[0] = 0;
+ while (1) {
+ if (n >= BUFSIZE-1) {
+ return False;
+ }
+
+ /* allow 4 seconds for some output to appear */
+ m = read_with_timeout(master, buf+n, 1, BUFSIZE-1-n, 4000, True);
+ if (m < 0)
+ return False;
+
+ n += m;
+ buf[n] = 0;
+
+ {
+ pstring s1,s2;
+ strcpy(s1,buf);
+ strcpy(s2,expected);
+ if (do_match(s1, s2, False))
+ return(True);
+ }
+ }
+}
+
+static void pwd_sub(char *buf)
+{
+ string_sub(buf,"\\n","\n");
+ string_sub(buf,"\\r","\r");
+ string_sub(buf,"\\s"," ");
+ string_sub(buf,"\\t","\t");
+}
+
+static void writestring(int fd,char *s)
+{
+ int l;
+
+ l = strlen (s);
+ write (fd, s, l);
+}
+
+
+static int talktochild(int master, char *chatsequence)
+{
+ char buf[BUFSIZE];
+ int count=0;
+ char *ptr=chatsequence;
+ fstring chatbuf;
+
+ *buf = 0;
+ sleep(1);
+
+ while (next_token(&ptr,chatbuf,NULL)) {
+ BOOL ok=True;
+ count++;
+ pwd_sub(chatbuf);
+ if (!strequal(chatbuf,"."))
+ ok = expect(master,chatbuf,buf);
+
+#if DEBUG_PASSWORD
+ DEBUG(100,("chatbuf=[%s] responsebuf=[%s]\n",chatbuf,buf));
+#endif
+
+ if (!ok) {
+ DEBUG(3,("response %d incorrect\n",count));
+ return(False);
+ }
+
+ if (!next_token(&ptr,chatbuf,NULL)) break;
+ pwd_sub(chatbuf);
+ if (!strequal(chatbuf,"."))
+ writestring(master,chatbuf);
+
+#if DEBUG_PASSWORD
+ DEBUG(100,("sendbuf=[%s]\n",chatbuf));
+#endif
+ }
+
+ if (count<1) return(False);
+
+ return (True);
+}
+
+
+BOOL chat_with_program(char *passwordprogram,char *name,char *chatsequence)
+{
+ char *slavedev;
+ int master;
+ pid_t pid, wpid;
+ int wstat;
+ BOOL chstat;
+
+ /* allocate a pseudo-terminal device */
+ if ((master = findpty (&slavedev)) < 0) {
+ DEBUG(3,("Cannot Allocate pty for password change: %s",name));
+ return(False);
+ }
+
+ if ((pid = fork()) < 0) {
+ DEBUG(3,("Cannot fork() child for password change: %s",name));
+ return(False);
+ }
+
+ /* we now have a pty */
+ if (pid > 0){ /* This is the parent process */
+ if ((chstat = talktochild(master, chatsequence)) == False) {
+ DEBUG(3,("Child failed to change password: %s\n",name));
+ kill(pid, SIGKILL); /* be sure to end this process */
+ return(False);
+ }
+ if ((wpid = waitpid(pid, &wstat, 0)) < 0) {
+ DEBUG(3,("The process is no longer waiting!\n\n"));
+ return(False);
+ }
+ if (pid != wpid) {
+ DEBUG(3,("We were waiting for the wrong process ID\n"));
+ return(False);
+ }
+ if (WIFEXITED(wstat) == 0) {
+ DEBUG(3,("The process exited while we were waiting\n"));
+ return(False);
+ }
+ if (WEXITSTATUS(wstat) != 0) {
+ DEBUG(3,("The status of the process exiting was %d\n", wstat));
+ return(False);
+ }
+
+ } else {
+ /* CHILD */
+
+ /* make sure it doesn't freeze */
+ alarm(20);
+
+ DEBUG(3,("Dochild for user %s (uid=%d,gid=%d)\n",name,getuid(),getgid()));
+ chstat = dochild(master, slavedev, name, passwordprogram);
+ }
+ DEBUG(3,("Password change %ssuccessful for user %s\n", (chstat?"":"un"), name));
+ return (chstat);
+}
+
+
+BOOL chgpasswd(char *name,char *oldpass,char *newpass)
+{
+ pstring passwordprogram;
+ pstring chatsequence;
+
+ strlower(name);
+ DEBUG(3,("Password change for user: %s\n",name));
+
+#if DEBUG_PASSWORD
+ DEBUG(100,("Passwords: old=%s new=%s\n",oldpass,newpass));
+#endif
+
+ /* Take the passed information and test it for minimum criteria */
+ /* Minimum password length */
+ if (strlen(newpass) < MINPASSWDLENGTH) /* too short, must be at least MINPASSWDLENGTH */
+ {
+ DEBUG(2,("Password Change: %s, New password is shorter than MINPASSWDLENGTH\n",name));
+ return (False); /* inform the user */
+ }
+
+ /* Password is same as old password */
+ if (strcmp(oldpass,newpass) == 0) /* don't allow same password */
+ {
+ DEBUG(2,("Password Change: %s, New password is same as old\n",name)); /* log the attempt */
+ return (False); /* inform the user */
+ }
+
+#if (defined(PASSWD_PROGRAM) && defined(PASSWD_CHAT))
+ strcpy(passwordprogram,PASSWD_PROGRAM);
+ strcpy(chatsequence,PASSWD_CHAT);
+#else
+ strcpy(passwordprogram,lp_passwd_program());
+ strcpy(chatsequence,lp_passwd_chat());
+#endif
+
+ if (!*chatsequence) {
+ DEBUG(2,("Null chat sequence - no password changing\n"));
+ return(False);
+ }
+
+ if (!*passwordprogram) {
+ DEBUG(2,("Null password program - no password changing\n"));
+ return(False);
+ }
+
+ string_sub(passwordprogram,"%u",name);
+ string_sub(passwordprogram,"%o",oldpass);
+ string_sub(passwordprogram,"%n",newpass);
+
+ string_sub(chatsequence,"%u",name);
+ string_sub(chatsequence,"%o",oldpass);
+ string_sub(chatsequence,"%n",newpass);
+ return(chat_with_program(passwordprogram,name,chatsequence));
+}
+
+#else
+BOOL chgpasswd(char *name,char *oldpass,char *newpass)
+{
+ DEBUG(0,("Password changing not compiled in (user=%s)\n",name));
+ return(False);
+}
+#endif
diff --git a/source/smbd/dir.c b/source/smbd/dir.c
new file mode 100644
index 00000000000..ac6f918b9da
--- /dev/null
+++ b/source/smbd/dir.c
@@ -0,0 +1,955 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Directory handling routines
+ Copyright (C) Andrew Tridgell 1992-1995
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "loadparm.h"
+
+extern int DEBUGLEVEL;
+extern connection_struct Connections[];
+
+/*
+ This module implements directory related functions for Samba.
+*/
+
+
+
+uint32 dircounter = 0;
+
+
+#define NUMDIRPTRS 256
+
+
+static struct dptr_struct
+{
+ int pid;
+ int cnum;
+ uint32 lastused;
+ void *ptr;
+ BOOL valid;
+ BOOL finished;
+ BOOL expect_close;
+ char *wcard; /* Field only used for lanman2 trans2_findfirst/next searches */
+ uint16 attr; /* Field only used for lanman2 trans2_findfirst/next searches */
+ char *path;
+}
+dirptrs[NUMDIRPTRS];
+
+
+static int dptrs_open = 0;
+
+/****************************************************************************
+initialise the dir array
+****************************************************************************/
+void init_dptrs(void)
+{
+ static BOOL dptrs_init=False;
+ int i;
+
+ if (dptrs_init) return;
+ for (i=0;i<NUMDIRPTRS;i++)
+ {
+ dirptrs[i].valid = False;
+ dirptrs[i].wcard = NULL;
+ dirptrs[i].ptr = NULL;
+ string_init(&dirptrs[i].path,"");
+ }
+ dptrs_init = True;
+}
+
+/****************************************************************************
+idle a dptr - the directory is closed but the control info is kept
+****************************************************************************/
+static void dptr_idle(int key)
+{
+ if (dirptrs[key].valid && dirptrs[key].ptr) {
+ DEBUG(4,("Idling dptr key %d\n",key));
+ dptrs_open--;
+ CloseDir(dirptrs[key].ptr);
+ dirptrs[key].ptr = NULL;
+ }
+}
+
+/****************************************************************************
+idle the oldest dptr
+****************************************************************************/
+static void dptr_idleoldest(void)
+{
+ int i;
+ uint32 old=dircounter+1;
+ int oldi= -1;
+ for (i=0;i<NUMDIRPTRS;i++)
+ if (dirptrs[i].valid && dirptrs[i].ptr && dirptrs[i].lastused < old) {
+ old = dirptrs[i].lastused;
+ oldi = i;
+ }
+ if (oldi != -1)
+ dptr_idle(oldi);
+ else
+ DEBUG(0,("No dptrs available to idle??\n"));
+}
+
+/****************************************************************************
+get the dir ptr for a dir index
+****************************************************************************/
+static void *dptr_get(int key,uint32 lastused)
+{
+ if (dirptrs[key].valid) {
+ if (lastused) dirptrs[key].lastused = lastused;
+ if (!dirptrs[key].ptr) {
+ if (dptrs_open >= MAXDIR)
+ dptr_idleoldest();
+ DEBUG(4,("Reopening dptr key %d\n",key));
+ if ((dirptrs[key].ptr = OpenDir(dirptrs[key].path)))
+ dptrs_open++;
+ }
+ return(dirptrs[key].ptr);
+ }
+ return(NULL);
+}
+
+/****************************************************************************
+get the dir path for a dir index
+****************************************************************************/
+char *dptr_path(int key)
+{
+ if (dirptrs[key].valid)
+ return(dirptrs[key].path);
+ return(NULL);
+}
+
+/****************************************************************************
+get the dir wcard for a dir index (lanman2 specific)
+****************************************************************************/
+char *dptr_wcard(int key)
+{
+ if (dirptrs[key].valid)
+ return(dirptrs[key].wcard);
+ return(NULL);
+}
+
+/****************************************************************************
+set the dir wcard for a dir index (lanman2 specific)
+Returns 0 on ok, 1 on fail.
+****************************************************************************/
+BOOL dptr_set_wcard(int key, char *wcard)
+{
+ if (dirptrs[key].valid) {
+ dirptrs[key].wcard = wcard;
+ return True;
+ }
+ return False;
+}
+
+/****************************************************************************
+set the dir attrib for a dir index (lanman2 specific)
+Returns 0 on ok, 1 on fail.
+****************************************************************************/
+BOOL dptr_set_attr(int key, uint16 attr)
+{
+ if (dirptrs[key].valid) {
+ dirptrs[key].attr = attr;
+ return True;
+ }
+ return False;
+}
+
+/****************************************************************************
+get the dir attrib for a dir index (lanman2 specific)
+****************************************************************************/
+uint16 dptr_attr(int key)
+{
+ if (dirptrs[key].valid)
+ return(dirptrs[key].attr);
+ return(0);
+}
+
+/****************************************************************************
+close a dptr
+****************************************************************************/
+void dptr_close(int key)
+{
+ if (dirptrs[key].valid) {
+ DEBUG(4,("closing dptr key %d\n",key));
+ if (dirptrs[key].ptr) {
+ CloseDir(dirptrs[key].ptr);
+ dptrs_open--;
+ }
+ /* Lanman 2 specific code */
+ if (dirptrs[key].wcard)
+ free(dirptrs[key].wcard);
+ dirptrs[key].valid = False;
+ string_set(&dirptrs[key].path,"");
+ }
+}
+
+/****************************************************************************
+close all dptrs for a cnum
+****************************************************************************/
+void dptr_closecnum(int cnum)
+{
+ int i;
+ for (i=0;i<NUMDIRPTRS;i++)
+ if (dirptrs[i].valid && dirptrs[i].cnum == cnum)
+ dptr_close(i);
+}
+
+/****************************************************************************
+idle all dptrs for a cnum
+****************************************************************************/
+void dptr_idlecnum(int cnum)
+{
+ int i;
+ for (i=0;i<NUMDIRPTRS;i++)
+ if (dirptrs[i].valid && dirptrs[i].cnum == cnum && dirptrs[i].ptr)
+ dptr_idle(i);
+}
+
+/****************************************************************************
+close a dptr that matches a given path, only if it matches the pid also
+****************************************************************************/
+void dptr_closepath(char *path,int pid)
+{
+ int i;
+ for (i=0;i<NUMDIRPTRS;i++)
+ if (dirptrs[i].valid && pid == dirptrs[i].pid &&
+ strequal(dirptrs[i].path,path))
+ dptr_close(i);
+}
+
+/****************************************************************************
+ start a directory listing
+****************************************************************************/
+static BOOL start_dir(int cnum,char *directory)
+{
+ DEBUG(5,("start_dir cnum=%d dir=%s\n",cnum,directory));
+
+ if (!check_name(directory,cnum))
+ return(False);
+
+ if (! *directory)
+ directory = ".";
+
+ Connections[cnum].dirptr = OpenDir(directory);
+ if (Connections[cnum].dirptr) {
+ dptrs_open++;
+ string_set(&Connections[cnum].dirpath,directory);
+ return(True);
+ }
+
+ return(False);
+}
+
+
+/****************************************************************************
+create a new dir ptr
+****************************************************************************/
+int dptr_create(int cnum,char *path, BOOL expect_close,int pid)
+{
+ int i;
+ uint32 old;
+ int oldi;
+
+ if (!start_dir(cnum,path))
+ return(-1);
+
+ if (dptrs_open >= MAXDIR)
+ dptr_idleoldest();
+
+ for (i=0;i<NUMDIRPTRS;i++)
+ if (!dirptrs[i].valid)
+ break;
+ if (i == NUMDIRPTRS) i = -1;
+
+
+ /* as a 2nd option, grab the oldest not marked for expect_close */
+ if (i == -1) {
+ old=dircounter+1;
+ oldi= -1;
+ for (i=0;i<NUMDIRPTRS;i++)
+ if (!dirptrs[i].expect_close && dirptrs[i].lastused < old) {
+ old = dirptrs[i].lastused;
+ oldi = i;
+ }
+ i = oldi;
+ }
+
+ /* a 3rd option - grab the oldest one */
+ if (i == -1) {
+ old=dircounter+1;
+ oldi= -1;
+ for (i=0;i<NUMDIRPTRS;i++)
+ if (dirptrs[i].lastused < old) {
+ old = dirptrs[i].lastused;
+ oldi = i;
+ }
+ i = oldi;
+ }
+
+ if (i == -1) {
+ DEBUG(0,("Error - all dirptrs in use??\n"));
+ return(-1);
+ }
+
+ if (dirptrs[i].valid)
+ dptr_close(i);
+
+ dirptrs[i].ptr = Connections[cnum].dirptr;
+ string_set(&dirptrs[i].path,path);
+ dirptrs[i].lastused = dircounter++;
+ dirptrs[i].finished = False;
+ dirptrs[i].cnum = cnum;
+ dirptrs[i].pid = pid;
+ dirptrs[i].expect_close = expect_close;
+ dirptrs[i].wcard = NULL; /* Only used in lanman2 searches */
+ dirptrs[i].attr = 0; /* Only used in lanman2 searches */
+ dirptrs[i].valid = True;
+
+ DEBUG(3,("creating new dirptr %d for path %s, expect_close = %d\n",
+ i,path,expect_close));
+
+ return(i);
+}
+
+#define DPTR_MASK ((uint32)(((uint32)1)<<31))
+
+/****************************************************************************
+fill the 5 byte server reserved dptr field
+****************************************************************************/
+BOOL dptr_fill(char *buf1,unsigned int key)
+{
+ unsigned char *buf = (unsigned char *)buf1;
+ void *p = dptr_get(key,0);
+ uint32 offset;
+ if (!p) {
+ DEBUG(1,("filling null dirptr %d\n",key));
+ return(False);
+ }
+ offset = TellDir(p);
+ DEBUG(6,("fill on key %d dirptr 0x%x now at %d\n",key,p,offset));
+ buf[0] = key;
+ SIVAL(buf,1,offset | DPTR_MASK);
+ return(True);
+}
+
+
+/****************************************************************************
+return True is the offset is at zero
+****************************************************************************/
+BOOL dptr_zero(char *buf)
+{
+ return((IVAL(buf,1)&~DPTR_MASK) == 0);
+}
+
+/****************************************************************************
+fetch the dir ptr and seek it given the 5 byte server field
+****************************************************************************/
+void *dptr_fetch(char *buf,int *num)
+{
+ unsigned int key = *(unsigned char *)buf;
+ void *p = dptr_get(key,dircounter++);
+ uint32 offset;
+ if (!p) {
+ DEBUG(3,("fetched null dirptr %d\n",key));
+ return(NULL);
+ }
+ *num = key;
+ offset = IVAL(buf,1)&~DPTR_MASK;
+ SeekDir(p,offset);
+ DEBUG(3,("fetching dirptr %d for path %s at offset %d\n",
+ key,dptr_path(key),offset));
+ return(p);
+}
+
+/****************************************************************************
+fetch the dir ptr and seek it given the lanman2 parameter block
+****************************************************************************/
+void *dptr_fetch_lanman2(char *params,int dptr_num)
+{
+ void *p = dptr_get(dptr_num,dircounter++);
+ uint32 resume_key = SVAL(params,6);
+ BOOL uses_resume_key = BITSETW(params+10,2);
+ BOOL continue_bit = BITSETW(params+10,3);
+
+ if (!p) {
+ DEBUG(3,("fetched null dirptr %d\n",dptr_num));
+ return(NULL);
+ }
+ if(uses_resume_key && !continue_bit)
+ SeekDir(p,resume_key);
+ DEBUG(3,("fetching dirptr %d for path %s\n",dptr_num,dptr_path(dptr_num)));
+ return(p);
+}
+
+/****************************************************************************
+ get a directory entry
+****************************************************************************/
+BOOL get_dir_entry(int cnum,char *mask,int dirtype,char *fname,int *size,int *mode,time_t *date,BOOL check_descend)
+{
+ char *dname;
+ BOOL found = False;
+ struct stat sbuf;
+ pstring path;
+ pstring pathreal;
+ BOOL isrootdir;
+ pstring filename;
+ BOOL matched;
+
+ *path = *pathreal = *filename = 0;
+
+ isrootdir = (strequal(Connections[cnum].dirpath,"./") ||
+ strequal(Connections[cnum].dirpath,".") ||
+ strequal(Connections[cnum].dirpath,"/"));
+
+ if (!Connections[cnum].dirptr)
+ return(False);
+
+ while (!found)
+ {
+ dname = ReadDirName(Connections[cnum].dirptr);
+
+ DEBUG(6,("readdir on dirptr 0x%x now at offset %d\n",
+ Connections[cnum].dirptr,TellDir(Connections[cnum].dirptr)));
+
+ if (dname == NULL)
+ return(False);
+
+ matched = False;
+
+ strcpy(filename,dname);
+
+ if ((strcmp(filename,mask) == 0) ||
+ (name_map_mangle(filename,True,SNUM(cnum)) &&
+ mask_match(filename,mask,False,False)))
+ {
+ if (isrootdir && (strequal(filename,"..") || strequal(filename,".")))
+ continue;
+
+ strcpy(fname,filename);
+ *path = 0;
+ strcpy(path,Connections[cnum].dirpath);
+ strcat(path,"/");
+ strcpy(pathreal,path);
+ strcat(path,fname);
+ strcat(pathreal,dname);
+ if (sys_stat(pathreal,&sbuf) != 0)
+ {
+ DEBUG(5,("Couldn't stat 1 [%s]\n",path));
+ continue;
+ }
+
+ if (check_descend &&
+ !strequal(fname,".") && !strequal(fname,".."))
+ continue;
+
+ *mode = dos_mode(cnum,pathreal,&sbuf);
+
+ if (((*mode & ~dirtype) & (aHIDDEN | aSYSTEM | aDIR)) != 0)
+ {
+ DEBUG(5,("[%s] attribs didn't match %x\n",filename,dirtype));
+ continue;
+ }
+ *size = sbuf.st_size;
+ *date = sbuf.st_mtime;
+
+ DEBUG(5,("get_dir_entry found %s fname=%s\n",pathreal,fname));
+
+ found = True;
+ }
+ }
+
+ return(found);
+}
+
+
+
+typedef struct
+{
+ int pos;
+ int numentries;
+ int mallocsize;
+ char *data;
+ char *current;
+} Dir;
+
+
+/*******************************************************************
+open a directory
+********************************************************************/
+void *OpenDir(char *name)
+{
+ Dir *dirp;
+ char *n;
+ void *p = sys_opendir(name);
+ int used=0;
+
+ if (!p) return(NULL);
+ dirp = (Dir *)malloc(sizeof(Dir));
+ if (!dirp) {
+ closedir(p);
+ return(NULL);
+ }
+ dirp->pos = dirp->numentries = dirp->mallocsize = 0;
+ dirp->data = dirp->current = NULL;
+
+ while ((n = readdirname(p))) {
+ int l = strlen(n)+1;
+ if (used + l > dirp->mallocsize) {
+ int s = MAX(used+l,used+2000);
+ char *r;
+ r = (char *)Realloc(dirp->data,s);
+ if (!r) {
+ DEBUG(0,("Out of memory in OpenDir\n"));
+ break;
+ }
+ dirp->data = r;
+ dirp->mallocsize = s;
+ dirp->current = dirp->data;
+ }
+ strcpy(dirp->data+used,n);
+ used += l;
+ dirp->numentries++;
+ }
+
+ closedir(p);
+ return((void *)dirp);
+}
+
+
+/*******************************************************************
+close a directory
+********************************************************************/
+void CloseDir(void *p)
+{
+ Dir *dirp = (Dir *)p;
+ if (!dirp) return;
+ if (dirp->data) free(dirp->data);
+ free(dirp);
+}
+
+/*******************************************************************
+read from a directory
+********************************************************************/
+char *ReadDirName(void *p)
+{
+ char *ret;
+ Dir *dirp = (Dir *)p;
+
+ if (!dirp || !dirp->current || dirp->pos >= dirp->numentries) return(NULL);
+
+ ret = dirp->current;
+ dirp->current = skip_string(dirp->current,1);
+ dirp->pos++;
+
+ return(ret);
+}
+
+
+/*******************************************************************
+seek a dir
+********************************************************************/
+BOOL SeekDir(void *p,int pos)
+{
+ Dir *dirp = (Dir *)p;
+
+ if (!dirp) return(False);
+
+ if (pos < dirp->pos) {
+ dirp->current = dirp->data;
+ dirp->pos = 0;
+ }
+
+ while (dirp->pos < pos && ReadDirName(p)) ;
+
+ return(dirp->pos == pos);
+}
+
+/*******************************************************************
+tell a dir position
+********************************************************************/
+int TellDir(void *p)
+{
+ Dir *dirp = (Dir *)p;
+
+ if (!dirp) return(-1);
+
+ return(dirp->pos);
+}
+
+
+static int dir_cache_size = 0;
+static struct dir_cache {
+ struct dir_cache *next;
+ struct dir_cache *prev;
+ char *path;
+ char *name;
+ char *dname;
+ int snum;
+} *dir_cache = NULL;
+
+/*******************************************************************
+add an entry to the directory cache
+********************************************************************/
+void DirCacheAdd(char *path,char *name,char *dname,int snum)
+{
+ struct dir_cache *entry = (struct dir_cache *)malloc(sizeof(*entry));
+ if (!entry) return;
+ entry->path = strdup(path);
+ entry->name = strdup(name);
+ entry->dname = strdup(dname);
+ entry->snum = snum;
+ if (!entry->path || !entry->name || !entry->dname) return;
+
+ entry->next = dir_cache;
+ entry->prev = NULL;
+ if (entry->next) entry->next->prev = entry;
+ dir_cache = entry;
+
+ DEBUG(4,("Added dir cache entry %s %s -> %s\n",path,name,dname));
+
+ if (dir_cache_size == DIRCACHESIZE) {
+ for (entry=dir_cache; entry->next; entry=entry->next) ;
+ free(entry->path);
+ free(entry->name);
+ free(entry->dname);
+ if (entry->prev) entry->prev->next = entry->next;
+ free(entry);
+ } else {
+ dir_cache_size++;
+ }
+}
+
+
+/*******************************************************************
+check for an entry in the directory cache
+********************************************************************/
+char *DirCacheCheck(char *path,char *name,int snum)
+{
+ struct dir_cache *entry;
+
+ for (entry=dir_cache; entry; entry=entry->next) {
+ if (entry->snum == snum &&
+ strcmp(path,entry->path) == 0 &&
+ strcmp(name,entry->name) == 0) {
+ DEBUG(4,("Got dir cache hit on %s %s -> %s\n",path,name,entry->dname));
+ return(entry->dname);
+ }
+ }
+
+ return(NULL);
+}
+
+/*******************************************************************
+flush entries in the dir_cache
+********************************************************************/
+void DirCacheFlush(int snum)
+{
+ struct dir_cache *entry,*next;
+
+ for (entry=dir_cache; entry; entry=next) {
+ if (entry->snum == snum) {
+ free(entry->path);
+ free(entry->dname);
+ free(entry->name);
+ next = entry->next;
+ if (entry->prev) entry->prev->next = entry->next;
+ if (entry->next) entry->next->prev = entry->prev;
+ if (dir_cache == entry) dir_cache = entry->next;
+ free(entry);
+ } else {
+ next = entry->next;
+ }
+ }
+}
+
+
+#ifdef REPLACE_GETWD
+/* This is getcwd.c from bash. It is needed in Interactive UNIX. To
+ * add support for another OS you need to determine which of the
+ * conditional compilation macros you need to define. All the options
+ * are defined for Interactive UNIX.
+ */
+#ifdef ISC
+#define HAVE_UNISTD_H
+#define USGr3
+#define USG
+#endif
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#if defined (__STDC__)
+# define CONST const
+# define PTR void *
+#else /* !__STDC__ */
+# define CONST
+# define PTR char *
+#endif /* !__STDC__ */
+
+#if !defined (PATH_MAX)
+# if defined (MAXPATHLEN)
+# define PATH_MAX MAXPATHLEN
+# else /* !MAXPATHLEN */
+# define PATH_MAX 1024
+# endif /* !MAXPATHLEN */
+#endif /* !PATH_MAX */
+
+#if defined (_POSIX_VERSION) || defined (USGr3) || defined (HAVE_DIRENT_H)
+# if !defined (HAVE_DIRENT)
+# define HAVE_DIRENT
+# endif /* !HAVE_DIRENT */
+#endif /* _POSIX_VERSION || USGr3 || HAVE_DIRENT_H */
+
+#if defined (HAVE_DIRENT)
+# define D_NAMLEN(d) (strlen ((d)->d_name))
+#else
+# define D_NAMLEN(d) ((d)->d_namlen)
+#endif /* ! (_POSIX_VERSION || USGr3) */
+
+#if defined (USG) || defined (USGr3)
+# define d_fileno d_ino
+#endif
+
+#if !defined (alloca)
+extern char *alloca ();
+#endif /* alloca */
+
+/* Get the pathname of the current working directory,
+ and put it in SIZE bytes of BUF. Returns NULL if the
+ directory couldn't be determined or SIZE was too small.
+ If successful, returns BUF. In GNU, if BUF is NULL,
+ an array is allocated with `malloc'; the array is SIZE
+ bytes long, unless SIZE <= 0, in which case it is as
+ big as necessary. */
+#if defined (__STDC__)
+char *
+getcwd (char *buf, size_t size)
+#else /* !__STDC__ */
+char *
+getcwd (buf, size)
+ char *buf;
+ int size;
+#endif /* !__STDC__ */
+{
+ static CONST char dots[]
+ = "../../../../../../../../../../../../../../../../../../../../../../../\
+../../../../../../../../../../../../../../../../../../../../../../../../../../\
+../../../../../../../../../../../../../../../../../../../../../../../../../..";
+ CONST char *dotp, *dotlist;
+ size_t dotsize;
+ dev_t rootdev, thisdev;
+ ino_t rootino, thisino;
+ char path[PATH_MAX + 1];
+ register char *pathp;
+ char *pathbuf;
+ size_t pathsize;
+ struct stat st;
+
+ if (buf != NULL && size == 0)
+ {
+ errno = EINVAL;
+ return ((char *)NULL);
+ }
+
+ pathsize = sizeof (path);
+ pathp = &path[pathsize];
+ *--pathp = '\0';
+ pathbuf = path;
+
+ if (stat (".", &st) < 0)
+ return ((char *)NULL);
+ thisdev = st.st_dev;
+ thisino = st.st_ino;
+
+ if (stat ("/", &st) < 0)
+ return ((char *)NULL);
+ rootdev = st.st_dev;
+ rootino = st.st_ino;
+
+ dotsize = sizeof (dots) - 1;
+ dotp = &dots[sizeof (dots)];
+ dotlist = dots;
+ while (!(thisdev == rootdev && thisino == rootino))
+ {
+ register DIR *dirstream;
+ register struct dirent *d;
+ dev_t dotdev;
+ ino_t dotino;
+ char mount_point;
+ int namlen;
+
+ /* Look at the parent directory. */
+ if (dotp == dotlist)
+ {
+ /* My, what a deep directory tree you have, Grandma. */
+ char *new;
+ if (dotlist == dots)
+ {
+ new = malloc (dotsize * 2 + 1);
+ if (new == NULL)
+ goto lose;
+ memcpy (new, dots, dotsize);
+ }
+ else
+ {
+ new = realloc ((PTR) dotlist, dotsize * 2 + 1);
+ if (new == NULL)
+ goto lose;
+ }
+ memcpy (&new[dotsize], new, dotsize);
+ dotp = &new[dotsize];
+ dotsize *= 2;
+ new[dotsize] = '\0';
+ dotlist = new;
+ }
+
+ dotp -= 3;
+
+ /* Figure out if this directory is a mount point. */
+ if (stat (dotp, &st) < 0)
+ goto lose;
+ dotdev = st.st_dev;
+ dotino = st.st_ino;
+ mount_point = dotdev != thisdev;
+
+ /* Search for the last directory. */
+ dirstream = opendir(dotp);
+ if (dirstream == NULL)
+ goto lose;
+ while ((d = (struct dirent *)readdir(dirstream)) != NULL)
+ {
+ if (d->d_name[0] == '.' &&
+ (d->d_name[1] == '\0' ||
+ (d->d_name[1] == '.' && d->d_name[2] == '\0')))
+ continue;
+ if (mount_point || d->d_fileno == thisino)
+ {
+ char *name;
+
+ namlen = D_NAMLEN(d);
+ name = (char *)
+ alloca (dotlist + dotsize - dotp + 1 + namlen + 1);
+ memcpy (name, dotp, dotlist + dotsize - dotp);
+ name[dotlist + dotsize - dotp] = '/';
+ memcpy (&name[dotlist + dotsize - dotp + 1],
+ d->d_name, namlen + 1);
+ if (lstat (name, &st) < 0)
+ {
+ int save = errno;
+ closedir(dirstream);
+ errno = save;
+ goto lose;
+ }
+ if (st.st_dev == thisdev && st.st_ino == thisino)
+ break;
+ }
+ }
+ if (d == NULL)
+ {
+ int save = errno;
+ closedir(dirstream);
+ errno = save;
+ goto lose;
+ }
+ else
+ {
+ size_t space;
+
+ while ((space = pathp - pathbuf) <= namlen)
+ {
+ char *new;
+
+ if (pathbuf == path)
+ {
+ new = malloc (pathsize * 2);
+ if (!new)
+ goto lose;
+ }
+ else
+ {
+ new = realloc ((PTR) pathbuf, (pathsize * 2));
+ if (!new)
+ goto lose;
+ pathp = new + space;
+ }
+ (void) memcpy (new + pathsize + space, pathp, pathsize - space);
+ pathp = new + pathsize + space;
+ pathbuf = new;
+ pathsize *= 2;
+ }
+
+ pathp -= namlen;
+ (void) memcpy (pathp, d->d_name, namlen);
+ *--pathp = '/';
+ closedir(dirstream);
+ }
+
+ thisdev = dotdev;
+ thisino = dotino;
+ }
+
+ if (pathp == &path[sizeof(path) - 1])
+ *--pathp = '/';
+
+ if (dotlist != dots)
+ free ((PTR) dotlist);
+
+ {
+ size_t len = pathbuf + pathsize - pathp;
+ if (buf == NULL)
+ {
+ if (len < (size_t) size)
+ len = size;
+ buf = (char *) malloc (len);
+ if (buf == NULL)
+ goto lose2;
+ }
+ else if ((size_t) size < len)
+ {
+ errno = ERANGE;
+ goto lose2;
+ }
+ (void) memcpy((PTR) buf, (PTR) pathp, len);
+ }
+
+ if (pathbuf != path)
+ free (pathbuf);
+
+ return (buf);
+
+ lose:
+ if ((dotlist != dots) && dotlist)
+ {
+ int e = errno;
+ free ((PTR) dotlist);
+ errno = e;
+ }
+
+ lose2:
+ if ((pathbuf != path) && pathbuf)
+ {
+ int e = errno;
+ free ((PTR) pathbuf);
+ errno = e;
+ }
+ return ((char *)NULL);
+}
+#endif
diff --git a/source/smbd/ipc.c b/source/smbd/ipc.c
new file mode 100644
index 00000000000..8852e57e8b6
--- /dev/null
+++ b/source/smbd/ipc.c
@@ -0,0 +1,2779 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Inter-process communication and named pipe handling
+ Copyright (C) Andrew Tridgell 1992-1995
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+/*
+ This file handles the named pipe and mailslot calls
+ in the SMBtrans protocol
+ */
+
+#include "includes.h"
+#include "loadparm.h"
+#include "pcap.h"
+
+#ifdef CHECK_TYPES
+#undef CHECK_TYPES
+#endif
+#define CHECK_TYPES 0
+
+extern int DEBUGLEVEL;
+extern int maxxmit;
+extern files_struct Files[];
+extern connection_struct Connections[];
+
+extern fstring local_machine;
+
+#define NERR_Success 0
+#define NERR_badpass 86
+#define NERR_notsupported 50
+
+#define NERR_BASE (2100)
+#define NERR_BufTooSmall (NERR_BASE+23)
+#define NERR_JobNotFound (NERR_BASE+51)
+#define NERR_DestNotFound (NERR_BASE+52)
+#define ERROR_INVALID_LEVEL 124
+#define ERROR_MORE_DATA 234
+
+#define REALLOC(ptr,size) Realloc(ptr,MAX((size),4*1024))
+
+#define ACCESS_READ 0x01
+#define ACCESS_WRITE 0x02
+#define ACCESS_CREATE 0x04
+
+#define SHPWLEN 8 /* share password length */
+#define NNLEN 12 /* 8.3 net name length */
+#define SNLEN 15 /* service name length */
+#define QNLEN 12 /* queue name maximum length */
+
+extern int Client;
+
+static int CopyExpanded(int cnum, int snum, char** dst, char* src, int* n)
+{
+ pstring buf;
+ int l;
+
+ if (!src || !dst || !n || !(*dst)) return(0);
+
+ StrnCpy(buf,src,sizeof(buf)/2);
+ string_sub(buf,"%S",lp_servicename(snum));
+ standard_sub(cnum,buf);
+ StrnCpy(*dst,buf,*n);
+ l = strlen(*dst) + 1;
+ (*dst) += l;
+ (*n) -= l;
+ return l;
+}
+
+static int CopyAndAdvance(char** dst, char* src, int* n)
+{
+ int l;
+ if (!src || !dst || !n || !(*dst)) return(0);
+ StrnCpy(*dst,src,*n);
+ l = strlen(*dst) + 1;
+ (*dst) += l;
+ (*n) -= l;
+ return l;
+}
+
+static int StrlenExpanded(int cnum, int snum, char* s)
+{
+ pstring buf;
+ if (!s) return(0);
+ StrnCpy(buf,s,sizeof(buf)/2);
+ string_sub(buf,"%S",lp_servicename(snum));
+ standard_sub(cnum,buf);
+ return strlen(buf) + 1;
+}
+
+static char* Expand(int cnum, int snum, char* s)
+{
+ static pstring buf;
+ if (!s) return(NULL);
+ StrnCpy(buf,s,sizeof(buf)/2);
+ string_sub(buf,"%S",lp_servicename(snum));
+ standard_sub(cnum,buf);
+ return &buf[0];
+}
+
+/*******************************************************************
+ check a API string for validity when we only need to check the prefix
+ ******************************************************************/
+static BOOL prefix_ok(char *str,char *prefix)
+{
+ return(strncmp(str,prefix,strlen(prefix)) == 0);
+}
+
+
+/****************************************************************************
+ send a trans reply
+ ****************************************************************************/
+static void send_trans_reply(char *outbuf,char *data,char *param,uint16 *setup,
+ int ldata,int lparam,int lsetup)
+{
+ int i;
+ int this_ldata,this_lparam;
+ int tot_data=0,tot_param=0;
+ int align;
+
+ this_lparam = MIN(lparam,maxxmit - (500+lsetup*SIZEOFWORD)); /* hack */
+ this_ldata = MIN(ldata,maxxmit - (500+lsetup*SIZEOFWORD+this_lparam));
+
+ align = (this_lparam%4);
+
+ set_message(outbuf,10+lsetup,align+this_ldata+this_lparam,True);
+ if (this_lparam)
+ memcpy(smb_buf(outbuf),param,this_lparam);
+ if (this_ldata)
+ memcpy(smb_buf(outbuf)+this_lparam+align,data,this_ldata);
+
+ SSVAL(outbuf,smb_vwv0,lparam);
+ SSVAL(outbuf,smb_vwv1,ldata);
+ SSVAL(outbuf,smb_vwv3,this_lparam);
+ SSVAL(outbuf,smb_vwv4,smb_offset(smb_buf(outbuf),outbuf));
+ SSVAL(outbuf,smb_vwv5,0);
+ SSVAL(outbuf,smb_vwv6,this_ldata);
+ SSVAL(outbuf,smb_vwv7,smb_offset(smb_buf(outbuf)+this_lparam+align,outbuf));
+ SSVAL(outbuf,smb_vwv8,0);
+ SSVAL(outbuf,smb_vwv9,lsetup);
+ for (i=0;i<lsetup;i++)
+ SSVAL(outbuf,smb_vwv10+i*SIZEOFWORD,setup[i]);
+
+ show_msg(outbuf);
+ send_smb(Client,outbuf);
+
+ tot_data = this_ldata;
+ tot_param = this_lparam;
+
+ while (tot_data < ldata || tot_param < lparam)
+ {
+ this_lparam = MIN(lparam-tot_param,maxxmit - 500); /* hack */
+ this_ldata = MIN(ldata-tot_data,maxxmit - (500+this_lparam));
+
+ align = (this_lparam%4);
+
+ set_message(outbuf,10,this_ldata+this_lparam+align,False);
+ if (this_lparam)
+ memcpy(smb_buf(outbuf),param+tot_param,this_lparam);
+ if (this_ldata)
+ memcpy(smb_buf(outbuf)+this_lparam+align,data+tot_data,this_ldata);
+
+ SSVAL(outbuf,smb_vwv3,this_lparam);
+ SSVAL(outbuf,smb_vwv4,smb_offset(smb_buf(outbuf),outbuf));
+ SSVAL(outbuf,smb_vwv5,tot_param);
+ SSVAL(outbuf,smb_vwv6,this_ldata);
+ SSVAL(outbuf,smb_vwv7,smb_offset(smb_buf(outbuf)+this_lparam+align,outbuf));
+ SSVAL(outbuf,smb_vwv8,tot_data);
+ SSVAL(outbuf,smb_vwv9,0);
+
+ show_msg(outbuf);
+ send_smb(Client,outbuf);
+
+ tot_data += this_ldata;
+ tot_param += this_lparam;
+ }
+}
+
+
+
+/****************************************************************************
+ get a print queue
+ ****************************************************************************/
+
+struct pack_desc {
+ char* format; /* formatstring for structure */
+ char* subformat; /* subformat for structure */
+ char* base; /* baseaddress of buffer */
+ int buflen; /* remaining size for fixed part; on init: length of base */
+ int subcount; /* count of substructures */
+ char* structbuf; /* pointer into buffer for remaining fixed part */
+ int stringlen; /* remaining size for variable part */
+ char* stringbuf; /* pointer into buffer for remaining variable part */
+ int neededlen; /* total needed size */
+ int usedlen; /* total used size (usedlen <= neededlen and usedlen <= buflen) */
+ char* curpos; /* current position; pointer into format or subformat */
+ int errcode;
+};
+
+static int get_counter(char** p)
+{
+ int i, n;
+ if (!p || !(*p)) return(1);
+ if (!isdigit(**p)) return 1;
+ for (n = 0;;) {
+ i = **p;
+ if (isdigit(i))
+ n = 10 * n + (i - '0');
+ else
+ return n;
+ (*p)++;
+ }
+}
+
+static int getlen(char* p)
+{
+ int n = 0;
+ if (!p) return(0);
+ while (*p) {
+ switch( *p++ ) {
+ case 'W': /* word (2 byte) */
+ n += 2;
+ break;
+ case 'N': /* count of substructures (word) at end */
+ n += 2;
+ break;
+ case 'D': /* double word (4 byte) */
+ case 'z': /* offset to zero terminated string (4 byte) */
+ case 'l': /* offset to user data (4 byte) */
+ n += 4;
+ break;
+ case 'b': /* offset to data (with counter) (4 byte) */
+ n += 4;
+ get_counter(&p);
+ break;
+ case 'B': /* byte (with optional counter) */
+ n += get_counter(&p);
+ break;
+ }
+ }
+ return n;
+}
+
+static BOOL init_package(struct pack_desc* p, int count, int subcount)
+{
+ int n = p->buflen;
+ int i;
+
+ if (!p->format || !p->base) return(False);
+
+ i = count * getlen(p->format);
+ if (p->subformat) i += subcount * getlen(p->subformat);
+ p->structbuf = p->base;
+ p->neededlen = 0;
+ p->usedlen = 0;
+ p->subcount = 0;
+ p->curpos = p->format;
+ if (i > n) {
+ i = n = 0;
+ p->errcode = NERR_BufTooSmall;
+ }
+
+ p->errcode = NERR_Success;
+ p->buflen = i;
+ n -= i;
+ p->stringbuf = p->base + i;
+ p->stringlen = n;
+ return(p->errcode == NERR_Success);
+}
+
+#ifdef __STDC__
+static int package(struct pack_desc* p, ...)
+{
+#else
+static int package(va_alist)
+va_dcl
+{
+ struct pack_desc* p;
+#endif
+ va_list args;
+ int needed=0, stringneeded;
+ char* str=NULL;
+ int is_string=0, stringused;
+ int32 temp;
+
+#ifdef __STDC__
+ va_start(args,p);
+#else
+ va_start(args);
+ p = va_arg(args,struct pack_desc *);
+#endif
+
+ if (!*p->curpos) {
+ if (!p->subcount)
+ p->curpos = p->format;
+ else {
+ p->curpos = p->subformat;
+ p->subcount--;
+ }
+ }
+#if CHECK_TYPES
+ str = va_arg(args,char*);
+ if (strncmp(str,p->curpos,strlen(str)) != 0) {
+ DEBUG(2,("type error in package: %s instead of %*s\n",str,
+ strlen(str),p->curpos));
+ va_end(args);
+#if AJT
+ ajt_panic();
+#endif
+ return 0;
+ }
+#endif
+ stringneeded = -1;
+
+ if (!p->curpos) return(0);
+
+ switch( *p->curpos++ ) {
+ case 'W': /* word (2 byte) */
+ needed = 2;
+ temp = va_arg(args,int);
+ if (p->buflen >= needed) SSVAL(p->structbuf,0,temp);
+ break;
+ case 'N': /* count of substructures (word) at end */
+ needed = 2;
+ p->subcount = va_arg(args,int);
+ if (p->buflen >= needed) SSVAL(p->structbuf,0,p->subcount);
+ break;
+ case 'D': /* double word (4 byte) */
+ needed = 4;
+ temp = va_arg(args,int);
+ if (p->buflen >= needed) SIVAL(p->structbuf,0,temp);
+ break;
+ case 'B': /* byte (with optional counter) */
+ needed = get_counter(&p->curpos);
+ {
+ char *s = va_arg(args,char*);
+ if (p->buflen >= needed) StrnCpy(p->structbuf,s?s:"",needed);
+ }
+ break;
+ case 'z': /* offset to zero terminated string (4 byte) */
+ str = va_arg(args,char*);
+ stringneeded = (str ? strlen(str)+1 : 0);
+ is_string = 1;
+ break;
+ case 'l': /* offset to user data (4 byte) */
+ str = va_arg(args,char*);
+ stringneeded = va_arg(args,int);
+ is_string = 0;
+ break;
+ case 'b': /* offset to data (with counter) (4 byte) */
+ str = va_arg(args,char*);
+ stringneeded = get_counter(&p->curpos);
+ is_string = 0;
+ break;
+ }
+ va_end(args);
+ if (stringneeded >= 0) {
+ needed = 4;
+ if (p->buflen >= needed) {
+ stringused = stringneeded;
+ if (stringused > p->stringlen) {
+ stringused = (is_string ? p->stringlen : 0);
+ if (p->errcode == NERR_Success) p->errcode = ERROR_MORE_DATA;
+ }
+ if (!stringused)
+ SIVAL(p->structbuf,0,0);
+ else {
+ SIVAL(p->structbuf,0,PTR_DIFF(p->stringbuf,p->base));
+ memcpy(p->stringbuf,str?str:"",stringused);
+ if (is_string) p->stringbuf[stringused-1] = '\0';
+ p->stringbuf += stringused;
+ p->stringlen -= stringused;
+ p->usedlen += stringused;
+ }
+ }
+ p->neededlen += stringneeded;
+ }
+ p->neededlen += needed;
+ if (p->buflen >= needed) {
+ p->structbuf += needed;
+ p->buflen -= needed;
+ p->usedlen += needed;
+ }
+ else {
+ if (p->errcode == NERR_Success) p->errcode = NERR_BufTooSmall;
+ }
+ return 1;
+}
+
+#if CHECK_TYPES
+#define PACK(desc,t,v) package(desc,t,v,0,0,0,0)
+#define PACKl(desc,t,v,l) package(desc,t,v,l,0,0,0,0)
+#else
+#define PACK(desc,t,v) package(desc,v)
+#define PACKl(desc,t,v,l) package(desc,v,l)
+#endif
+
+static void PACKI(struct pack_desc* desc,char *t,int v)
+{
+ PACK(desc,t,v);
+}
+
+static void PACKS(struct pack_desc* desc,char *t,char *v)
+{
+ PACK(desc,t,v);
+}
+
+static void PackDriverData(struct pack_desc* desc)
+{
+ char drivdata[4+4+32];
+ SIVAL(drivdata,0,sizeof drivdata); /* cb */
+ SIVAL(drivdata,4,1000); /* lVersion */
+ memset(drivdata+8,0,32); /* szDeviceName */
+ strcpy(drivdata+8,"NULL");
+ PACKl(desc,"l",drivdata,sizeof drivdata); /* pDriverData */
+}
+
+static int check_printq_info(struct pack_desc* desc,
+ int uLevel, const char* id1, const char* id2)
+{
+ desc->subformat = NULL;
+ switch( uLevel ) {
+ case 0:
+ desc->format = "B13";
+ break;
+ case 1:
+ desc->format = "B13BWWWzzzzzWW";
+ break;
+ case 2:
+ desc->format = "B13BWWWzzzzzWN";
+ desc->subformat = "WB21BB16B10zWWzDDz";
+ break;
+ case 3:
+ desc->format = "zWWWWzzzzWWzzl";
+ break;
+ case 4:
+ desc->format = "zWWWWzzzzWNzzl";
+ desc->subformat = "WWzWWDDzz";
+ break;
+ case 5:
+ desc->format = "z";
+ break;
+ default: return False;
+ }
+ if (strcmp(desc->format,id1) != 0) return False;
+ if (desc->subformat && strcmp(desc->subformat,id2) != 0) return False;
+ return True;
+}
+
+static void fill_printjob_info(int cnum, int snum, int uLevel,
+ struct pack_desc* desc,
+ print_queue_struct* queue, int n)
+{
+ time_t t = queue->time;
+
+ /* the client expects localtime */
+ t += GMT_TO_LOCAL*TimeDiff(t);
+
+ PACKI(desc,"W",((snum%0xFF)<<8) | (queue->job%0xFF)); /* uJobId */
+ if (uLevel == 1) {
+ PACKS(desc,"B21",queue->user); /* szUserName */
+ PACKS(desc,"B",""); /* pad */
+ PACKS(desc,"B16",""); /* szNotifyName */
+ PACKS(desc,"B10","PM_Q_RAW"); /* szDataType */
+ PACKS(desc,"z",""); /* pszParms */
+ PACKI(desc,"W",n+1); /* uPosition */
+ PACKI(desc,"W",queue->status); /* fsStatus */
+ PACKS(desc,"z",""); /* pszStatus */
+ PACKI(desc,"D",queue->time); /* ulSubmitted */
+ PACKI(desc,"D",queue->size); /* ulSize */
+ PACKS(desc,"z",queue->file); /* pszComment */
+ }
+ if (uLevel == 2 || uLevel == 3) {
+ PACKI(desc,"W",queue->priority); /* uPriority */
+ PACKS(desc,"z",queue->user); /* pszUserName */
+ PACKI(desc,"W",n+1); /* uPosition */
+ PACKI(desc,"W",queue->status); /* fsStatus */
+ PACKI(desc,"D",queue->time); /* ulSubmitted */
+ PACKI(desc,"D",queue->size); /* ulSize */
+ PACKS(desc,"z","Samba"); /* pszComment */
+ PACKS(desc,"z",queue->file); /* pszDocument */
+ if (uLevel == 3) {
+ PACKS(desc,"z",""); /* pszNotifyName */
+ PACKS(desc,"z","PM_Q_RAW"); /* pszDataType */
+ PACKS(desc,"z",""); /* pszParms */
+ PACKS(desc,"z",""); /* pszStatus */
+ PACKS(desc,"z",SERVICE(snum)); /* pszQueue */
+ PACKS(desc,"z","lpd"); /* pszQProcName */
+ PACKS(desc,"z",""); /* pszQProcParms */
+ PACKS(desc,"z","NULL"); /* pszDriverName */
+ PackDriverData(desc); /* pDriverData */
+ PACKS(desc,"z",""); /* pszPrinterName */
+ }
+ }
+}
+
+static void fill_printq_info(int cnum, int snum, int uLevel,
+ struct pack_desc* desc,
+ int count, print_queue_struct* queue,
+ print_status_struct* status)
+{
+ if (uLevel < 3) {
+ PACKS(desc,"B13",SERVICE(snum));
+ } else {
+ PACKS(desc,"z",Expand(cnum,snum,SERVICE(snum)));
+ }
+ if (uLevel == 1 || uLevel == 2) {
+ PACKS(desc,"B",""); /* alignment */
+ PACKI(desc,"W",5); /* priority */
+ PACKI(desc,"W",0); /* start time */
+ PACKI(desc,"W",0); /* until time */
+ PACKS(desc,"z",""); /* pSepFile */
+ PACKS(desc,"z","lpd"); /* pPrProc */
+ PACKS(desc,"z",SERVICE(snum)); /* pDestinations */
+ PACKS(desc,"z",""); /* pParms */
+ if (snum < 0) {
+ PACKS(desc,"z","UNKNOWN PRINTER");
+ PACKI(desc,"W",LPSTAT_ERROR);
+ }
+ else if (!status || !status->message[0]) {
+ PACKS(desc,"z",Expand(cnum,snum,lp_comment(snum)));
+ PACKI(desc,"W",LPSTAT_OK); /* status */
+ } else {
+ PACKS(desc,"z",status->message);
+ PACKI(desc,"W",status->status); /* status */
+ }
+ PACKI(desc,(uLevel == 1 ? "W" : "N"),count);
+ }
+ if (uLevel == 3 || uLevel == 4) {
+ PACKI(desc,"W",5); /* uPriority */
+ PACKI(desc,"W",0); /* uStarttime */
+ PACKI(desc,"W",0); /* uUntiltime */
+ PACKI(desc,"W",5); /* pad1 */
+ PACKS(desc,"z",""); /* pszSepFile */
+ PACKS(desc,"z","lpd"); /* pszPrProc */
+ PACKS(desc,"z",""); /* pszParms */
+ if (!status || !status->message[0]) {
+ PACKS(desc,"z",Expand(cnum,snum,lp_comment(snum))); /* pszComment */
+ PACKI(desc,"W",LPSTAT_OK); /* fsStatus */
+ } else {
+ PACKS(desc,"z",status->message); /* pszComment */
+ PACKI(desc,"W",status->status); /* fsStatus */
+ }
+ PACKI(desc,(uLevel == 3 ? "W" : "N"),count); /* cJobs */
+ PACKS(desc,"z",SERVICE(snum)); /* pszPrinters */
+ PACKS(desc,"z","NULL"); /* pszDriverName */
+ PackDriverData(desc); /* pDriverData */
+ }
+ if (uLevel == 2 || uLevel == 4) {
+ int i;
+ for (i=0;i<count;i++)
+ fill_printjob_info(cnum,snum,uLevel == 2 ? 1 : 2,desc,&queue[i],i);
+ }
+
+ DEBUG(3,("fill_printq_info on <%s> gave %d entries\n",SERVICE(snum),count));
+}
+
+static BOOL api_DosPrintQGetInfo(int cnum,int uid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ char *QueueName = p;
+ int uLevel,cbBuf;
+ int count=0;
+ int snum;
+ char* str3;
+ struct pack_desc desc;
+ print_queue_struct *queue=NULL;
+ print_status_struct status;
+
+ bzero(&status,sizeof(status));
+ bzero(&desc,sizeof(desc));
+
+ p = skip_string(p,1);
+ uLevel = SVAL(p,0);
+ cbBuf = SVAL(p,2);
+ str3 = p + 4;
+
+ if ((p = strchr(QueueName,'%'))) *p = 0;
+
+ DEBUG(3,("PrintQueue uLevel=%d name=%s\n",uLevel,QueueName));
+
+ /* check it's a supported varient */
+ if (!prefix_ok(str1,"zWrLh")) return False;
+ if (!check_printq_info(&desc,uLevel,str2,str3)) return False;
+
+ snum = lp_servicenumber(QueueName);
+ if (snum < 0 && pcap_printername_ok(QueueName,NULL)) {
+ int pnum = lp_servicenumber(PRINTERS_NAME);
+ if (pnum >= 0) {
+ lp_add_printer(QueueName,pnum);
+ snum = lp_servicenumber(QueueName);
+ }
+ }
+
+ if (snum < 0 || !VALID_SNUM(snum)) return(False);
+
+ count = get_printqueue(snum,cnum,&queue,&status);
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ if (init_package(&desc,1,count)) {
+ desc.subcount = count;
+ fill_printq_info(cnum,snum,uLevel,&desc,count,queue,&status);
+ }
+
+ *rdata_len = desc.usedlen;
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,desc.neededlen);
+
+ DEBUG(4,("printqgetinfo: errorcode %d\n",desc.errcode));
+
+ if (queue) free(queue);
+
+ return(True);
+}
+
+
+/****************************************************************************
+ view list of all print jobs on all queues
+ ****************************************************************************/
+static BOOL api_DosPrintQEnum(int cnum, int uid, char* param, char* data,
+ int mdrcnt, int mprcnt,
+ char **rdata, char** rparam,
+ int *rdata_len, int *rparam_len)
+{
+ char *param_format = param+2;
+ char *output_format1 = skip_string(param_format,1);
+ char *p = skip_string(output_format1,1);
+ int uLevel = SVAL(p,0);
+ char *output_format2 = p + 4;
+ int services = lp_numservices();
+ int i, n;
+ struct pack_desc desc;
+ print_queue_struct **queue = NULL;
+ print_status_struct *status = NULL;
+ int* subcntarr = NULL;
+ int queuecnt, subcnt=0, succnt=0;
+
+ bzero(&desc,sizeof(desc));
+
+ DEBUG(3,("DosPrintQEnum uLevel=%d\n",uLevel));
+
+ if (prefix_ok(param_format,"WrLeh")) return False;
+ if (!check_printq_info(&desc,uLevel,output_format1,output_format2))
+ return False;
+ queuecnt = 0;
+ for (i = 0; i < services; i++)
+ if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i))
+ queuecnt++;
+ if (uLevel > 0) {
+ queue = (print_queue_struct**)malloc(queuecnt*sizeof(print_queue_struct*));
+ memset(queue,0,queuecnt*sizeof(print_queue_struct*));
+ status = (print_status_struct*)malloc(queuecnt*sizeof(print_status_struct));
+ memset(status,0,queuecnt*sizeof(print_status_struct));
+ subcntarr = (int*)malloc(queuecnt*sizeof(int));
+ subcnt = 0;
+ n = 0;
+ for (i = 0; i < services; i++)
+ if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i)) {
+ subcntarr[n] = get_printqueue(i,cnum,&queue[n],&status[n]);
+ subcnt += subcntarr[n];
+ n++;
+ }
+ }
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+
+ if (init_package(&desc,queuecnt,subcnt)) {
+ n = 0;
+ succnt = 0;
+ for (i = 0; i < services; i++)
+ if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i)) {
+ fill_printq_info(cnum,i,uLevel,&desc,subcntarr[n],queue[n],&status[n]);
+ n++;
+ if (desc.errcode == NERR_Success) succnt = n;
+ }
+ }
+
+ if (subcntarr) free(subcntarr);
+
+ *rdata_len = desc.usedlen;
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,succnt);
+ SSVAL(*rparam,6,queuecnt);
+
+ for (i = 0; i < queuecnt; i++) {
+ if (queue && queue[i]) free(queue[i]);
+ }
+
+ if (queue) free(queue);
+ if (status) free(status);
+
+ return True;
+}
+
+/****************************************************************************
+ get info level for a server list query
+ ****************************************************************************/
+static BOOL check_server_info(int uLevel, char* id)
+{
+ switch( uLevel ) {
+ case 0:
+ if (strcmp(id,"B16") != 0) return False;
+ break;
+ case 1:
+ if (strcmp(id,"B16BBDz") != 0) return False;
+ break;
+ default:
+ return False;
+ }
+ return True;
+}
+
+/* used for server information: client, nameserv and ipc */
+struct srv_info_struct
+{
+ fstring name;
+ uint32 type;
+ fstring comment;
+ fstring domain; /* used ONLY in ipc.c NOT namework.c */
+ BOOL server_added; /* used ONLY in ipc.c NOT namework.c */
+};
+
+/*******************************************************************
+ filter out unwanted server info
+ ******************************************************************/
+static BOOL filter_server_info(struct srv_info_struct *server,
+ char *domain)
+{
+ if (*domain)
+ return(strequal(domain, server->domain));
+
+ return (True); /* be indiscriminate: get all servers! */
+}
+
+/*******************************************************************
+ find server in the files saved by nmbd. Return True if we find it.
+ ******************************************************************/
+static BOOL find_server(struct srv_info_struct *servers, int num_servers,
+ char *domain, char *name)
+{
+ int count;
+
+ if (!servers || num_servers == 0) return (False);
+
+ for (count = 0; count < num_servers; count++) {
+ struct srv_info_struct *s;
+
+ s = &servers[count];
+
+ if (strequal(name, s->name)) {
+ StrnCpy(domain, s->domain, sizeof(pstring)-1);
+ return (True);
+ }
+ }
+ return (False);
+}
+
+
+/*******************************************************************
+ get server info lists from the files saved by nmbd. Return the
+ number of entries
+ ******************************************************************/
+static int get_server_info(uint32 servertype,
+ struct srv_info_struct **servers)
+{
+ FILE *f;
+ pstring fname;
+ int count=0;
+ int alloced=0;
+ pstring line;
+
+ strcpy(fname,lp_lockdir());
+ trim_string(fname,NULL,"/");
+ strcat(fname,"/");
+ strcat(fname,SERVER_LIST);
+
+ f = fopen(fname,"r");
+
+ if (!f) {
+ DEBUG(4,("Can't open %s - %s\n",fname,strerror(errno)));
+ return(0);
+ }
+ if (servertype == SV_TYPE_ALL) servertype &= ~SV_TYPE_DOMAIN_ENUM;
+
+ while (!feof(f))
+ {
+ fstring stype;
+ struct srv_info_struct *s;
+ char *ptr = line;
+ *ptr = 0;
+
+ fgets(line,sizeof(line)-1,f);
+ if (!*line) continue;
+
+ if (count == alloced) {
+ alloced += 10;
+ (*servers) = (struct srv_info_struct *)
+ Realloc(*servers,sizeof(**servers)*alloced);
+ if (!(*servers)) return(0);
+ bzero((char *)((*servers)+count),sizeof(**servers)*(alloced-count));
+ }
+ s = &(*servers)[count];
+
+ s->server_added = True;
+
+ if (!next_token(&ptr,s->name , NULL)) continue;
+ if (!next_token(&ptr,stype , NULL)) continue;
+ if (!next_token(&ptr,s->comment, NULL)) continue;
+ if (!next_token(&ptr,s->domain , NULL)) {
+ /* this allows us to cop with an old nmbd */
+ strcpy(s->domain,my_workgroup());
+ }
+
+ if (sscanf(stype,"%X",&s->type) != 1) continue;
+
+ /* doesn't match up: don't want it */
+ if (!(servertype & s->type)) continue;
+
+ /* server entry is a domain, we haven't asked for domains: don't want it */
+ if ((s->type&SV_TYPE_DOMAIN_ENUM) && !(servertype&SV_TYPE_DOMAIN_ENUM))
+ continue;
+
+ DEBUG(4,("Server %20s %8x %25s %15s\n",
+ s->name, stype, s->comment, s->domain));
+
+ count++;
+ }
+
+ fclose(f);
+ return(count);
+}
+
+/*******************************************************************
+ fill in a server info structure
+ ******************************************************************/
+static int fill_srv_info(struct srv_info_struct *service,
+ int uLevel, char **buf, int *buflen,
+ char **stringbuf, int *stringspace, char *baseaddr)
+{
+ int struct_len;
+ char* p;
+ char* p2;
+ int l2;
+ int len;
+
+ switch (uLevel) {
+ case 0: struct_len = 16; break;
+ case 1: struct_len = 26; break;
+ default: return -1;
+ }
+
+ if (!buf)
+ {
+ len = 0;
+ switch (uLevel)
+ {
+ case 1:
+ len = strlen(service->comment)+1;
+ break;
+ }
+
+ if (buflen) *buflen = struct_len;
+ if (stringspace) *stringspace = len;
+ return struct_len + len;
+ }
+
+ len = struct_len;
+ p = *buf;
+ if (*buflen < struct_len) return -1;
+ if (stringbuf)
+ {
+ p2 = *stringbuf;
+ l2 = *stringspace;
+ }
+ else
+ {
+ p2 = p + struct_len;
+ l2 = *buflen - struct_len;
+ }
+ if (!baseaddr) baseaddr = p;
+
+ switch (uLevel)
+ {
+ case 0:
+ StrnCpy(p,service->name,15);
+ break;
+
+ case 1:
+ StrnCpy(p,service->name,15);
+ SIVAL(p,18,service->type);
+ SIVAL(p,22,PTR_DIFF(p2,baseaddr));
+ len += CopyAndAdvance(&p2,service->comment,&l2);
+ break;
+ }
+
+ if (stringbuf)
+ {
+ *buf = p + struct_len;
+ *buflen -= struct_len;
+ *stringbuf = p2;
+ *stringspace = l2;
+ }
+ else
+ {
+ *buf = p2;
+ *buflen -= len;
+ }
+ return len;
+}
+
+
+/****************************************************************************
+ view list of servers available (or possibly domains). The info is
+ extracted from lists saved by nmbd on the local host
+ ****************************************************************************/
+static BOOL api_RNetServerEnum(int cnum, int uid, char *param, char *data,
+ int mdrcnt, int mprcnt, char **rdata,
+ char **rparam, int *rdata_len, int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel = SVAL(p,0);
+ int buf_len = SVAL(p,2);
+ uint32 servertype = IVAL(p,4);
+ char *p2;
+ int data_len, fixed_len, string_len;
+ int f_len, s_len;
+ struct srv_info_struct *servers=NULL;
+ int counted=0,total=0;
+ int i;
+ fstring domain;
+ BOOL domain_request = (servertype & SV_TYPE_DOMAIN_ENUM) &&
+ !(servertype == SV_TYPE_ALL);
+
+ domain[0] = 0;
+ p += 8;
+
+ if (!prefix_ok(str1,"WrLehD")) return False;
+ if (!check_server_info(uLevel,str2)) return False;
+
+ DEBUG(4, ("server request level: %s\n", str2));
+
+ if (strcmp(str1, "WrLehDO") == 0)
+ {
+ /* asking for servers. we will have to work out which workgroup was
+ requested, as we maintain lists for multiple workgroups */
+ }
+ else if (strcmp(str1, "WrLehDz") == 0)
+ {
+ /* asking for a specific workgroup */
+ StrnCpy(domain, p, sizeof(fstring)-1);
+ }
+
+ if (lp_browse_list())
+ {
+ total = get_server_info(servertype,&servers);
+ }
+
+ if (!domain[0] && !domain_request) {
+ extern fstring remote_machine;
+ /* must be a server request with an assumed domain. find a domain */
+
+ if (find_server(servers, total, domain, remote_machine)) {
+ DEBUG(4, ("No domain specified: using %s for %s\n",
+ domain, remote_machine));
+ } else {
+ /* default to soemthing sensible */
+ strcpy(domain,my_workgroup());
+ }
+ }
+
+ data_len = fixed_len = string_len = 0;
+
+ for (i=0;i<total;i++)
+ if (filter_server_info(&servers[i],domain)) {
+ data_len += fill_srv_info(&servers[i],uLevel,0,&f_len,0,&s_len,0);
+ if (data_len <= buf_len)
+ {
+ counted++;
+ fixed_len += f_len;
+ string_len += s_len;
+ }
+ }
+
+ *rdata_len = fixed_len + string_len;
+ *rdata = REALLOC(*rdata,*rdata_len);
+ bzero(*rdata,*rdata_len);
+
+ p2 = (*rdata) + fixed_len; /* auxilliary data (strings) will go here */
+ p = *rdata;
+ f_len = fixed_len;
+ s_len = string_len;
+
+ {
+ int count2 = counted;
+ for (i = 0; i < total && count2;i++) {
+ if (filter_server_info(&servers[i],domain)) {
+ fill_srv_info(&servers[i],uLevel,&p,&f_len,&p2,&s_len,*rdata);
+ count2--;
+ }
+ }
+ }
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,counted);
+ SSVAL(*rparam,6,total);
+
+ if (servers) free(servers);
+
+ DEBUG(3,("NetServerEnum domain = %s uLevel=%d counted=%d total=%d\n",
+ domain,uLevel,counted,total));
+
+ return(True);
+}
+
+
+/****************************************************************************
+ get info about a share
+ ****************************************************************************/
+static BOOL check_share_info(int uLevel, char* id)
+{
+ switch( uLevel ) {
+ case 0:
+ if (strcmp(id,"B13") != 0) return False;
+ break;
+ case 1:
+ if (strcmp(id,"B13BWz") != 0) return False;
+ break;
+ case 2:
+ if (strcmp(id,"B13BWzWWWzB9B") != 0) return False;
+ break;
+ case 91:
+ if (strcmp(id,"B13BWzWWWzB9BB9BWzWWzWW") != 0) return False;
+ break;
+ default: return False;
+ }
+ return True;
+}
+
+static int fill_share_info(int cnum, int snum, int uLevel,
+ char** buf, int* buflen,
+ char** stringbuf, int* stringspace, char* baseaddr)
+{
+ int struct_len;
+ char* p;
+ char* p2;
+ int l2;
+ int len;
+
+ switch( uLevel ) {
+ case 0: struct_len = 13; break;
+ case 1: struct_len = 20; break;
+ case 2: struct_len = 40; break;
+ case 91: struct_len = 68; break;
+ default: return -1;
+ }
+
+
+ if (!buf)
+ {
+ len = 0;
+ if (uLevel > 0) len += StrlenExpanded(cnum,snum,lp_comment(snum));
+ if (uLevel > 1) len += strlen(lp_pathname(snum)) + 1;
+ if (buflen) *buflen = struct_len;
+ if (stringspace) *stringspace = len;
+ return struct_len + len;
+ }
+
+ len = struct_len;
+ p = *buf;
+ if ((*buflen) < struct_len) return -1;
+ if (stringbuf)
+ {
+ p2 = *stringbuf;
+ l2 = *stringspace;
+ }
+ else
+ {
+ p2 = p + struct_len;
+ l2 = (*buflen) - struct_len;
+ }
+ if (!baseaddr) baseaddr = p;
+
+ StrnCpy(p,lp_servicename(snum),13);
+
+ if (uLevel > 0)
+ {
+ int type;
+ CVAL(p,13) = 0;
+ type = STYPE_DISKTREE;
+ if (lp_print_ok(snum)) type = STYPE_PRINTQ;
+ if (strequal("IPC$",lp_servicename(snum))) type = STYPE_IPC;
+ SSVAL(p,14,type); /* device type */
+ SIVAL(p,16,PTR_DIFF(p2,baseaddr));
+ len += CopyExpanded(cnum,snum,&p2,lp_comment(snum),&l2);
+ }
+
+ if (uLevel > 1)
+ {
+ SSVAL(p,20,ACCESS_READ|ACCESS_WRITE|ACCESS_CREATE); /* permissions */
+ SSVALS(p,22,-1); /* max uses */
+ SSVAL(p,24,1); /* current uses */
+ SIVAL(p,26,PTR_DIFF(p2,baseaddr)); /* local pathname */
+ len += CopyAndAdvance(&p2,lp_pathname(snum),&l2);
+ memset(p+30,0,SHPWLEN+2); /* passwd (reserved), pad field */
+ }
+
+ if (uLevel > 2)
+ {
+ memset(p+40,0,SHPWLEN+2);
+ SSVAL(p,50,0);
+ SIVAL(p,52,0);
+ SSVAL(p,56,0);
+ SSVAL(p,58,0);
+ SIVAL(p,60,0);
+ SSVAL(p,64,0);
+ SSVAL(p,66,0);
+ }
+
+ if (stringbuf)
+ {
+ (*buf) = p + struct_len;
+ (*buflen) -= struct_len;
+ (*stringbuf) = p2;
+ (*stringspace) = l2;
+ }
+ else
+ {
+ (*buf) = p2;
+ (*buflen) -= len;
+ }
+ return len;
+}
+
+static BOOL api_RNetShareGetInfo(int cnum,int uid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *netname = skip_string(str2,1);
+ char *p = skip_string(netname,1);
+ int uLevel = SVAL(p,0);
+ int snum = find_service(netname);
+
+ if (snum < 0) return False;
+
+ /* check it's a supported varient */
+ if (!prefix_ok(str1,"zWrLh")) return False;
+ if (!check_share_info(uLevel,str2)) return False;
+
+ *rdata = REALLOC(*rdata,mdrcnt);
+ p = *rdata;
+ *rdata_len = fill_share_info(cnum,snum,uLevel,&p,&mdrcnt,0,0,0);
+ if (*rdata_len < 0) return False;
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0); /* converter word */
+ SSVAL(*rparam,4,*rdata_len);
+
+ return(True);
+}
+
+/****************************************************************************
+ view list of shares available
+ ****************************************************************************/
+static BOOL api_RNetShareEnum(int cnum,int uid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel = SVAL(p,0);
+ int buf_len = SVAL(p,2);
+ char *p2;
+ int count=lp_numservices();
+ int total=0,counted=0;
+ int i;
+ int data_len, fixed_len, string_len;
+ int f_len, s_len;
+
+ if (!prefix_ok(str1,"WrLeh")) return False;
+ if (!check_share_info(uLevel,str2)) return False;
+
+ data_len = fixed_len = string_len = 0;
+ for (i=0;i<count;i++)
+ if (lp_browseable(i) && lp_snum_ok(i))
+ {
+ total++;
+ data_len += fill_share_info(cnum,i,uLevel,0,&f_len,0,&s_len,0);
+ if (data_len <= buf_len)
+ {
+ counted++;
+ fixed_len += f_len;
+ string_len += s_len;
+ }
+ }
+ *rdata_len = fixed_len + string_len;
+ *rdata = REALLOC(*rdata,*rdata_len);
+ memset(*rdata,0,*rdata_len);
+
+ p2 = (*rdata) + fixed_len; /* auxillery data (strings) will go here */
+ p = *rdata;
+ f_len = fixed_len;
+ s_len = string_len;
+ for (i = 0; i < count;i++)
+ if (lp_browseable(i) && lp_snum_ok(i))
+ if (fill_share_info(cnum,i,uLevel,&p,&f_len,&p2,&s_len,*rdata) < 0)
+ break;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,counted);
+ SSVAL(*rparam,6,total);
+
+ DEBUG(3,("RNetShareEnum gave %d entries of %d (%d %d %d %d)\n",
+ counted,total,uLevel,
+ buf_len,*rdata_len,mdrcnt));
+ return(True);
+}
+
+
+
+/****************************************************************************
+ get the time of day info
+ ****************************************************************************/
+static BOOL api_NetRemoteTOD(int cnum,int uid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *p;
+ *rparam_len = 4;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ *rdata_len = 21;
+ *rdata = REALLOC(*rdata,*rdata_len);
+
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ p = *rdata;
+
+ {
+ struct tm *t;
+ time_t unixdate = time(NULL);
+
+ put_dos_date3(p,0,unixdate); /* this is the time that is looked at
+ by NT in a "net time" operation,
+ it seems to ignore the one below */
+
+ /* the client expects to get localtime, not GMT, in this bit
+ (I think, this needs testing) */
+ t = LocalTime(&unixdate,GMT_TO_LOCAL);
+
+ SIVAL(p,4,0); /* msecs ? */
+ CVAL(p,8) = t->tm_hour;
+ CVAL(p,9) = t->tm_min;
+ CVAL(p,10) = t->tm_sec;
+ CVAL(p,11) = 0; /* hundredths of seconds */
+ SSVALS(p,12,TimeDiff(unixdate)/60); /* timezone in minutes from GMT */
+ SSVAL(p,14,10000); /* timer interval in 0.0001 of sec */
+ CVAL(p,16) = t->tm_mday;
+ CVAL(p,17) = t->tm_mon + 1;
+ SSVAL(p,18,1900+t->tm_year);
+ CVAL(p,20) = t->tm_wday;
+ }
+
+
+ return(True);
+}
+
+/****************************************************************************
+ set the user password
+ ****************************************************************************/
+static BOOL api_SetUserPassword(int cnum,int uid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *p = skip_string(param+2,2);
+ fstring user;
+ fstring pass1,pass2;
+
+ strcpy(user,p);
+
+ p = skip_string(p,1);
+
+ StrnCpy(pass1,p,16);
+ StrnCpy(pass2,p+16,16);
+
+ *rparam_len = 4;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ *rdata_len = 0;
+
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ DEBUG(3,("Set password for <%s>\n",user));
+
+ if (!password_ok(user,pass1,strlen(pass1),NULL,False) ||
+ !chgpasswd(user,pass1,pass2))
+ SSVAL(*rparam,0,NERR_badpass);
+
+ bzero(pass1,sizeof(fstring));
+ bzero(pass2,sizeof(fstring));
+
+ return(True);
+}
+
+/****************************************************************************
+ delete a print job
+ Form: <W> <>
+ ****************************************************************************/
+static BOOL api_RDosPrintJobDel(int cnum,int uid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ int function = SVAL(param,0);
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int jobid = (SVAL(p,0)&0xFF); /* the snum and jobid are encoded
+ by the print queue api */
+ int snum = (SVAL(p,0)>>8);
+ int i, count;
+
+
+ /* check it's a supported varient */
+ if (!(strcsequal(str1,"W") && strcsequal(str2,"")))
+ return(False);
+
+ *rparam_len = 4;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ *rdata_len = 0;
+
+ SSVAL(*rparam,0,NERR_Success);
+
+ if (snum >= 0 && VALID_SNUM(snum))
+ {
+ print_queue_struct *queue=NULL;
+ lpq_reset(snum);
+ count = get_printqueue(snum,cnum,&queue,NULL);
+
+ for (i=0;i<count;i++)
+ if ((queue[i].job%0xFF) == jobid)
+ {
+ switch (function) {
+ case 81: /* delete */
+ DEBUG(3,("Deleting queue entry %d\n",queue[i].job));
+ del_printqueue(cnum,snum,queue[i].job);
+ break;
+ case 82: /* pause */
+ case 83: /* resume */
+ DEBUG(3,("%s queue entry %d\n",
+ (function==82?"pausing":"resuming"),queue[i].job));
+ status_printjob(cnum,snum,queue[i].job,
+ (function==82?LPQ_PAUSED:LPQ_QUEUED));
+ break;
+ }
+ break;
+ }
+
+ if (i==count)
+ SSVAL(*rparam,0,NERR_JobNotFound);
+
+ if (queue) free(queue);
+ }
+
+ SSVAL(*rparam,2,0); /* converter word */
+
+ return(True);
+}
+
+static BOOL api_WPrintQueuePurge(int cnum,int uid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *QueueName = skip_string(str2,1);
+ int snum;
+
+ /* check it's a supported varient */
+ if (!(strcsequal(str1,"z") && strcsequal(str2,"")))
+ return(False);
+
+ *rparam_len = 4;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ *rdata_len = 0;
+
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ snum = lp_servicenumber(QueueName);
+ if (snum < 0 && pcap_printername_ok(QueueName,NULL)) {
+ int pnum = lp_servicenumber(PRINTERS_NAME);
+ if (pnum >= 0) {
+ lp_add_printer(QueueName,pnum);
+ snum = lp_servicenumber(QueueName);
+ }
+ }
+
+ if (snum >= 0 && VALID_SNUM(snum)) {
+ print_queue_struct *queue=NULL;
+ int i, count;
+ lpq_reset(snum);
+
+ count = get_printqueue(snum,cnum,&queue,NULL);
+ for (i = 0; i < count; i++)
+ del_printqueue(cnum,snum,queue[i].job);
+
+ if (queue) free(queue);
+ }
+
+ DEBUG(3,("Print queue purge, queue=%s\n",QueueName));
+
+ return(True);
+}
+
+
+/****************************************************************************
+ set the property of a print job (undocumented?)
+ ? function = 0xb -> set name of print job
+ ? function = 0x6 -> move print job up/down
+ Form: <WWsTP> <WWzWWDDzzzzzzzzzzlz>
+ or <WWsTP> <WB21BB16B10zWWzDDz>
+****************************************************************************/
+static int check_printjob_info(struct pack_desc* desc,
+ int uLevel, char* id)
+{
+ desc->subformat = NULL;
+ switch( uLevel ) {
+ case 0: desc->format = "W"; break;
+ case 1: desc->format = "WB21BB16B10zWWzDDz"; break;
+ case 2: desc->format = "WWzWWDDzz"; break;
+ case 3: desc->format = "WWzWWDDzzzzzzzzzzlz"; break;
+ default: return False;
+ }
+ if (strcmp(desc->format,id) != 0) return False;
+ return True;
+}
+
+static BOOL api_PrintJobInfo(int cnum,int uid,char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ struct pack_desc desc;
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int jobid = (SVAL(p,0)&0xFF); /* the snum and jobid are encoded
+ by the print queue api */
+ int snum = (SVAL(p,0)>>8);
+ int uLevel = SVAL(p,2);
+ int function = SVAL(p,4); /* what is this ?? */
+ int i;
+ char *s = data;
+
+ *rparam_len = 4;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ *rdata_len = 0;
+
+ /* check it's a supported varient */
+ if ((strcmp(str1,"WWsTP")) || (!check_printjob_info(&desc,uLevel,str2)))
+ return(False);
+
+ switch (function) {
+ case 0x6: /* change job place in the queue, data gives the new place */
+ if (snum >= 0 && VALID_SNUM(snum))
+ {
+ print_queue_struct *queue=NULL;
+ int count;
+
+ lpq_reset(snum);
+ count = get_printqueue(snum,cnum,&queue,NULL);
+ for (i=0;i<count;i++) /* find job */
+ if ((queue[i].job%0xFF) == jobid) break;
+
+ if (i==count) {
+ desc.errcode=NERR_JobNotFound;
+ if (queue) free(queue);
+ }
+ else {
+ desc.errcode=NERR_Success;
+ i++;
+#if 0
+ {
+ int place= SVAL(data,0);
+ /* we currently have no way of doing this. Can any unix do it? */
+ if (i < place) /* move down */;
+ else if (i > place ) /* move up */;
+ }
+#endif
+ desc.errcode=NERR_notsupported; /* not yet supported */
+ if (queue) free(queue);
+ }
+ }
+ else desc.errcode=NERR_JobNotFound;
+ break;
+ case 0xb: /* change print job name, data gives the name */
+ /* jobid, snum should be zero */
+ if (isalpha(*s))
+ {
+ pstring name;
+ int l = 0;
+ while (l<64 && *s)
+ {
+ if (isalnum(*s) || strchr("-._",*s))
+ name[l++] = *s;
+ s++;
+ }
+ name[l] = 0;
+
+ DEBUG(3,("Setting print name to %s\n",name));
+
+ for (i=0;i<MAX_OPEN_FILES;i++)
+ if (Files[i].open && Files[i].print_file)
+ {
+ pstring wd;
+ GetWd(wd);
+ unbecome_user();
+
+ if (!become_user(Files[i].cnum,uid) ||
+ !become_service(Files[i].cnum,True))
+ break;
+
+ if (sys_rename(Files[i].name,name) == 0)
+ string_set(&Files[i].name,name);
+ break;
+ }
+ }
+ desc.errcode=NERR_Success;
+
+ break;
+ default: /* not implemented */
+ return False;
+ }
+
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ return(True);
+}
+
+
+/****************************************************************************
+ get info about the server
+ ****************************************************************************/
+static BOOL api_RNetServerGetInfo(int cnum,int uid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel = SVAL(p,0);
+ char *p2;
+ int struct_len;
+
+ DEBUG(4,("NetServerGetInfo level %d\n",uLevel));
+
+ /* check it's a supported varient */
+ if (!prefix_ok(str1,"WrLh")) return False;
+ switch( uLevel ) {
+ case 0:
+ if (strcmp(str2,"B16") != 0) return False;
+ struct_len = 16;
+ break;
+ case 1:
+ if (strcmp(str2,"B16BBDz") != 0) return False;
+ struct_len = 26;
+ break;
+ case 2:
+ if (strcmp(str2,"B16BBDzDDDWWzWWWWWWWBB21zWWWWWWWWWWWWWWWWWWWWWWz")
+ != 0) return False;
+ struct_len = 134;
+ break;
+ case 3:
+ if (strcmp(str2,"B16BBDzDDDWWzWWWWWWWBB21zWWWWWWWWWWWWWWWWWWWWWWzDWz")
+ != 0) return False;
+ struct_len = 144;
+ break;
+ case 20:
+ if (strcmp(str2,"DN") != 0) return False;
+ struct_len = 6;
+ break;
+ case 50:
+ if (strcmp(str2,"B16BBDzWWzzz") != 0) return False;
+ struct_len = 42;
+ break;
+ default: return False;
+ }
+
+ *rdata_len = mdrcnt;
+ *rdata = REALLOC(*rdata,*rdata_len);
+
+ p = *rdata;
+ p2 = p + struct_len;
+ if (uLevel != 20) {
+ StrnCpy(p,local_machine,16);
+ strupper(p);
+ }
+ p += 16;
+ if (uLevel > 0)
+ {
+ struct srv_info_struct *servers=NULL;
+ int i,count;
+ pstring comment;
+ uint32 servertype=SV_TYPE_SERVER_UNIX|SV_TYPE_WORKSTATION|
+ SV_TYPE_SERVER|SV_TYPE_TIME_SOURCE;
+
+ strcpy(comment,lp_serverstring());
+
+ if ((count=get_server_info(SV_TYPE_ALL,&servers))>0) {
+ for (i=0;i<count;i++)
+ if (strequal(servers[i].name,local_machine)) {
+ servertype = servers[i].type;
+ strcpy(comment,servers[i].comment);
+ }
+ }
+ if (servers) free(servers);
+
+ SCVAL(p,0,2); /* version_major */
+ SCVAL(p,1,0); /* version_minor */
+ SIVAL(p,2,servertype);
+ if (mdrcnt == struct_len) {
+ SIVAL(p,6,0);
+ } else {
+ SIVAL(p,6,PTR_DIFF(p2,*rdata));
+ standard_sub(cnum,comment);
+ StrnCpy(p2,comment,MAX(mdrcnt - struct_len,0));
+ p2 = skip_string(p2,1);
+ }
+ }
+ if (uLevel > 1)
+ {
+ return False; /* not yet implemented */
+ }
+
+ *rdata_len = PTR_DIFF(p2,*rdata);
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0); /* converter word */
+ SSVAL(*rparam,4,*rdata_len);
+
+ return(True);
+}
+
+
+/****************************************************************************
+ get info about the server
+ ****************************************************************************/
+static BOOL api_NetWkstaGetInfo(int cnum,int uid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ char *p2;
+ extern pstring sesssetup_user;
+ int level = SVAL(p,0);
+
+ DEBUG(4,("NetWkstaGetInfo level %d\n",level));
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ /* check it's a supported varient */
+ if (!(level==10 && strcsequal(str1,"WrLh") && strcsequal(str2,"zzzBBzz")))
+ return(False);
+
+ *rdata_len = mdrcnt + 1024;
+ *rdata = REALLOC(*rdata,*rdata_len);
+
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ p = *rdata;
+ p2 = p + 22;
+
+ SIVAL(p,0,PTR_DIFF(p2,*rdata));
+ strcpy(p2,local_machine);
+ p2 = skip_string(p2,1);
+ p += 4;
+
+ SIVAL(p,0,PTR_DIFF(p2,*rdata));
+ strcpy(p2,sesssetup_user);
+ p2 = skip_string(p2,1);
+ p += 4;
+
+ SIVAL(p,0,PTR_DIFF(p2,*rdata));
+ strcpy(p2,my_workgroup());
+ p2 = skip_string(p2,1);
+ p += 4;
+
+ SCVAL(p,0,2); /* major version?? */
+ SCVAL(p,1,1); /* minor version?? */
+ p += 2;
+
+ SIVAL(p,0,PTR_DIFF(p2,*rdata));
+ strcpy(p2,my_workgroup()); /* login domain?? */
+ p2 = skip_string(p2,1);
+ p += 4;
+
+ SIVAL(p,0,PTR_DIFF(p2,*rdata));
+ strcpy(p2,"");
+ p2 = skip_string(p2,1);
+ p += 4;
+
+ *rdata_len = PTR_DIFF(p2,*rdata);
+
+ SSVAL(*rparam,4,*rdata_len);
+
+ return(True);
+}
+
+
+/****************************************************************************
+ get info about a user
+ ****************************************************************************/
+
+#define USER_PRIV_GUEST 0
+#define USER_PRIV_USER 1
+#define USER_PRIV_ADMIN 2
+
+static BOOL api_RNetUserGetInfo(int cnum,int uid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *UserName = skip_string(str2,1);
+ char *p = skip_string(UserName,1);
+ int uLevel = SVAL(p,0);
+ char *p2;
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"zWrLh") != 0) return False;
+ switch( uLevel ) {
+ case 0: p2 = "B21"; break;
+ case 1: p2 = "B21BB16DWzzWz"; break;
+ case 2: p2 = "B21BB16DWzzWzDzzzzDDDDWb21WWzWW"; break;
+ case 10: p2 = "B21Bzzz"; break;
+ case 11: p2 = "B21BzzzWDDzzDDWWzWzDWb21W"; break;
+ default: return False;
+ }
+ if (strcmp(p2,str2) != 0) return False;
+
+ *rdata_len = mdrcnt + 1024;
+ *rdata = REALLOC(*rdata,*rdata_len);
+
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ p = *rdata;
+ p2 = p + 86;
+
+ memset(p,0,21);
+ strcpy(p,UserName);
+ if (uLevel > 0) {
+ SCVAL(p,21,0);
+ *p2 = 0;
+ if (uLevel >= 10) {
+ SIVAL(p,22,PTR_DIFF(p2,p)); /* comment */
+ strcpy(p2,"<Comment>");
+ p2 = skip_string(p2,1);
+ SIVAL(p,26,PTR_DIFF(p2,p)); /* user_comment */
+ strcpy(p2,"<UserComment>");
+ p2 = skip_string(p2,1);
+ SIVAL(p,30,PTR_DIFF(p2,p)); /* full name */
+ strcpy(p2,"<FullName>");
+ p2 = skip_string(p2,1);
+ }
+ if (uLevel == 11) { /* modelled after NTAS 3.51 reply */
+ SSVAL(p,34,USER_PRIV_USER); /* user privilege */
+ SIVAL(p,36,0); /* auth flags */
+ SIVALS(p,40,-1); /* password age */
+ SIVAL(p,44,PTR_DIFF(p2,p)); /* home dir */
+ strcpy(p2,"\\\\%L\\HOMES");
+ standard_sub_basic(p2);
+ p2 = skip_string(p2,1);
+ SIVAL(p,48,PTR_DIFF(p2,p)); /* parms */
+ strcpy(p2,"");
+ p2 = skip_string(p2,1);
+ SIVAL(p,52,0); /* last logon */
+ SIVAL(p,56,0); /* last logoff */
+ SSVALS(p,60,-1); /* bad pw counts */
+ SSVALS(p,62,-1); /* num logons */
+ SIVAL(p,64,PTR_DIFF(p2,p)); /* logon server */
+ strcpy(p2,"\\\\*");
+ p2 = skip_string(p2,1);
+ SSVAL(p,68,0); /* country code */
+
+ SIVAL(p,70,PTR_DIFF(p2,p)); /* workstations */
+ strcpy(p2,"");
+ p2 = skip_string(p2,1);
+
+ SIVALS(p,74,-1); /* max storage */
+ SSVAL(p,78,168); /* units per week */
+ SIVAL(p,80,PTR_DIFF(p2,p)); /* logon hours */
+ memset(p2,-1,21);
+ SCVAL(p2,21,0); /* fix zero termination */
+ p2 = skip_string(p2,1);
+
+ SSVAL(p,84,0); /* code page */
+ }
+ if (uLevel == 1 || uLevel == 2) {
+ memset(p+22,' ',16); /* password */
+ SIVALS(p,38,-1); /* password age */
+ SSVAL(p,42,USER_PRIV_ADMIN); /* user privilege */
+ SIVAL(p,44,PTR_DIFF(p2,*rdata)); /* home dir */
+ strcpy(p2,"\\\\%L\\HOMES");
+ standard_sub_basic(p2);
+ p2 = skip_string(p2,1);
+ SIVAL(p,48,PTR_DIFF(p2,*rdata)); /* comment */
+ *p2++ = 0;
+ SSVAL(p,52,0); /* flags */
+ SIVAL(p,54,0); /* script_path */
+ if (uLevel == 2) {
+ SIVAL(p,60,0); /* auth_flags */
+ SIVAL(p,64,PTR_DIFF(p2,*rdata)); /* full_name */
+ strcpy(p2,"<Full Name>");
+ p2 = skip_string(p2,1);
+ SIVAL(p,68,0); /* urs_comment */
+ SIVAL(p,72,PTR_DIFF(p2,*rdata)); /* parms */
+ strcpy(p2,"");
+ p2 = skip_string(p2,1);
+ SIVAL(p,76,0); /* workstations */
+ SIVAL(p,80,0); /* last_logon */
+ SIVAL(p,84,0); /* last_logoff */
+ SIVALS(p,88,-1); /* acct_expires */
+ SIVALS(p,92,-1); /* max_storage */
+ SSVAL(p,96,168); /* units_per_week */
+ SIVAL(p,98,PTR_DIFF(p2,*rdata)); /* logon_hours */
+ memset(p2,-1,21);
+ p2 += 21;
+ SSVALS(p,102,-1); /* bad_pw_count */
+ SSVALS(p,104,-1); /* num_logons */
+ SIVAL(p,106,PTR_DIFF(p2,*rdata)); /* logon_server */
+ strcpy(p2,"\\\\%L");
+ standard_sub_basic(p2);
+ p2 = skip_string(p2,1);
+ SSVAL(p,110,49); /* country_code */
+ SSVAL(p,112,860); /* code page */
+ }
+ }
+ }
+
+ *rdata_len = PTR_DIFF(p2,*rdata);
+
+ SSVAL(*rparam,4,*rdata_len); /* is this right?? */
+
+ return(True);
+}
+
+
+/*******************************************************************
+ get groups that a user is a member of
+ ******************************************************************/
+static BOOL api_NetUserGetGroups(int cnum,int uid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *UserName = skip_string(str2,1);
+ char *p = skip_string(UserName,1);
+ int uLevel = SVAL(p,0);
+ char *p2;
+ int count=0;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"zWrLeh") != 0) return False;
+ switch( uLevel ) {
+ case 0: p2 = "B21"; break;
+ default: return False;
+ }
+ if (strcmp(p2,str2) != 0) return False;
+
+ *rdata_len = mdrcnt + 1024;
+ *rdata = REALLOC(*rdata,*rdata_len);
+
+ SSVAL(*rparam,0,NERR_Success);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ p = *rdata;
+
+ /* XXXX we need a real SAM database some day */
+ strcpy(p,"Users"); p += 21; count++;
+ strcpy(p,"Domain Users"); p += 21; count++;
+ strcpy(p,"Guests"); p += 21; count++;
+ strcpy(p,"Domain Guests"); p += 21; count++;
+
+ *rdata_len = PTR_DIFF(p,*rdata);
+
+ SSVAL(*rparam,4,count); /* is this right?? */
+ SSVAL(*rparam,6,count); /* is this right?? */
+
+ return(True);
+}
+
+
+static BOOL api_WWkstaUserLogon(int cnum,int uid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel;
+ struct pack_desc desc;
+ char* name;
+
+ uLevel = SVAL(p,0);
+ name = p + 2;
+
+ bzero(&desc,sizeof(desc));
+
+ DEBUG(3,("WWkstaUserLogon uLevel=%d name=%s\n",uLevel,name));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"OOWb54WrLh") != 0) return False;
+ if (uLevel != 1 || strcmp(str2,"WB21BWDWWDDDDDDDzzzD") != 0) return False;
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ desc.subformat = NULL;
+ desc.format = str2;
+
+
+
+ if (init_package(&desc,1,0)) {
+ PACKI(&desc,"W",0); /* code */
+ PACKS(&desc,"B21",name); /* eff. name */
+ PACKS(&desc,"B",""); /* pad */
+ PACKI(&desc,"W",
+ Connections[cnum].admin_user?USER_PRIV_ADMIN:USER_PRIV_USER);
+ PACKI(&desc,"D",0); /* auth flags XXX */
+ PACKI(&desc,"W",0); /* num logons */
+ PACKI(&desc,"W",0); /* bad pw count */
+ PACKI(&desc,"D",-1); /* last logon */
+ PACKI(&desc,"D",-1); /* last logoff */
+ PACKI(&desc,"D",-1); /* logoff time */
+ PACKI(&desc,"D",-1); /* kickoff time */
+ PACKI(&desc,"D",0); /* password age */
+ PACKI(&desc,"D",0); /* password can change */
+ PACKI(&desc,"D",-1); /* password must change */
+ {
+ fstring mypath;
+ strcpy(mypath,"\\\\");
+ strcat(mypath,local_machine);
+ strupper(mypath);
+ PACKS(&desc,"z",mypath); /* computer */
+ }
+ PACKS(&desc,"z",my_workgroup());/* domain */
+ PACKS(&desc,"z",lp_logon_script()); /* script path */
+ PACKI(&desc,"D",0); /* reserved */
+ }
+
+ *rdata_len = desc.usedlen;
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,desc.neededlen);
+
+ DEBUG(4,("WWkstaUserLogon: errorcode %d\n",desc.errcode));
+ return(True);
+}
+
+
+/****************************************************************************
+ api_WAccessGetUserPerms
+ ****************************************************************************/
+static BOOL api_WAccessGetUserPerms(int cnum,int uid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *user = skip_string(str2,1);
+ char *resource = skip_string(user,1);
+
+ DEBUG(3,("WAccessGetUserPerms user=%s resource=%s\n",user,resource));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"zzh") != 0) return False;
+ if (strcmp(str2,"") != 0) return False;
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,0); /* errorcode */
+ SSVAL(*rparam,2,0); /* converter word */
+ SSVAL(*rparam,4,0x7f); /* permission flags */
+
+ return(True);
+}
+
+/****************************************************************************
+ api_WPrintJobEnumerate
+ ****************************************************************************/
+static BOOL api_WPrintJobGetInfo(int cnum,int uid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uJobId = SVAL(p,0);
+ int uLevel,cbBuf;
+ int count;
+ int i;
+ int snum;
+ int job;
+ struct pack_desc desc;
+ print_queue_struct *queue=NULL;
+ print_status_struct status;
+
+ uLevel = SVAL(p,2);
+ cbBuf = SVAL(p,4);
+
+ bzero(&desc,sizeof(desc));
+ bzero(&status,sizeof(status));
+
+ DEBUG(3,("WPrintJobGetInfo uLevel=%d uJobId=0x%X\n",uLevel,uJobId));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"WWrLh") != 0) return False;
+ if (!check_printjob_info(&desc,uLevel,str2)) return False;
+
+ snum = (unsigned int)uJobId >> 8; /*## valid serice number??*/
+ job = uJobId & 0xFF;
+
+ if (snum < 0 || !VALID_SNUM(snum)) return(False);
+
+ count = get_printqueue(snum,cnum,&queue,&status);
+ for (i = 0; i < count; i++) {
+ if ((queue[i].job % 0xFF) == job) break;
+ }
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+
+ if (init_package(&desc,1,0)) {
+ if (i < count) {
+ fill_printjob_info(cnum,snum,uLevel,&desc,&queue[i],i);
+ *rdata_len = desc.usedlen;
+ }
+ else {
+ desc.errcode = NERR_JobNotFound;
+ *rdata_len = 0;
+ }
+ }
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,desc.neededlen);
+
+ if (queue) free(queue);
+
+ DEBUG(4,("WPrintJobGetInfo: errorcode %d\n",desc.errcode));
+ return(True);
+}
+
+static BOOL api_WPrintJobEnumerate(int cnum,int uid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ char* name = p;
+ int uLevel,cbBuf;
+ int count;
+ int i, succnt=0;
+ int snum;
+ struct pack_desc desc;
+ print_queue_struct *queue=NULL;
+ print_status_struct status;
+
+ bzero(&desc,sizeof(desc));
+ bzero(&status,sizeof(status));
+
+ p = skip_string(p,1);
+ uLevel = SVAL(p,0);
+ cbBuf = SVAL(p,2);
+
+ DEBUG(3,("WPrintJobEnumerate uLevel=%d name=%s\n",uLevel,name));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"zWrLeh") != 0) return False;
+ if (uLevel > 2) return False; /* defined only for uLevel 0,1,2 */
+ if (!check_printjob_info(&desc,uLevel,str2)) return False;
+
+ snum = lp_servicenumber(name);
+ if (snum < 0 && pcap_printername_ok(name,NULL)) {
+ int pnum = lp_servicenumber(PRINTERS_NAME);
+ if (pnum >= 0) {
+ lp_add_printer(name,pnum);
+ snum = lp_servicenumber(name);
+ }
+ }
+
+ if (snum < 0 || !VALID_SNUM(snum)) return(False);
+
+ count = get_printqueue(snum,cnum,&queue,&status);
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+
+ if (init_package(&desc,count,0)) {
+ succnt = 0;
+ for (i = 0; i < count; i++) {
+ fill_printjob_info(cnum,snum,uLevel,&desc,&queue[i],i);
+ if (desc.errcode == NERR_Success) succnt = i+1;
+ }
+ }
+
+ *rdata_len = desc.usedlen;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,succnt);
+ SSVAL(*rparam,6,count);
+
+ if (queue) free(queue);
+
+ DEBUG(4,("WPrintJobEnumerate: errorcode %d\n",desc.errcode));
+ return(True);
+}
+
+static int check_printdest_info(struct pack_desc* desc,
+ int uLevel, char* id)
+{
+ desc->subformat = NULL;
+ switch( uLevel ) {
+ case 0: desc->format = "B9"; break;
+ case 1: desc->format = "B9B21WWzW"; break;
+ case 2: desc->format = "z"; break;
+ case 3: desc->format = "zzzWWzzzWW"; break;
+ default: return False;
+ }
+ if (strcmp(desc->format,id) != 0) return False;
+ return True;
+}
+
+static void fill_printdest_info(int cnum, int snum, int uLevel,
+ struct pack_desc* desc)
+{
+ char buf[100];
+ strcpy(buf,SERVICE(snum));
+ strupper(buf);
+ if (uLevel <= 1) {
+ PACKS(desc,"B9",buf); /* szName */
+ if (uLevel == 1) {
+ PACKS(desc,"B21",""); /* szUserName */
+ PACKI(desc,"W",0); /* uJobId */
+ PACKI(desc,"W",0); /* fsStatus */
+ PACKS(desc,"z",""); /* pszStatus */
+ PACKI(desc,"W",0); /* time */
+ }
+ }
+ if (uLevel == 2 || uLevel == 3) {
+ PACKS(desc,"z",buf); /* pszPrinterName */
+ if (uLevel == 3) {
+ PACKS(desc,"z",""); /* pszUserName */
+ PACKS(desc,"z",""); /* pszLogAddr */
+ PACKI(desc,"W",0); /* uJobId */
+ PACKI(desc,"W",0); /* fsStatus */
+ PACKS(desc,"z",""); /* pszStatus */
+ PACKS(desc,"z",""); /* pszComment */
+ PACKS(desc,"z","NULL"); /* pszDrivers */
+ PACKI(desc,"W",0); /* time */
+ PACKI(desc,"W",0); /* pad1 */
+ }
+ }
+}
+
+static BOOL api_WPrintDestGetInfo(int cnum,int uid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ char* PrinterName = p;
+ int uLevel,cbBuf;
+ struct pack_desc desc;
+ int snum;
+
+ bzero(&desc,sizeof(desc));
+
+ p = skip_string(p,1);
+ uLevel = SVAL(p,0);
+ cbBuf = SVAL(p,2);
+
+ DEBUG(3,("WPrintDestGetInfo uLevel=%d PrinterName=%s\n",uLevel,PrinterName));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"zWrLh") != 0) return False;
+ if (!check_printdest_info(&desc,uLevel,str2)) return False;
+
+ snum = lp_servicenumber(PrinterName);
+ if (snum < 0 && pcap_printername_ok(PrinterName,NULL)) {
+ int pnum = lp_servicenumber(PRINTERS_NAME);
+ if (pnum >= 0) {
+ lp_add_printer(PrinterName,pnum);
+ snum = lp_servicenumber(PrinterName);
+ }
+ }
+
+ if (snum < 0) {
+ *rdata_len = 0;
+ desc.errcode = NERR_DestNotFound;
+ desc.neededlen = 0;
+ }
+ else {
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ if (init_package(&desc,1,0)) {
+ fill_printdest_info(cnum,snum,uLevel,&desc);
+ }
+ *rdata_len = desc.usedlen;
+ }
+
+ *rparam_len = 6;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,desc.neededlen);
+
+ DEBUG(4,("WPrintDestGetInfo: errorcode %d\n",desc.errcode));
+ return(True);
+}
+
+static BOOL api_WPrintDestEnum(int cnum,int uid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel,cbBuf;
+ int queuecnt;
+ int i, n, succnt=0;
+ struct pack_desc desc;
+ int services = lp_numservices();
+
+ bzero(&desc,sizeof(desc));
+
+ uLevel = SVAL(p,0);
+ cbBuf = SVAL(p,2);
+
+ DEBUG(3,("WPrintDestEnum uLevel=%d\n",uLevel));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"WrLeh") != 0) return False;
+ if (!check_printdest_info(&desc,uLevel,str2)) return False;
+
+ queuecnt = 0;
+ for (i = 0; i < services; i++)
+ if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i))
+ queuecnt++;
+
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ if (init_package(&desc,queuecnt,0)) {
+ succnt = 0;
+ n = 0;
+ for (i = 0; i < services; i++) {
+ if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i)) {
+ fill_printdest_info(cnum,i,uLevel,&desc);
+ n++;
+ if (desc.errcode == NERR_Success) succnt = n;
+ }
+ }
+ }
+
+ *rdata_len = desc.usedlen;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,succnt);
+ SSVAL(*rparam,6,queuecnt);
+
+ DEBUG(4,("WPrintDestEnumerate: errorcode %d\n",desc.errcode));
+ return(True);
+}
+
+static BOOL api_WPrintDriverEnum(int cnum,int uid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel,cbBuf;
+ int succnt;
+ struct pack_desc desc;
+
+ bzero(&desc,sizeof(desc));
+
+ uLevel = SVAL(p,0);
+ cbBuf = SVAL(p,2);
+
+ DEBUG(3,("WPrintDriverEnum uLevel=%d\n",uLevel));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"WrLeh") != 0) return False;
+ if (uLevel != 0 || strcmp(str2,"B41") != 0) return False;
+
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ if (init_package(&desc,1,0)) {
+ PACKS(&desc,"B41","NULL");
+ }
+
+ succnt = (desc.errcode == NERR_Success ? 1 : 0);
+
+ *rdata_len = desc.usedlen;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,succnt);
+ SSVAL(*rparam,6,1);
+
+ DEBUG(4,("WPrintDriverEnum: errorcode %d\n",desc.errcode));
+ return(True);
+}
+
+static BOOL api_WPrintQProcEnum(int cnum,int uid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel,cbBuf;
+ int succnt;
+ struct pack_desc desc;
+
+ bzero(&desc,sizeof(desc));
+
+ uLevel = SVAL(p,0);
+ cbBuf = SVAL(p,2);
+
+ DEBUG(3,("WPrintQProcEnum uLevel=%d\n",uLevel));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"WrLeh") != 0) return False;
+ if (uLevel != 0 || strcmp(str2,"B13") != 0) return False;
+
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ desc.format = str2;
+ if (init_package(&desc,1,0)) {
+ PACKS(&desc,"B13","lpd");
+ }
+
+ succnt = (desc.errcode == NERR_Success ? 1 : 0);
+
+ *rdata_len = desc.usedlen;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,succnt);
+ SSVAL(*rparam,6,1);
+
+ DEBUG(4,("WPrintQProcEnum: errorcode %d\n",desc.errcode));
+ return(True);
+}
+
+static BOOL api_WPrintPortEnum(int cnum,int uid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ char *str1 = param+2;
+ char *str2 = skip_string(str1,1);
+ char *p = skip_string(str2,1);
+ int uLevel,cbBuf;
+ int succnt;
+ struct pack_desc desc;
+
+ bzero(&desc,sizeof(desc));
+
+ uLevel = SVAL(p,0);
+ cbBuf = SVAL(p,2);
+
+ DEBUG(3,("WPrintPortEnum uLevel=%d\n",uLevel));
+
+ /* check it's a supported varient */
+ if (strcmp(str1,"WrLeh") != 0) return False;
+ if (uLevel != 0 || strcmp(str2,"B9") != 0) return False;
+
+ if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt);
+ bzero(&desc,sizeof(desc));
+ desc.base = *rdata;
+ desc.buflen = mdrcnt;
+ desc.format = str2;
+ if (init_package(&desc,1,0)) {
+ PACKS(&desc,"B13","lp0");
+ }
+
+ succnt = (desc.errcode == NERR_Success ? 1 : 0);
+
+ *rdata_len = desc.usedlen;
+
+ *rparam_len = 8;
+ *rparam = REALLOC(*rparam,*rparam_len);
+ SSVALS(*rparam,0,desc.errcode);
+ SSVAL(*rparam,2,0);
+ SSVAL(*rparam,4,succnt);
+ SSVAL(*rparam,6,1);
+
+ DEBUG(4,("WPrintPortEnum: errorcode %d\n",desc.errcode));
+ return(True);
+}
+
+/****************************************************************************
+ the buffer was too small
+ ****************************************************************************/
+static BOOL api_TooSmall(int cnum,int uid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ *rparam_len = MIN(*rparam_len,mprcnt);
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ *rdata_len = 0;
+
+ SSVAL(*rparam,0,NERR_BufTooSmall);
+
+ DEBUG(3,("Supplied buffer too small in API command\n"));
+
+ return(True);
+}
+
+
+/****************************************************************************
+ the request is not supported
+ ****************************************************************************/
+static BOOL api_Unsupported(int cnum,int uid, char *param,char *data,
+ int mdrcnt,int mprcnt,
+ char **rdata,char **rparam,
+ int *rdata_len,int *rparam_len)
+{
+ *rparam_len = 4;
+ *rparam = REALLOC(*rparam,*rparam_len);
+
+ *rdata_len = 0;
+
+ SSVAL(*rparam,0,NERR_notsupported);
+ SSVAL(*rparam,2,0); /* converter word */
+
+ DEBUG(3,("Unsupported API command\n"));
+
+ return(True);
+}
+
+
+
+
+struct
+{
+ char *name;
+ int id;
+ BOOL (*fn)();
+ int flags;
+} api_commands[] = {
+ {"RNetShareEnum", 0, api_RNetShareEnum,0},
+ {"RNetShareGetInfo", 1, api_RNetShareGetInfo,0},
+ {"RNetServerGetInfo", 13, api_RNetServerGetInfo,0},
+ {"RNetUserGetInfo", 56, api_RNetUserGetInfo,0},
+ {"NetUserGetGroups", 59, api_NetUserGetGroups,0},
+ {"NetWkstaGetInfo", 63, api_NetWkstaGetInfo,0},
+ {"DosPrintQEnum", 69, api_DosPrintQEnum,0},
+ {"DosPrintQGetInfo", 70, api_DosPrintQGetInfo,0},
+ {"WPrintJobEnumerate",76, api_WPrintJobEnumerate,0},
+ {"WPrintJobGetInfo", 77, api_WPrintJobGetInfo,0},
+ {"RDosPrintJobDel", 81, api_RDosPrintJobDel,0},
+ {"RDosPrintJobPause", 82, api_RDosPrintJobDel,0},
+ {"RDosPrintJobResume",83, api_RDosPrintJobDel,0},
+ {"WPrintDestEnum", 84, api_WPrintDestEnum,0},
+ {"WPrintDestGetInfo", 85, api_WPrintDestGetInfo,0},
+ {"NetRemoteTOD", 91, api_NetRemoteTOD,0},
+ {"WPrintQueuePurge", 103, api_WPrintQueuePurge,0},
+ {"NetServerEnum", 104, api_RNetServerEnum,0},
+ {"WAccessGetUserPerms",105, api_WAccessGetUserPerms,0},
+ {"SetUserPassword", 115, api_SetUserPassword,0},
+ {"WWkstaUserLogon", 132, api_WWkstaUserLogon,0},
+ {"PrintJobInfo", 147, api_PrintJobInfo,0},
+ {"WPrintDriverEnum", 205, api_WPrintDriverEnum,0},
+ {"WPrintQProcEnum", 206, api_WPrintQProcEnum,0},
+ {"WPrintPortEnum", 207, api_WPrintPortEnum,0},
+ {NULL, -1, api_Unsupported,0}};
+
+
+/****************************************************************************
+ handle remote api calls
+ ****************************************************************************/
+static int api_reply(int cnum,int uid,char *outbuf,char *data,char *params,
+ int tdscnt,int tpscnt,int mdrcnt,int mprcnt)
+{
+ int api_command = SVAL(params,0);
+ char *rdata = NULL;
+ char *rparam = NULL;
+ int rdata_len = 0;
+ int rparam_len = 0;
+ BOOL reply=False;
+ int i;
+
+ DEBUG(3,("Got API command %d of form <%s> <%s> (tdscnt=%d,tpscnt=%d,mdrcnt=%d,mprcnt=%d)\n",
+ api_command,params+2,skip_string(params+2,1),
+ tdscnt,tpscnt,mdrcnt,mprcnt));
+
+ for (i=0;api_commands[i].name;i++)
+ if (api_commands[i].id == api_command && api_commands[i].fn)
+ {
+ DEBUG(3,("Doing %s\n",api_commands[i].name));
+ break;
+ }
+
+ rdata = (char *)malloc(1024); if (rdata) bzero(rdata,1024);
+ rparam = (char *)malloc(1024); if (rparam) bzero(rparam,1024);
+
+ reply = api_commands[i].fn(cnum,uid,params,data,mdrcnt,mprcnt,
+ &rdata,&rparam,&rdata_len,&rparam_len);
+
+
+ if (rdata_len > mdrcnt ||
+ rparam_len > mprcnt)
+ {
+ reply = api_TooSmall(cnum,uid,params,data,mdrcnt,mprcnt,
+ &rdata,&rparam,&rdata_len,&rparam_len);
+ }
+
+
+ /* if we get False back then it's actually unsupported */
+ if (!reply)
+ api_Unsupported(cnum,uid,params,data,mdrcnt,mprcnt,
+ &rdata,&rparam,&rdata_len,&rparam_len);
+
+
+
+ /* now send the reply */
+ send_trans_reply(outbuf,rdata,rparam,NULL,rdata_len,rparam_len,0);
+
+ if (rdata)
+ free(rdata);
+ if (rparam)
+ free(rparam);
+
+ return(-1);
+}
+
+/****************************************************************************
+ handle named pipe commands
+ ****************************************************************************/
+static int named_pipe(int cnum,int uid, char *outbuf,char *name,
+ uint16 *setup,char *data,char *params,
+ int suwcnt,int tdscnt,int tpscnt,
+ int msrcnt,int mdrcnt,int mprcnt)
+{
+
+ if (strequal(name,"LANMAN"))
+ return(api_reply(cnum,uid,outbuf,data,params,tdscnt,tpscnt,mdrcnt,mprcnt));
+
+ DEBUG(3,("named pipe command on <%s> 0x%X setup1=%d\n",
+ name,(int)setup[0],(int)setup[1]));
+
+ return(0);
+}
+
+
+/****************************************************************************
+ reply to a SMBtrans
+ ****************************************************************************/
+int reply_trans(char *inbuf,char *outbuf)
+{
+ fstring name;
+
+ char *data=NULL,*params=NULL;
+ uint16 *setup=NULL;
+
+ int outsize = 0;
+ int cnum = SVAL(inbuf,smb_tid);
+ int uid = SVAL(inbuf,smb_uid);
+
+ int tpscnt = SVAL(inbuf,smb_vwv0);
+ int tdscnt = SVAL(inbuf,smb_vwv1);
+ int mprcnt = SVAL(inbuf,smb_vwv2);
+ int mdrcnt = SVAL(inbuf,smb_vwv3);
+ int msrcnt = CVAL(inbuf,smb_vwv4);
+ BOOL close_on_completion = BITSETW(inbuf+smb_vwv5,0);
+ BOOL one_way = BITSETW(inbuf+smb_vwv5,1);
+ int pscnt = SVAL(inbuf,smb_vwv9);
+ int psoff = SVAL(inbuf,smb_vwv10);
+ int dscnt = SVAL(inbuf,smb_vwv11);
+ int dsoff = SVAL(inbuf,smb_vwv12);
+ int suwcnt = CVAL(inbuf,smb_vwv13);
+
+ StrnCpy(name,smb_buf(inbuf),sizeof(name)-1);
+
+ if (tdscnt)
+ {
+ data = (char *)malloc(tdscnt);
+ memcpy(data,smb_base(inbuf)+dsoff,dscnt);
+ }
+ if (tpscnt)
+ {
+ params = (char *)malloc(tpscnt);
+ memcpy(params,smb_base(inbuf)+psoff,pscnt);
+ }
+
+ if (suwcnt)
+ {
+ int i;
+ setup = (uint16 *)malloc(suwcnt*sizeof(setup[0]));
+ for (i=0;i<suwcnt;i++)
+ setup[i] = SVAL(inbuf,smb_vwv14+i*SIZEOFWORD);
+ }
+
+
+ if (pscnt < tpscnt || dscnt < tdscnt)
+ {
+ /* We need to send an interim response then receive the rest
+ of the parameter/data bytes */
+ outsize = set_message(outbuf,0,0,True);
+ show_msg(outbuf);
+ send_smb(Client,outbuf);
+ }
+
+ /* receive the rest of the trans packet */
+ while (pscnt < tpscnt || dscnt < tdscnt)
+ {
+ int pcnt,poff,dcnt,doff,pdisp,ddisp;
+
+ receive_smb(Client,inbuf, 0);
+ show_msg(inbuf);
+
+ /* Ensure this is still a trans packet (sanity check) */
+ if(CVAL(inbuf, smb_com) != SMBtrans)
+ {
+ DEBUG(2,("Invalid secondary trans2 packet\n"));
+ if (params) free(params);
+ if (data) free(data);
+ if (setup) free(setup);
+ return(ERROR(ERRSRV,ERRerror));
+ }
+
+ tpscnt = SVAL(inbuf,smb_vwv0);
+ tdscnt = SVAL(inbuf,smb_vwv1);
+
+ pcnt = SVAL(inbuf,smb_vwv2);
+ poff = SVAL(inbuf,smb_vwv3);
+ pdisp = SVAL(inbuf,smb_vwv4);
+
+ dcnt = SVAL(inbuf,smb_vwv5);
+ doff = SVAL(inbuf,smb_vwv6);
+ ddisp = SVAL(inbuf,smb_vwv7);
+
+ pscnt += pcnt;
+ dscnt += dcnt;
+
+ if (pcnt)
+ memcpy(params+pdisp,smb_base(inbuf)+poff,pcnt);
+ if (dcnt)
+ memcpy(data+ddisp,smb_base(inbuf)+doff,dcnt);
+ }
+
+
+ DEBUG(3,("trans <%s> data=%d params=%d setup=%d\n",name,tdscnt,tpscnt,suwcnt));
+
+
+ if (strncmp(name,"\\PIPE\\",strlen("\\PIPE\\")) == 0)
+ outsize = named_pipe(cnum,uid,outbuf,name+strlen("\\PIPE\\"),setup,data,params,
+ suwcnt,tdscnt,tpscnt,msrcnt,mdrcnt,mprcnt);
+
+
+ if (data) free(data);
+ if (params) free(params);
+ if (setup) free(setup);
+
+ if (close_on_completion)
+ close_cnum(cnum,uid);
+
+ if (one_way)
+ return(-1);
+
+ if (outsize == 0)
+ return(ERROR(ERRSRV,ERRnosupport));
+
+ return(outsize);
+}
+
+
diff --git a/source/smbd/mangle.c b/source/smbd/mangle.c
new file mode 100644
index 00000000000..8f1490c528d
--- /dev/null
+++ b/source/smbd/mangle.c
@@ -0,0 +1,610 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Name mangling
+ Copyright (C) Andrew Tridgell 1992-1995
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "loadparm.h"
+
+extern int DEBUGLEVEL;
+extern int case_default;
+extern BOOL case_mangle;
+
+/****************************************************************************
+provide a checksum on a string
+****************************************************************************/
+int str_checksum(char *s)
+{
+ int res = 0;
+ int c;
+ int i=0;
+ while (*s)
+ {
+ c = *s;
+ res ^= (c << (i % 15)) ^ (c >> (15-(i%15)));
+ s++; i++;
+ }
+ return(res);
+}
+
+/****************************************************************************
+return True if a name is a special msdos reserved name
+****************************************************************************/
+static BOOL is_reserved_msdos(char *fname)
+{
+ char upperFname[13];
+ char *p;
+
+ StrnCpy (upperFname, fname, 12);
+
+ /* lpt1.txt and con.txt etc are also illegal */
+ p=strchr(upperFname,'.');
+ if (p)
+ *p='\0';
+ strupper (upperFname);
+ if ((strcmp(upperFname,"CLOCK$") == 0) ||
+ (strcmp(upperFname,"CON") == 0) ||
+ (strcmp(upperFname,"AUX") == 0) ||
+ (strcmp(upperFname,"COM1") == 0) ||
+ (strcmp(upperFname,"COM2") == 0) ||
+ (strcmp(upperFname,"COM3") == 0) ||
+ (strcmp(upperFname,"COM4") == 0) ||
+ (strcmp(upperFname,"LPT1") == 0) ||
+ (strcmp(upperFname,"LPT2") == 0) ||
+ (strcmp(upperFname,"LPT3") == 0) ||
+ (strcmp(upperFname,"NUL") == 0) ||
+ (strcmp(upperFname,"PRN") == 0))
+ return (True) ;
+
+ return (False);
+}
+
+
+
+/****************************************************************************
+return True if a name is in 8.3 dos format
+****************************************************************************/
+BOOL is_8_3(char *fname)
+{
+ int len;
+ char *dot_pos;
+ char *slash_pos = strrchr(fname,'/');
+ int l;
+
+ if (slash_pos) fname = slash_pos+1;
+ len = strlen(fname);
+
+ dot_pos = strchr(fname,'.');
+
+ DEBUG(5,("checking %s for 8.3\n",fname));
+
+ if (case_mangle)
+ switch (case_default)
+ {
+ case CASE_LOWER:
+ if (strhasupper(fname)) return(False);
+ break;
+ case CASE_UPPER:
+ if (strhaslower(fname)) return(False);
+ break;
+ }
+
+ /* can't be longer than 12 chars */
+ if (len == 0 || len > 12)
+ return(False);
+
+ /* can't be an MS-DOS Special file such as lpt1 or even lpt1.txt */
+ if (is_reserved_msdos(fname))
+ return(False);
+
+ /* can't contain invalid dos chars */
+ /* Windows use the ANSI charset.
+ But filenames are translated in the PC charset.
+ This Translation may be more or less relaxed depending
+ the Windows application. */
+
+ /* %%% A nice improvment to name mangling would be to translate
+ filename to ANSI charset on the smb server host */
+
+ {
+ char *p = fname;
+#ifdef KANJI
+ dot_pos = 0;
+ while (*p)
+ {
+ if (is_shift_jis (*p)) {
+ p += 2;
+ } else if (is_kana (*p)) {
+ p ++;
+ } else {
+ if (*p == '.' && !dot_pos)
+ dot_pos = (char *) p;
+ if (!isdoschar(*p))
+ return(False);
+ p++;
+ }
+ }
+#else
+ while (*p)
+ {
+ if (!isdoschar(*p))
+ return(False);
+ p++;
+ }
+#endif /* KANJI */
+ }
+
+ /* no dot and less than 9 means OK */
+ if (!dot_pos)
+ return(len <= 8);
+
+ l = PTR_DIFF(dot_pos,fname);
+
+ /* base must be at least 1 char except special cases . and .. */
+ if (l == 0)
+ return(strcmp(fname,".") == 0 || strcmp(fname,"..") == 0);
+
+ /* base can't be greater than 8 */
+ if (l > 8)
+ return(False);
+
+ if (lp_strip_dot() &&
+ len - l == 1 &&
+ !strchr(dot_pos+1,'.'))
+ {
+ *dot_pos = 0;
+ return(True);
+ }
+
+ /* extension must be between 1 and 3 */
+ if ( (len - l < 2 ) || (len - l > 4) )
+ return(False);
+
+ /* extension can't have a dot */
+ if (strchr(dot_pos+1,'.'))
+ return(False);
+
+ /* must be in 8.3 format */
+ return(True);
+}
+
+
+
+/*
+keep a stack of name mangling results - just
+so file moves and copies have a chance of working
+*/
+fstring *mangled_stack = NULL;
+int mangled_stack_size = 0;
+int mangled_stack_len = 0;
+
+/****************************************************************************
+create the mangled stack
+****************************************************************************/
+void create_mangled_stack(int size)
+{
+ if (mangled_stack)
+ {
+ free(mangled_stack);
+ mangled_stack_size = 0;
+ mangled_stack_len = 0;
+ }
+ if (size > 0)
+ mangled_stack = (fstring *)malloc(sizeof(fstring)*size);
+ if (mangled_stack) mangled_stack_size = size;
+}
+
+/****************************************************************************
+push a mangled name onto the stack
+****************************************************************************/
+static void push_mangled_name(char *s)
+{
+ int i;
+ char *p;
+
+ if (!mangled_stack)
+ return;
+
+ for (i=0;i<mangled_stack_len;i++)
+ if (strcmp(s,mangled_stack[i]) == 0)
+ {
+ array_promote(mangled_stack[0],sizeof(fstring),i);
+ return;
+ }
+
+ memmove(mangled_stack[1],mangled_stack[0],
+ sizeof(fstring)*MIN(mangled_stack_len,mangled_stack_size-1));
+ strcpy(mangled_stack[0],s);
+ p = strrchr(mangled_stack[0],'.');
+ if (p && (!strhasupper(p+1)) && (strlen(p+1) < 4))
+ *p = 0;
+ mangled_stack_len = MIN(mangled_stack_size,mangled_stack_len+1);
+}
+
+/****************************************************************************
+check for a name on the mangled name stack
+****************************************************************************/
+BOOL check_mangled_stack(char *s)
+{
+ int i;
+ pstring tmpname;
+ char extension[5];
+ char *p = strrchr(s,'.');
+ BOOL check_extension = False;
+
+ extension[0] = 0;
+
+ if (!mangled_stack) return(False);
+
+ if (p)
+ {
+ check_extension = True;
+ StrnCpy(extension,p,4);
+ strlower(extension); /* XXXXXXX */
+ }
+
+ for (i=0;i<mangled_stack_len;i++)
+ {
+ strcpy(tmpname,mangled_stack[i]);
+ mangle_name_83(tmpname);
+ if (strequal(tmpname,s))
+ {
+ strcpy(s,mangled_stack[i]);
+ break;
+ }
+ if (check_extension && !strchr(mangled_stack[i],'.'))
+ {
+ strcpy(tmpname,mangled_stack[i]);
+ strcat(tmpname,extension);
+ mangle_name_83(tmpname);
+ if (strequal(tmpname,s))
+ {
+ strcpy(s,mangled_stack[i]);
+ strcat(s,extension);
+ break;
+ }
+ }
+ }
+
+ if (i < mangled_stack_len)
+ {
+ DEBUG(3,("Found %s on mangled stack as %s\n",s,mangled_stack[i]));
+ array_promote(mangled_stack[0],sizeof(fstring),i);
+ return(True);
+ }
+
+ return(False);
+}
+
+static char *map_filename(char *s, /* This is null terminated */
+ char *pattern, /* This isn't. */
+ int len) /* This is the length of pattern. */
+{
+ static pstring matching_bit; /* The bit of the string which matches */
+ /* a * in pattern if indeed there is a * */
+ char *sp; /* Pointer into s. */
+ char *pp; /* Pointer into p. */
+ char *match_start; /* Where the matching bit starts. */
+ pstring pat;
+
+ StrnCpy(pat, pattern, len); /* Get pattern into a proper string! */
+ strcpy(matching_bit,""); /* Match but no star gets this. */
+ pp = pat; /* Initialise the pointers. */
+ sp = s;
+ if ((len == 1) && (*pattern == '*')) {
+ return NULL; /* Impossible, too ambiguous for */
+ /* words! */
+ }
+
+ while ((*sp) /* Not the end of the string. */
+ && (*pp) /* Not the end of the pattern. */
+ && (*sp == *pp) /* The two match. */
+ && (*pp != '*')) { /* No wildcard. */
+ sp++; /* Keep looking. */
+ pp++;
+ }
+ if (!*sp && !*pp) /* End of pattern. */
+ return matching_bit; /* Simple match. Return empty string. */
+ if (*pp == '*') {
+ pp++; /* Always interrested in the chacter */
+ /* after the '*' */
+ if (!*pp) { /* It is at the end of the pattern. */
+ StrnCpy(matching_bit, s, sp-s);
+ return matching_bit;
+ } else {
+ /* The next character in pattern must match a character further */
+ /* along s than sp so look for that character. */
+ match_start = sp;
+ while ((*sp) /* Not the end of s. */
+ && (*sp != *pp)) /* Not the same */
+ sp++; /* Keep looking. */
+ if (!*sp) { /* Got to the end without a match. */
+ return NULL;
+ } else { /* Still hope for a match. */
+ /* Now sp should point to a matching character. */
+ StrnCpy(matching_bit, match_start, sp-match_start);
+ /* Back to needing a stright match again. */
+ while ((*sp) /* Not the end of the string. */
+ && (*pp) /* Not the end of the pattern. */
+ && (*sp == *pp)) { /* The two match. */
+ sp++; /* Keep looking. */
+ pp++;
+ }
+ if (!*sp && !*pp) /* Both at end so it matched */
+ return matching_bit;
+ else
+ return NULL;
+ }
+ }
+ }
+ return NULL; /* No match. */
+}
+
+
+/* this is the magic char used for mangling */
+char magic_char = '~';
+
+
+/****************************************************************************
+determine whther is name could be a mangled name
+****************************************************************************/
+BOOL is_mangled(char *s)
+{
+ char *m = strchr(s,magic_char);
+ if (!m) return(False);
+
+ /* we use two base 36 chars efore the extension */
+ if (m[1] == '.' || m[1] == 0 ||
+ m[2] == '.' || m[2] == 0 ||
+ (m[3] != '.' && m[3] != 0))
+ return(is_mangled(m+1));
+
+ /* it could be */
+ return(True);
+}
+
+
+
+/****************************************************************************
+return a base 36 character. v must be from 0 to 35.
+****************************************************************************/
+static char base36(int v)
+{
+ v = v % 36;
+ if (v < 10)
+ return('0'+v);
+ else /* needed to work around a DEC C compiler bug */
+ return('A' + (v-10));
+}
+
+
+static void do_fwd_mangled_map(char *s, char *MangledMap)
+{
+ /* MangledMap is a series of name pairs in () separated by spaces.
+ * If s matches the first of the pair then the name given is the
+ * second of the pair. A * means any number of any character and if
+ * present in the second of the pair as well as the first the
+ * matching part of the first string takes the place of the * in the
+ * second.
+ *
+ * I wanted this so that we could have RCS files which can be used
+ * by UNIX and DOS programs. My mapping string is (RCS rcs) which
+ * converts the UNIX RCS file subdirectory to lowercase thus
+ * preventing mangling.
+ */
+ char *start=MangledMap; /* Use this to search for mappings. */
+ char *end; /* Used to find the end of strings. */
+ char *match_string;
+ pstring new_string; /* Make up the result here. */
+ char *np; /* Points into new_string. */
+
+ DEBUG(5,("Mangled Mapping '%s' map '%s'\n", s, MangledMap));
+ while (*start) {
+ while ((*start) && (*start != '('))
+ start++;
+ start++; /* Skip the ( */
+ if (!*start)
+ continue; /* Always check for the end. */
+ end = start; /* Search for the ' ' or a ')' */
+ DEBUG(5,("Start of first in pair '%s'\n", start));
+ while ((*end) && !((*end == ' ') || (*end == ')')))
+ end++;
+ if (!*end) {
+ start = end;
+ continue; /* Always check for the end. */
+ }
+ DEBUG(5,("End of first in pair '%s'\n", end));
+ if ((match_string = map_filename(s, start, end-start))) {
+ DEBUG(5,("Found a match\n"));
+ /* Found a match. */
+ start = end+1; /* Point to start of what it is to become. */
+ DEBUG(5,("Start of second in pair '%s'\n", start));
+ end = start;
+ np = new_string;
+ while ((*end) /* Not the end of string. */
+ && (*end != ')') /* Not the end of the pattern. */
+ && (*end != '*')) /* Not a wildcard. */
+ *np++ = *end++;
+ if (!*end) {
+ start = end;
+ continue; /* Always check for the end. */
+ }
+ if (*end == '*') {
+ strcpy(np, match_string);
+ np += strlen(match_string);
+ end++; /* Skip the '*' */
+ while ((*end) /* Not the end of string. */
+ && (*end != ')') /* Not the end of the pattern. */
+ && (*end != '*')) /* Not a wildcard. */
+ *np++ = *end++;
+ }
+ if (!*end) {
+ start = end;
+ continue; /* Always check for the end. */
+ }
+ *np++ = '\0'; /* NULL terminate it. */
+ DEBUG(5,("End of second in pair '%s'\n", end));
+ strcpy(s, new_string); /* Substitute with the new name. */
+ DEBUG(5,("s is now '%s'\n", s));
+ }
+ start = end; /* Skip a bit which cannot be wanted */
+ /* anymore. */
+ start++;
+ }
+}
+
+/****************************************************************************
+do the actual mangling to 8.3 format
+****************************************************************************/
+void mangle_name_83(char *s)
+{
+ int csum = str_checksum(s);
+ char *p;
+ char extension[4];
+ char base[9];
+ int baselen = 0;
+ int extlen = 0;
+
+ extension[0]=0;
+ base[0]=0;
+
+ p = strrchr(s,'.');
+ if (p && (strlen(p+1)<4) )
+ {
+ BOOL all_normal = (strisnormal(p+1)); /* XXXXXXXXX */
+ if (all_normal && p[1] != 0)
+ {
+ *p = 0;
+ csum = str_checksum(s);
+ *p = '.';
+ }
+ }
+
+
+ strupper(s);
+
+ DEBUG(5,("Mangling name %s to ",s));
+
+ if (p)
+ {
+ if (p == s)
+ strcpy(extension,"___");
+ else
+ {
+ *p++ = 0;
+ while (*p && extlen < 3)
+ {
+ if (isdoschar(*p) && *p != '.')
+ extension[extlen++] = *p;
+ p++;
+ }
+ extension[extlen] = 0;
+ }
+ }
+
+ p = s;
+
+ while (*p && baselen < 5)
+ {
+ if (isdoschar(*p) && *p != '.')
+ base[baselen++] = *p;
+ p++;
+ }
+ base[baselen] = 0;
+
+ csum = csum % (36*36);
+
+ sprintf(s,"%s%c%c%c",base,magic_char,base36(csum/36),base36(csum%36));
+
+ if (*extension)
+ {
+ strcat(s,".");
+ strcat(s,extension);
+ }
+ DEBUG(5,("%s\n",s));
+}
+
+
+
+/*******************************************************************
+ work out if a name is illegal, even for long names
+ ******************************************************************/
+static BOOL illegal_name(char *name)
+{
+ static unsigned char illegal[256];
+ static BOOL initialised=False;
+ unsigned char *s;
+
+ if (!initialised) {
+ char *ill = "*\\/?<>|\":{}";
+ initialised = True;
+
+ bzero((char *)illegal,256);
+ for (s = (unsigned char *)ill; *s; s++)
+ illegal[*s] = True;
+ }
+
+#ifdef KANJI
+ for (s = (unsigned char *)name; *s;) {
+ if (is_shift_jis (*s)) {
+ s += 2;
+ } else if (illegal[*s]) {
+ return(True);
+ } else {
+ s++;
+ }
+ }
+#else
+ for (s = (unsigned char *)name;*s;s++)
+ if (illegal[*s]) return(True);
+#endif
+
+
+ return(False);
+}
+
+
+/****************************************************************************
+convert a filename to DOS format. return True if successful.
+****************************************************************************/
+BOOL name_map_mangle(char *OutName,BOOL need83,int snum)
+{
+#ifdef MANGLE_LONG_FILENAMES
+ if (!need83 && illegal_name(OutName)) need83 = True;
+#endif
+
+ /* apply any name mappings */
+ {
+ char *map = lp_mangled_map(snum);
+ if (map && *map)
+ do_fwd_mangled_map(OutName,map);
+ }
+
+ /* check if it's already in 8.3 format */
+ if (need83 && !is_8_3(OutName)) {
+ if (!lp_manglednames(snum)) return(False);
+
+ /* mangle it into 8.3 */
+ push_mangled_name(OutName);
+ mangle_name_83(OutName);
+ }
+
+ return(True);
+}
+
diff --git a/source/smbd/message.c b/source/smbd/message.c
new file mode 100644
index 00000000000..6a96b4c7a9c
--- /dev/null
+++ b/source/smbd/message.c
@@ -0,0 +1,204 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ SMB messaging
+ Copyright (C) Andrew Tridgell 1992-1995
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+/*
+ This file handles the messaging system calls for winpopup style
+ messages
+*/
+
+
+#include "includes.h"
+#include "loadparm.h"
+
+/* look in server.c for some explanation of these variables */
+extern int DEBUGLEVEL;
+
+
+static char msgbuf[1600];
+static int msgpos=0;
+static fstring msgfrom="";
+static fstring msgto="";
+
+/****************************************************************************
+deliver the message
+****************************************************************************/
+static void msg_deliver(void)
+{
+ pstring s;
+ fstring name;
+ FILE *f;
+ int i;
+
+ if (! (*lp_msg_command()))
+ {
+ DEBUG(1,("no messaging command specified\n"));
+ msgpos = 0;
+ return;
+ }
+
+ /* put it in a temporary file */
+ sprintf(s,"/tmp/msg.XXXXXX");
+ strcpy(name,(char *)mktemp(s));
+
+ f = fopen(name,"w");
+ if (!f)
+ {
+ DEBUG(1,("can't open message file %s\n",name));
+ return;
+ }
+
+ for (i=0;i<msgpos;)
+ {
+ if (msgbuf[i]=='\r' && i<(msgpos-1) && msgbuf[i+1]=='\n')
+ i++;
+ fputc(msgbuf[i++],f);
+ }
+
+ fclose(f);
+
+
+ /* run the command */
+ if (*lp_msg_command())
+ {
+ strcpy(s,lp_msg_command());
+ string_sub(s,"%s",name);
+ string_sub(s,"%f",msgfrom);
+ string_sub(s,"%t",msgto);
+ standard_sub(-1,s);
+ smbrun(s,NULL);
+ }
+
+ msgpos = 0;
+}
+
+
+
+/****************************************************************************
+ reply to a sends
+****************************************************************************/
+int reply_sends(char *inbuf,char *outbuf)
+{
+ int len;
+ char *orig,*dest,*msg;
+ int outsize = 0;
+
+ msgpos = 0;
+
+
+ if (! (*lp_msg_command()))
+ return(ERROR(ERRSRV,ERRmsgoff));
+
+ outsize = set_message(outbuf,0,0,True);
+
+ orig = smb_buf(inbuf)+1;
+ dest = skip_string(orig,1)+1;
+ msg = skip_string(dest,1)+1;
+
+ strcpy(msgfrom,orig);
+ strcpy(msgto,dest);
+
+ len = SVAL(msg,0);
+ len = MIN(len,1600-msgpos);
+
+ memcpy(&msgbuf[msgpos],msg+2,len);
+ msgpos += len;
+
+ DEBUG(3,("%s SMBsends (from %s to %s)\n",timestring(),orig,dest));
+
+ msg_deliver();
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a sendstrt
+****************************************************************************/
+int reply_sendstrt(char *inbuf,char *outbuf)
+{
+ char *orig,*dest;
+ int outsize = 0;
+
+ if (! (*lp_msg_command()))
+ return(ERROR(ERRSRV,ERRmsgoff));
+
+ outsize = set_message(outbuf,1,0,True);
+
+ msgpos = 0;
+
+ orig = smb_buf(inbuf)+1;
+ dest = skip_string(orig,1)+1;
+
+ strcpy(msgfrom,orig);
+ strcpy(msgto,dest);
+
+ DEBUG(3,("%s SMBsendstrt (from %s to %s)\n",timestring(),orig,dest));
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a sendtxt
+****************************************************************************/
+int reply_sendtxt(char *inbuf,char *outbuf)
+{
+ int len;
+ int outsize = 0;
+ char *msg;
+
+ if (! (*lp_msg_command()))
+ return(ERROR(ERRSRV,ERRmsgoff));
+
+ outsize = set_message(outbuf,0,0,True);
+
+ msg = smb_buf(inbuf) + 1;
+
+ len = SVAL(msg,0);
+ len = MIN(len,1600-msgpos);
+
+ memcpy(&msgbuf[msgpos],msg+2,len);
+ msgpos += len;
+
+ DEBUG(3,("%s SMBsendtxt\n",timestring()));
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a sendend
+****************************************************************************/
+int reply_sendend(char *inbuf,char *outbuf)
+{
+ int outsize = 0;
+
+ if (! (*lp_msg_command()))
+ return(ERROR(ERRSRV,ERRmsgoff));
+
+ outsize = set_message(outbuf,0,0,True);
+
+ DEBUG(3,("%s SMBsendend\n",timestring()));
+
+ msg_deliver();
+
+ return(outsize);
+}
+
diff --git a/source/smbd/password.c b/source/smbd/password.c
new file mode 100644
index 00000000000..87c1fef94c5
--- /dev/null
+++ b/source/smbd/password.c
@@ -0,0 +1,1416 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Password and authentication handling
+ Copyright (C) Andrew Tridgell 1992-1995
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "loadparm.h"
+
+extern int DEBUGLEVEL;
+extern int Protocol;
+
+/* users from session setup */
+static pstring session_users="";
+
+/* these are kept here to keep the string_combinations function simple */
+static char this_user[100]="";
+static char this_salt[100]="";
+static char this_crypted[100]="";
+
+#ifdef SMB_PASSWD
+/* Data to do lanman1/2 password challenge. */
+static unsigned char saved_challenge[8];
+static BOOL challenge_sent=False;
+
+/*******************************************************************
+Get the next challenge value - no repeats.
+********************************************************************/
+void generate_next_challenge(char *challenge)
+{
+ extern void E1(char *,char *,char *);
+ static int counter = 0;
+ struct timeval tval;
+ int v1,v2;
+ GetTimeOfDay(&tval);
+ v1 = (counter++) + getpid() + tval.tv_sec;
+ v2 = (counter++) * getpid() + tval.tv_usec;
+ SIVAL(challenge,0,v1);
+ SIVAL(challenge,4,v2);
+ E1(challenge,"SAMBA",saved_challenge);
+ memcpy(challenge,saved_challenge,8);
+ challenge_sent = True;
+}
+
+/*******************************************************************
+set the last challenge sent, usually from a password server
+********************************************************************/
+BOOL set_challenge(char *challenge)
+{
+ memcpy(saved_challenge,challenge,8);
+ challenge_sent = True;
+ return(True);
+}
+
+/*******************************************************************
+get the last challenge sent
+********************************************************************/
+BOOL last_challenge(char *challenge)
+{
+ if (!challenge_sent) return(False);
+ memcpy(challenge,saved_challenge,8);
+ return(True);
+}
+#endif
+
+/* this holds info on user ids that are already validated for this VC */
+static user_struct *validated_users = NULL;
+static int num_validated_users = 0;
+
+/****************************************************************************
+check if a uid has been validated, and return an index if it has. -1 if not
+****************************************************************************/
+int valid_uid(int uid)
+{
+ int i;
+ if (uid == -1) return(-1);
+
+ for (i=0;i<num_validated_users;i++)
+ if (validated_users[i].uid == uid)
+ {
+ DEBUG(3,("valid uid %d mapped to vuid %d (user=%s)\n",
+ uid,i,validated_users[i].name));
+ return(i);
+ }
+ return(-1);
+}
+
+/****************************************************************************
+check if a uid has been validated, and return an pointer to the user_struct
+if it has. NULL if not
+****************************************************************************/
+user_struct *get_valid_user_struct(int uid)
+{
+ int vuid = valid_uid(uid);
+ if(vuid == -1 || validated_users[vuid].guest)
+ return NULL;
+ return &validated_users[vuid];
+}
+
+/****************************************************************************
+invalidate a uid
+****************************************************************************/
+void invalidate_uid(int uid)
+{
+ int i;
+ for (i=0;i<num_validated_users;i++)
+ if (validated_users[i].uid == uid)
+ {
+ user_struct *vuser = &validated_users[i];
+ vuser->uid = -1;
+ vuser->gid = -1;
+ vuser->user_ngroups = 0;
+ if(vuser->user_groups &&
+ (vuser->user_groups != (gid_t *)vuser->user_igroups))
+ free(vuser->user_groups);
+ vuser->user_groups = NULL;
+ if(vuser->user_igroups)
+ free(vuser->user_igroups);
+ vuser->user_igroups = NULL;
+ }
+}
+
+
+/****************************************************************************
+return a validated username
+****************************************************************************/
+char *validated_username(int vuid)
+{
+ return(validated_users[vuid].name);
+}
+
+/****************************************************************************
+register a uid/name pair as being valid and that a valid password
+has been given.
+****************************************************************************/
+void register_uid(int uid,int gid, char *name,BOOL guest)
+{
+ user_struct *vuser;
+
+ if (valid_uid(uid) >= 0)
+ return;
+ validated_users = (user_struct *)Realloc(validated_users,
+ sizeof(user_struct)*
+ (num_validated_users+1));
+
+ if (!validated_users)
+ {
+ DEBUG(0,("Failed to realloc users struct!\n"));
+ return;
+ }
+
+ vuser = &validated_users[num_validated_users];
+ vuser->uid = uid;
+ vuser->gid = gid;
+ vuser->guest = guest;
+ strcpy(vuser->name,name);
+
+ vuser->user_ngroups = 0;
+ vuser->user_groups = NULL;
+ vuser->user_igroups = NULL;
+
+ /* Find all the groups this uid is in and store them.
+ Used by become_user() */
+ setup_groups(name,uid,gid,
+ &vuser->user_ngroups,
+ &vuser->user_igroups,
+ &vuser->user_groups);
+
+ DEBUG(3,("uid %d registered to name %s\n",uid,name));
+
+ num_validated_users++;
+}
+
+
+/****************************************************************************
+add a name to the session users list
+****************************************************************************/
+void add_session_user(char *user)
+{
+ fstring suser;
+ StrnCpy(suser,user,sizeof(suser)-1);
+
+ if (!Get_Pwnam(suser,True)) return;
+
+ if (suser && *suser && !in_list(suser,session_users,False))
+ {
+ if (strlen(suser) + strlen(session_users) + 2 >= sizeof(pstring))
+ DEBUG(1,("Too many session users??\n"));
+ else
+ {
+ strcat(session_users," ");
+ strcat(session_users,suser);
+ }
+ }
+}
+
+
+#ifdef NO_GETSPNAM
+/* a fake shadow password routine which just fills a fake spwd struct
+ * with the sp_pwdp field. (sreiz@aie.nl)
+ */
+static struct spwd *getspnam(char *username) /* fake shadow password routine */
+{
+ FILE *f;
+ char line[1024];
+ static char pw[20];
+ static struct spwd static_spwd;
+
+ static_spwd.sp_pwdp=0;
+ if (!(f=fopen("/etc/master.passwd", "r")))
+ return 0;
+ while (fgets(line, 1024, f)) {
+ if (!strncmp(line, username, strlen(username)) &&
+ line[strlen(username)]==':') { /* found entry */
+ char *p, *q;
+
+ p=line+strlen(username)+1;
+ if ((q=strchr(p, ':'))) {
+ *q=0;
+ if (q-p+1>20)
+ break;
+ strcpy(pw, p);
+ static_spwd.sp_pwdp=pw;
+ }
+ break;
+ }
+ }
+ fclose(f);
+ if (static_spwd.sp_pwdp)
+ return &static_spwd;
+ return 0;
+}
+#endif
+
+
+#ifdef OSF1_ENH_SEC
+/****************************************************************************
+an enhanced crypt for OSF1
+****************************************************************************/
+static char *osf1_bigcrypt(char *password,char *salt1)
+{
+ static char result[AUTH_MAX_PASSWD_LENGTH] = "";
+ char *p1;
+ char *p2=password;
+ char salt[3];
+ int i;
+ int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
+ if (strlen(password)%AUTH_CLEARTEXT_SEG_CHARS)
+ parts++;
+
+ StrnCpy(salt,salt1,2);
+ StrnCpy(result,salt1,2);
+
+ for (i=0; i<parts;i++)
+ {
+ p1 = crypt(p2,salt);
+ strcat(result,p1+2);
+ StrnCpy(salt,&result[2+i*AUTH_CIPHERTEXT_SEG_CHARS],2);
+ p2 += AUTH_CLEARTEXT_SEG_CHARS;
+ }
+
+ return(result);
+}
+#endif
+
+
+/****************************************************************************
+update the enhanced security database. Only relevant for OSF1 at the moment.
+****************************************************************************/
+static void update_protected_database( char *user, BOOL result)
+{
+#ifdef OSF1_ENH_SEC
+ struct pr_passwd *mypasswd;
+ time_t starttime;
+ long tz;
+
+ mypasswd = getprpwnam (user);
+ starttime = time (NULL);
+ tz = mktime ( localtime ( &starttime ) );
+
+ if (result)
+ {
+ mypasswd->ufld.fd_slogin = tz;
+ mypasswd->ufld.fd_nlogins = 0;
+
+ putprpwnam(user,mypasswd);
+
+ DEBUG(3,("Update protected database for Account %s after succesful connection\n",user));
+ }
+ else
+ {
+ mypasswd->ufld.fd_ulogin = tz;
+ mypasswd->ufld.fd_nlogins = mypasswd->ufld.fd_nlogins + 1;
+ if ( mypasswd->ufld.fd_max_tries != 0 && mypasswd->ufld.fd_nlogins > mypasswd->ufld.fd_max_tries )
+ {
+ mypasswd->uflg.fg_lock = 0;
+ DEBUG(3,("Account is disabled -- see Account Administrator.\n"));
+ }
+ putprpwnam ( user , mypasswd );
+ DEBUG(3,("Update protected database for Account %s after refusing connection\n",user));
+ }
+#else
+ DEBUG(6,("Updated database with %s %s\n",user,BOOLSTR(result)));
+#endif
+}
+
+
+#ifdef AFS_AUTH
+/*******************************************************************
+check on AFS authentication
+********************************************************************/
+static BOOL afs_auth(char *this_user,char *password)
+{
+ long password_expires = 0;
+ char *reason;
+
+ /* For versions of AFS prior to 3.3, this routine has few arguments, */
+ /* but since I can't find the old documentation... :-) */
+ setpag();
+ if (ka_UserAuthenticateGeneral(KA_USERAUTH_VERSION+KA_USERAUTH_DOSETPAG,
+ this_user,
+ (char *) 0, /* instance */
+ (char *) 0, /* cell */
+ password,
+ 0, /* lifetime, default */
+ &password_expires, /*days 'til it expires */
+ 0, /* spare 2 */
+ &reason) == 0)
+ return(True);
+ return(False);
+}
+#endif
+
+
+#ifdef DFS_AUTH
+
+sec_login_handle_t my_dce_sec_context;
+int dcelogin_atmost_once = 0;
+
+/*******************************************************************
+check on a DCE/DFS authentication
+********************************************************************/
+static BOOL dfs_auth(char *this_user,char *password)
+{
+ error_status_t err;
+ int err2;
+ int prterr;
+ boolean32 password_reset;
+ sec_passwd_rec_t my_dce_password;
+ sec_login_auth_src_t auth_src = sec_login_auth_src_network;
+ unsigned char dce_errstr[dce_c_error_string_len];
+
+ /*
+ * We only go for a DCE login context if the given password
+ * matches that stored in the local password file..
+ * Assumes local passwd file is kept in sync w/ DCE RGY!
+ */
+
+ if (!strcmp((char *)crypt(password,this_salt),this_crypted) ||
+ dcelogin_atmost_once)
+ return(False);
+
+ if (sec_login_setup_identity(
+ (unsigned char *)this_user,
+ sec_login_no_flags,
+ &my_dce_sec_context,
+ &err) == 0)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0,("DCE Setup Identity for %s failed: %s\n",
+ this_user,dce_errstr));
+ return(False);
+ }
+
+ my_dce_password.version_number = sec_passwd_c_version_none;
+ my_dce_password.pepper = NULL;
+ my_dce_password.key.key_type = sec_passwd_plain;
+ my_dce_password.key.tagged_union.plain = (idl_char *)password;
+
+ if (sec_login_valid_and_cert_ident(my_dce_sec_context,
+ &my_dce_password,
+ &password_reset,
+ &auth_src,
+ &err) == 0 )
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0,("DCE Identity Validation failed for principal %s: %s\n",
+ this_user,dce_errstr));
+
+ return(False);
+ }
+
+ sec_login_set_context(my_dce_sec_context, &err);
+ if (err != error_status_ok )
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0,("DCE login failed for principal %s, cant set context: %s\n",
+ this_user,dce_errstr));
+ sec_login_purge_context(my_dce_sec_context, &err);
+ return(False);
+ }
+ else
+ {
+ DEBUG(0,("DCE login succeeded for principal %s on pid %d\n",
+ this_user, getpid()));
+ }
+
+ dcelogin_atmost_once = 1;
+ return (True);
+}
+
+void dfs_unlogin(void)
+{
+ error_status_t err;
+ int err2;
+ unsigned char dce_errstr[dce_c_error_string_len];
+
+ sec_login_purge_context(my_dce_sec_context, &err);
+ if (err != error_status_ok )
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0,("DCE purge login context failed for server instance %d: %s\n",
+ getpid(), dce_errstr));
+ }
+}
+
+#endif
+
+
+#ifdef LINUX_BIGCRYPT
+/****************************************************************************
+an enhanced crypt for Linux to handle password longer than 8 characters
+****************************************************************************/
+static int linux_bigcrypt(char *password,char *salt1, char *crypted)
+{
+#define LINUX_PASSWORD_SEG_CHARS 8
+ char salt[3];
+ int i;
+
+ StrnCpy(salt,salt1,2);
+ crypted +=2;
+
+ for ( i=strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) {
+ char * p = crypt(password,salt) + 2;
+ if(strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
+ return(0);
+ password += LINUX_PASSWORD_SEG_CHARS;
+ crypted += strlen(p);
+ }
+
+ return(1);
+}
+#endif
+
+
+/****************************************************************************
+apply a function to upper/lower case combinations
+of a string and return true if one of them returns true.
+try all combinations with N uppercase letters.
+offset is the first char to try and change (start with 0)
+it assumes the string starts lowercased
+****************************************************************************/
+static BOOL string_combinations2(char *s,int offset,BOOL (*fn)(),int N)
+{
+ int len = strlen(s);
+ int i;
+
+#ifdef PASSWORD_LENGTH
+ len = MIN(len,PASSWORD_LENGTH);
+#endif
+
+ if (N <= 0 || offset >= len)
+ return(fn(s));
+
+ for (i=offset;i<(len-(N-1));i++)
+ {
+ char c = s[i];
+ if (!islower(c)) continue;
+ s[i] = toupper(c);
+ if (string_combinations2(s,i+1,fn,N-1))
+ return(True);
+ s[i] = c;
+ }
+ return(False);
+}
+
+/****************************************************************************
+apply a function to upper/lower case combinations
+of a string and return true if one of them returns true.
+try all combinations with up to N uppercase letters.
+offset is the first char to try and change (start with 0)
+it assumes the string starts lowercased
+****************************************************************************/
+static BOOL string_combinations(char *s,BOOL (*fn)(),int N)
+{
+ int n;
+ for (n=1;n<=N;n++)
+ if (string_combinations2(s,0,fn,n)) return(True);
+ return(False);
+}
+
+
+
+/****************************************************************************
+core of password checking routine
+****************************************************************************/
+BOOL password_check(char *password)
+{
+#ifdef AFS_AUTH
+ if (afs_auth(this_user,password)) return(True);
+#endif
+
+#ifdef DFS_AUTH
+ if (dfs_auth(this_user,password)) return(True);
+#endif
+
+#ifdef PWDAUTH
+ if (pwdauth(this_user,password) == 0)
+ return(True);
+#endif
+
+#ifdef OSF1_ENH_SEC
+ return(strcmp(osf1_bigcrypt(password,this_salt),this_crypted) == 0);
+#endif
+
+#ifdef ULTRIX_AUTH
+ return (strcmp((char *)crypt16(password, this_salt ),this_crypted) == 0);
+#endif
+
+#ifdef LINUX_BIGCRYPT
+ return(linux_bigcrypt(password,this_salt,this_crypted));
+#endif
+
+#ifdef NO_CRYPT
+ DEBUG(1,("Warning - no crypt available\n"));
+ return(False);
+#else
+ return(strcmp((char *)crypt(password,this_salt),this_crypted) == 0);
+#endif
+}
+
+#ifdef SMB_PASSWD
+/****************************************************************************
+core of smb password checking routine.
+****************************************************************************/
+BOOL smb_password_check(char *password, unsigned char *part_passwd, unsigned char *c8)
+{
+ /* Finish the encryption of part_passwd. */
+ unsigned char p21[21];
+ unsigned char p24[24];
+
+ if(part_passwd == NULL)
+ DEBUG(10,("No password set - allowing access\n"));
+ /* No password set - always true ! */
+ if(part_passwd == NULL)
+ return 1;
+
+ memset(p21,'\0',21);
+ memcpy(p21,part_passwd,16);
+ E_P24(p21, c8, p24);
+#if DEBUG_PASSWORD
+ {
+ int i;
+ DEBUG(100,("Part password (P16) was |"));
+ for(i = 0; i < 16; i++)
+ DEBUG(100,("%X ", (unsigned char)part_passwd[i]));
+ DEBUG(100,("|\n"));
+ DEBUG(100,("Password from client was |"));
+ for(i = 0; i < 24; i++)
+ DEBUG(100,("%X ", (unsigned char)password[i]));
+ DEBUG(100,("|\n"));
+ DEBUG(100,("Given challenge was |"));
+ for(i = 0; i < 8; i++)
+ DEBUG(100,("%X ", (unsigned char)c8[i]));
+ DEBUG(100,("|\n"));
+ DEBUG(100,("Value from encryption was |"));
+ for(i = 0; i < 24; i++)
+ DEBUG(100,("%X ", (unsigned char)p24[i]));
+ DEBUG(100,("|\n"));
+ }
+#endif
+ return (memcmp(p24, password, 24) == 0);
+}
+#endif
+
+/****************************************************************************
+check if a username/password is OK
+****************************************************************************/
+BOOL password_ok(char *user,char *password, int pwlen, struct passwd *pwd, BOOL is_nt_password)
+{
+ pstring pass2;
+ int level = lp_passwordlevel();
+ struct passwd *pass;
+#ifdef SMB_PASSWD
+ char challenge[8];
+ struct smb_passwd *smb_pass;
+ BOOL challenge_done = False;
+#endif
+
+ if (password) password[pwlen] = 0;
+
+#ifdef SMB_PASSWD
+ if (pwlen == 24)
+ challenge_done = last_challenge(challenge);
+#endif
+
+#if DEBUG_PASSWORD
+#ifdef SMB_PASSWD
+ if (challenge_done)
+ {
+ int i;
+ DEBUG(100,("checking user=[%s] pass=[",user));
+ for( i = 0; i < 24; i++)
+ DEBUG(100,("%0x ", (unsigned char)password[i]));
+ DEBUG(100,("]\n"));
+ }
+ else
+#endif
+ DEBUG(100,("checking user=[%s] pass=[%s]\n",user,password));
+#endif
+
+ if (!password)
+ return(False);
+
+ if (((!*password) || (!pwlen)) && !lp_null_passwords())
+ return(False);
+
+ if (pwd && !user)
+ {
+ pass = (struct passwd *) pwd;
+ user = pass->pw_name;
+ }
+ else
+ pass = Get_Pwnam(user,True);
+
+#ifdef SMB_PASSWD
+
+ DEBUG(4,("SMB Password - pwlen = %d, challenge_done = %d\n", pwlen, challenge_done));
+
+ if((pwlen == 24) && challenge_done)
+ {
+ DEBUG(4,("Checking SMB password for user %s (l=24)\n",user));
+
+ if (!pass)
+ {
+ DEBUG(3,("Couldn't find user %s\n",user));
+ return(False);
+ }
+
+ smb_pass = get_smbpwnam(user);
+ if(!smb_pass)
+ {
+ DEBUG(3,("Couldn't find user %s in smb_passwd file.\n", user));
+ return(False);
+ }
+
+ /* Ensure the uid's match */
+ if(smb_pass->smb_userid != pass->pw_uid)
+ {
+ DEBUG(3,("Error : UNIX and SMB uids in password files do not match !\n"));
+ return(False);
+ }
+
+ if(Protocol >= PROTOCOL_NT1 && is_nt_password)
+ {
+ /* We have the NT MD4 hash challenge available - see if we can
+ use it (ie. does it exist in the smbpasswd file).
+ */
+ if(smb_pass->smb_nt_passwd != NULL)
+ {
+ DEBUG(4,("Checking NT MD4 password\n"));
+ if(smb_password_check(password, smb_pass->smb_nt_passwd, challenge))
+ {
+ update_protected_database(user,True);
+ return(True);
+ }
+ DEBUG(4,("NT MD4 password check failed\n"));
+ return (False);
+ }
+ }
+
+ /* Try against the lanman password */
+
+ if(smb_password_check(password, smb_pass->smb_passwd, challenge))
+ {
+ update_protected_database(user,True);
+ return(True);
+ }
+
+ DEBUG(3,("Error smb_password_check failed\n"));
+ }
+#endif
+
+ DEBUG(4,("Checking password for user %s (l=%d)\n",user,pwlen));
+
+ if (!pass)
+ {
+ DEBUG(3,("Couldn't find user %s\n",user));
+ return(False);
+ }
+
+#ifdef SHADOW_PWD
+ {
+ struct spwd *spass;
+
+ /* many shadow systems require you to be root to get the password,
+ in most cases this should already be the case when this
+ function is called, except perhaps for IPC password changing
+ requests */
+
+ spass = getspnam(pass->pw_name);
+ if (spass && spass->sp_pwdp)
+ pass->pw_passwd = spass->sp_pwdp;
+ }
+#endif
+
+#ifdef SecureWare
+ {
+ struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
+ if (pr_pw && pr_pw->ufld.fd_encrypt)
+ pass->pw_passwd = pr_pw->ufld.fd_encrypt;
+ }
+#endif
+
+#ifdef HPUX_10_TRUSTED
+ {
+ struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
+ if (pr_pw && pr_pw->ufld.fd_encrypt)
+ pass->pw_passwd = pr_pw->ufld.fd_encrypt;
+ }
+#endif
+
+#ifdef OSF1_ENH_SEC
+ {
+ struct pr_passwd *mypasswd;
+ DEBUG(5,("Checking password for user %s in OSF1_ENH_SEC\n",user));
+ mypasswd = getprpwnam (user);
+ if ( mypasswd )
+ {
+ strcpy(pass->pw_name,mypasswd->ufld.fd_name);
+ strcpy(pass->pw_passwd,mypasswd->ufld.fd_encrypt);
+ }
+ else
+ {
+ DEBUG(5,("No entry for user %s in protected database !\n",user));
+ return(False);
+ }
+ }
+#endif
+
+#ifdef ULTRIX_AUTH
+ {
+ AUTHORIZATION *ap = getauthuid( pass->pw_uid );
+ if (ap)
+ {
+ strcpy( pass->pw_passwd, ap->a_password );
+ endauthent();
+ }
+ }
+#endif
+
+ /* extract relevant info */
+ strcpy(this_user,pass->pw_name);
+ strcpy(this_salt,pass->pw_passwd);
+ strcpy(this_crypted,pass->pw_passwd);
+
+ if (!*this_crypted) {
+ if (!lp_null_passwords()) {
+ DEBUG(2,("Disallowing access to %s due to null password\n",this_user));
+ return(False);
+ }
+#ifndef PWDAUTH
+ if (!*password) {
+ DEBUG(3,("Allowing access to %s with null password\n",this_user));
+ return(True);
+ }
+#endif
+ }
+
+ /* try it as it came to us */
+ if (password_check(password))
+ {
+ update_protected_database(user,True);
+ return(True);
+ }
+
+ /* if the password was given to us with mixed case then we don't
+ need to proceed as we know it hasn't been case modified by the
+ client */
+ if (strhasupper(password) && strhaslower(password))
+ return(False);
+
+ /* make a copy of it */
+ StrnCpy(pass2,password,sizeof(pstring)-1);
+
+ /* try all lowercase */
+ strlower(password);
+ if (password_check(password))
+ {
+ update_protected_database(user,True);
+ return(True);
+ }
+
+ /* give up? */
+ if(level < 1)
+ {
+ update_protected_database(user,False);
+
+ /* restore it */
+ strcpy(password,pass2);
+
+ return(False);
+ }
+
+ /* last chance - all combinations of up to level chars upper! */
+ strlower(password);
+
+ if (string_combinations(password,password_check,level))
+ {
+ update_protected_database(user,True);
+ return(True);
+ }
+
+ update_protected_database(user,False);
+
+ /* restore it */
+ strcpy(password,pass2);
+
+ return(False);
+}
+
+
+
+/****************************************************************************
+check if a username is valid
+****************************************************************************/
+BOOL user_ok(char *user,int snum)
+{
+ pstring valid, invalid;
+ BOOL ret;
+
+ StrnCpy(valid, lp_valid_users(snum), sizeof(pstring));
+ StrnCpy(invalid, lp_invalid_users(snum), sizeof(pstring));
+
+ string_sub(valid,"%S",lp_servicename(snum));
+ string_sub(invalid,"%S",lp_servicename(snum));
+
+ ret = !user_in_list(user,invalid);
+
+ if (ret && valid && *valid)
+ ret = user_in_list(user,valid);
+
+ if (ret && lp_onlyuser(snum)) {
+ char *user_list = lp_username(snum);
+ string_sub(user_list,"%S",lp_servicename(snum));
+ ret = user_in_list(user,user_list);
+ }
+
+ return(ret);
+}
+
+
+
+
+/****************************************************************************
+validate a group username entry. Return the username or NULL
+****************************************************************************/
+static char *validate_group(char *group,char *password,int pwlen,int snum)
+{
+#ifdef NETGROUP
+ {
+ char *host, *user, *domain;
+ setnetgrent(group);
+ while (getnetgrent(&host, &user, &domain)) {
+ if (user) {
+ if (user_ok(user, snum) &&
+ password_ok(user,password,pwlen,NULL,False)) {
+ endnetgrent();
+ return(user);
+ }
+ }
+ }
+ endnetgrent();
+ }
+#endif
+
+#if HAVE_GETGRNAM
+ {
+ struct group *gptr = (struct group *)getgrnam(group);
+ char **member;
+ if (gptr)
+ {
+ member = gptr->gr_mem;
+ while (member && *member)
+ {
+ static fstring name;
+ strcpy(name,*member);
+ if (user_ok(name,snum) &&
+ password_ok(name,password,pwlen,NULL,False))
+ return(&name[0]);
+ member++;
+ }
+#ifdef GROUP_CHECK_PWENT
+ {
+ struct passwd *pwd;
+ static fstring tm;
+
+ setpwent ();
+ while (pwd = getpwent ()) {
+ if (*(pwd->pw_passwd) && pwd->pw_gid == gptr->gr_gid) {
+ /* This Entry have PASSWORD and same GID then check pwd */
+ if (password_ok(NULL, password, pwlen, pwd,False)) {
+ strcpy(tm, pwd->pw_name);
+ endpwent ();
+ return tm;
+ }
+ }
+ }
+ endpwent ();
+ }
+#endif /* GROUP_CHECK_PWENT */
+ }
+ }
+#endif
+ return(NULL);
+}
+
+
+
+/****************************************************************************
+check for authority to login to a service with a given username/password
+****************************************************************************/
+BOOL authorise_login(int snum,char *user,char *password, int pwlen,
+ BOOL *guest,BOOL *force,int vuid)
+{
+ BOOL ok = False;
+
+ *guest = False;
+
+#if DEBUG_PASSWORD
+ DEBUG(100,("checking authorisation on user=%s pass=%s\n",user,password));
+#endif
+
+ /* there are several possabilities:
+ 1) login as the given user with given password
+ 2) login as a previously registered username with the given password
+ 3) login as a session list username with the given password
+ 4) login as a previously validated user/password pair
+ 5) login as the "user =" user with given password
+ 6) login as the "user =" user with no password (guest connection)
+ 7) login as guest user with no password
+
+ if the service is guest_only then steps 1 to 5 are skipped
+ */
+
+ if (GUEST_ONLY(snum)) *force = True;
+
+ if (!(GUEST_ONLY(snum) && GUEST_OK(snum)))
+ {
+
+ /* check the given username and password */
+ if (!ok && (*user) && user_ok(user,snum)) {
+ ok = password_ok(user,password, pwlen, NULL, False);
+ if (ok) DEBUG(3,("ACCEPTED: given username password ok\n"));
+ }
+
+ /* check for a previously registered guest username */
+ if (!ok && (vuid >= 0) && validated_users[vuid].guest) {
+ if (user_ok(validated_users[vuid].name,snum) &&
+ password_ok(validated_users[vuid].name, password, pwlen, NULL, False)) {
+ strcpy(user, validated_users[vuid].name);
+ validated_users[vuid].guest = False;
+ DEBUG(3,("ACCEPTED: given password with registered user %s\n", user));
+ ok = True;
+ }
+ }
+
+
+ /* now check the list of session users */
+ if (!ok)
+ {
+ char *auser;
+ char *user_list = strdup(session_users);
+ if (!user_list) return(False);
+
+ for (auser=strtok(user_list,LIST_SEP);
+ !ok && auser;
+ auser = strtok(NULL,LIST_SEP))
+ {
+ fstring user2;
+ strcpy(user2,auser);
+ if (!user_ok(user2,snum)) continue;
+
+ if (password_ok(user2,password, pwlen, NULL, False)) {
+ ok = True;
+ strcpy(user,user2);
+ DEBUG(3,("ACCEPTED: session list username and given password ok\n"));
+ }
+ }
+ free(user_list);
+ }
+
+ /* check for a previously validated username/password pair */
+ if (!ok && !lp_revalidate(snum) &&
+ (vuid >= 0) && !validated_users[vuid].guest &&
+ user_ok(validated_users[vuid].name,snum)) {
+ strcpy(user,validated_users[vuid].name);
+ *guest = False;
+ DEBUG(3,("ACCEPTED: validated uid ok as non-guest\n"));
+ ok = True;
+ }
+
+ /* check for a rhosts entry */
+ if (!ok && user_ok(user,snum) && check_hosts_equiv(user)) {
+ ok = True;
+ DEBUG(3,("ACCEPTED: hosts equiv or rhosts entry\n"));
+ }
+
+ /* check the user= fields and the given password */
+ if (!ok && lp_username(snum)) {
+ char *auser;
+ pstring user_list;
+ StrnCpy(user_list,lp_username(snum),sizeof(pstring));
+
+ string_sub(user_list,"%S",lp_servicename(snum));
+
+ for (auser=strtok(user_list,LIST_SEP);
+ auser && !ok;
+ auser = strtok(NULL,LIST_SEP))
+ {
+ if (*auser == '@')
+ {
+ auser = validate_group(auser+1,password,pwlen,snum);
+ if (auser)
+ {
+ ok = True;
+ strcpy(user,auser);
+ DEBUG(3,("ACCEPTED: group username and given password ok\n"));
+ }
+ }
+ else
+ {
+ fstring user2;
+ strcpy(user2,auser);
+ if (user_ok(user2,snum) &&
+ password_ok(user2,password,pwlen,NULL, False))
+ {
+ ok = True;
+ strcpy(user,user2);
+ DEBUG(3,("ACCEPTED: user list username and given password ok\n"));
+ }
+ }
+ }
+ }
+ } /* not guest only */
+
+ /* check for a normal guest connection */
+ if (!ok && GUEST_OK(snum))
+ {
+ fstring guestname;
+ StrnCpy(guestname,lp_guestaccount(snum),sizeof(guestname)-1);
+ if (Get_Pwnam(guestname,True))
+ {
+ strcpy(user,guestname);
+ ok = True;
+ DEBUG(3,("ACCEPTED: guest account and guest ok\n"));
+ }
+ else
+ DEBUG(0,("Invalid guest account %s??\n",guestname));
+ *guest = True;
+ *force = True;
+ }
+
+ if (ok && !user_ok(user,snum))
+ {
+ DEBUG(0,("rejected invalid user %s\n",user));
+ ok = False;
+ }
+
+ return(ok);
+}
+
+
+/****************************************************************************
+read the a hosts.equiv or .rhosts file and check if it
+allows this user from this machine
+****************************************************************************/
+static BOOL check_user_equiv(char *user, char *remote, char *equiv_file)
+{
+ pstring buf;
+ int plus_allowed = 1;
+ char *file_host;
+ char *file_user;
+ FILE *fp = fopen(equiv_file, "r");
+ DEBUG(5, ("check_user_equiv %s %s %s\n", user, remote, equiv_file));
+ if (! fp) return False;
+ while(fgets(buf, sizeof(buf), fp))
+ {
+ trim_string(buf," "," ");
+
+ if (buf[0] != '#' && buf[0] != '\n')
+ {
+ BOOL is_group = False;
+ int plus = 1;
+ char *bp = buf;
+ if (strcmp(buf, "NO_PLUS\n") == 0)
+ {
+ DEBUG(6, ("check_user_equiv NO_PLUS\n"));
+ plus_allowed = 0;
+ }
+ else {
+ if (buf[0] == '+')
+ {
+ bp++;
+ if (*bp == '\n' && plus_allowed)
+ {
+ /* a bare plus means everbody allowed */
+ DEBUG(6, ("check_user_equiv everybody allowed\n"));
+ fclose(fp);
+ return True;
+ }
+ }
+ else if (buf[0] == '-')
+ {
+ bp++;
+ plus = 0;
+ }
+ if (*bp == '@')
+ {
+ is_group = True;
+ bp++;
+ }
+ file_host = strtok(bp, " \t\n");
+ file_user = strtok(NULL, " \t\n");
+ DEBUG(7, ("check_user_equiv %s %s\n", file_host, file_user));
+ if (file_host && *file_host)
+ {
+ BOOL host_ok = False;
+
+#ifdef NETGROUP
+ /* THIS IS UNTESTED!! */
+ if (is_group)
+ {
+ static char *mydomain = NULL;
+ if (!mydomain)
+ yp_get_default_domain(&mydomain);
+ if (mydomain && innetgr(remote,file_host,user,mydomain))
+ host_ok = True;
+ }
+#else
+ if (is_group)
+ {
+ DEBUG(1,("Netgroups not configured - add -DNETGROUP and recompile\n"));
+ continue;
+ }
+#endif
+
+ /* is it this host */
+ /* the fact that remote has come from a call of gethostbyaddr
+ * means that it may have the fully qualified domain name
+ * so we could look up the file version to get it into
+ * a canonical form, but I would rather just type it
+ * in full in the equiv file
+ */
+ if (!host_ok && !is_group && strequal(remote, file_host))
+ host_ok = True;
+
+ if (!host_ok)
+ continue;
+
+ /* is it this user */
+ if (file_user == 0 || strequal(user, file_user))
+ {
+ fclose(fp);
+ DEBUG(5, ("check_user_equiv matched %s%s %s\n",
+ (plus ? "+" : "-"), file_host,
+ (file_user ? file_user : "")));
+ return (plus ? True : False);
+ }
+ }
+ }
+ }
+ }
+ fclose(fp);
+ return False;
+}
+
+
+/****************************************************************************
+check for a possible hosts equiv or rhosts entry for the user
+****************************************************************************/
+BOOL check_hosts_equiv(char *user)
+{
+ char *fname = NULL;
+ pstring rhostsfile;
+ struct passwd *pass = Get_Pwnam(user,True);
+
+ extern struct from_host Client_info;
+ extern int Client;
+
+ if (!pass)
+ return(False);
+
+ fromhost(Client,&Client_info);
+
+ fname = lp_hosts_equiv();
+
+ /* note: don't allow hosts.equiv on root */
+ if (fname && *fname && (pass->pw_uid != 0))
+ {
+ if (check_user_equiv(user,Client_info.name,fname))
+ return(True);
+ }
+
+ if (lp_use_rhosts())
+ {
+ char *home = get_home_dir(user);
+ if (home)
+ {
+ sprintf(rhostsfile, "%s/.rhosts", home);
+ if (check_user_equiv(user,Client_info.name,rhostsfile))
+ return(True);
+ }
+ }
+
+ return(False);
+}
+
+
+static int password_client = -1;
+static fstring pserver;
+
+/****************************************************************************
+attempted support for server level security
+****************************************************************************/
+BOOL server_cryptkey(char *buf)
+{
+ pstring inbuf,outbuf;
+ fstring pass_protocol;
+ extern fstring remote_machine;
+ char *p;
+ int len;
+ fstring desthost;
+ struct in_addr dest_ip;
+ extern struct in_addr myip;
+ int port = 139;
+ BOOL ret;
+
+ if (password_client >= 0)
+ close(password_client);
+ password_client = -1;
+
+ if (Protocol < PROTOCOL_NT1) {
+ strcpy(pass_protocol,"LM1.2X002");
+ } else {
+ strcpy(pass_protocol,"NT LM 0.12");
+ }
+
+ bzero(inbuf,sizeof(inbuf));
+ bzero(outbuf,sizeof(outbuf));
+
+ for (p=strtok(lp_passwordserver(),LIST_SEP); p ; p = strtok(NULL,LIST_SEP)) {
+ strcpy(desthost,p);
+ standard_sub_basic(desthost);
+ strupper(desthost);
+
+ dest_ip = *interpret_addr2(desthost);
+ if (zero_ip(dest_ip)) {
+ DEBUG(1,("Can't resolve address for %s\n",p));
+ continue;
+ }
+
+ if (memcmp(&dest_ip,&myip,sizeof(dest_ip)) == 0) {
+ DEBUG(1,("Password server loop - disabling password server %s\n",p));
+ continue;
+ }
+
+ password_client = open_socket_out(SOCK_STREAM, &dest_ip, port);
+ if (password_client >= 0) {
+ DEBUG(3,("connected to password server %s\n",p));
+ StrnCpy(pserver,p,sizeof(pserver)-1);
+ break;
+ }
+ }
+
+ if (password_client < 0) {
+ DEBUG(1,("password server not available\n"));
+ return(False);
+ }
+
+
+ /* send a session request (RFC 8002) */
+
+ /* put in the destination name */
+ len = 4;
+ p = outbuf+len;
+ name_mangle(desthost,p,' ');
+ len += name_len(p);
+
+ /* and my name */
+ p = outbuf+len;
+ name_mangle(remote_machine,p,' ');
+ len += name_len(p);
+
+ _smb_setlen(outbuf,len);
+ CVAL(outbuf,0) = 0x81;
+
+ send_smb(password_client,outbuf);
+ receive_smb(password_client,inbuf,5000);
+
+ if (CVAL(inbuf,0) != 0x82) {
+ DEBUG(1,("%s rejected the session\n",pserver));
+ close(password_client); password_client = -1;
+ return(False);
+ }
+
+ DEBUG(3,("got session\n"));
+
+ bzero(outbuf,smb_size);
+
+ /* setup the protocol string */
+ set_message(outbuf,0,strlen(pass_protocol)+2,True);
+ p = smb_buf(outbuf);
+ *p++ = 2;
+ strcpy(p,pass_protocol);
+
+ CVAL(outbuf,smb_com) = SMBnegprot;
+ CVAL(outbuf,smb_flg) = 0x8;
+ SSVAL(outbuf,smb_flg2,0x1);
+
+ send_smb(password_client,outbuf);
+ ret = receive_smb(password_client,inbuf,5000);
+
+ if (!ret || CVAL(inbuf,smb_rcls) || SVAL(inbuf,smb_vwv0)) {
+ DEBUG(1,("%s rejected the protocol\n",pserver));
+ close(password_client); password_client= -1;
+ return(False);
+ }
+
+ if (!(CVAL(inbuf,smb_vwv1) & 1)) {
+ DEBUG(1,("%s isn't in user level security mode\n",pserver));
+ close(password_client); password_client= -1;
+ return(False);
+ }
+
+ memcpy(buf,inbuf,smb_len(inbuf)+4);
+
+ DEBUG(3,("password server OK\n"));
+
+ return(True);
+}
+
+/****************************************************************************
+attempted support for server level security
+****************************************************************************/
+BOOL server_validate(char *buf)
+{
+ pstring inbuf,outbuf;
+ BOOL ret;
+
+ if (password_client < 0) {
+ DEBUG(1,("%s not connected\n",pserver));
+ return(False);
+ }
+
+ bzero(inbuf,sizeof(inbuf));
+ memcpy(outbuf,buf,sizeof(outbuf));
+
+ /* send a session setup command */
+ CVAL(outbuf,smb_flg) = 0x8;
+ SSVAL(outbuf,smb_flg2,0x1);
+ CVAL(outbuf,smb_vwv0) = 0xFF;
+
+ set_message(outbuf,smb_numwords(outbuf),smb_buflen(outbuf),False);
+
+ SCVAL(inbuf,smb_rcls,1);
+
+ send_smb(password_client,outbuf);
+ ret = receive_smb(password_client,inbuf,5000);
+
+ if (!ret || CVAL(inbuf,smb_rcls) != 0) {
+ DEBUG(1,("password server %s rejected the password\n",pserver));
+ return(False);
+ }
+
+ /* if logged in as guest then reject */
+ if ((SVAL(inbuf,smb_vwv2) & 1) != 0) {
+ DEBUG(1,("password server %s gave us guest only\n",pserver));
+ return(False);
+ }
+
+ DEBUG(3,("password server %s accepted the password\n",pserver));
+
+#ifndef KEEP_PASSWORD_SERVER_OPEN
+ close(password_client); password_client= -1;
+#endif
+
+ return(True);
+}
+
+
diff --git a/source/smbd/reply.c b/source/smbd/reply.c
new file mode 100644
index 00000000000..b7b51775bb8
--- /dev/null
+++ b/source/smbd/reply.c
@@ -0,0 +1,3210 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Main SMB reply routines
+ Copyright (C) Andrew Tridgell 1992-1995
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+/*
+ This file handles most of the reply_ calls that the server
+ makes to handle specific protocols
+*/
+
+
+#include "includes.h"
+#include "loadparm.h"
+#include "trans2.h"
+
+/* look in server.c for some explanation of these variables */
+extern int Protocol;
+extern int DEBUGLEVEL;
+extern int chain_size;
+extern int maxxmit;
+extern int chain_fnum;
+extern char magic_char;
+extern connection_struct Connections[];
+extern files_struct Files[];
+extern BOOL case_sensitive;
+extern pstring sesssetup_user;
+extern int Client;
+
+/* this macro should always be used to extract an fnum (smb_fid) from
+a packet to ensure chaining works correctly */
+#define GETFNUM(buf,where) (chain_fnum!= -1?chain_fnum:SVAL(buf,where))
+
+
+/****************************************************************************
+ reply to an special message
+****************************************************************************/
+int reply_special(char *inbuf,char *outbuf)
+{
+ int outsize = 4;
+ int msg_type = CVAL(inbuf,0);
+ int msg_flags = CVAL(inbuf,1);
+ pstring name1,name2;
+ extern fstring remote_machine;
+ extern fstring local_machine;
+ char *p;
+
+ *name1 = *name2 = 0;
+
+ smb_setlen(outbuf,0);
+
+ switch (msg_type)
+ {
+ case 0x81: /* session request */
+ CVAL(outbuf,0) = 0x82;
+ CVAL(outbuf,3) = 0;
+ if (name_len(inbuf+4) > 50)
+ {
+ DEBUG(0,("Invalid name length in session request\n"));
+ return(0);
+ }
+ name_extract(inbuf,4,name1);
+ name_extract(inbuf,4 + name_len(inbuf + 4),name2);
+ DEBUG(2,("netbios connect: name1=%s name2=%s\n",name1,name2));
+
+ strcpy(remote_machine,name2);
+ trim_string(remote_machine," "," ");
+ p = strchr(remote_machine,' ');
+ strlower(remote_machine);
+ if (p) *p = 0;
+
+ strcpy(local_machine,name1);
+ trim_string(local_machine," "," ");
+ p = strchr(local_machine,' ');
+ strlower(local_machine);
+ if (p) *p = 0;
+
+ add_session_user(remote_machine);
+
+ reload_services(True);
+ reopen_logs();
+
+ break;
+ case 0x85: /* session keepalive */
+ default:
+ return(0);
+ }
+
+ DEBUG(5,("%s init msg_type=0x%x msg_flags=0x%x\n",timestring(),msg_type,msg_flags));
+
+ return(outsize);
+}
+
+
+/*******************************************************************
+work out what error to give to a failed connection
+********************************************************************/
+static int connection_error(char *inbuf,char *outbuf,int connection_num)
+{
+ switch (connection_num)
+ {
+ case -8:
+ return(ERROR(ERRSRV,ERRnoresource));
+ case -7:
+ return(ERROR(ERRSRV,ERRbaduid));
+ case -6:
+ return(ERROR(ERRSRV,ERRinvdevice));
+ case -5:
+ return(ERROR(ERRSRV,ERRinvnetname));
+ case -4:
+ return(ERROR(ERRSRV,ERRaccess));
+ case -3:
+ return(ERROR(ERRDOS,ERRnoipc));
+ case -2:
+ return(ERROR(ERRSRV,ERRinvnetname));
+ }
+ return(ERROR(ERRSRV,ERRbadpw));
+}
+
+
+/****************************************************************************
+ reply to a tcon
+****************************************************************************/
+int reply_tcon(char *inbuf,char *outbuf)
+{
+ pstring service;
+ pstring user;
+ pstring password;
+ pstring dev;
+ int connection_num;
+ int outsize = 0;
+ int uid = SVAL(inbuf,smb_uid);
+ int vuid;
+ int pwlen;
+
+ *service = *user = *password = *dev = 0;
+
+ vuid = valid_uid(uid);
+
+ parse_connect(inbuf,service,user,password,&pwlen,dev);
+
+ connection_num = make_connection(service,user,password,pwlen,dev,vuid);
+
+ if (connection_num < 0)
+ return(connection_error(inbuf,outbuf,connection_num));
+
+ outsize = set_message(outbuf,2,0,True);
+ SSVAL(outbuf,smb_vwv0,maxxmit);
+ SSVAL(outbuf,smb_vwv1,connection_num);
+ SSVAL(outbuf,smb_tid,connection_num);
+
+ DEBUG(3,("%s tcon service=%s user=%s cnum=%d\n",timestring(),service,user,connection_num));
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a tcon and X
+****************************************************************************/
+int reply_tcon_and_X(char *inbuf,char *outbuf,int length,int bufsize)
+{
+ pstring service;
+ pstring user;
+ pstring password;
+ pstring devicename;
+ int connection_num;
+ int outsize = 0;
+ int uid = SVAL(inbuf,smb_uid);
+ int vuid;
+ int smb_com2 = SVAL(inbuf,smb_vwv0);
+ int smb_off2 = SVAL(inbuf,smb_vwv1);
+ int passlen = SVAL(inbuf,smb_vwv3);
+
+ *service = *user = *password = *devicename = 0;
+
+ /* we might have to close an old one */
+ if ((SVAL(inbuf,smb_vwv2) & 0x1) != 0)
+ close_cnum(SVAL(inbuf,smb_tid),uid);
+
+ vuid = valid_uid(uid);
+
+ {
+ char *path;
+ char *p;
+ memcpy(password,smb_buf(inbuf),passlen);
+ password[passlen]=0;
+ path = smb_buf(inbuf) + passlen;
+ DEBUG(4,("parsing net-path %s, passlen=%d\n",path,passlen));
+ strcpy(service,path+2);
+ p = strchr(service,'\\');
+ if (!p)
+ return(ERROR(ERRSRV,ERRinvnetname));
+ *p = 0;
+ strcpy(service,p+1);
+ p = strchr(service,'%');
+ if (p)
+ {
+ *p++ = 0;
+ strcpy(user,p);
+ }
+ StrnCpy(devicename,path + strlen(path) + 1,6);
+ DEBUG(4,("Got device type %s\n",devicename));
+ }
+
+ connection_num = make_connection(service,user,password,passlen,devicename,vuid);
+
+ if (connection_num < 0)
+ return(connection_error(inbuf,outbuf,connection_num));
+
+ outsize = set_message(outbuf,2,strlen(devicename)+1,True);
+
+ DEBUG(3,("%s tconX service=%s user=%s cnum=%d\n",timestring(),service,user,connection_num));
+
+ /* set the incoming and outgoing tid to the just created one */
+ SSVAL(inbuf,smb_tid,connection_num);
+ SSVAL(outbuf,smb_tid,connection_num);
+
+ CVAL(outbuf,smb_vwv0) = smb_com2;
+ SSVAL(outbuf,smb_vwv1,(chain_size + outsize)-4);
+
+ strcpy(smb_buf(outbuf),devicename);
+
+ if (smb_com2 != 0xFF)
+ outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4,
+ outbuf,outbuf+outsize,
+ length,bufsize);
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to an unknown type
+****************************************************************************/
+int reply_unknown(char *inbuf,char *outbuf)
+{
+ int cnum;
+ int type;
+ cnum = SVAL(inbuf,smb_tid);
+ type = CVAL(inbuf,smb_com);
+
+ DEBUG(0,("%s unknown command type (%s): cnum=%d type=%d (0x%X)\n",
+ timestring(),
+ smb_fn_name(type),
+ cnum,type,type));
+
+ return(ERROR(ERRSRV,ERRunknownsmb));
+}
+
+
+/****************************************************************************
+ reply to an ioctl
+****************************************************************************/
+int reply_ioctl(char *inbuf,char *outbuf)
+{
+ DEBUG(3,("ignoring ioctl\n"));
+
+ return(ERROR(ERRSRV,ERRnosupport));
+}
+
+
+/****************************************************************************
+reply to a session setup command
+****************************************************************************/
+int reply_sesssetup_and_X(char *inbuf,char *outbuf,int length,int bufsize)
+{
+ int outsize = 0;
+ int sess_uid;
+ int gid;
+ int smb_com2;
+ int smb_off2;
+ int smb_bufsize;
+ int smb_mpxmax;
+ int smb_vc_num;
+ uint32 smb_sesskey;
+ int smb_apasslen;
+ pstring smb_apasswd;
+ int smb_ntpasslen = 0;
+ pstring smb_ntpasswd;
+ BOOL valid_nt_password = False;
+ pstring user;
+ BOOL guest=False;
+
+ *smb_apasswd = 0;
+
+ sess_uid = SVAL(inbuf,smb_uid);
+ smb_com2 = CVAL(inbuf,smb_vwv0);
+ smb_off2 = SVAL(inbuf,smb_vwv1);
+ smb_bufsize = SVAL(inbuf,smb_vwv2);
+ smb_mpxmax = SVAL(inbuf,smb_vwv3);
+ smb_vc_num = SVAL(inbuf,smb_vwv4);
+ smb_sesskey = IVAL(inbuf,smb_vwv5);
+
+ if (Protocol < PROTOCOL_NT1) {
+ smb_apasslen = SVAL(inbuf,smb_vwv7);
+ memcpy(smb_apasswd,smb_buf(inbuf),smb_apasslen);
+ StrnCpy(user,smb_buf(inbuf)+smb_apasslen,sizeof(user)-1);
+ } else {
+ uint16 passlen1 = SVAL(inbuf,smb_vwv7);
+ uint16 passlen2 = SVAL(inbuf,smb_vwv8);
+ BOOL doencrypt = SMBENCRYPT();
+ char *p = smb_buf(inbuf);
+ if (passlen1 > 256) passlen1 = 0;
+ if (passlen2 > 256) passlen2 = 0; /* I don't know why NT gives weird
+ lengths sometimes */
+ if(doencrypt) {
+ /* Save the lanman2 password and the NT md4 password. */
+ smb_apasslen = passlen1;
+ memcpy(smb_apasswd,p,smb_apasslen);
+ smb_ntpasslen = passlen2;
+ memcpy(smb_ntpasswd,p+passlen1,smb_ntpasslen);
+ } else {
+ /* for Win95 */
+ if (passlen1 > passlen2) {
+ smb_apasslen = passlen1;
+ StrnCpy(smb_apasswd,p,smb_apasslen);
+ } else {
+ smb_apasslen = passlen2;
+ StrnCpy(smb_apasswd,p + passlen1,smb_apasslen);
+ }
+ }
+ if (passlen2 == 1) {
+ /* apparently NT sometimes sets passlen2 to 1 when it means 0. This
+ tries to work around that problem */
+ passlen2 = 0;
+ }
+ p += passlen1 + passlen2;
+ strcpy(user,p); p = skip_string(p,1);
+ DEBUG(3,("Domain=[%s] NativeOS=[%s] NativeLanMan=[%s]\n",
+ p,skip_string(p,1),skip_string(p,2)));
+ }
+
+
+ DEBUG(3,("sesssetupX:name=[%s]\n",user));
+
+ if (!*user)
+ strcpy(user,lp_guestaccount(-1));
+
+ strlower(user);
+
+ strcpy(sesssetup_user,user);
+
+ reload_services(True);
+
+ add_session_user(user);
+
+
+ if (!(lp_security() == SEC_SERVER && server_validate(inbuf)) &&
+ !check_hosts_equiv(user))
+ {
+
+ if (strequal(user,lp_guestaccount(-1)) && (*smb_apasswd == 0))
+ guest = True;
+
+ /* now check if it's a valid username/password */
+ /* If an NT password was supplied try and validate with that
+ first. This is superior as the passwords are mixed case 128 length unicode */
+ if(smb_ntpasslen && !guest)
+ {
+ if(!password_ok(user,smb_ntpasswd,smb_ntpasslen,NULL,True))
+ DEBUG(0,("NT Password did not match ! Defaulting to Lanman\n"));
+ else
+ valid_nt_password = True;
+ }
+ if (!valid_nt_password && !guest && !password_ok(user,smb_apasswd,smb_apasslen,NULL,False))
+ {
+ if (lp_security() >= SEC_USER) {
+#if (GUEST_SESSSETUP == 0)
+ return(ERROR(ERRSRV,ERRbadpw));
+#endif
+#if (GUEST_SESSSETUP == 1)
+ if (Get_Pwnam(user,True))
+ return(ERROR(ERRSRV,ERRbadpw));
+#endif
+ }
+ if (*smb_apasswd || !Get_Pwnam(user,True))
+ strcpy(user,lp_guestaccount(-1));
+ DEBUG(3,("Registered username %s for guest access\n",user));
+ guest = True;
+ }
+ }
+
+ if (!Get_Pwnam(user,True)) {
+ DEBUG(3,("No such user %s - using guest account\n",user));
+ strcpy(user,lp_guestaccount(-1));
+ guest = True;
+ }
+
+ if (!strequal(user,lp_guestaccount(-1)) &&
+ lp_servicenumber(user) < 0)
+ {
+ int homes = lp_servicenumber(HOMES_NAME);
+ char *home = get_home_dir(user);
+ if (homes >= 0 && home)
+ lp_add_home(user,homes,home);
+ }
+
+
+ /* it's ok - setup a reply */
+ if (Protocol < PROTOCOL_NT1) {
+ outsize = set_message(outbuf,3,0,True);
+ } else {
+ char *p;
+ outsize = set_message(outbuf,3,3,True);
+ p = smb_buf(outbuf);
+ strcpy(p,"Unix"); p = skip_string(p,1);
+ strcpy(p,"Samba "); strcat(p,VERSION); p = skip_string(p,1);
+ strcpy(p,my_workgroup()); p = skip_string(p,1);
+ outsize = set_message(outbuf,3,PTR_DIFF(p,smb_buf(outbuf)),False);
+ /* perhaps grab OS version here?? */
+ }
+
+ /* Set the correct uid in the outgoing and incoming packets
+ We will use this on future requests to determine which
+ user we should become.
+ */
+ {
+ struct passwd *pw = Get_Pwnam(user,False);
+ if (!pw) {
+ DEBUG(1,("Username %s is invalid on this system\n",user));
+ return(ERROR(ERRSRV,ERRbadpw));
+ }
+ gid = pw->pw_gid;
+ SSVAL(outbuf,smb_uid,(uint16)pw->pw_uid);
+ SSVAL(inbuf,smb_uid,(uint16)pw->pw_uid);
+ }
+
+ CVAL(outbuf,smb_vwv0) = smb_com2;
+ SSVAL(outbuf,smb_vwv1,(chain_size+outsize)-4);
+
+ if (guest)
+ SSVAL(outbuf,smb_vwv2,1);
+
+ /* register the name and uid as being validated, so further connections
+ to a uid can get through without a password, on the same VC */
+ register_uid(SVAL(inbuf,smb_uid),gid,user,guest);
+
+ maxxmit = MIN(maxxmit,smb_bufsize);
+
+ if (smb_com2 != 0xFF)
+ outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4,
+ outbuf,outbuf+outsize,
+ length,bufsize);
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a chkpth
+****************************************************************************/
+int reply_chkpth(char *inbuf,char *outbuf)
+{
+ int outsize = 0;
+ int cnum,mode;
+ pstring name;
+ BOOL ok = False;
+
+ cnum = SVAL(inbuf,smb_tid);
+
+ strcpy(name,smb_buf(inbuf) + 1);
+ unix_convert(name,cnum);
+
+ mode = SVAL(inbuf,smb_vwv0);
+
+ if (check_name(name,cnum))
+ ok = directory_exist(name,NULL);
+
+ if (!ok)
+ return(ERROR(ERRDOS,ERRbadpath));
+
+ outsize = set_message(outbuf,0,0,True);
+
+ DEBUG(3,("%s chkpth %s cnum=%d mode=%d\n",timestring(),name,cnum,mode));
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a getatr
+****************************************************************************/
+int reply_getatr(char *inbuf,char *outbuf)
+{
+ pstring fname;
+ int cnum;
+ int outsize = 0;
+ struct stat sbuf;
+ BOOL ok = False;
+ int mode=0;
+ uint32 size=0;
+ time_t mtime=0;
+
+ cnum = SVAL(inbuf,smb_tid);
+
+ strcpy(fname,smb_buf(inbuf) + 1);
+ unix_convert(fname,cnum);
+
+ /* dos smetimes asks for a stat of "" - it returns a "hidden directory"
+ under WfWg - weird! */
+ if (! (*fname))
+ {
+ mode = aHIDDEN | aDIR;
+ if (!CAN_WRITE(cnum)) mode |= aRONLY;
+ size = 0;
+ mtime = 0;
+ ok = True;
+ }
+ else
+ if (check_name(fname,cnum))
+ {
+ if (sys_stat(fname,&sbuf) == 0)
+ {
+ mode = dos_mode(cnum,fname,&sbuf);
+ size = sbuf.st_size;
+ mtime = sbuf.st_mtime;
+ if (mode & aDIR)
+ size = 0;
+ ok = True;
+ }
+ else
+ DEBUG(3,("stat of %s failed (%s)\n",fname,strerror(errno)));
+ }
+
+ if (!ok)
+ return(UNIXERROR(ERRDOS,ERRbadfile));
+
+ outsize = set_message(outbuf,10,0,True);
+
+ SSVAL(outbuf,smb_vwv0,mode);
+ put_dos_date3(outbuf,smb_vwv1,mtime);
+ SIVAL(outbuf,smb_vwv3,size);
+
+ if (Protocol >= PROTOCOL_NT1) {
+ char *p = strrchr(fname,'/');
+ uint16 flg2 = SVAL(outbuf,smb_flg2);
+ if (!p) p = fname;
+ if (!is_8_3(fname))
+ SSVAL(outbuf,smb_flg2,flg2 | 0x40); /* IS_LONG_NAME */
+ }
+
+ DEBUG(3,("%s getatr name=%s mode=%d size=%d\n",timestring(),fname,mode,size));
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a setatr
+****************************************************************************/
+int reply_setatr(char *inbuf,char *outbuf)
+{
+ pstring fname;
+ int cnum;
+ int outsize = 0;
+ BOOL ok=False;
+ int mode;
+ time_t mtime;
+
+ cnum = SVAL(inbuf,smb_tid);
+
+ strcpy(fname,smb_buf(inbuf) + 1);
+ unix_convert(fname,cnum);
+
+ mode = SVAL(inbuf,smb_vwv0);
+ mtime = make_unix_date3(inbuf+smb_vwv1);
+
+ if (directory_exist(fname,NULL))
+ mode |= aDIR;
+ if (check_name(fname,cnum))
+ ok = (dos_chmod(cnum,fname,mode,NULL) == 0);
+ if (ok)
+ ok = set_filetime(fname,mtime);
+
+ if (!ok)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ outsize = set_message(outbuf,0,0,True);
+
+ DEBUG(3,("%s setatr name=%s mode=%d\n",timestring(),fname,mode));
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a dskattr
+****************************************************************************/
+int reply_dskattr(char *inbuf,char *outbuf)
+{
+ int cnum;
+ int outsize = 0;
+ int dfree,dsize,bsize;
+
+ cnum = SVAL(inbuf,smb_tid);
+
+ sys_disk_free(".",&bsize,&dfree,&dsize);
+
+ outsize = set_message(outbuf,5,0,True);
+
+ SSVAL(outbuf,smb_vwv0,dsize);
+ SSVAL(outbuf,smb_vwv1,bsize/512);
+ SSVAL(outbuf,smb_vwv2,512);
+ SSVAL(outbuf,smb_vwv3,dfree);
+
+ DEBUG(3,("%s dskattr cnum=%d dfree=%d\n",timestring(),cnum,dfree));
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a search
+ Can be called from SMBsearch, SMBffirst or SMBfunique.
+****************************************************************************/
+int reply_search(char *inbuf,char *outbuf)
+{
+ pstring mask;
+ pstring directory;
+ pstring fname;
+ int size,mode;
+ time_t date;
+ int dirtype;
+ int cnum;
+ int outsize = 0;
+ int numentries = 0;
+ BOOL finished = False;
+ int maxentries;
+ int i;
+ char *p;
+ BOOL ok = False;
+ int status_len;
+ char *path;
+ char status[21];
+ int dptr_num= -1;
+ BOOL check_descend = False;
+ BOOL expect_close = False;
+ BOOL can_open = True;
+
+ *mask = *directory = *fname = 0;
+
+ /* If we were called as SMBffirst then we must expect close. */
+ if(CVAL(inbuf,smb_com) == SMBffirst)
+ expect_close = True;
+
+ cnum = SVAL(inbuf,smb_tid);
+
+ outsize = set_message(outbuf,1,3,True);
+ maxentries = SVAL(inbuf,smb_vwv0);
+ dirtype = SVAL(inbuf,smb_vwv1);
+ path = smb_buf(inbuf) + 1;
+ status_len = SVAL(smb_buf(inbuf),3 + strlen(path));
+
+
+ /* dirtype &= ~aDIR; */
+
+ DEBUG(5,("path=%s status_len=%d\n",path,status_len));
+
+
+ if (status_len == 0)
+ {
+ pstring dir2;
+
+ strcpy(directory,smb_buf(inbuf)+1);
+ strcpy(dir2,smb_buf(inbuf)+1);
+ unix_convert(directory,cnum);
+ unix_format(dir2);
+
+ if (!check_name(directory,cnum))
+ can_open = False;
+
+ p = strrchr(dir2,'/');
+ if (p == NULL)
+ {strcpy(mask,dir2);*dir2 = 0;}
+ else
+ {*p = 0;strcpy(mask,p+1);}
+
+ p = strrchr(directory,'/');
+ if (!p)
+ *directory = 0;
+ else
+ *p = 0;
+
+ if (strlen(directory) == 0)
+ strcpy(directory,"./");
+ bzero(status,21);
+ CVAL(status,0) = dirtype;
+ }
+ else
+ {
+ memcpy(status,smb_buf(inbuf) + 1 + strlen(path) + 4,21);
+ memcpy(mask,status+1,11);
+ mask[11] = 0;
+ dirtype = CVAL(status,0) & 0x1F;
+ Connections[cnum].dirptr = dptr_fetch(status+12,&dptr_num);
+ if (!Connections[cnum].dirptr)
+ goto SearchEmpty;
+ string_set(&Connections[cnum].dirpath,dptr_path(dptr_num));
+ if (!case_sensitive)
+ strnorm(mask);
+ }
+
+ /* turn strings of spaces into a . */
+ {
+ trim_string(mask,NULL," ");
+ if ((p = strrchr(mask,' ')))
+ {
+ fstring ext;
+ strcpy(ext,p+1);
+ *p = 0;
+ trim_string(mask,NULL," ");
+ strcat(mask,".");
+ strcat(mask,ext);
+ }
+ }
+
+ {
+ for (p=mask; *p; p++)
+ {
+ if (*p != '?' && *p != '*' && !isdoschar(*p))
+ {
+ DEBUG(5,("Invalid char [%c] in search mask?\n",*p));
+ *p = '?';
+ }
+ }
+ }
+
+ if (!strchr(mask,'.') && strlen(mask)>8)
+ {
+ fstring tmp;
+ strcpy(tmp,&mask[8]);
+ mask[8] = '.';
+ mask[9] = 0;
+ strcat(mask,tmp);
+ }
+
+ DEBUG(5,("mask=%s directory=%s\n",mask,directory));
+
+ if (can_open)
+ {
+ p = smb_buf(outbuf) + 3;
+
+ ok = True;
+
+ if (status_len == 0)
+ {
+ dptr_num = dptr_create(cnum,directory,expect_close,SVAL(inbuf,smb_pid));
+ if (dptr_num < 0)
+ return(ERROR(ERRDOS,ERRnofids));
+ }
+
+ DEBUG(4,("dptr_num is %d\n",dptr_num));
+
+ if (ok)
+ {
+ if ((dirtype&0x1F) == aVOLID)
+ {
+ memcpy(p,status,21);
+ make_dir_struct(p,"???????????",volume_label(SNUM(cnum)),0,aVOLID,0);
+ dptr_fill(p+12,dptr_num);
+ if (dptr_zero(p+12) && (status_len==0))
+ numentries = 1;
+ else
+ numentries = 0;
+ p += DIR_STRUCT_SIZE;
+ }
+ else
+ {
+ DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n",Connections[cnum].dirpath,lp_dontdescend(SNUM(cnum))));
+ if (in_list(Connections[cnum].dirpath,
+ lp_dontdescend(SNUM(cnum)),True))
+ check_descend = True;
+
+ for (i=numentries;(i<maxentries) && !finished;i++)
+ {
+ finished =
+ !get_dir_entry(cnum,mask,dirtype,fname,&size,&mode,&date,check_descend);
+ if (!finished)
+ {
+ memcpy(p,status,21);
+ make_dir_struct(p,mask,fname,size,mode,date);
+ dptr_fill(p+12,dptr_num);
+ numentries++;
+ }
+ p += DIR_STRUCT_SIZE;
+ }
+ }
+ }
+ }
+
+
+ SearchEmpty:
+
+ if (numentries == 0 || !ok)
+ {
+ CVAL(outbuf,smb_rcls) = ERRDOS;
+ SSVAL(outbuf,smb_err,ERRnofiles);
+ }
+
+ /* If we were called as SMBffirst with smb_search_id == NULL
+ and no entries were found then return error and close dirptr
+ (X/Open spec) */
+
+ if(ok && expect_close && numentries == 0 && status_len == 0)
+ {
+ CVAL(outbuf,smb_rcls) = ERRDOS;
+ SSVAL(outbuf,smb_err,ERRnofiles);
+ /* Also close the dptr - we know it's gone */
+ dptr_close(dptr_num);
+ }
+
+ /* If we were called as SMBfunique, then we can close the dirptr now ! */
+ if(dptr_num >= 0 && CVAL(inbuf,smb_com) == SMBfunique)
+ dptr_close(dptr_num);
+
+ SSVAL(outbuf,smb_vwv0,numentries);
+ SSVAL(outbuf,smb_vwv1,3 + numentries * DIR_STRUCT_SIZE);
+ CVAL(smb_buf(outbuf),0) = 5;
+ SSVAL(smb_buf(outbuf),1,numentries*DIR_STRUCT_SIZE);
+
+ if (Protocol >= PROTOCOL_NT1) {
+ uint16 flg2 = SVAL(outbuf,smb_flg2);
+ SSVAL(outbuf,smb_flg2,flg2 | 0x40); /* IS_LONG_NAME */
+ }
+
+ outsize += DIR_STRUCT_SIZE*numentries;
+ smb_setlen(outbuf,outsize - 4);
+
+ if ((! *directory) && dptr_path(dptr_num))
+ sprintf(directory,"(%s)",dptr_path(dptr_num));
+
+ DEBUG(4,("%s %s mask=%s path=%s cnum=%d dtype=%d nument=%d of %d\n",
+ timestring(),
+ smb_fn_name(CVAL(inbuf,smb_com)),
+ mask,directory,cnum,dirtype,numentries,maxentries));
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a fclose (stop directory search)
+****************************************************************************/
+int reply_fclose(char *inbuf,char *outbuf)
+{
+ int cnum;
+ int outsize = 0;
+ int status_len;
+ char *path;
+ char status[21];
+ int dptr_num= -1;
+
+ cnum = SVAL(inbuf,smb_tid);
+
+ outsize = set_message(outbuf,1,0,True);
+ path = smb_buf(inbuf) + 1;
+ status_len = SVAL(smb_buf(inbuf),3 + strlen(path));
+
+
+ if (status_len == 0)
+ return(ERROR(ERRSRV,ERRsrverror));
+
+ memcpy(status,smb_buf(inbuf) + 1 + strlen(path) + 4,21);
+
+ if(dptr_fetch(status+12,&dptr_num)) {
+ /* Close the dptr - we know it's gone */
+ dptr_close(dptr_num);
+ }
+
+ SSVAL(outbuf,smb_vwv0,0);
+
+ DEBUG(3,("%s search close cnum=%d\n",timestring(),cnum));
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to an open
+****************************************************************************/
+int reply_open(char *inbuf,char *outbuf)
+{
+ pstring fname;
+ int cnum;
+ int fnum = -1;
+ int outsize = 0;
+ int fmode=0;
+ int share_mode;
+ int size = 0;
+ time_t mtime=0;
+ int unixmode;
+ int rmode=0;
+ struct stat sbuf;
+
+ cnum = SVAL(inbuf,smb_tid);
+
+ share_mode = SVAL(inbuf,smb_vwv0);
+
+ strcpy(fname,smb_buf(inbuf)+1);
+ unix_convert(fname,cnum);
+
+ fnum = find_free_file();
+ if (fnum < 0)
+ return(ERROR(ERRSRV,ERRnofids));
+
+ if (!check_name(fname,cnum))
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ unixmode = unix_mode(cnum,aARCH);
+
+ open_file_shared(fnum,cnum,fname,share_mode,3,unixmode,&rmode,NULL);
+
+ if (!Files[fnum].open)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ if (fstat(Files[fnum].fd,&sbuf) != 0) {
+ close_file(fnum);
+ return(ERROR(ERRDOS,ERRnoaccess));
+ }
+
+ size = sbuf.st_size;
+ fmode = dos_mode(cnum,fname,&sbuf);
+ mtime = sbuf.st_mtime;
+
+ if (fmode & aDIR) {
+ DEBUG(3,("attempt to open a directory %s\n",fname));
+ close_file(fnum);
+ return(ERROR(ERRDOS,ERRnoaccess));
+ }
+
+ outsize = set_message(outbuf,7,0,True);
+ SSVAL(outbuf,smb_vwv0,fnum);
+ SSVAL(outbuf,smb_vwv1,fmode);
+ put_dos_date3(outbuf,smb_vwv2,mtime);
+ SIVAL(outbuf,smb_vwv4,size);
+ SSVAL(outbuf,smb_vwv6,rmode);
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to an open and X
+****************************************************************************/
+int reply_open_and_X(char *inbuf,char *outbuf,int length,int bufsize)
+{
+ pstring fname;
+ int cnum = SVAL(inbuf,smb_tid);
+ int fnum = -1;
+ int outsize = 0;
+ int openmode = 0;
+ int smb_com2 = CVAL(inbuf,smb_vwv0);
+ int smb_off2 = SVAL(inbuf,smb_vwv1);
+ int smb_mode = SVAL(inbuf,smb_vwv3);
+ int smb_attr = SVAL(inbuf,smb_vwv5);
+#if 0
+ int open_flags = SVAL(inbuf,smb_vwv2);
+ int smb_sattr = SVAL(inbuf,smb_vwv4);
+ uint32 smb_time = make_unix_date3(inbuf+smb_vwv6);
+#endif
+ int smb_ofun = SVAL(inbuf,smb_vwv8);
+ int unixmode;
+ int size=0,fmode=0,mtime=0,rmode=0;
+ struct stat sbuf;
+ int smb_action = 0;
+
+ /* XXXX we need to handle passed times, sattr and flags */
+
+ strcpy(fname,smb_buf(inbuf));
+ unix_convert(fname,cnum);
+
+ /* now add create and trunc bits */
+ if (smb_ofun & 0x10)
+ openmode |= O_CREAT;
+ if ((smb_ofun & 0x3) == 2)
+ openmode |= O_TRUNC;
+
+ fnum = find_free_file();
+ if (fnum < 0)
+ return(ERROR(ERRSRV,ERRnofids));
+
+ if (!check_name(fname,cnum))
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ unixmode = unix_mode(cnum,smb_attr | aARCH);
+
+ open_file_shared(fnum,cnum,fname,smb_mode,smb_ofun,unixmode,
+ &rmode,&smb_action);
+
+ if (!Files[fnum].open)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ if (fstat(Files[fnum].fd,&sbuf) != 0) {
+ close_file(fnum);
+ return(ERROR(ERRDOS,ERRnoaccess));
+ }
+
+ size = sbuf.st_size;
+ fmode = dos_mode(cnum,fname,&sbuf);
+ mtime = sbuf.st_mtime;
+ if (fmode & aDIR) {
+ close_file(fnum);
+ return(ERROR(ERRDOS,ERRnoaccess));
+ }
+
+ outsize = set_message(outbuf,15,0,True);
+ CVAL(outbuf,smb_vwv0) = smb_com2;
+ SSVAL(outbuf,smb_vwv1,(chain_size+outsize)-4);
+ SSVAL(outbuf,smb_vwv2,fnum);
+ SSVAL(outbuf,smb_vwv3,fmode);
+ put_dos_date3(outbuf,smb_vwv4,mtime);
+ SIVAL(outbuf,smb_vwv6,size);
+ SSVAL(outbuf,smb_vwv8,rmode);
+ SSVAL(outbuf,smb_vwv11,smb_action);
+
+ chain_fnum = fnum;
+
+ if (smb_com2 != 0xFF)
+ outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4,
+ outbuf,outbuf+outsize,
+ length,bufsize);
+
+ chain_fnum = -1;
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a SMBulogoffX
+****************************************************************************/
+int reply_ulogoffX(char *inbuf,char *outbuf,int length,int bufsize)
+{
+ int outsize = 0;
+ int smb_com2 = CVAL(inbuf,smb_vwv0);
+ int smb_off2 = SVAL(inbuf,smb_vwv1);
+ int uid = SVAL(inbuf,smb_uid);
+
+ invalidate_uid(uid);
+
+ outsize = set_message(outbuf,2,0,True);
+ CVAL(outbuf,smb_vwv0) = smb_com2;
+ SSVAL(outbuf,smb_vwv1,(chain_size+outsize)-4);
+
+ DEBUG(3,("%s ulogoffX uid=%d\n",timestring(),uid));
+
+ if (smb_com2 != 0xFF)
+ outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4,
+ outbuf,outbuf+outsize,
+ length,bufsize);
+
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a mknew
+****************************************************************************/
+int reply_mknew(char *inbuf,char *outbuf)
+{
+ pstring fname;
+ int cnum,com;
+ int fnum = -1;
+ int outsize = 0;
+ int createmode;
+ mode_t unixmode;
+
+ com = SVAL(inbuf,smb_com);
+ cnum = SVAL(inbuf,smb_tid);
+
+ createmode = SVAL(inbuf,smb_vwv0);
+ strcpy(fname,smb_buf(inbuf)+1);
+ unix_convert(fname,cnum);
+
+ if (createmode & aVOLID)
+ {
+ DEBUG(0,("Attempt to create file (%s) with volid set - please report this\n",fname));
+ }
+
+ unixmode = unix_mode(cnum,createmode);
+
+ if (com == SMBmknew && file_exist(fname,NULL))
+ return(ERROR(ERRDOS,ERRfilexists));
+
+ fnum = find_free_file();
+ if (fnum < 0)
+ return(ERROR(ERRSRV,ERRnofids));
+
+ if (!check_name(fname,cnum))
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ open_file(fnum,cnum,fname,O_RDWR | O_CREAT | O_TRUNC,unixmode);
+
+ if (!Files[fnum].open)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ outsize = set_message(outbuf,1,0,True);
+ SSVAL(outbuf,smb_vwv0,fnum);
+
+ DEBUG(2,("new file %s\n",fname));
+ DEBUG(3,("%s mknew %s fd=%d fnum=%d cnum=%d dmode=%d umode=%o\n",timestring(),fname,Files[fnum].fd,fnum,cnum,createmode,unixmode));
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a create temporary file
+****************************************************************************/
+int reply_ctemp(char *inbuf,char *outbuf)
+{
+ pstring fname;
+ pstring fname2;
+ int cnum;
+ int fnum = -1;
+ int outsize = 0;
+ int createmode;
+ mode_t unixmode;
+
+ cnum = SVAL(inbuf,smb_tid);
+ createmode = SVAL(inbuf,smb_vwv0);
+ sprintf(fname,"%s/TMXXXXXX",smb_buf(inbuf)+1);
+ unix_convert(fname,cnum);
+
+ unixmode = unix_mode(cnum,createmode);
+
+ fnum = find_free_file();
+ if (fnum < 0)
+ return(ERROR(ERRSRV,ERRnofids));
+
+ if (!check_name(fname,cnum))
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ strcpy(fname2,(char *)mktemp(fname));
+
+ open_file(fnum,cnum,fname2,O_RDWR | O_CREAT | O_TRUNC,unixmode);
+
+ if (!Files[fnum].open)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ outsize = set_message(outbuf,1,2 + strlen(fname2),True);
+ SSVAL(outbuf,smb_vwv0,fnum);
+ CVAL(smb_buf(outbuf),0) = 4;
+ strcpy(smb_buf(outbuf) + 1,fname2);
+
+ DEBUG(2,("created temp file %s\n",fname2));
+ DEBUG(3,("%s ctemp %s fd=%d fnum=%d cnum=%d dmode=%d umode=%o\n",timestring(),fname2,Files[fnum].fd,fnum,cnum,createmode,unixmode));
+
+ return(outsize);
+}
+
+
+/*******************************************************************
+check if a user is allowed to delete a file
+********************************************************************/
+static BOOL can_delete(char *fname,int cnum,int dirtype)
+{
+ struct stat sbuf;
+ int fmode;
+
+ if (!CAN_WRITE(cnum)) return(False);
+
+ if (sys_lstat(fname,&sbuf) != 0) return(False);
+ fmode = dos_mode(cnum,fname,&sbuf);
+ if (fmode & aDIR) return(False);
+ if (fmode & aRONLY) return(False);
+ if ((fmode & ~dirtype) & (aHIDDEN | aSYSTEM))
+ return(False);
+ if (!check_file_sharing(cnum,fname)) return(False);
+ return(True);
+}
+
+/****************************************************************************
+ reply to a unlink
+****************************************************************************/
+int reply_unlink(char *inbuf,char *outbuf)
+{
+ int outsize = 0;
+ pstring name;
+ int cnum;
+ int dirtype;
+ pstring directory;
+ pstring mask;
+ char *p;
+ int count=0;
+ int error = ERRnoaccess;
+ BOOL has_wild;
+ BOOL exists=False;
+
+ *directory = *mask = 0;
+
+ cnum = SVAL(inbuf,smb_tid);
+ dirtype = SVAL(inbuf,smb_vwv0);
+
+ strcpy(name,smb_buf(inbuf) + 1);
+
+ DEBUG(3,("reply_unlink : %s\n",name));
+
+ unix_convert(name,cnum);
+
+ p = strrchr(name,'/');
+ if (!p) {
+ strcpy(directory,"./");
+ strcpy(mask,name);
+ } else {
+ *p = 0;
+ strcpy(directory,name);
+ strcpy(mask,p+1);
+ }
+
+ if (is_mangled(mask))
+ check_mangled_stack(mask);
+
+ has_wild = strchr(mask,'*') || strchr(mask,'?');
+
+ if (!has_wild) {
+ strcat(directory,"/");
+ strcat(directory,mask);
+ if (can_delete(directory,cnum,dirtype) && !sys_unlink(directory)) count++;
+ if (!count) exists = file_exist(directory,NULL);
+ } else {
+ void *dirptr = NULL;
+ char *dname;
+
+ if (check_name(directory,cnum))
+ dirptr = OpenDir(directory);
+
+ if (dirptr)
+ {
+ error = ERRbadfile;
+
+ if (strequal(mask,"????????.???"))
+ strcpy(mask,"*");
+
+ while ((dname = ReadDirName(dirptr)))
+ {
+ pstring fname;
+ strcpy(fname,dname);
+
+ if(!mask_match(fname, mask, case_sensitive, False)) continue;
+
+ error = ERRnoaccess;
+ sprintf(fname,"%s/%s",directory,dname);
+ if (!can_delete(fname,cnum,dirtype)) continue;
+ if (!sys_unlink(fname)) count++;
+ DEBUG(3,("reply_unlink : doing unlink on %s\n",fname));
+ }
+ CloseDir(dirptr);
+ }
+ }
+
+ if (count == 0) {
+ if (exists)
+ return(ERROR(ERRDOS,error));
+ else
+ return(UNIXERROR(ERRDOS,error));
+ }
+
+ outsize = set_message(outbuf,0,0,True);
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a readbraw (core+ protocol)
+****************************************************************************/
+int reply_readbraw(char *inbuf, char *outbuf)
+{
+ int cnum,maxcount,mincount,fnum;
+ int nread = 0;
+ int startpos;
+ char *header = outbuf;
+ int ret=0;
+ int fd;
+ char *fname;
+
+ cnum = SVAL(inbuf,smb_tid);
+ fnum = GETFNUM(inbuf,smb_vwv0);
+
+ startpos = IVAL(inbuf,smb_vwv1);
+ maxcount = SVAL(inbuf,smb_vwv3);
+ mincount = SVAL(inbuf,smb_vwv4);
+
+ /* ensure we don't overrun the packet size */
+ maxcount = MIN(65535,maxcount);
+ maxcount = MAX(mincount,maxcount);
+
+ if (!FNUM_OK(fnum,cnum) || !Files[fnum].can_read)
+ {
+ DEBUG(3,("fnum %d not open in readbraw - cache prime?\n",fnum));
+ _smb_setlen(header,0);
+ transfer_file(0,Client,0,header,4,0);
+ return(-1);
+ }
+ else
+ {
+ fd = Files[fnum].fd;
+ fname = Files[fnum].name;
+ }
+
+
+ if (!is_locked(fnum,cnum,maxcount,startpos))
+ {
+ int size = Files[fnum].size;
+ int sizeneeded = startpos + maxcount;
+
+ if (size < sizeneeded) {
+ struct stat st;
+ if (fstat(Files[fnum].fd,&st) == 0)
+ size = st.st_size;
+ if (!Files[fnum].can_write)
+ Files[fnum].size = size;
+ }
+
+ nread = MIN(maxcount,size - startpos);
+ }
+
+ if (nread < mincount)
+ nread = 0;
+
+ DEBUG(3,("%s readbraw fnum=%d cnum=%d start=%d max=%d min=%d nread=%d\n",
+ timestring(),
+ fnum,cnum,startpos,
+ maxcount,mincount,nread));
+
+#if UNSAFE_READRAW
+ {
+ int predict=0;
+ _smb_setlen(header,nread);
+
+ if (!Files[fnum].can_write)
+ predict = read_predict(fd,startpos,header+4,NULL,nread);
+
+ if ((nread-predict) > 0)
+ seek_file(fnum,startpos + predict);
+
+ ret = transfer_file(fd,Client,nread-predict,header,4+predict,
+ startpos+predict);
+ }
+
+ if (ret != nread+4)
+ DEBUG(0,("ERROR: file read failure on %s at %d for %d bytes (%d)\n",
+ fname,startpos,nread,ret));
+
+#else
+ ret = read_file(fnum,header+4,startpos,nread,nread,-1,False);
+ if (ret < mincount) ret = 0;
+
+ _smb_setlen(header,ret);
+ transfer_file(0,Client,0,header,4+ret,0);
+#endif
+
+ DEBUG(5,("readbraw finished\n"));
+ return -1;
+}
+
+
+/****************************************************************************
+ reply to a lockread (core+ protocol)
+****************************************************************************/
+int reply_lockread(char *inbuf,char *outbuf)
+{
+ int cnum,fnum;
+ int nread = -1;
+ char *data;
+ int outsize = 0;
+ uint32 startpos, numtoread;
+ int eclass;
+ uint32 ecode;
+
+ cnum = SVAL(inbuf,smb_tid);
+ fnum = GETFNUM(inbuf,smb_vwv0);
+
+ CHECK_FNUM(fnum,cnum);
+ CHECK_READ(fnum);
+ CHECK_ERROR(fnum);
+
+ numtoread = SVAL(inbuf,smb_vwv1);
+ startpos = IVAL(inbuf,smb_vwv2);
+
+ outsize = set_message(outbuf,5,3,True);
+ numtoread = MIN(BUFFER_SIZE-outsize,numtoread);
+ data = smb_buf(outbuf) + 3;
+
+ if(!do_lock( fnum, cnum, numtoread, startpos, &eclass, &ecode))
+ return (ERROR(eclass,ecode));
+
+ nread = read_file(fnum,data,startpos,numtoread,numtoread,-1,False);
+
+ if (nread < 0)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ outsize += nread;
+ SSVAL(outbuf,smb_vwv0,nread);
+ SSVAL(outbuf,smb_vwv5,nread+3);
+ SSVAL(smb_buf(outbuf),1,nread);
+
+ DEBUG(3,("%s lockread fnum=%d cnum=%d num=%d nread=%d\n",timestring(),fnum,cnum,numtoread,nread));
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a read
+****************************************************************************/
+int reply_read(char *inbuf,char *outbuf)
+{
+ int cnum,numtoread,fnum;
+ int nread = 0;
+ char *data;
+ int startpos;
+ int outsize = 0;
+
+ cnum = SVAL(inbuf,smb_tid);
+ fnum = GETFNUM(inbuf,smb_vwv0);
+
+ CHECK_FNUM(fnum,cnum);
+ CHECK_READ(fnum);
+ CHECK_ERROR(fnum);
+
+ numtoread = SVAL(inbuf,smb_vwv1);
+ startpos = IVAL(inbuf,smb_vwv2);
+
+ outsize = set_message(outbuf,5,3,True);
+ numtoread = MIN(BUFFER_SIZE-outsize,numtoread);
+ data = smb_buf(outbuf) + 3;
+
+ if (is_locked(fnum,cnum,numtoread,startpos))
+ return(ERROR(ERRDOS,ERRlock));
+
+ if (numtoread > 0)
+ nread = read_file(fnum,data,startpos,numtoread,numtoread,-1,False);
+
+ if (nread < 0)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ outsize += nread;
+ SSVAL(outbuf,smb_vwv0,nread);
+ SSVAL(outbuf,smb_vwv5,nread+3);
+ CVAL(smb_buf(outbuf),0) = 1;
+ SSVAL(smb_buf(outbuf),1,nread);
+
+ DEBUG(3,("%s read fnum=%d cnum=%d num=%d nread=%d\n",timestring(),fnum,cnum,numtoread,nread));
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a read and X
+****************************************************************************/
+int reply_read_and_X(char *inbuf,char *outbuf,int length,int bufsize)
+{
+ int smb_com2 = CVAL(inbuf,smb_vwv0);
+ int smb_off2 = SVAL(inbuf,smb_vwv1);
+ int fnum = GETFNUM(inbuf,smb_vwv2);
+ uint32 smb_offs = IVAL(inbuf,smb_vwv3);
+ int smb_maxcnt = SVAL(inbuf,smb_vwv5);
+ int smb_mincnt = SVAL(inbuf,smb_vwv6);
+ int cnum;
+ int nread = -1;
+ char *data;
+ int outsize = 0;
+ BOOL ok = False;
+
+ cnum = SVAL(inbuf,smb_tid);
+
+ CHECK_FNUM(fnum,cnum);
+ CHECK_READ(fnum);
+ CHECK_ERROR(fnum);
+
+ outsize = set_message(outbuf,12,0,True);
+ data = smb_buf(outbuf);
+
+ if (is_locked(fnum,cnum,smb_maxcnt,smb_offs))
+ return(ERROR(ERRDOS,ERRlock));
+ nread = read_file(fnum,data,smb_offs,smb_maxcnt,smb_maxcnt,-1,False);
+ ok = True;
+
+ if (nread < 0)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ outsize += nread;
+ CVAL(outbuf,smb_vwv0) = smb_com2;
+ SSVAL(outbuf,smb_vwv1,(outsize+chain_size)-4);
+ SSVAL(outbuf,smb_vwv5,nread);
+ SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf) + chain_size);
+ SSVAL(smb_buf(outbuf),-2,nread);
+
+ DEBUG(3,("%s readX fnum=%d cnum=%d min=%d max=%d nread=%d com2=%d off2=%d\n",
+ timestring(),fnum,cnum,
+ smb_mincnt,smb_maxcnt,nread,smb_com2,smb_off2));
+
+ chain_fnum = fnum;
+
+ if (smb_com2 != 0xFF)
+ outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4,
+ outbuf,outbuf+outsize,
+ length,bufsize);
+
+ chain_fnum = -1;
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a writebraw (core+ or LANMAN1.0 protocol)
+****************************************************************************/
+int reply_writebraw(char *inbuf,char *outbuf)
+{
+ int nwritten=0;
+ int total_written=0;
+ int numtowrite=0;
+ int cnum,fnum;
+ int outsize = 0;
+ long startpos;
+ char *data=NULL;
+ BOOL write_through;
+ int tcount;
+
+ cnum = SVAL(inbuf,smb_tid);
+ fnum = GETFNUM(inbuf,smb_vwv0);
+
+ CHECK_FNUM(fnum,cnum);
+ CHECK_WRITE(fnum);
+ CHECK_ERROR(fnum);
+
+ tcount = IVAL(inbuf,smb_vwv1);
+ startpos = IVAL(inbuf,smb_vwv3);
+ write_through = BITSETW(inbuf+smb_vwv7,0);
+
+ /* We have to deal with slightly different formats depending
+ on whether we are using the core+ or lanman1.0 protocol */
+ if(Protocol <= PROTOCOL_COREPLUS) {
+ numtowrite = SVAL(smb_buf(inbuf),-2);
+ data = smb_buf(inbuf);
+ } else {
+ numtowrite = SVAL(inbuf,smb_vwv10);
+ data = smb_base(inbuf) + SVAL(inbuf, smb_vwv11);
+ }
+
+ /* force the error type */
+ CVAL(inbuf,smb_com) = SMBwritec;
+ CVAL(outbuf,smb_com) = SMBwritec;
+
+ if (is_locked(fnum,cnum,tcount,startpos))
+ return(ERROR(ERRDOS,ERRlock));
+
+ if (seek_file(fnum,startpos) != startpos)
+ DEBUG(0,("couldn't seek to %d in writebraw\n",startpos));
+
+ if (numtowrite>0)
+ nwritten = write_file(fnum,data,numtowrite);
+
+ DEBUG(3,("%s writebraw1 fnum=%d cnum=%d start=%d num=%d wrote=%d sync=%d\n",
+ timestring(),fnum,cnum,startpos,numtowrite,nwritten,write_through));
+
+ if (nwritten < numtowrite)
+ return(UNIXERROR(ERRHRD,ERRdiskfull));
+
+ total_written = nwritten;
+
+ /* Return a message to the redirector to tell it
+ to send more bytes */
+ CVAL(outbuf,smb_com) = SMBwritebraw;
+ SSVALS(outbuf,smb_vwv0,-1);
+ outsize = set_message(outbuf,Protocol>PROTOCOL_COREPLUS?1:0,0,True);
+ send_smb(Client,outbuf);
+
+ /* Now read the raw data into the buffer and write it */
+ if(read_smb_length(Client,inbuf,0) == -1) {
+ exit_server("secondary writebraw failed");
+ }
+
+ /* Even though this is not an smb message, smb_len
+ returns the generic length of an smb message */
+ numtowrite = smb_len(inbuf);
+
+ if (tcount > nwritten+numtowrite) {
+ DEBUG(3,("Client overestimated the write %d %d %d\n",
+ tcount,nwritten,numtowrite));
+ }
+
+ nwritten = transfer_file(Client,Files[fnum].fd,numtowrite,NULL,0,
+ startpos+nwritten);
+ total_written += nwritten;
+
+ /* Set up outbuf to return the correct return */
+ outsize = set_message(outbuf,1,0,True);
+ CVAL(outbuf,smb_com) = SMBwritec;
+ SSVAL(outbuf,smb_vwv0,total_written);
+
+ if (nwritten < numtowrite) {
+ CVAL(outbuf,smb_rcls) = ERRHRD;
+ SSVAL(outbuf,smb_err,ERRdiskfull);
+ }
+
+ if (lp_syncalways(SNUM(cnum)) || write_through)
+ sync_file(fnum);
+
+ DEBUG(3,("%s writebraw2 fnum=%d cnum=%d start=%d num=%d wrote=%d\n",
+ timestring(),fnum,cnum,startpos,numtowrite,total_written));
+
+ /* we won't return a status if write through is not selected - this
+ follows what WfWg does */
+ if (!write_through && total_written==tcount)
+ return(-1);
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a writeunlock (core+)
+****************************************************************************/
+int reply_writeunlock(char *inbuf,char *outbuf)
+{
+ int cnum,fnum;
+ int nwritten = -1;
+ int outsize = 0;
+ char *data;
+ uint32 numtowrite,startpos;
+ int eclass;
+ uint32 ecode;
+
+ cnum = SVAL(inbuf,smb_tid);
+ fnum = GETFNUM(inbuf,smb_vwv0);
+
+ CHECK_FNUM(fnum,cnum);
+ CHECK_WRITE(fnum);
+ CHECK_ERROR(fnum);
+
+ numtowrite = SVAL(inbuf,smb_vwv1);
+ startpos = IVAL(inbuf,smb_vwv2);
+ data = smb_buf(inbuf) + 3;
+
+ if (is_locked(fnum,cnum,numtowrite,startpos))
+ return(ERROR(ERRDOS,ERRlock));
+
+ seek_file(fnum,startpos);
+
+ /* The special X/Open SMB protocol handling of
+ zero length writes is *NOT* done for
+ this call */
+ if(numtowrite == 0)
+ nwritten = 0;
+ else
+ nwritten = write_file(fnum,data,numtowrite);
+
+ if (lp_syncalways(SNUM(cnum)))
+ sync_file(fnum);
+
+ if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0))
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ if(!do_unlock(fnum, cnum, numtowrite, startpos, &eclass, &ecode))
+ return(ERROR(eclass,ecode));
+
+ outsize = set_message(outbuf,1,0,True);
+
+ SSVAL(outbuf,smb_vwv0,nwritten);
+
+ DEBUG(3,("%s writeunlock fnum=%d cnum=%d num=%d wrote=%d\n",
+ timestring(),fnum,cnum,numtowrite,nwritten));
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a write
+****************************************************************************/
+int reply_write(char *inbuf,char *outbuf,int dum1,int dum2)
+{
+ int cnum,numtowrite,fnum;
+ int nwritten = -1;
+ int outsize = 0;
+ int startpos;
+ char *data;
+
+ dum1 = dum2 = 0;
+
+
+ cnum = SVAL(inbuf,smb_tid);
+ fnum = GETFNUM(inbuf,smb_vwv0);
+
+ CHECK_FNUM(fnum,cnum);
+ CHECK_WRITE(fnum);
+ CHECK_ERROR(fnum);
+
+ numtowrite = SVAL(inbuf,smb_vwv1);
+ startpos = IVAL(inbuf,smb_vwv2);
+ data = smb_buf(inbuf) + 3;
+
+ if (is_locked(fnum,cnum,numtowrite,startpos))
+ return(ERROR(ERRDOS,ERRlock));
+
+ seek_file(fnum,startpos);
+
+ /* X/Open SMB protocol says that if smb_vwv1 is
+ zero then the file size should be extended or
+ truncated to the size given in smb_vwv[2-3] */
+ if(numtowrite == 0)
+ nwritten = set_filelen(Files[fnum].fd, startpos);
+ else
+ nwritten = write_file(fnum,data,numtowrite);
+
+ if (lp_syncalways(SNUM(cnum)))
+ sync_file(fnum);
+
+ if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0))
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ outsize = set_message(outbuf,1,0,True);
+
+ SSVAL(outbuf,smb_vwv0,nwritten);
+
+ if (nwritten < numtowrite) {
+ CVAL(outbuf,smb_rcls) = ERRHRD;
+ SSVAL(outbuf,smb_err,ERRdiskfull);
+ }
+
+ DEBUG(3,("%s write fnum=%d cnum=%d num=%d wrote=%d\n",timestring(),fnum,cnum,numtowrite,nwritten));
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a write and X
+****************************************************************************/
+int reply_write_and_X(char *inbuf,char *outbuf,int length,int bufsize)
+{
+ int smb_com2 = CVAL(inbuf,smb_vwv0);
+ int smb_off2 = SVAL(inbuf,smb_vwv1);
+ int fnum = GETFNUM(inbuf,smb_vwv2);
+ uint32 smb_offs = IVAL(inbuf,smb_vwv3);
+ int smb_dsize = SVAL(inbuf,smb_vwv10);
+ int smb_doff = SVAL(inbuf,smb_vwv11);
+ BOOL write_through = BITSETW(inbuf+smb_vwv7,0);
+ int cnum;
+ int nwritten = -1;
+ int outsize = 0;
+ char *data;
+
+ cnum = SVAL(inbuf,smb_tid);
+
+ CHECK_FNUM(fnum,cnum);
+ CHECK_WRITE(fnum);
+ CHECK_ERROR(fnum);
+
+ data = smb_base(inbuf) + smb_doff;
+
+ if (is_locked(fnum,cnum,smb_dsize,smb_offs))
+ return(ERROR(ERRDOS,ERRlock));
+
+ seek_file(fnum,smb_offs);
+
+ /* X/Open SMB protocol says that, unlike SMBwrite
+ if the length is zero then NO truncation is
+ done, just a write of zero. To truncate a file,
+ use SMBwrite. */
+ if(smb_dsize == 0)
+ nwritten = 0;
+ else
+ nwritten = write_file(fnum,data,smb_dsize);
+
+ if(((nwritten == 0) && (smb_dsize != 0))||(nwritten < 0))
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ outsize = set_message(outbuf,6,0,True);
+
+ CVAL(outbuf,smb_vwv0) = smb_com2;
+ SSVAL(outbuf,smb_vwv1,(outsize+chain_size)-4);
+ SSVAL(outbuf,smb_vwv2,nwritten);
+
+ if (nwritten < smb_dsize) {
+ CVAL(outbuf,smb_rcls) = ERRHRD;
+ SSVAL(outbuf,smb_err,ERRdiskfull);
+ }
+
+ DEBUG(3,("%s writeX fnum=%d cnum=%d num=%d wrote=%d\n",timestring(),fnum,cnum,smb_dsize,nwritten));
+
+ chain_fnum = fnum;
+
+ if (lp_syncalways(SNUM(cnum)) || write_through)
+ sync_file(fnum);
+
+ if (smb_com2 != 0xFF)
+ outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4,
+ outbuf,outbuf+outsize,
+ length,bufsize);
+
+ chain_fnum = -1;
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a lseek
+****************************************************************************/
+int reply_lseek(char *inbuf,char *outbuf)
+{
+ int cnum,fnum;
+ uint32 startpos;
+ int32 res= -1;
+ int mode,umode;
+ int outsize = 0;
+
+ cnum = SVAL(inbuf,smb_tid);
+ fnum = GETFNUM(inbuf,smb_vwv0);
+
+ CHECK_FNUM(fnum,cnum);
+ CHECK_ERROR(fnum);
+
+ mode = SVAL(inbuf,smb_vwv1) & 3;
+ startpos = IVAL(inbuf,smb_vwv2);
+
+ switch (mode & 3)
+ {
+ case 0: umode = SEEK_SET; break;
+ case 1: umode = SEEK_CUR; break;
+ case 2: umode = SEEK_END; break;
+ default:
+ umode = SEEK_SET; break;
+ }
+
+ res = lseek(Files[fnum].fd,startpos,umode);
+ Files[fnum].pos = res;
+
+ outsize = set_message(outbuf,2,0,True);
+ SIVALS(outbuf,smb_vwv0,res);
+
+ DEBUG(3,("%s lseek fnum=%d cnum=%d ofs=%d mode=%d\n",timestring(),fnum,cnum,startpos,mode));
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a flush
+****************************************************************************/
+int reply_flush(char *inbuf,char *outbuf)
+{
+ int cnum, fnum;
+ int outsize = set_message(outbuf,0,0,True);
+
+ cnum = SVAL(inbuf,smb_tid);
+ fnum = GETFNUM(inbuf,smb_vwv0);
+
+ if (fnum != 0xFFFF) {
+ CHECK_FNUM(fnum,cnum);
+ CHECK_ERROR(fnum);
+ }
+
+ if (fnum == 0xFFFF)
+ {
+ int i;
+ for (i=0;i<MAX_OPEN_FILES;i++)
+ if (OPEN_FNUM(i))
+ sync_file(i);
+ }
+ else
+ sync_file(fnum);
+
+ DEBUG(3,("%s flush fnum=%d\n",timestring(),fnum));
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a exit
+****************************************************************************/
+int reply_exit(char *inbuf,char *outbuf)
+{
+ int outsize = set_message(outbuf,0,0,True);
+ DEBUG(3,("%s exit\n",timestring()));
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a close
+****************************************************************************/
+int reply_close(char *inbuf,char *outbuf)
+{
+ int fnum,cnum;
+ int outsize = 0;
+ time_t mtime;
+ int32 eclass = 0, err = 0;
+
+ outsize = set_message(outbuf,0,0,True);
+
+ cnum = SVAL(inbuf,smb_tid);
+
+ fnum = GETFNUM(inbuf,smb_vwv0);
+ CHECK_FNUM(fnum,cnum);
+
+ if(HAS_CACHED_ERROR(fnum)) {
+ eclass = Files[fnum].wbmpx_ptr->wr_errclass;
+ err = Files[fnum].wbmpx_ptr->wr_error;
+ }
+
+ mtime = make_unix_date3(inbuf+smb_vwv1);
+
+ close_file(fnum);
+
+ /* try and set the date */
+ set_filetime(Files[fnum].name,mtime);
+
+ /* We have a cached error */
+ if(eclass || err)
+ return(ERROR(eclass,err));
+
+ DEBUG(3,("%s close fd=%d fnum=%d cnum=%d (numopen=%d)\n",
+ timestring(),Files[fnum].fd,fnum,cnum,
+ Connections[cnum].num_files_open));
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a writeclose (Core+ protocol)
+****************************************************************************/
+int reply_writeclose(char *inbuf,char *outbuf)
+{
+ int cnum,numtowrite,fnum;
+ int nwritten = -1;
+ int outsize = 0;
+ int startpos;
+ char *data;
+ time_t mtime;
+
+ cnum = SVAL(inbuf,smb_tid);
+ fnum = GETFNUM(inbuf,smb_vwv0);
+
+ CHECK_FNUM(fnum,cnum);
+ CHECK_WRITE(fnum);
+ CHECK_ERROR(fnum);
+
+ numtowrite = SVAL(inbuf,smb_vwv1);
+ startpos = IVAL(inbuf,smb_vwv2);
+ mtime = make_unix_date3(inbuf+smb_vwv4);
+ data = smb_buf(inbuf) + 1;
+
+ if (is_locked(fnum,cnum,numtowrite,startpos))
+ return(ERROR(ERRDOS,ERRlock));
+
+ seek_file(fnum,startpos);
+
+ nwritten = write_file(fnum,data,numtowrite);
+
+ close_file(fnum);
+
+ set_filetime(Files[fnum].name,mtime);
+
+ DEBUG(3,("%s writeclose fnum=%d cnum=%d num=%d wrote=%d (numopen=%d)\n",
+ timestring(),fnum,cnum,numtowrite,nwritten,
+ Connections[cnum].num_files_open));
+
+ if (nwritten <= 0)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ outsize = set_message(outbuf,1,0,True);
+
+ SSVAL(outbuf,smb_vwv0,nwritten);
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a lock
+****************************************************************************/
+int reply_lock(char *inbuf,char *outbuf)
+{
+ int fnum,cnum;
+ int outsize = set_message(outbuf,0,0,True);
+ uint32 count,offset;
+ int eclass;
+ uint32 ecode;
+
+ cnum = SVAL(inbuf,smb_tid);
+ fnum = GETFNUM(inbuf,smb_vwv0);
+
+ CHECK_FNUM(fnum,cnum);
+ CHECK_ERROR(fnum);
+
+ count = IVAL(inbuf,smb_vwv1);
+ offset = IVAL(inbuf,smb_vwv3);
+
+ DEBUG(3,("%s lock fd=%d fnum=%d cnum=%d ofs=%d cnt=%d\n",timestring(),Files[fnum].fd,fnum,cnum,offset,count));
+
+ if(!do_lock( fnum, cnum, count, offset, &eclass, &ecode))
+ return (ERROR(eclass,ecode));
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a unlock
+****************************************************************************/
+int reply_unlock(char *inbuf,char *outbuf)
+{
+ int fnum,cnum;
+ int outsize = set_message(outbuf,0,0,True);
+ uint32 count,offset;
+ int eclass;
+ uint32 ecode;
+
+ cnum = SVAL(inbuf,smb_tid);
+ fnum = GETFNUM(inbuf,smb_vwv0);
+
+ CHECK_FNUM(fnum,cnum);
+ CHECK_ERROR(fnum);
+
+ count = IVAL(inbuf,smb_vwv1);
+ offset = IVAL(inbuf,smb_vwv3);
+
+ if(!do_unlock(fnum, cnum, count, offset, &eclass, &ecode))
+ return (ERROR(eclass,ecode));
+
+ DEBUG(3,("%s unlock fd=%d fnum=%d cnum=%d ofs=%d cnt=%d\n",timestring(),Files[fnum].fd,fnum,cnum,offset,count));
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a tdis
+****************************************************************************/
+int reply_tdis(char *inbuf,char *outbuf)
+{
+ int cnum, uid;
+ int outsize = set_message(outbuf,0,0,True);
+
+ cnum = SVAL(inbuf,smb_tid);
+ uid = SVAL(inbuf,smb_uid);
+
+ Connections[cnum].used = False;
+
+ close_cnum(cnum,uid);
+
+ DEBUG(3,("%s tdis cnum=%d\n",timestring(),cnum));
+
+ return outsize;
+}
+
+
+
+/****************************************************************************
+ reply to a echo
+****************************************************************************/
+int reply_echo(char *inbuf,char *outbuf)
+{
+ int cnum;
+ int smb_reverb = SVAL(inbuf,smb_vwv0);
+ int seq_num;
+ int data_len = smb_buflen(inbuf);
+ int outsize = set_message(outbuf,1,data_len,True);
+
+ cnum = SVAL(inbuf,smb_tid);
+
+ if (cnum != 0xFFFF && !OPEN_CNUM(cnum))
+ {
+ DEBUG(4,("Invalid cnum in echo (%d)\n",cnum));
+ return(ERROR(ERRSRV,ERRinvnid));
+ }
+
+ /* copy any incoming data back out */
+ if (data_len > 0)
+ memcpy(smb_buf(outbuf),smb_buf(inbuf),data_len);
+
+ if (smb_reverb > 100)
+ {
+ DEBUG(0,("large reverb (%d)?? Setting to 100\n",smb_reverb));
+ smb_reverb = 100;
+ }
+
+ for (seq_num =1 ; seq_num <= smb_reverb ; seq_num++)
+ {
+ SSVAL(outbuf,smb_vwv0,seq_num);
+
+ smb_setlen(outbuf,outsize - 4);
+
+ send_smb(Client,outbuf);
+ }
+
+ DEBUG(3,("%s echo %d times cnum=%d\n",timestring(),smb_reverb,cnum));
+
+ return -1;
+}
+
+
+/****************************************************************************
+ reply to a printopen
+****************************************************************************/
+int reply_printopen(char *inbuf,char *outbuf)
+{
+ pstring fname;
+ pstring fname2;
+ int cnum;
+ int fnum = -1;
+ int outsize = 0;
+
+ *fname = *fname2 = 0;
+
+ cnum = SVAL(inbuf,smb_tid);
+
+ if (!CAN_PRINT(cnum))
+ return(ERROR(ERRDOS,ERRnoaccess));
+
+ {
+ pstring s;
+ char *p;
+ StrnCpy(s,smb_buf(inbuf)+1,sizeof(pstring)-1);
+ p = s;
+ while (*p)
+ {
+ if (!(isalnum(*p) || strchr("._-",*p)))
+ *p = 'X';
+ p++;
+ }
+
+ if (strlen(s) > 10) s[10] = 0;
+
+ sprintf(fname,"%s.XXXXXX",s);
+ }
+
+ fnum = find_free_file();
+ if (fnum < 0)
+ return(ERROR(ERRSRV,ERRnofids));
+
+ strcpy(fname2,(char *)mktemp(fname));
+
+ if (!check_name(fname2,cnum))
+ return(ERROR(ERRDOS,ERRnoaccess));
+
+ open_file(fnum,cnum,fname2,O_WRONLY | O_CREAT | O_TRUNC,
+ unix_mode(cnum,0));
+
+ if (!Files[fnum].open)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ /* force it to be a print file */
+ Files[fnum].print_file = True;
+
+ outsize = set_message(outbuf,1,0,True);
+ SSVAL(outbuf,smb_vwv0,fnum);
+
+ DEBUG(3,("%s openprint %s fd=%d fnum=%d cnum=%d\n",timestring(),fname2,Files[fnum].fd,fnum,cnum));
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a printclose
+****************************************************************************/
+int reply_printclose(char *inbuf,char *outbuf)
+{
+ int fnum,cnum;
+ int outsize = set_message(outbuf,0,0,True);
+
+ cnum = SVAL(inbuf,smb_tid);
+ fnum = GETFNUM(inbuf,smb_vwv0);
+
+ CHECK_FNUM(fnum,cnum);
+ CHECK_ERROR(fnum);
+
+ if (!CAN_PRINT(cnum))
+ return(ERROR(ERRDOS,ERRnoaccess));
+
+ close_file(fnum);
+
+ DEBUG(3,("%s printclose fd=%d fnum=%d cnum=%d\n",timestring(),Files[fnum].fd,fnum,cnum));
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a printqueue
+****************************************************************************/
+int reply_printqueue(char *inbuf,char *outbuf)
+{
+ int cnum, uid;
+ int outsize = set_message(outbuf,2,3,True);
+ int max_count = SVAL(inbuf,smb_vwv0);
+ int start_index = SVAL(inbuf,smb_vwv1);
+
+ cnum = SVAL(inbuf,smb_tid);
+ uid = SVAL(inbuf,smb_uid);
+
+/* allow checking the queue for anyone */
+#if 0
+ if (!CAN_PRINT(cnum))
+ return(ERROR(ERRDOS,ERRnoaccess));
+#endif
+
+ SSVAL(outbuf,smb_vwv0,0);
+ SSVAL(outbuf,smb_vwv1,0);
+ CVAL(smb_buf(outbuf),0) = 1;
+ SSVAL(smb_buf(outbuf),1,0);
+
+ DEBUG(3,("%s printqueue cnum=%d start_index=%d max_count=%d\n",
+ timestring(),cnum,start_index,max_count));
+
+ if (!OPEN_CNUM(cnum) || !Connections[cnum].printer)
+ {
+ int i;
+ cnum = -1;
+
+ for (i=0;i<MAX_CONNECTIONS;i++)
+ if (CAN_PRINT(i) && Connections[i].printer)
+ cnum = i;
+
+ if (cnum == -1)
+ for (i=0;i<MAX_CONNECTIONS;i++)
+ if (OPEN_CNUM(i))
+ cnum = i;
+
+ if (!OPEN_CNUM(cnum))
+ return(ERROR(ERRSRV,ERRinvnid));
+
+ DEBUG(5,("connection not open or not a printer, using cnum %d\n",cnum));
+ }
+
+ if (!become_user(cnum,uid))
+ return(ERROR(ERRSRV,ERRinvnid));
+
+ {
+ print_queue_struct *queue = NULL;
+ char *p = smb_buf(outbuf) + 3;
+ int count = get_printqueue(SNUM(cnum),cnum,&queue,NULL);
+ int num_to_get = ABS(max_count);
+ int first = (max_count>0?start_index:start_index+max_count+1);
+ int i;
+
+ if (first >= count)
+ num_to_get = 0;
+ else
+ num_to_get = MIN(num_to_get,count-first);
+
+
+ for (i=first;i<first+num_to_get;i++)
+ {
+ put_dos_date2(p,0,queue[i].time);
+ CVAL(p,4) = (queue[i].status==LPQ_PRINTING?2:3);
+ SSVAL(p,5,queue[i].job);
+ SIVAL(p,7,queue[i].size);
+ CVAL(p,11) = 0;
+ StrnCpy(p+12,queue[i].user,16);
+ p += 28;
+ }
+
+ if (count > 0)
+ {
+ outsize = set_message(outbuf,2,28*count+3,False);
+ SSVAL(outbuf,smb_vwv0,count);
+ SSVAL(outbuf,smb_vwv1,(max_count>0?first+count:first-1));
+ CVAL(smb_buf(outbuf),0) = 1;
+ SSVAL(smb_buf(outbuf),1,28*count);
+ }
+
+ if (queue) free(queue);
+
+ DEBUG(3,("%d entries returned in queue\n",count));
+ }
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a printwrite
+****************************************************************************/
+int reply_printwrite(char *inbuf,char *outbuf)
+{
+ int cnum,numtowrite,fnum;
+ int outsize = set_message(outbuf,0,0,True);
+ char *data;
+
+ cnum = SVAL(inbuf,smb_tid);
+
+ if (!CAN_PRINT(cnum))
+ return(ERROR(ERRDOS,ERRnoaccess));
+
+ fnum = GETFNUM(inbuf,smb_vwv0);
+
+ CHECK_FNUM(fnum,cnum);
+ CHECK_WRITE(fnum);
+ CHECK_ERROR(fnum);
+
+ numtowrite = SVAL(smb_buf(inbuf),1);
+ data = smb_buf(inbuf) + 3;
+
+ if (write_file(fnum,data,numtowrite) != numtowrite)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ DEBUG(3,("%s printwrite fnum=%d cnum=%d num=%d\n",timestring(),fnum,cnum,numtowrite));
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a mkdir
+****************************************************************************/
+int reply_mkdir(char *inbuf,char *outbuf)
+{
+ pstring directory;
+ int cnum;
+ int outsize,ret= -1;
+
+ strcpy(directory,smb_buf(inbuf) + 1);
+ cnum = SVAL(inbuf,smb_tid);
+ unix_convert(directory,cnum);
+
+ if (check_name(directory,cnum))
+ ret = sys_mkdir(directory,unix_mode(cnum,aDIR));
+
+ if (ret < 0)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ outsize = set_message(outbuf,0,0,True);
+
+ DEBUG(3,("%s mkdir %s cnum=%d ret=%d\n",timestring(),directory,cnum,ret));
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a rmdir
+****************************************************************************/
+int reply_rmdir(char *inbuf,char *outbuf)
+{
+ pstring directory;
+ int cnum;
+ int outsize = 0;
+ BOOL ok = False;
+
+ cnum = SVAL(inbuf,smb_tid);
+ strcpy(directory,smb_buf(inbuf) + 1);
+ unix_convert(directory,cnum);
+
+ if (check_name(directory,cnum))
+ {
+ dptr_closepath(directory,SVAL(inbuf,smb_pid));
+ ok = (sys_rmdir(directory) == 0);
+ if (!ok)
+ DEBUG(3,("couldn't remove directory %s : %s\n",
+ directory,strerror(errno)));
+ }
+
+ if (!ok)
+ return(UNIXERROR(ERRDOS,ERRbadpath));
+
+ outsize = set_message(outbuf,0,0,True);
+
+ DEBUG(3,("%s rmdir %s\n",timestring(),directory));
+
+ return(outsize);
+}
+
+
+/*******************************************************************
+resolve wildcards in a filename rename
+********************************************************************/
+static BOOL resolve_wildcards(char *name1,char *name2)
+{
+ fstring root1,root2;
+ fstring ext1,ext2;
+ char *p,*p2;
+
+ name1 = strrchr(name1,'/');
+ name2 = strrchr(name2,'/');
+
+ if (!name1 || !name2) return(False);
+
+ strcpy(root1,name1);
+ strcpy(root2,name2);
+ p = strrchr(root1,'.');
+ if (p) {
+ *p = 0;
+ strcpy(ext1,p+1);
+ } else {
+ strcpy(ext1,"");
+ }
+ p = strrchr(root2,'.');
+ if (p) {
+ *p = 0;
+ strcpy(ext2,p+1);
+ } else {
+ strcpy(ext2,"");
+ }
+
+ p = root1;
+ p2 = root2;
+ while (*p2) {
+ if (*p2 == '?') {
+ *p2 = *p;
+ p2++;
+ } else {
+ p2++;
+ }
+ if (*p) p++;
+ }
+
+ p = ext1;
+ p2 = ext2;
+ while (*p2) {
+ if (*p2 == '?') {
+ *p2 = *p;
+ p2++;
+ } else {
+ p2++;
+ }
+ if (*p) p++;
+ }
+
+ strcpy(name2,root2);
+ if (ext2[0]) {
+ strcat(name2,".");
+ strcat(name2,ext2);
+ }
+
+ return(True);
+}
+
+/*******************************************************************
+check if a user is allowed to rename a file
+********************************************************************/
+static BOOL can_rename(char *fname,int cnum)
+{
+ struct stat sbuf;
+
+ if (!CAN_WRITE(cnum)) return(False);
+
+ if (sys_lstat(fname,&sbuf) != 0) return(False);
+ if (!check_file_sharing(cnum,fname)) return(False);
+
+ return(True);
+}
+
+/****************************************************************************
+ reply to a mv
+****************************************************************************/
+int reply_mv(char *inbuf,char *outbuf)
+{
+ int outsize = 0;
+ pstring name;
+ int cnum;
+ pstring directory;
+ pstring mask,newname;
+ char *p;
+ int count=0;
+ int error = ERRnoaccess;
+ BOOL has_wild;
+ BOOL exists=False;
+
+ *directory = *mask = 0;
+
+ cnum = SVAL(inbuf,smb_tid);
+
+ strcpy(name,smb_buf(inbuf) + 1);
+ strcpy(newname,smb_buf(inbuf) + 3 + strlen(name));
+
+ DEBUG(3,("reply_mv : %s -> %s\n",name,newname));
+
+ unix_convert(name,cnum);
+ unix_convert(newname,cnum);
+
+ p = strrchr(name,'/');
+ if (!p) {
+ strcpy(directory,"./");
+ strcpy(mask,name);
+ } else {
+ *p = 0;
+ strcpy(directory,name);
+ strcpy(mask,p+1);
+ }
+
+ if (is_mangled(mask))
+ check_mangled_stack(mask);
+
+ has_wild = strchr(mask,'*') || strchr(mask,'?');
+
+ if (!has_wild) {
+ strcat(directory,"/");
+ strcat(directory,mask);
+ if (resolve_wildcards(directory,newname) &&
+ can_rename(directory,cnum) &&
+ !file_exist(newname,NULL) &&
+ !sys_rename(directory,newname)) count++;
+ if (!count) exists = file_exist(directory,NULL);
+ if (!count && exists && file_exist(newname,NULL)) {
+ exists = True;
+ error = 183;
+ }
+ } else {
+ void *dirptr = NULL;
+ char *dname;
+ pstring destname;
+
+ if (check_name(directory,cnum))
+ dirptr = OpenDir(directory);
+
+ if (dirptr)
+ {
+ error = ERRbadfile;
+
+ if (strequal(mask,"????????.???"))
+ strcpy(mask,"*");
+
+ while ((dname = ReadDirName(dirptr)))
+ {
+ pstring fname;
+ strcpy(fname,dname);
+
+ if(!mask_match(fname, mask, case_sensitive, False)) continue;
+
+ error = ERRnoaccess;
+ sprintf(fname,"%s/%s",directory,dname);
+ if (!can_rename(fname,cnum)) continue;
+ strcpy(destname,newname);
+
+ if (!resolve_wildcards(fname,destname)) continue;
+
+ if (file_exist(destname,NULL)) {
+ error = 183;
+ continue;
+ }
+ if (!sys_rename(fname,destname)) count++;
+ DEBUG(3,("reply_mv : doing rename on %s -> %s\n",fname,destname));
+ }
+ CloseDir(dirptr);
+ }
+ }
+
+ if (count == 0) {
+ if (exists)
+ return(ERROR(ERRDOS,error));
+ else
+ return(UNIXERROR(ERRDOS,error));
+ }
+
+ outsize = set_message(outbuf,0,0,True);
+
+ return(outsize);
+}
+
+/*******************************************************************
+ copy a file as part of a reply_copy
+ ******************************************************************/
+static BOOL copy_file(char *src,char *dest1,int cnum,int ofun,
+ int count,BOOL target_is_directory)
+{
+ int Access,action;
+ struct stat st;
+ int ret=0;
+ int fnum1,fnum2;
+ pstring dest;
+
+ strcpy(dest,dest1);
+ if (target_is_directory) {
+ char *p = strrchr(src,'/');
+ if (p)
+ p++;
+ else
+ p = src;
+ strcat(dest,"/");
+ strcat(dest,p);
+ }
+
+ if (!file_exist(src,&st)) return(False);
+
+ fnum1 = find_free_file();
+ if (fnum1<0) return(False);
+ open_file_shared(fnum1,cnum,src,(DENY_NONE<<4),
+ 1,0,&Access,&action);
+
+ if (!Files[fnum1].open) return(False);
+
+ if (!target_is_directory && count)
+ ofun = 1;
+
+ fnum2 = find_free_file();
+ if (fnum2<0) {
+ close_file(fnum1);
+ return(False);
+ }
+ open_file_shared(fnum2,cnum,dest,(DENY_NONE<<4)|1,
+ ofun,st.st_mode,&Access,&action);
+
+ if (!Files[fnum2].open) {
+ close_file(fnum1);
+ return(False);
+ }
+
+ if ((ofun&3) == 1) {
+ lseek(Files[fnum2].fd,0,SEEK_END);
+ }
+
+ if (st.st_size)
+ ret = transfer_file(Files[fnum1].fd,Files[fnum2].fd,st.st_size,NULL,0,0);
+
+ close_file(fnum1);
+ close_file(fnum2);
+
+ return(ret == st.st_size);
+}
+
+
+
+/****************************************************************************
+ reply to a file copy.
+ ****************************************************************************/
+int reply_copy(char *inbuf,char *outbuf)
+{
+ int outsize = 0;
+ pstring name;
+ int cnum;
+ pstring directory;
+ pstring mask,newname;
+ char *p;
+ int count=0;
+ int error = ERRnoaccess;
+ BOOL has_wild;
+ BOOL exists=False;
+ int tid2 = SVAL(inbuf,smb_vwv0);
+ int ofun = SVAL(inbuf,smb_vwv1);
+ int flags = SVAL(inbuf,smb_vwv2);
+ BOOL target_is_directory=False;
+
+ *directory = *mask = 0;
+
+ cnum = SVAL(inbuf,smb_tid);
+
+ strcpy(name,smb_buf(inbuf));
+ strcpy(newname,smb_buf(inbuf) + 1 + strlen(name));
+
+ DEBUG(3,("reply_copy : %s -> %s\n",name,newname));
+
+ if (tid2 != cnum) {
+ /* can't currently handle inter share copies XXXX */
+ DEBUG(3,("Rejecting inter-share copy\n"));
+ return(ERROR(ERRSRV,ERRinvdevice));
+ }
+
+ unix_convert(name,cnum);
+ unix_convert(newname,cnum);
+
+ target_is_directory = directory_exist(newname,NULL);
+
+ if ((flags&1) && target_is_directory) {
+ return(ERROR(ERRDOS,ERRbadfile));
+ }
+
+ if ((flags&2) && !target_is_directory) {
+ return(ERROR(ERRDOS,ERRbadpath));
+ }
+
+ if ((flags&(1<<5)) && directory_exist(name,NULL)) {
+ /* wants a tree copy! XXXX */
+ DEBUG(3,("Rejecting tree copy\n"));
+ return(ERROR(ERRSRV,ERRerror));
+ }
+
+ p = strrchr(name,'/');
+ if (!p) {
+ strcpy(directory,"./");
+ strcpy(mask,name);
+ } else {
+ *p = 0;
+ strcpy(directory,name);
+ strcpy(mask,p+1);
+ }
+
+ if (is_mangled(mask))
+ check_mangled_stack(mask);
+
+ has_wild = strchr(mask,'*') || strchr(mask,'?');
+
+ if (!has_wild) {
+ strcat(directory,"/");
+ strcat(directory,mask);
+ if (resolve_wildcards(directory,newname) &&
+ copy_file(directory,newname,cnum,ofun,
+ count,target_is_directory)) count++;
+ if (!count) exists = file_exist(directory,NULL);
+ } else {
+ void *dirptr = NULL;
+ char *dname;
+ pstring destname;
+
+ if (check_name(directory,cnum))
+ dirptr = OpenDir(directory);
+
+ if (dirptr)
+ {
+ error = ERRbadfile;
+
+ if (strequal(mask,"????????.???"))
+ strcpy(mask,"*");
+
+ while ((dname = ReadDirName(dirptr)))
+ {
+ pstring fname;
+ strcpy(fname,dname);
+
+ if(!mask_match(fname, mask, case_sensitive, False)) continue;
+
+ error = ERRnoaccess;
+ sprintf(fname,"%s/%s",directory,dname);
+ strcpy(destname,newname);
+ if (resolve_wildcards(fname,destname) &&
+ copy_file(directory,newname,cnum,ofun,
+ count,target_is_directory)) count++;
+ DEBUG(3,("reply_copy : doing copy on %s -> %s\n",fname,destname));
+ }
+ CloseDir(dirptr);
+ }
+ }
+
+ if (count == 0) {
+ if (exists)
+ return(ERROR(ERRDOS,error));
+ else
+ return(UNIXERROR(ERRDOS,error));
+ }
+
+ outsize = set_message(outbuf,1,0,True);
+ SSVAL(outbuf,smb_vwv0,count);
+
+ return(outsize);
+}
+
+
+
+/****************************************************************************
+ reply to a setdir
+****************************************************************************/
+int reply_setdir(char *inbuf,char *outbuf)
+{
+ int cnum,snum;
+ int outsize = 0;
+ BOOL ok = False;
+ pstring newdir;
+
+ cnum = SVAL(inbuf,smb_tid);
+
+ snum = Connections[cnum].service;
+ if (!CAN_SETDIR(snum))
+ return(ERROR(ERRDOS,ERRnoaccess));
+
+ strcpy(newdir,smb_buf(inbuf) + 1);
+ strlower(newdir);
+
+ if (strlen(newdir) == 0)
+ ok = True;
+ else
+ {
+ ok = directory_exist(newdir,NULL);
+ if (ok)
+ string_set(&Connections[cnum].connectpath,newdir);
+ }
+
+ if (!ok)
+ return(ERROR(ERRDOS,ERRbadpath));
+
+ outsize = set_message(outbuf,0,0,True);
+ CVAL(outbuf,smb_reh) = CVAL(inbuf,smb_reh);
+
+ DEBUG(3,("%s setdir %s cnum=%d\n",timestring(),newdir,cnum));
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a lockingX request
+****************************************************************************/
+int reply_lockingX(char *inbuf,char *outbuf,int length,int bufsize)
+{
+ int smb_com2 = CVAL(inbuf,smb_vwv0);
+ int smb_off2 = SVAL(inbuf,smb_vwv1);
+ int fnum = GETFNUM(inbuf,smb_vwv2);
+ uint16 locktype = SVAL(inbuf,smb_vwv3);
+ uint16 num_ulocks = SVAL(inbuf,smb_vwv6);
+ uint16 num_locks = SVAL(inbuf,smb_vwv7);
+ uint32 count, offset;
+
+ int cnum;
+ int i;
+ char *data;
+ uint32 ecode=0, dummy2;
+ int outsize, eclass=0, dummy1;
+
+ cnum = SVAL(inbuf,smb_tid);
+
+ CHECK_FNUM(fnum,cnum);
+ CHECK_ERROR(fnum);
+
+ data = smb_buf(inbuf);
+ /* Data now points at the beginning of the list
+ of smb_unlkrng structs */
+ for(i = 0; i < (int)num_ulocks; i++) {
+ count = IVAL(data,SMB_LKLEN_OFFSET(i));
+ offset = IVAL(data,SMB_LKOFF_OFFSET(i));
+ if(!do_unlock(fnum,cnum,count,offset,&eclass, &ecode))
+ return ERROR(eclass,ecode);
+ }
+
+ /* Now do any requested locks */
+ data += 10*num_ulocks;
+ /* Data now points at the beginning of the list
+ of smb_lkrng structs */
+ for(i = 0; i < (int)num_locks; i++) {
+ count = IVAL(data,SMB_LKLEN_OFFSET(i));
+ offset = IVAL(data,SMB_LKOFF_OFFSET(i));
+ if(!do_lock(fnum,cnum,count,offset, &eclass, &ecode))
+ break;
+ }
+
+ /* If any of the above locks failed, then we must unlock
+ all of the previous locks (X/Open spec). */
+ if(i != num_locks && num_locks != 0) {
+ for(; i >= 0; i--) {
+ count = IVAL(data,SMB_LKLEN_OFFSET(i));
+ offset = IVAL(data,SMB_LKOFF_OFFSET(i));
+ do_unlock(fnum,cnum,count,offset,&dummy1,&dummy2);
+ }
+ return ERROR(eclass,ecode);
+ }
+
+ outsize = set_message(outbuf,2,0,True);
+
+ CVAL(outbuf,smb_vwv0) = smb_com2;
+ SSVAL(outbuf,smb_vwv1,(outsize+chain_size)-4);
+
+ DEBUG(3,("%s lockingX fnum=%d cnum=%d type=%d num_locks=%d num_ulocks=%d\n",
+ timestring(),fnum,cnum,locktype,num_locks,num_ulocks));
+
+ chain_fnum = fnum;
+
+ if (smb_com2 != 0xFF)
+ outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4,
+ outbuf,outbuf+outsize,
+ length,bufsize);
+
+ chain_fnum = -1;
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a SMBreadbmpx (read block multiplex) request
+****************************************************************************/
+int reply_readbmpx(char *inbuf,char *outbuf,int length,int bufsize)
+{
+ int cnum,fnum;
+ int nread = -1;
+ int total_read;
+ char *data;
+ int32 startpos;
+ int outsize, mincount, maxcount;
+ int max_per_packet;
+ int tcount;
+ int pad;
+
+ /* this function doesn't seem to work - disable by default */
+ if (!lp_readbmpx())
+ return(ERROR(ERRSRV,ERRuseSTD));
+
+ outsize = set_message(outbuf,8,0,True);
+
+ cnum = SVAL(inbuf,smb_tid);
+ fnum = GETFNUM(inbuf,smb_vwv0);
+
+ CHECK_FNUM(fnum,cnum);
+ CHECK_READ(fnum);
+ CHECK_ERROR(fnum);
+
+ startpos = IVAL(inbuf,smb_vwv1);
+ maxcount = SVAL(inbuf,smb_vwv3);
+ mincount = SVAL(inbuf,smb_vwv4);
+
+ data = smb_buf(outbuf);
+ pad = ((int)data)%4;
+ if (pad) pad = 4 - pad;
+ data += pad;
+
+ max_per_packet = bufsize-(outsize+pad);
+ tcount = maxcount;
+ total_read = 0;
+
+ if (is_locked(fnum,cnum,maxcount,startpos))
+ return(ERROR(ERRDOS,ERRlock));
+
+ do
+ {
+ int N = MIN(max_per_packet,tcount-total_read);
+
+ nread = read_file(fnum,data,startpos,N,N,-1,False);
+
+ if (nread <= 0) nread = 0;
+
+ if (nread < N)
+ tcount = total_read + nread;
+
+ set_message(outbuf,8,nread,False);
+ SIVAL(outbuf,smb_vwv0,startpos);
+ SSVAL(outbuf,smb_vwv2,tcount);
+ SSVAL(outbuf,smb_vwv6,nread);
+ SSVAL(outbuf,smb_vwv7,smb_offset(data,outbuf));
+
+ send_smb(Client,outbuf);
+
+ total_read += nread;
+ startpos += nread;
+ }
+ while (total_read < tcount);
+
+ return(-1);
+}
+
+
+/****************************************************************************
+ reply to a SMBwritebmpx (write block multiplex primary) request
+****************************************************************************/
+int reply_writebmpx(char *inbuf,char *outbuf)
+{
+ int cnum,numtowrite,fnum;
+ int nwritten = -1;
+ int outsize = 0;
+ int32 startpos;
+ int tcount, write_through, smb_doff;
+ char *data;
+
+ cnum = SVAL(inbuf,smb_tid);
+ fnum = GETFNUM(inbuf,smb_vwv0);
+
+ CHECK_FNUM(fnum,cnum);
+ CHECK_WRITE(fnum);
+ CHECK_ERROR(fnum);
+
+ tcount = SVAL(inbuf,smb_vwv1);
+ startpos = IVAL(inbuf,smb_vwv3);
+ write_through = BITSETW(inbuf+smb_vwv7,0);
+ numtowrite = SVAL(inbuf,smb_vwv10);
+ smb_doff = SVAL(inbuf,smb_vwv11);
+
+ data = smb_base(inbuf) + smb_doff;
+
+ /* If this fails we need to send an SMBwriteC response,
+ not an SMBwritebmpx - set this up now so we don't forget */
+ CVAL(outbuf,smb_com) = SMBwritec;
+
+ if (is_locked(fnum,cnum,tcount,startpos))
+ return(ERROR(ERRDOS,ERRlock));
+
+ seek_file(fnum,startpos);
+ nwritten = write_file(fnum,data,numtowrite);
+
+ if(lp_syncalways(SNUM(cnum)) || write_through)
+ sync_file(fnum);
+
+ if(nwritten < numtowrite)
+ return(UNIXERROR(ERRHRD,ERRdiskfull));
+
+ /* If the maximum to be written to this file
+ is greater than what we just wrote then set
+ up a secondary struct to be attached to this
+ fd, we will use this to cache error messages etc. */
+ if(tcount > nwritten)
+ {
+ write_bmpx_struct *wbms;
+ if(Files[fnum].wbmpx_ptr != NULL)
+ wbms = Files[fnum].wbmpx_ptr; /* Use an existing struct */
+ else
+ wbms = (write_bmpx_struct *)malloc(sizeof(write_bmpx_struct));
+ if(!wbms)
+ {
+ DEBUG(0,("Out of memory in reply_readmpx\n"));
+ return(ERROR(ERRSRV,ERRnoresource));
+ }
+ wbms->wr_mode = write_through;
+ wbms->wr_discard = False; /* No errors yet */
+ wbms->wr_total_written = nwritten;
+ wbms->wr_errclass = 0;
+ wbms->wr_error = 0;
+ Files[fnum].wbmpx_ptr = wbms;
+ }
+
+ /* We are returning successfully, set the message type back to
+ SMBwritebmpx */
+ CVAL(outbuf,smb_com) = SMBwriteBmpx;
+
+ outsize = set_message(outbuf,1,0,True);
+
+ SSVALS(outbuf,smb_vwv0,-1); /* We don't support smb_remaining */
+
+ DEBUG(3,("%s writebmpx fnum=%d cnum=%d num=%d wrote=%d\n",
+ timestring(),fnum,cnum,numtowrite,nwritten));
+
+ if (write_through && tcount==nwritten) {
+ /* we need to send both a primary and a secondary response */
+ smb_setlen(outbuf,outsize - 4);
+ send_smb(Client,outbuf);
+
+ /* now the secondary */
+ outsize = set_message(outbuf,1,0,True);
+ CVAL(outbuf,smb_com) = SMBwritec;
+ SSVAL(outbuf,smb_vwv0,nwritten);
+ }
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a SMBwritebs (write block multiplex secondary) request
+****************************************************************************/
+int reply_writebs(char *inbuf,char *outbuf)
+{
+ int cnum,numtowrite,fnum;
+ int nwritten = -1;
+ int outsize = 0;
+ int32 startpos;
+ int tcount, write_through, smb_doff;
+ char *data;
+ write_bmpx_struct *wbms;
+ BOOL send_response = False;
+
+ cnum = SVAL(inbuf,smb_tid);
+ fnum = GETFNUM(inbuf,smb_vwv0);
+ CHECK_FNUM(fnum,cnum);
+ CHECK_WRITE(fnum);
+
+ tcount = SVAL(inbuf,smb_vwv1);
+ startpos = IVAL(inbuf,smb_vwv2);
+ numtowrite = SVAL(inbuf,smb_vwv6);
+ smb_doff = SVAL(inbuf,smb_vwv7);
+
+ data = smb_base(inbuf) + smb_doff;
+
+ /* We need to send an SMBwriteC response, not an SMBwritebs */
+ CVAL(outbuf,smb_com) = SMBwritec;
+
+ /* This fd should have an auxiliary struct attached,
+ check that it does */
+ wbms = Files[fnum].wbmpx_ptr;
+ if(!wbms) return(-1);
+
+ /* If write through is set we can return errors, else we must
+ cache them */
+ write_through = wbms->wr_mode;
+
+ /* Check for an earlier error */
+ if(wbms->wr_discard)
+ return -1; /* Just discard the packet */
+
+ seek_file(fnum,startpos);
+ nwritten = write_file(fnum,data,numtowrite);
+
+ if(lp_syncalways(SNUM(cnum)) || write_through)
+ sync_file(fnum);
+
+ if (nwritten < numtowrite)
+ {
+ if(write_through) {
+ /* We are returning an error - we can delete the aux struct */
+ if (wbms) free((char *)wbms);
+ Files[fnum].wbmpx_ptr = NULL;
+ return(ERROR(ERRHRD,ERRdiskfull));
+ }
+ return(CACHE_ERROR(wbms,ERRHRD,ERRdiskfull));
+ }
+
+ /* Increment the total written, if this matches tcount
+ we can discard the auxiliary struct (hurrah !) and return a writeC */
+ wbms->wr_total_written += nwritten;
+ if(wbms->wr_total_written >= tcount)
+ {
+ if (write_through) {
+ outsize = set_message(outbuf,1,0,True);
+ SSVAL(outbuf,smb_vwv0,wbms->wr_total_written);
+ send_response = True;
+ }
+
+ free((char *)wbms);
+ Files[fnum].wbmpx_ptr = NULL;
+ }
+
+ if(send_response)
+ return(outsize);
+
+ return(-1);
+}
+
+
+/****************************************************************************
+ reply to a SMBsetattrE
+****************************************************************************/
+int reply_setattrE(char *inbuf,char *outbuf)
+{
+ int cnum,fnum;
+ struct utimbuf unix_times;
+ int outsize = 0;
+
+ outsize = set_message(outbuf,0,0,True);
+
+ cnum = SVAL(inbuf,smb_tid);
+ fnum = GETFNUM(inbuf,smb_vwv0);
+
+ CHECK_FNUM(fnum,cnum);
+ CHECK_ERROR(fnum);
+
+ /* Convert the DOS times into unix times. Ignore create
+ time as UNIX can't set this.
+ */
+ unix_times.actime = make_unix_date2(inbuf+smb_vwv3);
+ unix_times.modtime = make_unix_date2(inbuf+smb_vwv5);
+
+ /* Set the date on this file */
+ if(sys_utime(Files[fnum].name, &unix_times))
+ return(ERROR(ERRDOS,ERRnoaccess));
+
+ DEBUG(3,("%s reply_setattrE fnum=%d cnum=%d\n",timestring(),fnum,cnum));
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a SMBgetattrE
+****************************************************************************/
+int reply_getattrE(char *inbuf,char *outbuf)
+{
+ int cnum,fnum;
+ struct stat sbuf;
+ int outsize = 0;
+ int mode;
+
+ outsize = set_message(outbuf,11,0,True);
+
+ cnum = SVAL(inbuf,smb_tid);
+ fnum = GETFNUM(inbuf,smb_vwv0);
+
+ CHECK_FNUM(fnum,cnum);
+ CHECK_ERROR(fnum);
+
+ /* Do an fstat on this file */
+ if(fstat(Files[fnum].fd, &sbuf))
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ mode = dos_mode(cnum,Files[fnum].name,&sbuf);
+
+ /* Convert the times into dos times. Set create
+ date to be last modify date as UNIX doesn't save
+ this */
+ put_dos_date2(outbuf,smb_vwv0,sbuf.st_mtime);
+ put_dos_date2(outbuf,smb_vwv2,sbuf.st_atime);
+ put_dos_date2(outbuf,smb_vwv4,sbuf.st_mtime);
+ if (mode & aDIR)
+ {
+ SIVAL(outbuf,smb_vwv6,0);
+ SIVAL(outbuf,smb_vwv8,0);
+ }
+ else
+ {
+ SIVAL(outbuf,smb_vwv6,sbuf.st_size);
+ SIVAL(outbuf,smb_vwv8,ROUNDUP(sbuf.st_size,1024));
+ }
+ SSVAL(outbuf,smb_vwv10, mode);
+
+ DEBUG(3,("%s reply_getattrE fnum=%d cnum=%d\n",timestring(),fnum,cnum));
+
+ return(outsize);
+}
+
+
+
+
+
diff --git a/source/smbd/server.c b/source/smbd/server.c
new file mode 100644
index 00000000000..5d8facef33f
--- /dev/null
+++ b/source/smbd/server.c
@@ -0,0 +1,4300 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Main SMB server routines
+ Copyright (C) Andrew Tridgell 1992-1995
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "loadparm.h"
+#include "pcap.h"
+#include "trans2.h"
+#include "reply.h"
+
+pstring servicesf = CONFIGFILE;
+pstring OriginalDir ="/";
+extern pstring debugf;
+extern pstring sesssetup_user;
+
+char *InBuffer = NULL;
+char *OutBuffer = NULL;
+char *last_inbuf = NULL;
+
+int initial_uid = 0;
+int initial_gid = 0;
+
+BOOL share_mode_pending = False;
+
+/* have I done a become_user? */
+static struct {
+ int cnum, uid;
+} last_user;
+
+/* the last message the was processed */
+int last_message = -1;
+
+/* a useful macro to debug the last message processed */
+#define LAST_MESSAGE() smb_fn_name(last_message)
+
+extern pstring scope;
+extern int DEBUGLEVEL;
+extern int case_default;
+extern BOOL case_sensitive;
+extern BOOL case_preserve;
+extern BOOL use_mangled_map;
+extern BOOL short_case_preserve;
+extern BOOL case_mangle;
+extern time_t smb_last_time;
+
+extern pstring user_socket_options;
+
+connection_struct Connections[MAX_CONNECTIONS];
+files_struct Files[MAX_OPEN_FILES];
+
+extern int Protocol;
+
+int maxxmit = BUFFER_SIZE;
+
+int chain_size = 0;
+
+/* a fnum to use when chaining */
+int chain_fnum = -1;
+
+/* number of open connections */
+static int num_connections_open = 0;
+
+extern fstring remote_machine;
+
+
+/* these can be set by some functions to override the error codes */
+int unix_ERR_class=SUCCESS;
+int unix_ERR_code=0;
+
+
+extern int extra_time_offset;
+
+extern pstring myhostname;
+extern struct in_addr myip;
+
+
+static int find_free_connection(int hash);
+
+#ifdef SMB_PASSWD
+extern void generate_next_challenge(char *challenge);
+extern void set_challenge(char *challenge);
+#endif
+
+/* for readability... */
+#define IS_DOS_READONLY(test_mode) (((test_mode) & aRONLY) != 0)
+#define IS_DOS_DIR(test_mode) (((test_mode) & aDIR) != 0)
+#define IS_DOS_ARCHIVE(test_mode) (((test_mode) & aARCH) != 0)
+#define IS_DOS_SYSTEM(test_mode) (((test_mode) & aSYSTEM) != 0)
+#define IS_DOS_HIDDEN(test_mode) (((test_mode) & aHIDDEN) != 0)
+
+
+
+/****************************************************************************
+ change a dos mode to a unix mode
+ base permission for files:
+ everybody gets read bit set
+ dos readonly is represented in unix by removing everyone's write bit
+ dos archive is represented in unix by the user's execute bit
+ dos system is represented in unix by the group's execute bit
+ dos hidden is represented in unix by the other's execute bit
+ base permission for directories:
+ dos directory is represented in unix by unix's dir bit and the exec bit
+****************************************************************************/
+mode_t unix_mode(int cnum,int dosmode)
+{
+ mode_t result = (S_IRUSR | S_IRGRP | S_IROTH);
+
+ if ( !IS_DOS_READONLY(dosmode) )
+ result |= (S_IWUSR | S_IWGRP | S_IWOTH);
+
+ if (IS_DOS_DIR(dosmode))
+ result |= (S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH | S_IWUSR);
+
+ if (MAP_ARCHIVE(cnum) && IS_DOS_ARCHIVE(dosmode))
+ result |= S_IXUSR;
+
+ if (MAP_SYSTEM(cnum) && IS_DOS_SYSTEM(dosmode))
+ result |= S_IXGRP;
+
+ if (MAP_HIDDEN(cnum) && IS_DOS_HIDDEN(dosmode))
+ result |= S_IXOTH;
+
+ result &= CREATE_MODE(cnum);
+ return(result);
+}
+
+
+/****************************************************************************
+ change a unix mode to a dos mode
+****************************************************************************/
+int dos_mode(int cnum,char *path,struct stat *sbuf)
+{
+ int result = 0;
+
+#if OLD_DOS_MODE
+ if (!CAN_WRITE(cnum) || !((sbuf->st_mode & S_IWOTH) ||
+ Connections[cnum].admin_user ||
+ ((sbuf->st_mode & S_IWUSR) &&
+ Connections[cnum].uid==sbuf->st_uid) ||
+ ((sbuf->st_mode & S_IWGRP) &&
+ in_group(sbuf->st_gid,Connections[cnum].gid,
+ Connections[cnum].ngroups,
+ Connections[cnum].igroups))))
+ result |= aRONLY;
+#else
+ if (CAN_WRITE(cnum) && !lp_alternate_permissions(SNUM(cnum))) {
+ if (!((sbuf->st_mode & S_IWOTH) ||
+ Connections[cnum].admin_user ||
+ ((sbuf->st_mode & S_IWUSR) && Connections[cnum].uid==sbuf->st_uid) ||
+ ((sbuf->st_mode & S_IWGRP) &&
+ in_group(sbuf->st_gid,Connections[cnum].gid,
+ Connections[cnum].ngroups,Connections[cnum].igroups))))
+ result |= aRONLY;
+ } else {
+ if ((sbuf->st_mode & S_IWUSR) == 0)
+ result |= aRONLY;
+ }
+#endif
+
+ if ((sbuf->st_mode & S_IXUSR) != 0)
+ result |= aARCH;
+
+ if (MAP_SYSTEM(cnum) && ((sbuf->st_mode & S_IXGRP) != 0))
+ result |= aSYSTEM;
+
+ if (MAP_HIDDEN(cnum) && ((sbuf->st_mode & S_IXOTH) != 0))
+ result |= aHIDDEN;
+
+ if (S_ISDIR(sbuf->st_mode))
+ result = aDIR | (result & aRONLY);
+
+#if LINKS_READ_ONLY
+ if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
+ result |= aRONLY;
+#endif
+
+ /* hide files with a name starting with a . */
+ if (lp_hide_dot_files(SNUM(cnum)))
+ {
+ char *p = strrchr(path,'/');
+ if (p)
+ p++;
+ else
+ p = path;
+
+ if (p[0] == '.' && p[1] != '.' && p[1] != 0)
+ result |= aHIDDEN;
+ }
+
+ return(result);
+}
+
+
+/*******************************************************************
+chmod a file - but preserve some bits
+********************************************************************/
+int dos_chmod(int cnum,char *fname,int dosmode,struct stat *st)
+{
+ struct stat st1;
+ int mask=0;
+ int tmp;
+ int unixmode;
+
+ if (!st) {
+ st = &st1;
+ if (sys_stat(fname,st)) return(-1);
+ }
+
+ if (S_ISDIR(st->st_mode)) dosmode |= aDIR;
+
+ if (dos_mode(cnum,fname,st) == dosmode) return(0);
+
+ unixmode = unix_mode(cnum,dosmode);
+
+ /* preserve the s bits */
+ mask |= (S_ISUID | S_ISGID);
+
+ /* preserve the t bit */
+#ifdef S_ISVTX
+ mask |= S_ISVTX;
+#endif
+
+ /* possibly preserve the x bits */
+ if (!MAP_ARCHIVE(cnum)) mask |= S_IXUSR;
+ if (!MAP_SYSTEM(cnum)) mask |= S_IXGRP;
+ if (!MAP_HIDDEN(cnum)) mask |= S_IXOTH;
+
+ unixmode |= (st->st_mode & mask);
+
+ /* if we previously had any r bits set then leave them alone */
+ if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
+ unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
+ unixmode |= tmp;
+ }
+
+ /* if we previously had any w bits set then leave them alone
+ if the new mode is not rdonly */
+ if (!IS_DOS_READONLY(dosmode) &&
+ (tmp = st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH))) {
+ unixmode &= ~(S_IWUSR|S_IWGRP|S_IWOTH);
+ unixmode |= tmp;
+ }
+
+ return(chmod(fname,unixmode));
+}
+
+
+/****************************************************************************
+check if two filenames are equal
+
+this needs to be careful about whether we are case sensitive
+****************************************************************************/
+static BOOL fname_equal(char *name1, char *name2)
+{
+ int l1 = strlen(name1);
+ int l2 = strlen(name2);
+
+ /* handle filenames ending in a single dot */
+ if (l1-l2 == 1 && name1[l1-1] == '.' && lp_strip_dot())
+ {
+ BOOL ret;
+ name1[l1-1] = 0;
+ ret = fname_equal(name1,name2);
+ name1[l1-1] = '.';
+ return(ret);
+ }
+
+ if (l2-l1 == 1 && name2[l2-1] == '.' && lp_strip_dot())
+ {
+ BOOL ret;
+ name2[l2-1] = 0;
+ ret = fname_equal(name1,name2);
+ name2[l2-1] = '.';
+ return(ret);
+ }
+
+ /* now normal filename handling */
+ if (case_sensitive)
+ return(strcmp(name1,name2) == 0);
+
+ return(strequal(name1,name2));
+}
+
+
+/****************************************************************************
+mangle the 2nd name and check if it is then equal to the first name
+****************************************************************************/
+static BOOL mangled_equal(char *name1, char *name2)
+{
+ pstring tmpname;
+
+ if (is_8_3(name2))
+ return(False);
+
+ strcpy(tmpname,name2);
+ mangle_name_83(tmpname);
+
+ return(strequal(name1,tmpname));
+}
+
+
+/****************************************************************************
+scan a directory to find a filename, matching without case sensitivity
+
+If the name looks like a mangled name then try via the mangling functions
+****************************************************************************/
+static BOOL scan_directory(char *path, char *name,int snum,BOOL docache)
+{
+ void *cur_dir;
+ char *dname;
+ BOOL mangled;
+ fstring name2;
+
+ mangled = is_mangled(name);
+
+ /* handle null paths */
+ if (*path == 0)
+ path = ".";
+
+ if (docache && (dname = DirCacheCheck(path,name,snum))) {
+ strcpy(name, dname);
+ return(True);
+ }
+
+ if (mangled)
+ check_mangled_stack(name);
+
+ /* open the directory */
+ if (!(cur_dir = OpenDir(path)))
+ {
+ DEBUG(3,("scan dir didn't open dir [%s]\n",path));
+ return(False);
+ }
+
+ /* now scan for matching names */
+ while ((dname = ReadDirName(cur_dir)))
+ {
+ if (*dname == '.' &&
+ (strequal(dname,".") || strequal(dname,"..")))
+ continue;
+
+ strcpy(name2,dname);
+ if (!name_map_mangle(name2,False,snum)) continue;
+
+ if ((mangled && mangled_equal(name,name2))
+ || fname_equal(name, name2))
+ {
+ /* we've found the file, change it's name and return */
+ if (docache) DirCacheAdd(path,name,dname,snum);
+ strcpy(name, dname);
+ CloseDir(cur_dir);
+ return(True);
+ }
+ }
+
+ CloseDir(cur_dir);
+ return(False);
+}
+
+/****************************************************************************
+This routine is called to convert names from the dos namespace to unix
+namespace. It needs to handle any case conversions, mangling, format
+changes etc.
+
+We assume that we have already done a chdir() to the right "root" directory
+for this service.
+
+The function will return False if some part of the name except for the last
+part cannot be resolved
+****************************************************************************/
+BOOL unix_convert(char *name,int cnum)
+{
+ struct stat st;
+ char *start, *end;
+ pstring dirpath;
+
+ *dirpath = 0;
+
+ /* convert to basic unix format - removing \ chars and cleaning it up */
+ unix_format(name);
+ unix_clean_name(name);
+
+ if (!case_sensitive &&
+ (!case_preserve || (is_8_3(name) && !short_case_preserve)))
+ strnorm(name);
+
+ /* names must be relative to the root of the service - trim any leading /.
+ also trim trailing /'s */
+ trim_string(name,"/","/");
+
+ /* check if it's a printer file */
+ if (Connections[cnum].printer)
+ {
+ if ((! *name) || strchr(name,'/') || !is_8_3(name))
+ {
+ fstring name2;
+ sprintf(name2,"%.6s.XXXXXX",remote_machine);
+ strcpy(name,(char *)mktemp(name2));
+ }
+ return(True);
+ }
+
+ /* stat the name - if it exists then we are all done! */
+ if (sys_stat(name,&st) == 0)
+ return(True);
+
+ DEBUG(5,("unix_convert(%s,%d)\n",name,cnum));
+
+ /* a special case - if we don't have any mangling chars and are case
+ sensitive then searching won't help */
+ if (case_sensitive && !is_mangled(name) &&
+ !lp_strip_dot() && !use_mangled_map)
+ return(False);
+
+ /* now we need to recursively match the name against the real
+ directory structure */
+
+ start = name;
+ while (strncmp(start,"./",2) == 0)
+ start += 2;
+
+ /* now match each part of the path name separately, trying the names
+ as is first, then trying to scan the directory for matching names */
+ for (;start;start = (end?end+1:(char *)NULL))
+ {
+ /* pinpoint the end of this section of the filename */
+ end = strchr(start, '/');
+
+ /* chop the name at this point */
+ if (end) *end = 0;
+
+ /* check if the name exists up to this point */
+ if (sys_stat(name, &st) == 0)
+ {
+ /* it exists. it must either be a directory or this must be
+ the last part of the path for it to be OK */
+ if (end && !(st.st_mode & S_IFDIR))
+ {
+ /* an intermediate part of the name isn't a directory */
+ DEBUG(5,("Not a dir %s\n",start));
+ *end = '/';
+ return(False);
+ }
+ }
+ else
+ {
+ pstring rest;
+
+ *rest = 0;
+
+ /* remember the rest of the pathname so it can be restored
+ later */
+ if (end) strcpy(rest,end+1);
+
+
+ /* try to find this part of the path in the directory */
+ if (strchr(start,'?') || strchr(start,'*') ||
+ !scan_directory(dirpath, start, SNUM(cnum), end?True:False))
+ {
+ if (end)
+ {
+ /* an intermediate part of the name can't be found */
+ DEBUG(5,("Intermediate not found %s\n",start));
+ *end = '/';
+ return(False);
+ }
+
+ /* just the last part of the name doesn't exist */
+ /* we may need to strupper() or strlower() it in case
+ this conversion is being used for file creation
+ purposes */
+ /* if the filename is of mixed case then don't normalise it */
+ if (!case_preserve &&
+ (!strhasupper(start) || !strhaslower(start)))
+ strnorm(start);
+
+ /* check on the mangled stack to see if we can recover the
+ base of the filename */
+ if (is_mangled(start))
+ check_mangled_stack(start);
+
+ DEBUG(5,("New file %s\n",start));
+ return(True);
+ }
+
+ /* restore the rest of the string */
+ if (end)
+ {
+ strcpy(start+strlen(start)+1,rest);
+ end = start + strlen(start);
+ }
+ }
+
+ /* add to the dirpath that we have resolved so far */
+ if (*dirpath) strcat(dirpath,"/");
+ strcat(dirpath,start);
+
+ /* restore the / that we wiped out earlier */
+ if (end) *end = '/';
+ }
+
+ /* the name has been resolved */
+ DEBUG(5,("conversion finished %s\n",name));
+ return(True);
+}
+
+
+
+
+#ifdef QUOTAS
+#ifdef LINUX
+/****************************************************************************
+try to get the disk space from disk quotas (LINUX version)
+****************************************************************************/
+/*
+If you didn't make the symlink to the quota package, too bad :(
+*/
+#include "quota/quotactl.c"
+#include "quota/hasquota.c"
+static BOOL disk_quotas(char *path, int *bsize, int *dfree, int *dsize)
+{
+ uid_t euser_id;
+ struct dqblk D;
+ struct stat S;
+ dev_t devno ;
+ struct mntent *mnt;
+ FILE *fp;
+ int found ;
+ int qcmd, fd ;
+ char *qfpathname;
+
+ /* find the block device file */
+
+ if ( stat(path, &S) == -1 )
+ return(False) ;
+
+ devno = S.st_dev ;
+
+ fp = setmntent(MOUNTED,"r");
+ found = False ;
+
+ while ((mnt = getmntent(fp)) != (struct mntent *) 0) {
+ if ( stat(mnt->mnt_dir,&S) == -1 )
+ continue ;
+ if (S.st_dev == devno) {
+ found = True ;
+ break ;
+ }
+ }
+ endmntent(fp) ;
+
+ if ( ! found )
+ return(False) ;
+
+ qcmd = QCMD(Q_GETQUOTA, USRQUOTA);
+
+ if (hasmntopt(mnt, MNTOPT_NOAUTO) || hasmntopt(mnt, MNTOPT_NOQUOTA))
+ return(False) ;
+
+ if (!hasquota(mnt, USRQUOTA, &qfpathname))
+ return(False) ;
+
+ euser_id = geteuid();
+ seteuid(0);
+
+ if (quotactl(qcmd, mnt->mnt_fsname, euser_id, (caddr_t)&D) != 0) {
+ if ((fd = open(qfpathname, O_RDONLY)) < 0) {
+ seteuid(euser_id);
+ return(False);
+ }
+ lseek(fd, (long) dqoff(euser_id), L_SET);
+ switch (read(fd, &D, sizeof(struct dqblk))) {
+ case 0:/* EOF */
+ memset((caddr_t)&D, 0, sizeof(struct dqblk));
+ break;
+ case sizeof(struct dqblk): /* OK */
+ break;
+ default: /* ERROR */
+ close(fd);
+ seteuid(euser_id);
+ return(False);
+ }
+ }
+ seteuid(euser_id);
+ *bsize=1024;
+
+ if (D.dqb_bsoftlimit==0)
+ return(False);
+ if ((D.dqb_curblocks>D.dqb_bsoftlimit)||(D.dqb_curinodes>D.dqb_isoftlimit))
+ {
+ *dfree = 0;
+ *dsize = D.dqb_curblocks;
+ }
+ else {
+ *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
+ *dsize = D.dqb_bsoftlimit;
+ }
+ return (True);
+}
+#else
+#ifndef CRAY
+/****************************************************************************
+try to get the disk space from disk quotas
+****************************************************************************/
+static BOOL disk_quotas(char *path, int *bsize, int *dfree, int *dsize)
+{
+ uid_t user_id, euser_id;
+ int r;
+ char dev_disk[256];
+ struct dqblk D;
+ struct stat S;
+ /* find the block device file */
+ if ((stat(path, &S)<0) ||
+ (devnm(S_IFBLK, S.st_dev, dev_disk, 256, 0)<0)) return (False);
+
+ euser_id = geteuid();
+
+#ifdef USE_SETRES
+ /* for HPUX, real uid must be same as euid to execute quotactl for euid */
+ user_id = getuid();
+ setresuid(euser_id,-1,-1);
+#endif
+ r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D);
+ #ifdef USE_SETRES
+ if (setresuid(user_id,-1,-1))
+ DEBUG(5,("Unable to reset uid to %d\n", user_id));
+ #endif
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+ *bsize = 1024;
+ if (r)
+ {
+ if (errno == EDQUOT)
+ {
+ *dfree =0;
+ *dsize =D.dqb_curblocks;
+ return (True);
+ }
+ else return(False);
+ }
+ /* Use softlimit to determine disk space, except when it has been exceeded */
+ if ((D.dqb_curblocks>D.dqb_bsoftlimit)||(D.dqb_curfiles>D.dqb_fsoftlimit))
+ {
+ *dfree = 0;
+ *dsize = D.dqb_curblocks;
+ }
+ else {
+ *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
+ *dsize = D.dqb_bsoftlimit;
+ }
+ return (True);
+}
+#else
+/****************************************************************************
+try to get the disk space from disk quotas (CRAY VERSION)
+****************************************************************************/
+static BOOL disk_quotas(char *path, int *bsize, int *dfree, int *dsize)
+{
+ struct mntent *mnt;
+ FILE *fd;
+ struct stat sbuf;
+ dev_t devno ;
+ static dev_t devno_cached = 0 ;
+ static char name[MNTMAXSTR] ;
+ struct q_request request ;
+ struct qf_header header ;
+ static int quota_default = 0 ;
+ int found ;
+
+ if ( stat(path,&sbuf) == -1 )
+ return(False) ;
+
+ devno = sbuf.st_dev ;
+
+ if ( devno != devno_cached ) {
+
+ devno_cached = devno ;
+
+ if ((fd = setmntent(KMTAB)) == NULL)
+ return(False) ;
+
+ found = False ;
+
+ while ((mnt = getmntent(fd)) != NULL) {
+
+ if ( stat(mnt->mnt_dir,&sbuf) == -1 )
+ continue ;
+
+ if (sbuf.st_dev == devno) {
+
+ found = True ;
+ break ;
+
+ }
+
+ }
+
+ strcpy(name,mnt->mnt_dir) ;
+ endmntent(fd) ;
+
+ if ( ! found )
+ return(False) ;
+ }
+
+ request.qf_magic = QF_MAGIC ;
+ request.qf_entry.id = geteuid() ;
+
+ if (quotactl(name, Q_GETQUOTA, &request) == -1)
+ return(False) ;
+
+ if ( ! request.user )
+ return(False) ;
+
+ if ( request.qf_entry.user_q.f_quota == QFV_DEFAULT ) {
+
+ if ( ! quota_default ) {
+
+ if ( quotactl(name, Q_GETHEADER, &header) == -1 )
+ return(False) ;
+ else
+ quota_default = header.user_h.def_fq ;
+ }
+
+ *dfree = quota_default ;
+
+ }else if ( request.qf_entry.user_q.f_quota == QFV_PREVENT ) {
+
+ *dfree = 0 ;
+
+ }else{
+
+ *dfree = request.qf_entry.user_q.f_quota ;
+
+ }
+
+ *dsize = request.qf_entry.user_q.f_use ;
+
+ if ( *dfree )
+ *dfree -= *dsize ;
+
+ if ( *dfree < 0 )
+ *dfree = 0 ;
+
+ *bsize = 4096 ; /* Cray blocksize */
+
+ return(True) ;
+
+}
+#endif /* CRAY */
+#endif /* LINUX */
+#endif /* QUOTAS */
+
+
+/****************************************************************************
+normalise for DOS usage
+****************************************************************************/
+static void disk_norm(int *bsize,int *dfree,int *dsize)
+{
+ /* check if the disk is beyond the max disk size */
+ int maxdisksize = lp_maxdisksize();
+ if (maxdisksize) {
+ /* convert to blocks - and don't overflow */
+ maxdisksize = ((maxdisksize*1024)/(*bsize))*1024;
+ if (*dsize > maxdisksize) *dsize = maxdisksize;
+ if (*dfree > maxdisksize) *dfree = maxdisksize-1; /* the -1 should stop
+ applications getting
+ div by 0 errors */
+ }
+
+ while (*dfree > WORDMAX || *dsize > WORDMAX || *bsize < 512)
+ {
+ *dfree /= 2;
+ *dsize /= 2;
+ *bsize *= 2;
+ if (*bsize > WORDMAX )
+ {
+ *bsize = WORDMAX;
+ if (*dsize > WORDMAX)
+ *dsize = WORDMAX;
+ if (*dfree > WORDMAX)
+ *dfree = WORDMAX;
+ break;
+ }
+ }
+}
+
+/****************************************************************************
+ return number of 1K blocks available on a path and total number
+****************************************************************************/
+int disk_free(char *path,int *bsize,int *dfree,int *dsize)
+{
+ char *df_command = lp_dfree_command();
+#ifndef NO_STATFS
+#ifdef USE_STATVFS
+ struct statvfs fs;
+#else
+#ifdef ULTRIX
+ struct fs_data fs;
+#else
+ struct statfs fs;
+#endif
+#endif
+#endif
+
+#ifdef QUOTAS
+ if (disk_quotas(path, bsize, dfree, dsize))
+ {
+ disk_norm(bsize,dfree,dsize);
+ return(((*bsize)/1024)*(*dfree));
+ }
+#endif
+
+
+ /* possibly use system() to get the result */
+ if (df_command && *df_command)
+ {
+ int ret;
+ pstring syscmd;
+ pstring outfile;
+
+ sprintf(outfile,"/tmp/dfree.smb.%d",(int)getpid());
+ sprintf(syscmd,"%s %s",df_command,path);
+ standard_sub_basic(syscmd);
+
+ ret = smbrun(syscmd,outfile);
+ DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret));
+
+ {
+ FILE *f = fopen(outfile,"r");
+ *dsize = 0;
+ *dfree = 0;
+ *bsize = 1024;
+ if (f)
+ {
+ fscanf(f,"%d %d %d",dsize,dfree,bsize);
+ fclose(f);
+ }
+ else
+ DEBUG(0,("Can't open %s\n",outfile));
+ }
+
+ unlink(outfile);
+ disk_norm(bsize,dfree,dsize);
+ return(((*bsize)/1024)*(*dfree));
+ }
+
+#ifdef NO_STATFS
+ DEBUG(1,("Warning - no statfs function\n"));
+ return(1);
+#else
+#ifdef STATFS4
+ if (statfs(path,&fs,sizeof(fs),0) != 0)
+#else
+#ifdef USE_STATVFS
+ if (statvfs(path, &fs))
+#else
+#ifdef STATFS3
+ if (statfs(path,&fs,sizeof(fs)) == -1)
+#else
+ if (statfs(path,&fs) == -1)
+#endif /* STATFS3 */
+#endif /* USE_STATVFS */
+#endif /* STATFS4 */
+ {
+ DEBUG(3,("dfree call failed code errno=%d\n",errno));
+ *bsize = 1024;
+ *dfree = 1;
+ *dsize = 1;
+ return(((*bsize)/1024)*(*dfree));
+ }
+
+#ifdef ULTRIX
+ *bsize = 1024;
+ *dfree = fs.fd_req.bfree;
+ *dsize = fs.fd_req.btot;
+#else
+#ifdef USE_STATVFS
+ *bsize = fs.f_frsize;
+#else
+#ifdef USE_F_FSIZE
+ /* eg: osf1 has f_fsize = fundamental filesystem block size,
+ f_bsize = optimal transfer block size (MX: 94-04-19) */
+ *bsize = fs.f_fsize;
+#else
+ *bsize = fs.f_bsize;
+#endif /* STATFS3 */
+#endif /* USE_STATVFS */
+
+#ifdef STATFS4
+ *dfree = fs.f_bfree;
+#else
+ *dfree = fs.f_bavail;
+#endif /* STATFS4 */
+ *dsize = fs.f_blocks;
+#endif /* ULTRIX */
+
+#if defined(SCO) || defined(ISC) || defined(MIPS)
+ *bsize = 512;
+#endif
+
+/* handle rediculous bsize values - some OSes are broken */
+if ((*bsize) < 512 || (*bsize)>0xFFFF) *bsize = 1024;
+
+ disk_norm(bsize,dfree,dsize);
+
+ if (*bsize < 256)
+ *bsize = 512;
+ if ((*dsize)<1)
+ {
+ DEBUG(0,("dfree seems to be broken on your system\n"));
+ *dsize = 20*1024*1024/(*bsize);
+ *dfree = MAX(1,*dfree);
+ }
+ return(((*bsize)/1024)*(*dfree));
+#endif
+}
+
+
+/****************************************************************************
+wrap it to get filenames right
+****************************************************************************/
+int sys_disk_free(char *path,int *bsize,int *dfree,int *dsize)
+{
+ return(disk_free(dos_to_unix(path,False),bsize,dfree,dsize));
+}
+
+
+
+/****************************************************************************
+check a filename - possibly caling reducename
+
+This is called by every routine before it allows an operation on a filename.
+It does any final confirmation necessary to ensure that the filename is
+a valid one for the user to access.
+****************************************************************************/
+BOOL check_name(char *name,int cnum)
+{
+ BOOL ret;
+
+ errno = 0;
+
+ ret = reduce_name(name,Connections[cnum].connectpath,lp_widelinks(SNUM(cnum)));
+ if (!ret)
+ DEBUG(5,("check_name on %s failed\n",name));
+
+ return(ret);
+}
+
+/****************************************************************************
+check a filename - possibly caling reducename
+****************************************************************************/
+static void check_for_pipe(char *fname)
+{
+ /* special case of pipe opens */
+ char s[10];
+ StrnCpy(s,fname,9);
+ strlower(s);
+ if (strstr(s,"pipe/"))
+ {
+ DEBUG(3,("Rejecting named pipe open for %s\n",fname));
+ unix_ERR_class = ERRSRV;
+ unix_ERR_code = ERRaccess;
+ }
+}
+
+
+/****************************************************************************
+open a file
+****************************************************************************/
+void open_file(int fnum,int cnum,char *fname1,int flags,int mode)
+{
+ pstring fname;
+
+ Files[fnum].open = False;
+ Files[fnum].fd = -1;
+ errno = EPERM;
+
+ strcpy(fname,fname1);
+
+ /* check permissions */
+ if ((flags != O_RDONLY) && !CAN_WRITE(cnum) && !Connections[cnum].printer)
+ {
+ DEBUG(3,("Permission denied opening %s\n",fname));
+ check_for_pipe(fname);
+ return;
+ }
+
+ /* this handles a bug in Win95 - it doesn't say to create the file when it
+ should */
+ if (Connections[cnum].printer)
+ flags |= O_CREAT;
+
+/*
+ if (flags == O_WRONLY)
+ DEBUG(3,("Bug in client? Set O_WRONLY without O_CREAT\n"));
+*/
+
+#if UTIME_WORKAROUND
+ /* XXXX - is this OK?? */
+ /* this works around a utime bug but can cause other problems */
+ if ((flags & (O_WRONLY|O_RDWR)) && (flags & O_CREAT) && !(flags & O_APPEND))
+ sys_unlink(fname);
+#endif
+
+
+ Files[fnum].fd = sys_open(fname,flags,mode);
+
+ if ((Files[fnum].fd>=0) &&
+ Connections[cnum].printer && lp_minprintspace(SNUM(cnum))) {
+ pstring dname;
+ int dum1,dum2,dum3;
+ char *p;
+ strcpy(dname,fname);
+ p = strrchr(dname,'/');
+ if (p) *p = 0;
+ if (sys_disk_free(dname,&dum1,&dum2,&dum3) <
+ lp_minprintspace(SNUM(cnum))) {
+ close(Files[fnum].fd);
+ Files[fnum].fd = -1;
+ sys_unlink(fname);
+ errno = ENOSPC;
+ return;
+ }
+ }
+
+
+ /* Fix for files ending in '.' */
+ if((Files[fnum].fd == -1) && (errno == ENOENT) &&
+ (strchr(fname,'.')==NULL))
+ {
+ strcat(fname,".");
+ Files[fnum].fd = sys_open(fname,flags,mode);
+ }
+
+#if (defined(ENAMETOOLONG) && defined(HAVE_PATHCONF))
+ if ((Files[fnum].fd == -1) && (errno == ENAMETOOLONG))
+ {
+ int max_len;
+ char *p = strrchr(fname, '/');
+
+ if (p == fname) /* name is "/xxx" */
+ {
+ max_len = pathconf("/", _PC_NAME_MAX);
+ p++;
+ }
+ else if ((p == NULL) || (p == fname))
+ {
+ p = fname;
+ max_len = pathconf(".", _PC_NAME_MAX);
+ }
+ else
+ {
+ *p = '\0';
+ max_len = pathconf(fname, _PC_NAME_MAX);
+ *p = '/';
+ p++;
+ }
+ if (strlen(p) > max_len)
+ {
+ char tmp = p[max_len];
+
+ p[max_len] = '\0';
+ if ((Files[fnum].fd = sys_open(fname,flags,mode)) == -1)
+ p[max_len] = tmp;
+ }
+ }
+#endif
+
+ if (Files[fnum].fd < 0)
+ {
+ DEBUG(3,("Error opening file %s (%s) (flags=%d)\n",
+ fname,strerror(errno),flags));
+ check_for_pipe(fname);
+ return;
+ }
+
+ if (Files[fnum].fd >= 0)
+ {
+ struct stat st;
+ Connections[cnum].num_files_open++;
+ fstat(Files[fnum].fd,&st);
+ Files[fnum].mode = st.st_mode;
+ Files[fnum].open_time = time(NULL);
+ Files[fnum].size = 0;
+ Files[fnum].pos = -1;
+ Files[fnum].open = True;
+ Files[fnum].mmap_ptr = NULL;
+ Files[fnum].mmap_size = 0;
+ Files[fnum].can_lock = True;
+ Files[fnum].can_read = ((flags & O_WRONLY)==0);
+ Files[fnum].can_write = ((flags & (O_WRONLY|O_RDWR))!=0);
+ Files[fnum].share_mode = 0;
+ Files[fnum].share_pending = False;
+ Files[fnum].print_file = Connections[cnum].printer;
+ Files[fnum].modified = False;
+ Files[fnum].cnum = cnum;
+ string_set(&Files[fnum].name,fname);
+ Files[fnum].wbmpx_ptr = NULL;
+
+ /*
+ * If the printer is marked as postscript output a leading
+ * file identifier to ensure the file is treated as a raw
+ * postscript file.
+ * This has a similar effect as CtrlD=0 in WIN.INI file.
+ * tim@fsg.com 09/06/94
+ */
+ if (Files[fnum].print_file && POSTSCRIPT(cnum) &&
+ Files[fnum].can_write)
+ {
+ DEBUG(3,("Writing postscript line\n"));
+ write_file(fnum,"%!\n",3);
+ }
+
+ DEBUG(2,("%s %s opened file %s read=%s write=%s (numopen=%d fnum=%d)\n",
+ timestring(),Connections[cnum].user,fname,
+ BOOLSTR(Files[fnum].can_read),BOOLSTR(Files[fnum].can_write),
+ Connections[cnum].num_files_open,fnum));
+
+ }
+
+#if USE_MMAP
+ /* mmap it if read-only */
+ if (!Files[fnum].can_write)
+ {
+ Files[fnum].mmap_size = file_size(fname);
+ Files[fnum].mmap_ptr = (char *)mmap(NULL,Files[fnum].mmap_size,
+ PROT_READ,MAP_SHARED,Files[fnum].fd,0);
+
+ if (Files[fnum].mmap_ptr == (char *)-1 || !Files[fnum].mmap_ptr)
+ {
+ DEBUG(3,("Failed to mmap() %s - %s\n",fname,strerror(errno)));
+ Files[fnum].mmap_ptr = NULL;
+ }
+ }
+#endif
+}
+
+/*******************************************************************
+sync a file
+********************************************************************/
+void sync_file(int fnum)
+{
+#ifndef NO_FSYNC
+ fsync(Files[fnum].fd);
+#endif
+}
+
+/****************************************************************************
+run a file if it is a magic script
+****************************************************************************/
+static void check_magic(int fnum,int cnum)
+{
+ if (!*lp_magicscript(SNUM(cnum)))
+ return;
+
+ DEBUG(5,("checking magic for %s\n",Files[fnum].name));
+
+ {
+ char *p;
+ if (!(p = strrchr(Files[fnum].name,'/')))
+ p = Files[fnum].name;
+ else
+ p++;
+
+ if (!strequal(lp_magicscript(SNUM(cnum)),p))
+ return;
+ }
+
+ {
+ int ret;
+ pstring magic_output;
+ pstring fname;
+ strcpy(fname,Files[fnum].name);
+
+ if (*lp_magicoutput(SNUM(cnum)))
+ strcpy(magic_output,lp_magicoutput(SNUM(cnum)));
+ else
+ sprintf(magic_output,"%s.out",fname);
+
+ chmod(fname,0755);
+ ret = smbrun(fname,magic_output);
+ DEBUG(3,("Invoking magic command %s gave %d\n",fname,ret));
+ unlink(fname);
+ }
+}
+
+
+/****************************************************************************
+close a file - possibly invalidating the read prediction
+****************************************************************************/
+void close_file(int fnum)
+{
+ int cnum = Files[fnum].cnum;
+ invalidate_read_prediction(Files[fnum].fd);
+ Files[fnum].open = False;
+ Connections[cnum].num_files_open--;
+ if(Files[fnum].wbmpx_ptr)
+ {
+ free((char *)Files[fnum].wbmpx_ptr);
+ Files[fnum].wbmpx_ptr = NULL;
+ }
+
+#if USE_MMAP
+ if(Files[fnum].mmap_ptr)
+ {
+ munmap(Files[fnum].mmap_ptr,Files[fnum].mmap_size);
+ Files[fnum].mmap_ptr = NULL;
+ }
+#endif
+
+ if (lp_share_modes(SNUM(cnum)))
+ del_share_mode(fnum);
+
+ if (Files[fnum].modified) {
+ struct stat st;
+ if (fstat(Files[fnum].fd,&st) == 0) {
+ int dosmode = dos_mode(cnum,Files[fnum].name,&st);
+ if (!IS_DOS_ARCHIVE(dosmode)) {
+ dos_chmod(cnum,Files[fnum].name,dosmode | aARCH,&st);
+ }
+ }
+ }
+
+ close(Files[fnum].fd);
+
+ /* NT uses smbclose to start a print - weird */
+ if (Files[fnum].print_file)
+ print_file(fnum);
+
+ /* check for magic scripts */
+ check_magic(fnum,cnum);
+
+ DEBUG(2,("%s %s closed file %s (numopen=%d)\n",
+ timestring(),Connections[cnum].user,Files[fnum].name,
+ Connections[cnum].num_files_open));
+}
+
+enum {AFAIL,AREAD,AWRITE,AALL};
+
+/*******************************************************************
+reproduce the share mode access table
+********************************************************************/
+static int access_table(int new_deny,int old_deny,int old_mode,
+ int share_pid,char *fname)
+{
+ if (new_deny == DENY_ALL || old_deny == DENY_ALL) return(AFAIL);
+
+ if (new_deny == DENY_DOS || old_deny == DENY_DOS) {
+ if (old_deny == new_deny && share_pid == getpid())
+ return(AALL);
+
+ if (old_mode == 0) return(AREAD);
+
+ /* the new smbpub.zip spec says that if the file extension is
+ .com, .dll, .exe or .sym then allow the open. I will force
+ it to read-only as this seems sensible although the spec is
+ a little unclear on this. */
+ if ((fname = strrchr(fname,'.'))) {
+ if (strequal(fname,".com") ||
+ strequal(fname,".dll") ||
+ strequal(fname,".exe") ||
+ strequal(fname,".sym"))
+ return(AREAD);
+ }
+
+ return(AFAIL);
+ }
+
+ switch (new_deny)
+ {
+ case DENY_WRITE:
+ if (old_deny==DENY_WRITE && old_mode==0) return(AREAD);
+ if (old_deny==DENY_READ && old_mode==0) return(AWRITE);
+ if (old_deny==DENY_NONE && old_mode==0) return(AALL);
+ return(AFAIL);
+ case DENY_READ:
+ if (old_deny==DENY_WRITE && old_mode==1) return(AREAD);
+ if (old_deny==DENY_READ && old_mode==1) return(AWRITE);
+ if (old_deny==DENY_NONE && old_mode==1) return(AALL);
+ return(AFAIL);
+ case DENY_NONE:
+ if (old_deny==DENY_WRITE) return(AREAD);
+ if (old_deny==DENY_READ) return(AWRITE);
+ if (old_deny==DENY_NONE) return(AALL);
+ return(AFAIL);
+ }
+ return(AFAIL);
+}
+
+/*******************************************************************
+check if the share mode on a file allows it to be deleted or unlinked
+return True if sharing doesn't prevent the operation
+********************************************************************/
+BOOL check_file_sharing(int cnum,char *fname)
+{
+ int pid=0;
+ int share_mode = get_share_mode_byname(cnum,fname,&pid);
+
+ if (!pid || !share_mode) return(True);
+
+ if (share_mode == DENY_DOS)
+ return(pid == getpid());
+
+ /* XXXX exactly what share mode combinations should be allowed for
+ deleting/renaming? */
+ return(False);
+}
+
+/****************************************************************************
+ C. Hoch 11/22/95
+ Helper for open_file_shared.
+ Truncate a file after checking locking; close file if locked.
+ **************************************************************************/
+static void truncate_unless_locked(int fnum, int cnum)
+{
+ if (Files[fnum].can_write){
+ if (is_locked(fnum,cnum,0x3FFFFFFF,0)){
+ close_file(fnum);
+ errno = EACCES;
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRlock;
+ }
+ else
+ ftruncate(Files[fnum].fd,0);
+ }
+}
+
+
+/****************************************************************************
+open a file with a share mode
+****************************************************************************/
+void open_file_shared(int fnum,int cnum,char *fname,int share_mode,int ofun,
+ int mode,int *Access,int *action)
+{
+ int flags=0;
+ int flags2=0;
+ int deny_mode = (share_mode>>4)&7;
+ struct stat sbuf;
+ BOOL file_existed = file_exist(fname,&sbuf);
+ BOOL fcbopen = False;
+ int share_pid=0;
+
+ Files[fnum].open = False;
+ Files[fnum].fd = -1;
+
+ /* this is for OS/2 EAs - try and say we don't support them */
+ if (strstr(fname,".+,;=[].")) {
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERROR_EAS_NOT_SUPPORTED;
+ return;
+ }
+
+ if ((ofun & 0x3) == 0 && file_existed) {
+ errno = EEXIST;
+ return;
+ }
+
+ if (ofun & 0x10)
+ flags2 |= O_CREAT;
+ if ((ofun & 0x3) == 2)
+ flags2 |= O_TRUNC;
+
+ /* note that we ignore the append flag as
+ append does not mean the same thing under dos and unix */
+
+ switch (share_mode&0xF)
+ {
+ case 1:
+ flags = O_WRONLY;
+ break;
+ case 0xF:
+ fcbopen = True;
+ flags = O_RDWR;
+ break;
+ case 2:
+ flags = O_RDWR;
+ break;
+ default:
+ flags = O_RDONLY;
+ break;
+ }
+
+ if (flags != O_RDONLY && file_existed &&
+ (!CAN_WRITE(cnum) || IS_DOS_READONLY(dos_mode(cnum,fname,&sbuf)))) {
+ if (!fcbopen) {
+ errno = EACCES;
+ return;
+ }
+ flags = O_RDONLY;
+ }
+
+ if (deny_mode > DENY_NONE && deny_mode!=DENY_FCB) {
+ DEBUG(2,("Invalid deny mode %d on file %s\n",deny_mode,fname));
+ errno = EINVAL;
+ return;
+ }
+
+ if (deny_mode == DENY_FCB) deny_mode = DENY_DOS;
+
+ if (lp_share_modes(SNUM(cnum))) {
+ int old_share=0;
+
+ if (file_existed)
+ old_share = get_share_mode(cnum,&sbuf,&share_pid);
+
+ if (share_pid) {
+ /* someone else has a share lock on it, check to see
+ if we can too */
+ int old_open_mode = old_share&0xF;
+ int old_deny_mode = (old_share>>4)&7;
+
+ if (deny_mode > 4 || old_deny_mode > 4 || old_open_mode > 2) {
+ DEBUG(2,("Invalid share mode (%d,%d,%d) on file %s\n",
+ deny_mode,old_deny_mode,old_open_mode,fname));
+ errno = EACCES;
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadshare;
+ return;
+ }
+
+ {
+ int access_allowed = access_table(deny_mode,old_deny_mode,old_open_mode,
+ share_pid,fname);
+
+ if ((access_allowed == AFAIL) ||
+ (access_allowed == AREAD && flags == O_WRONLY) ||
+ (access_allowed == AWRITE && flags == O_RDONLY)) {
+ DEBUG(2,("Share violation on file (%d,%d,%d,%d,%s) = %d\n",
+ deny_mode,old_deny_mode,old_open_mode,
+ share_pid,fname,
+ access_allowed));
+ errno = EACCES;
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadshare;
+ return;
+ }
+
+ if (access_allowed == AREAD)
+ flags = O_RDONLY;
+
+ if (access_allowed == AWRITE)
+ flags = O_WRONLY;
+ }
+ }
+ }
+
+ DEBUG(4,("calling open_file with flags=0x%X flags2=0x%X mode=0%o\n",
+ flags,flags2,mode));
+
+ open_file(fnum,cnum,fname,flags|(flags2&~(O_TRUNC)),mode);
+ if (!Files[fnum].open && flags==O_RDWR && errno!=ENOENT && fcbopen) {
+ flags = O_RDONLY;
+ open_file(fnum,cnum,fname,flags,mode);
+ }
+
+ if (Files[fnum].open) {
+ int open_mode=0;
+ switch (flags) {
+ case O_RDONLY:
+ open_mode = 0;
+ break;
+ case O_RDWR:
+ open_mode = 2;
+ break;
+ case O_WRONLY:
+ open_mode = 1;
+ break;
+ }
+
+ Files[fnum].share_mode = (deny_mode<<4) | open_mode;
+ Files[fnum].share_pending = True;
+
+ if (Access) {
+ (*Access) = open_mode;
+ }
+
+ if (action) {
+ if (file_existed && !(flags2 & O_TRUNC)) *action = 1;
+ if (!file_existed) *action = 2;
+ if (file_existed && (flags2 & O_TRUNC)) *action = 3;
+ }
+
+ if (!share_pid)
+ share_mode_pending = True;
+
+ if ((flags2&O_TRUNC) && file_existed)
+ truncate_unless_locked(fnum,cnum);
+ }
+}
+
+
+
+/*******************************************************************
+check for files that we should now set our share modes on
+********************************************************************/
+static void check_share_modes(void)
+{
+ int i;
+ for (i=0;i<MAX_OPEN_FILES;i++)
+ if(Files[i].open && Files[i].share_pending) {
+ if (lp_share_modes(SNUM(Files[i].cnum))) {
+ int pid=0;
+ get_share_mode_by_fnum(Files[i].cnum,i,&pid);
+ if (!pid) {
+ set_share_mode(i,Files[i].share_mode);
+ Files[i].share_pending = False;
+ }
+ } else {
+ Files[i].share_pending = False;
+ }
+ }
+}
+
+
+/****************************************************************************
+seek a file. Try to avoid the seek if possible
+****************************************************************************/
+int seek_file(int fnum,int pos)
+{
+ int offset = 0;
+ if (Files[fnum].print_file && POSTSCRIPT(Files[fnum].cnum))
+ offset = 3;
+
+ Files[fnum].pos = lseek(Files[fnum].fd,pos+offset,SEEK_SET) - offset;
+ return(Files[fnum].pos);
+}
+
+/****************************************************************************
+read from a file
+****************************************************************************/
+int read_file(int fnum,char *data,int pos,int mincnt,int maxcnt,int timeout,BOOL exact)
+{
+ int ret=0;
+
+ if (!Files[fnum].can_write)
+ {
+ ret = read_predict(Files[fnum].fd,
+ pos,
+ data,
+ NULL,
+ maxcnt);
+
+ data += ret;
+ maxcnt -= ret;
+ mincnt = MAX(mincnt-ret,0);
+ pos += ret;
+ }
+
+#if USE_MMAP
+ if (Files[fnum].mmap_ptr)
+ {
+ int num = MIN(maxcnt,Files[fnum].mmap_size-pos);
+ if (num > 0)
+ {
+ memcpy(data,Files[fnum].mmap_ptr+pos,num);
+ data += num;
+ pos += num;
+ maxcnt -= num;
+ mincnt = MAX(mincnt-num,0);
+ ret += num;
+ }
+ }
+#endif
+
+ if (maxcnt <= 0)
+ return(ret);
+
+ if (seek_file(fnum,pos) != pos)
+ {
+ DEBUG(3,("Failed to seek to %d\n",pos));
+ return(ret);
+ }
+
+ if (maxcnt > 0)
+ ret += read_with_timeout(Files[fnum].fd,
+ data,
+ mincnt,
+ maxcnt,
+ timeout,
+ exact);
+
+ return(ret);
+}
+
+
+/****************************************************************************
+write to a file
+****************************************************************************/
+int write_file(int fnum,char *data,int n)
+{
+ if (!Files[fnum].can_write) {
+ errno = EPERM;
+ return(0);
+ }
+
+ Files[fnum].modified = True;
+
+ return(write_data(Files[fnum].fd,data,n));
+}
+
+
+static int old_umask = 022;
+
+/****************************************************************************
+load parameters specific to a connection/service
+****************************************************************************/
+BOOL become_service(int cnum,BOOL do_chdir)
+{
+ extern char magic_char;
+ static int last_cnum = -1;
+ int snum;
+
+ if (!OPEN_CNUM(cnum))
+ {
+ last_cnum = -1;
+ return(False);
+ }
+
+ Connections[cnum].lastused = smb_last_time;
+
+ snum = SNUM(cnum);
+
+ if (do_chdir &&
+ ChDir(Connections[cnum].connectpath) != 0 &&
+ ChDir(Connections[cnum].origpath) != 0)
+ {
+ DEBUG(0,("%s chdir (%s) failed cnum=%d\n",timestring(),
+ Connections[cnum].connectpath,cnum));
+ return(False);
+ }
+
+ if (cnum == last_cnum)
+ return(True);
+
+ last_cnum = cnum;
+
+ case_default = lp_defaultcase(snum);
+ case_preserve = lp_preservecase(snum);
+ short_case_preserve = lp_shortpreservecase(snum);
+ case_mangle = lp_casemangle(snum);
+ case_sensitive = lp_casesensitive(snum);
+ magic_char = lp_magicchar(snum);
+ use_mangled_map = (*lp_mangled_map(snum) ? True:False);
+ return(True);
+}
+
+
+/****************************************************************************
+ become the specified uid
+****************************************************************************/
+static BOOL become_uid(int uid)
+{
+ if (initial_uid != 0)
+ return(True);
+
+#ifdef AIX
+ {
+ /* AIX 3 stuff - inspired by a code fragment in wu-ftpd */
+ priv_t priv;
+
+ priv.pv_priv[0] = 0;
+ priv.pv_priv[1] = 0;
+ if (setpriv(PRIV_SET|PRIV_INHERITED|PRIV_EFFECTIVE|PRIV_BEQUEATH,
+ &priv, sizeof(priv_t)) < 0 ||
+ setuidx(ID_REAL|ID_EFFECTIVE, (uid_t)uid) < 0 ||
+ seteuid((uid_t)uid) < 0)
+ DEBUG(1,("Can't set uid (AIX3)"));
+ }
+#endif
+
+#ifdef USE_SETRES
+ if (setresuid(-1,uid,-1) != 0)
+#else
+ if ((seteuid(uid) != 0) &&
+ (setuid(uid) != 0))
+#endif
+ {
+ DEBUG(0,("Couldn't set uid %d currently set to (%d,%d)\n",
+ uid,getuid(), geteuid()));
+ if (uid > 32000)
+ DEBUG(0,("Looks like your OS doesn't like high uid values - try using a different account\n"));
+ return(False);
+ }
+
+ if (((uid == -1) || (uid == 65535)) && geteuid() != uid)
+ {
+ DEBUG(0,("Invalid uid -1. perhaps you have a account with uid 65535?\n"));
+ return(False);
+ }
+
+ return(True);
+}
+
+
+/****************************************************************************
+ become the specified gid
+****************************************************************************/
+static BOOL become_gid(int gid)
+{
+ if (initial_uid != 0)
+ return(True);
+
+#ifdef USE_SETRES
+ if (setresgid(-1,gid,-1) != 0)
+#else
+ if (setgid(gid) != 0)
+#endif
+ {
+ DEBUG(0,("Couldn't set gid %d currently set to (%d,%d)\n",
+ gid,getgid(),getegid()));
+ if (gid > 32000)
+ DEBUG(0,("Looks like your OS doesn't like high gid values - try using a different account\n"));
+ return(False);
+ }
+
+ return(True);
+}
+
+
+/****************************************************************************
+ become the specified uid and gid
+****************************************************************************/
+static BOOL become_id(int uid,int gid)
+{
+ return(become_gid(gid) && become_uid(uid));
+}
+
+/****************************************************************************
+become the guest user
+****************************************************************************/
+static BOOL become_guest(void)
+{
+ BOOL ret;
+ static struct passwd *pass=NULL;
+
+ if (initial_uid != 0)
+ return(True);
+
+ if (!pass)
+ pass = Get_Pwnam(lp_guestaccount(-1),True);
+ if (!pass) return(False);
+
+ ret = become_id(pass->pw_uid,pass->pw_gid);
+
+ if (!ret)
+ DEBUG(1,("Failed to become guest. Invalid guest account?\n"));
+
+ last_user.cnum = -2;
+
+ return(ret);
+}
+
+/*******************************************************************
+check if a username is OK
+********************************************************************/
+static BOOL check_user_ok(int cnum,user_struct *vuser,int snum)
+{
+ int i;
+ for (i=0;i<Connections[cnum].uid_cache.entries;i++)
+ if (Connections[cnum].uid_cache.list[i] == vuser->uid) return(True);
+
+ if (!user_ok(vuser->name,snum)) return(False);
+
+ i = Connections[cnum].uid_cache.entries % UID_CACHE_SIZE;
+ Connections[cnum].uid_cache.list[i] = vuser->uid;
+
+ if (Connections[cnum].uid_cache.entries < UID_CACHE_SIZE)
+ Connections[cnum].uid_cache.entries++;
+
+ return(True);
+}
+
+
+/****************************************************************************
+ become the user of a connection number
+****************************************************************************/
+BOOL become_user(int cnum, int uid)
+{
+ int new_umask;
+ user_struct *vuser;
+ int snum,gid;
+ int ngroups;
+ gid_t *groups;
+
+ if (last_user.cnum == cnum && last_user.uid == uid) {
+ DEBUG(4,("Skipping become_user - already user\n"));
+ return(True);
+ }
+
+ unbecome_user();
+
+ if (!OPEN_CNUM(cnum)) {
+ DEBUG(2,("Connection %d not open\n",cnum));
+ return(False);
+ }
+
+ snum = Connections[cnum].service;
+
+ if (Connections[cnum].force_user ||
+ lp_security() == SEC_SHARE ||
+ !(vuser = get_valid_user_struct(uid)) ||
+ !check_user_ok(cnum,vuser,snum)) {
+ uid = Connections[cnum].uid;
+ gid = Connections[cnum].gid;
+ groups = Connections[cnum].groups;
+ ngroups = Connections[cnum].ngroups;
+ } else {
+ if (!vuser) {
+ DEBUG(2,("Invalid vuid used %d\n",uid));
+ return(False);
+ }
+ uid = vuser->uid;
+ if(!*lp_force_group(snum))
+ gid = vuser->gid;
+ else
+ gid = Connections[cnum].gid;
+ groups = vuser->user_groups;
+ ngroups = vuser->user_ngroups;
+ }
+
+ if (initial_uid == 0)
+ {
+ if (!become_gid(gid)) return(False);
+
+#ifndef NO_SETGROUPS
+ if (!IS_IPC(cnum)) {
+ /* groups stuff added by ih/wreu */
+ if (ngroups > 0)
+ if (setgroups(ngroups,groups)<0)
+ DEBUG(0,("setgroups call failed!\n"));
+ }
+#endif
+
+ if (!Connections[cnum].admin_user && !become_uid(uid))
+ return(False);
+ }
+
+ new_umask = 0777 & ~CREATE_MODE(cnum);
+ old_umask = umask(new_umask);
+
+ last_user.cnum = cnum;
+ last_user.uid = uid;
+
+ DEBUG(5,("become_user uid=(%d,%d) gid=(%d,%d) new_umask=0%o\n",
+ getuid(),geteuid(),getgid(),getegid(),new_umask));
+
+ return(True);
+}
+
+/****************************************************************************
+ unbecome the user of a connection number
+****************************************************************************/
+BOOL unbecome_user(void )
+{
+ if (last_user.cnum == -1)
+ return(False);
+
+ ChDir(OriginalDir);
+
+ umask(old_umask);
+
+ if (initial_uid == 0)
+ {
+#ifdef USE_SETRES
+ setresuid(-1,getuid(),-1);
+ setresgid(-1,getgid(),-1);
+#else
+ if (seteuid(initial_uid) != 0)
+ setuid(initial_uid);
+ setgid(initial_gid);
+#endif
+ }
+#ifdef NO_EID
+ if (initial_uid == 0)
+ DEBUG(2,("Running with no EID\n"));
+ initial_uid = getuid();
+ initial_gid = getgid();
+#else
+ if (geteuid() != initial_uid)
+ {
+ DEBUG(0,("Warning: You appear to have a trapdoor uid system\n"));
+ initial_uid = geteuid();
+ }
+ if (getegid() != initial_gid)
+ {
+ DEBUG(0,("Warning: You appear to have a trapdoor gid system\n"));
+ initial_gid = getegid();
+ }
+#endif
+
+ if (ChDir(OriginalDir) != 0)
+ DEBUG(0,("%s chdir(%s) failed in unbecome_user\n",
+ timestring(),OriginalDir));
+
+ DEBUG(5,("unbecome_user now uid=(%d,%d) gid=(%d,%d)\n",
+ getuid(),geteuid(),getgid(),getegid()));
+
+ last_user.cnum = -1;
+
+ return(True);
+}
+
+/****************************************************************************
+ find a service entry
+****************************************************************************/
+int find_service(char *service)
+{
+ int iService;
+
+ string_sub(service,"\\","/");
+
+ iService = lp_servicenumber(service);
+
+ /* now handle the special case of a home directory */
+ if (iService < 0)
+ {
+ char *phome_dir = get_home_dir(service);
+ DEBUG(3,("checking for home directory %s gave %s\n",service,
+ phome_dir?phome_dir:"(NULL)"));
+ if (phome_dir)
+ {
+ int iHomeService;
+ if ((iHomeService = lp_servicenumber(HOMES_NAME)) >= 0)
+ {
+ lp_add_home(service,iHomeService,phome_dir);
+ iService = lp_servicenumber(service);
+ }
+ }
+ }
+
+ /* If we still don't have a service, attempt to add it as a printer. */
+ if (iService < 0)
+ {
+ int iPrinterService;
+
+ if ((iPrinterService = lp_servicenumber(PRINTERS_NAME)) >= 0)
+ {
+ char *pszTemp;
+
+ DEBUG(3,("checking whether %s is a valid printer name...\n", service));
+ pszTemp = PRINTCAP;
+ if ((pszTemp != NULL) && pcap_printername_ok(service, pszTemp))
+ {
+ DEBUG(3,("%s is a valid printer name\n", service));
+ DEBUG(3,("adding %s as a printer service\n", service));
+ lp_add_printer(service,iPrinterService);
+ iService = lp_servicenumber(service);
+ if (iService < 0)
+ DEBUG(0,("failed to add %s as a printer service!\n", service));
+ }
+ else
+ DEBUG(3,("%s is not a valid printer name\n", service));
+ }
+ }
+
+ /* just possibly it's a default service? */
+ if (iService < 0)
+ {
+ char *defservice = lp_defaultservice();
+ if (defservice && *defservice && !strequal(defservice,service)) {
+ iService = find_service(defservice);
+ if (iService >= 0) {
+ string_sub(service,"_","/");
+ iService = lp_add_service(service,iService);
+ }
+ }
+ }
+
+ if (iService >= 0)
+ if (!VALID_SNUM(iService))
+ {
+ DEBUG(0,("Invalid snum %d for %s\n",iService,service));
+ iService = -1;
+ }
+
+ if (iService < 0)
+ DEBUG(3,("find_service() failed to find service %s\n", service));
+
+ return (iService);
+}
+
+
+/****************************************************************************
+ create an error packet from a cached error.
+****************************************************************************/
+int cached_error_packet(char *inbuf,char *outbuf,int fnum,int line)
+{
+ write_bmpx_struct *wbmpx = Files[fnum].wbmpx_ptr;
+
+ int32 eclass = wbmpx->wr_errclass;
+ int32 err = wbmpx->wr_error;
+
+ /* We can now delete the auxiliary struct */
+ free((char *)wbmpx);
+ Files[fnum].wbmpx_ptr = NULL;
+ return error_packet(inbuf,outbuf,eclass,err,line);
+}
+
+
+struct
+{
+ int unixerror;
+ int smbclass;
+ int smbcode;
+} unix_smb_errmap[] =
+{
+ {EPERM,ERRDOS,ERRnoaccess},
+ {EACCES,ERRDOS,ERRnoaccess},
+ {ENOENT,ERRDOS,ERRbadfile},
+ {EIO,ERRHRD,ERRgeneral},
+ {EBADF,ERRSRV,ERRsrverror},
+ {EINVAL,ERRSRV,ERRsrverror},
+ {EEXIST,ERRDOS,ERRfilexists},
+ {ENFILE,ERRDOS,ERRnofids},
+ {EMFILE,ERRDOS,ERRnofids},
+ {ENOSPC,ERRHRD,ERRdiskfull},
+#ifdef EDQUOT
+ {EDQUOT,ERRHRD,ERRdiskfull},
+#endif
+#ifdef ENOTEMPTY
+ {ENOTEMPTY,ERRDOS,ERRnoaccess},
+#endif
+#ifdef EXDEV
+ {EXDEV,ERRDOS,ERRdiffdevice},
+#endif
+ {EROFS,ERRHRD,ERRnowrite},
+ {0,0,0}
+};
+
+
+/****************************************************************************
+ create an error packet from errno
+****************************************************************************/
+int unix_error_packet(char *inbuf,char *outbuf,int def_class,uint32 def_code,int line)
+{
+ int eclass=def_class;
+ int ecode=def_code;
+ int i=0;
+
+ if (unix_ERR_class != SUCCESS)
+ {
+ eclass = unix_ERR_class;
+ ecode = unix_ERR_code;
+ unix_ERR_class = SUCCESS;
+ unix_ERR_code = 0;
+ }
+ else
+ {
+ while (unix_smb_errmap[i].smbclass != 0)
+ {
+ if (unix_smb_errmap[i].unixerror == errno)
+ {
+ eclass = unix_smb_errmap[i].smbclass;
+ ecode = unix_smb_errmap[i].smbcode;
+ break;
+ }
+ i++;
+ }
+ }
+
+ return(error_packet(inbuf,outbuf,eclass,ecode,line));
+}
+
+
+/****************************************************************************
+ create an error packet. Normally called using the ERROR() macro
+****************************************************************************/
+int error_packet(char *inbuf,char *outbuf,int error_class,uint32 error_code,int line)
+{
+ int outsize = set_message(outbuf,0,0,True);
+ int cmd;
+ cmd = CVAL(inbuf,smb_com);
+
+ CVAL(outbuf,smb_rcls) = error_class;
+ SSVAL(outbuf,smb_err,error_code);
+
+ DEBUG(3,("%s error packet at line %d cmd=%d (%s) eclass=%d ecode=%d\n",
+ timestring(),
+ line,
+ (int)CVAL(inbuf,smb_com),
+ smb_fn_name(CVAL(inbuf,smb_com)),
+ error_class,
+ error_code));
+
+ if (errno != 0)
+ DEBUG(3,("error string = %s\n",strerror(errno)));
+
+ return(outsize);
+}
+
+
+#ifndef SIGCLD_IGNORE
+/****************************************************************************
+this prevents zombie child processes
+****************************************************************************/
+static int sig_cld()
+{
+ static int depth = 0;
+ if (depth != 0)
+ {
+ DEBUG(0,("ERROR: Recursion in sig_cld? Perhaps you need `#define USE_WAITPID'?\n"));
+ depth=0;
+ return(0);
+ }
+ depth++;
+
+ BlockSignals(True);
+ DEBUG(5,("got SIGCLD\n"));
+
+#ifdef USE_WAITPID
+ while (waitpid((pid_t)-1,(int *)NULL, WNOHANG) > 0);
+#endif
+
+ /* Stop zombies */
+ /* Stevens, Adv. Unix Prog. says that on system V you must call
+ wait before reinstalling the signal handler, because the kernel
+ calls the handler from within the signal-call when there is a
+ child that has exited. This would lead to an infinite recursion
+ if done vice versa. */
+
+#ifndef DONT_REINSTALL_SIG
+#ifdef SIGCLD_IGNORE
+ signal(SIGCLD, SIG_IGN);
+#else
+ signal(SIGCLD, SIGNAL_CAST sig_cld);
+#endif
+#endif
+
+#ifndef USE_WAITPID
+ while (wait3(WAIT3_CAST1 NULL, WNOHANG, WAIT3_CAST2 NULL) > 0);
+#endif
+ depth--;
+ BlockSignals(False);
+ return 0;
+}
+#endif
+
+/****************************************************************************
+ this is called when the client exits abruptly
+ **************************************************************************/
+static int sig_pipe()
+{
+ exit_server("Got sigpipe\n");
+ return(0);
+}
+
+/****************************************************************************
+ open the socket communication
+****************************************************************************/
+static BOOL open_sockets(BOOL is_daemon,int port)
+{
+ extern int Client;
+
+ if (is_daemon)
+ {
+ int s;
+ struct sockaddr addr;
+ int in_addrlen = sizeof(addr);
+
+ /* Stop zombies */
+#ifdef SIGCLD_IGNORE
+ signal(SIGCLD, SIG_IGN);
+#else
+ signal(SIGCLD, SIGNAL_CAST sig_cld);
+#endif
+
+ /* open an incoming socket */
+ s = open_socket_in(SOCK_STREAM, port, 0);
+ if (s == -1)
+ return(False);
+
+ /* ready to listen */
+ if (listen(s, 5) == -1)
+ {
+ DEBUG(0,("listen: %s",strerror(errno)));
+ close(s);
+ return False;
+ }
+
+ /* now accept incoming connections - forking a new process
+ for each incoming connection */
+ DEBUG(2,("waiting for a connection\n"));
+ while (1)
+ {
+ Client = accept(s,&addr,&in_addrlen);
+
+ if (Client == -1 && errno == EINTR)
+ continue;
+
+ if (Client == -1)
+ {
+ DEBUG(0,("accept: %s",strerror(errno)));
+ return False;
+ }
+
+#ifdef NO_FORK_DEBUG
+#ifndef NO_SIGNAL_TEST
+ signal(SIGPIPE, SIGNAL_CAST sig_pipe);
+ signal(SIGCLD, SIGNAL_CAST SIG_DFL);
+#endif
+ return True;
+#else
+ if (Client != -1 && fork()==0)
+ {
+#ifndef NO_SIGNAL_TEST
+ signal(SIGPIPE, SIGNAL_CAST sig_pipe);
+ signal(SIGCLD, SIGNAL_CAST SIG_DFL);
+#endif
+ /* close our standard file descriptors */
+ close_low_fds();
+
+ set_socket_options(Client,"SO_KEEPALIVE");
+ set_socket_options(Client,user_socket_options);
+
+ return True;
+ }
+ close(Client); /* The parent doesn't need this socket */
+#endif
+ }
+ }
+ else
+ {
+ /* We will abort gracefully when the client or remote system
+ goes away */
+#ifndef NO_SIGNAL_TEST
+ signal(SIGPIPE, SIGNAL_CAST sig_pipe);
+#endif
+ Client = dup(0);
+
+ /* close our standard file descriptors */
+ close_low_fds();
+
+ set_socket_options(Client,"SO_KEEPALIVE");
+ set_socket_options(Client,user_socket_options);
+ }
+
+ return True;
+}
+
+
+/****************************************************************************
+check if a snum is in use
+****************************************************************************/
+BOOL snum_used(int snum)
+{
+ int i;
+ for (i=0;i<MAX_CONNECTIONS;i++)
+ if (OPEN_CNUM(i) && (SNUM(i) == snum))
+ return(True);
+ return(False);
+}
+
+/****************************************************************************
+ reload the services file
+ **************************************************************************/
+BOOL reload_services(BOOL test)
+{
+ BOOL ret;
+
+ if (lp_loaded())
+ {
+ pstring fname;
+ strcpy(fname,lp_configfile());
+ if (file_exist(fname,NULL) && !strcsequal(fname,servicesf))
+ {
+ strcpy(servicesf,fname);
+ test = False;
+ }
+ }
+
+ reopen_logs();
+
+ if (test && !lp_file_list_changed())
+ return(True);
+
+ lp_killunused(snum_used);
+
+ ret = lp_load(servicesf,False);
+
+ /* perhaps the config filename is now set */
+ if (!test)
+ reload_services(True);
+
+ reopen_logs();
+
+ {
+ extern int Client;
+ if (Client != -1) {
+ set_socket_options(Client,"SO_KEEPALIVE");
+ set_socket_options(Client,user_socket_options);
+ }
+ }
+
+ create_mangled_stack(lp_mangledstack());
+
+ /* this forces service parameters to be flushed */
+ become_service(-1,True);
+
+ return(ret);
+}
+
+
+
+/****************************************************************************
+this prevents zombie child processes
+****************************************************************************/
+static int sig_hup()
+{
+ BlockSignals(True);
+ DEBUG(0,("Got SIGHUP\n"));
+ reload_services(False);
+#ifndef DONT_REINSTALL_SIG
+ signal(SIGHUP,SIGNAL_CAST sig_hup);
+#endif
+ BlockSignals(False);
+ return(0);
+}
+
+/****************************************************************************
+Setup the groups a user belongs to.
+****************************************************************************/
+int setup_groups(char *user, int uid, int gid, int *p_ngroups,
+ int **p_igroups, gid_t **p_groups)
+{
+ if (-1 == initgroups(user,gid))
+ {
+ if (getuid() == 0)
+ {
+ DEBUG(0,("Unable to initgroups!\n"));
+ if (gid < 0 || gid > 16000 || uid < 0 || uid > 16000)
+ DEBUG(0,("This is probably a problem with the account %s\n",user));
+ }
+ }
+ else
+ {
+ int i,ngroups;
+ int *igroups;
+ gid_t grp = 0;
+ ngroups = getgroups(0,&grp);
+ if (ngroups <= 0)
+ ngroups = 32;
+ igroups = (int *)malloc(sizeof(int)*ngroups);
+ for (i=0;i<ngroups;i++)
+ igroups[i] = 0x42424242;
+ ngroups = getgroups(ngroups,(gid_t *)igroups);
+
+ if (igroups[0] == 0x42424242)
+ ngroups = 0;
+
+ *p_ngroups = ngroups;
+
+ /* The following bit of code is very strange. It is due to the
+ fact that some OSes use int* and some use gid_t* for
+ getgroups, and some (like SunOS) use both, one in prototypes,
+ and one in man pages and the actual code. Thus we detect it
+ dynamically using some very ugly code */
+ if (ngroups > 0)
+ {
+ /* does getgroups return ints or gid_t ?? */
+ static BOOL groups_use_ints = True;
+
+ if (groups_use_ints &&
+ ngroups == 1 &&
+ SVAL(igroups,2) == 0x4242)
+ groups_use_ints = False;
+
+ for (i=0;groups_use_ints && i<ngroups;i++)
+ if (igroups[i] == 0x42424242)
+ groups_use_ints = False;
+
+ if (groups_use_ints)
+ {
+ *p_igroups = igroups;
+ *p_groups = (gid_t *)igroups;
+ }
+ else
+ {
+ gid_t *groups = (gid_t *)igroups;
+ igroups = (int *)malloc(sizeof(int)*ngroups);
+ for (i=0;i<ngroups;i++)
+ igroups[i] = groups[i];
+ *p_igroups = igroups;
+ *p_groups = (gid_t *)groups;
+ }
+ }
+ DEBUG(3,("%s is in %d groups\n",user,ngroups));
+ for (i=0;i<ngroups;i++)
+ DEBUG(3,("%d ",igroups[i]));
+ DEBUG(3,("\n"));
+ }
+ return 0;
+}
+
+/****************************************************************************
+ make a connection to a service
+****************************************************************************/
+int make_connection(char *service,char *user,char *password, int pwlen, char *dev,int vuid)
+{
+ int cnum;
+ int snum;
+ struct passwd *pass = NULL;
+ connection_struct *pcon;
+ BOOL guest = False;
+ BOOL force = False;
+ static BOOL first_connection = True;
+
+ strlower(service);
+
+ snum = find_service(service);
+ if (snum < 0)
+ {
+ if (strequal(service,"IPC$"))
+ {
+ DEBUG(3,("%s refusing IPC connection\n",timestring()));
+ return(-3);
+ }
+
+ DEBUG(0,("%s couldn't find service %s\n",timestring(),service));
+ return(-2);
+ }
+
+ if (strequal(service,HOMES_NAME))
+ {
+ if (*user && Get_Pwnam(user,True))
+ return(make_connection(user,user,password,pwlen,dev,vuid));
+
+ if (validated_username(vuid))
+ {
+ strcpy(user,validated_username(vuid));
+ return(make_connection(user,user,password,pwlen,dev,vuid));
+ }
+ }
+
+ if (!lp_snum_ok(snum) || !check_access(snum)) {
+ return(-4);
+ }
+
+ /* you can only connect to the IPC$ service as an ipc device */
+ if (strequal(service,"IPC$"))
+ strcpy(dev,"IPC");
+
+ if (*dev == '?' || !*dev)
+ {
+ if (lp_print_ok(snum))
+ strcpy(dev,"LPT1:");
+ else
+ strcpy(dev,"A:");
+ }
+
+ /* if the request is as a printer and you can't print then refuse */
+ strupper(dev);
+ if (!lp_print_ok(snum) && (strncmp(dev,"LPT",3) == 0)) {
+ DEBUG(1,("Attempt to connect to non-printer as a printer\n"));
+ return(-6);
+ }
+
+ /* lowercase the user name */
+ strlower(user);
+
+ /* add it as a possible user name */
+ add_session_user(service);
+
+ /* shall we let them in? */
+ if (!authorise_login(snum,user,password,pwlen,&guest,&force,vuid))
+ {
+ DEBUG(2,("%s invalid username/password for %s\n",timestring(),service));
+ return(-1);
+ }
+
+ cnum = find_free_connection(str_checksum(service) + str_checksum(user));
+ if (cnum < 0)
+ {
+ DEBUG(0,("%s couldn't find free connection\n",timestring()));
+ return(-1);
+ }
+
+ pcon = &Connections[cnum];
+ bzero((char *)pcon,sizeof(*pcon));
+
+ /* find out some info about the user */
+ pass = Get_Pwnam(user,True);
+
+ if (pass == NULL)
+ {
+ DEBUG(0,("%s couldn't find account %s\n",timestring(),user));
+ return(-7);
+ }
+
+ pcon->read_only = lp_readonly(snum);
+
+ {
+ pstring list;
+ StrnCpy(list,lp_readlist(snum),sizeof(pstring)-1);
+ string_sub(list,"%S",service);
+
+ if (user_in_list(user,list))
+ pcon->read_only = True;
+
+ StrnCpy(list,lp_writelist(snum),sizeof(pstring)-1);
+ string_sub(list,"%S",service);
+
+ if (user_in_list(user,list))
+ pcon->read_only = False;
+ }
+
+ /* admin user check */
+ if (user_in_list(user,lp_admin_users(snum)) &&
+ !pcon->read_only)
+ {
+ pcon->admin_user = True;
+ DEBUG(0,("%s logged in as admin user (root privileges)\n",user));
+ }
+ else
+ pcon->admin_user = False;
+
+ pcon->force_user = force;
+ pcon->uid = pass->pw_uid;
+ pcon->gid = pass->pw_gid;
+ pcon->num_files_open = 0;
+ pcon->lastused = time(NULL);
+ pcon->service = snum;
+ pcon->used = True;
+ pcon->printer = (strncmp(dev,"LPT",3) == 0);
+ pcon->ipc = (strncmp(dev,"IPC",3) == 0);
+ pcon->dirptr = NULL;
+ string_set(&pcon->dirpath,"");
+ string_set(&pcon->user,user);
+
+#if HAVE_GETGRNAM
+ if (*lp_force_group(snum))
+ {
+ struct group *gptr = (struct group *)getgrnam(lp_force_group(snum));
+ if (gptr)
+ {
+ pcon->gid = gptr->gr_gid;
+ DEBUG(3,("Forced group %s\n",lp_force_group(snum)));
+ }
+ else
+ DEBUG(1,("Couldn't find group %s\n",lp_force_group(snum)));
+ }
+#endif
+
+ if (*lp_force_user(snum))
+ {
+ struct passwd *pass2;
+ fstring fuser;
+ strcpy(fuser,lp_force_user(snum));
+ pass2 = (struct passwd *)Get_Pwnam(fuser,True);
+ if (pass2)
+ {
+ pcon->uid = pass2->pw_uid;
+ string_set(&pcon->user,fuser);
+ strcpy(user,fuser);
+ pcon->force_user = True;
+ DEBUG(3,("Forced user %s\n",fuser));
+ }
+ else
+ DEBUG(1,("Couldn't find user %s\n",fuser));
+ }
+
+ {
+ pstring s;
+ strcpy(s,lp_pathname(snum));
+ standard_sub(cnum,s);
+ string_set(&pcon->connectpath,s);
+ DEBUG(3,("Connect path is %s\n",s));
+ }
+
+ /* groups stuff added by ih */
+ pcon->ngroups = 0;
+ pcon->groups = NULL;
+
+ if (!IS_IPC(cnum))
+ {
+ /* Find all the groups this uid is in and store them. Used by become_user() */
+ setup_groups(pcon->user,pcon->uid,pcon->gid,&pcon->ngroups,&pcon->igroups,&pcon->groups);
+
+ /* check number of connections */
+ if (!claim_connection(cnum,
+ lp_servicename(SNUM(cnum)),
+ lp_max_connections(SNUM(cnum)),False))
+ {
+ DEBUG(1,("too many connections - rejected\n"));
+ return(-8);
+ }
+
+ if (lp_status(SNUM(cnum)))
+ claim_connection(cnum,"STATUS.",MAXSTATUS,first_connection);
+
+ first_connection = False;
+ } /* IS_IPC */
+
+ pcon->open = True;
+
+ /* execute any "root preexec = " line */
+ if (*lp_rootpreexec(SNUM(cnum)))
+ {
+ pstring cmd;
+ strcpy(cmd,lp_rootpreexec(SNUM(cnum)));
+ standard_sub(cnum,cmd);
+ DEBUG(5,("cmd=%s\n",cmd));
+ smbrun(cmd,NULL);
+ }
+
+ if (!become_user(cnum,pcon->uid))
+ {
+ DEBUG(0,("Can't become connected user!\n"));
+ pcon->open = False;
+ if (!IS_IPC(cnum)) {
+ yield_connection(cnum,
+ lp_servicename(SNUM(cnum)),
+ lp_max_connections(SNUM(cnum)));
+ if (lp_status(SNUM(cnum))) yield_connection(cnum,"STATUS.",MAXSTATUS);
+ }
+ return(-1);
+ }
+
+ if (ChDir(pcon->connectpath) != 0)
+ {
+ DEBUG(0,("Can't change directory to %s\n",pcon->connectpath));
+ pcon->open = False;
+ unbecome_user();
+ if (!IS_IPC(cnum)) {
+ yield_connection(cnum,
+ lp_servicename(SNUM(cnum)),
+ lp_max_connections(SNUM(cnum)));
+ if (lp_status(SNUM(cnum))) yield_connection(cnum,"STATUS.",MAXSTATUS);
+ }
+ return(-5);
+ }
+
+ string_set(&pcon->origpath,pcon->connectpath);
+
+#if SOFTLINK_OPTIMISATION
+ /* resolve any soft links early */
+ {
+ pstring s;
+ strcpy(s,pcon->connectpath);
+ GetWd(s);
+ string_set(&pcon->connectpath,s);
+ ChDir(pcon->connectpath);
+ }
+#endif
+
+ num_connections_open++;
+ add_session_user(user);
+
+ /* execute any "preexec = " line */
+ if (*lp_preexec(SNUM(cnum)))
+ {
+ pstring cmd;
+ strcpy(cmd,lp_preexec(SNUM(cnum)));
+ standard_sub(cnum,cmd);
+ smbrun(cmd,NULL);
+ }
+
+ /* we've finished with the sensitive stuff */
+ unbecome_user();
+
+ {
+ extern struct from_host Client_info;
+ DEBUG(IS_IPC(cnum)?3:1,("%s %s (%s) connect to service %s as user %s (uid=%d,gid=%d) (pid %d)\n",
+ timestring(),
+ Client_info.name,Client_info.addr,
+ lp_servicename(SNUM(cnum)),user,
+ pcon->uid,
+ pcon->gid,
+ (int)getpid()));
+ }
+
+ return(cnum);
+}
+
+
+/****************************************************************************
+ find first available file slot
+****************************************************************************/
+int find_free_file(void )
+{
+ int i;
+ for (i=1;i<MAX_OPEN_FILES;i++)
+ if (!Files[i].open)
+ return(i);
+ DEBUG(1,("ERROR! Out of file structures - perhaps increase MAX_OPEN_FILES?\n"));
+ return(-1);
+}
+
+/****************************************************************************
+ find first available connection slot, starting from a random position.
+The randomisation stops problems with the server dieing and clients
+thinking the server is still available.
+****************************************************************************/
+static int find_free_connection(int hash )
+{
+ int i;
+ BOOL used=False;
+ hash = (hash % (MAX_CONNECTIONS-2))+1;
+
+ again:
+
+ for (i=hash+1;i!=hash;)
+ {
+ if (!Connections[i].open && Connections[i].used == used)
+ {
+ DEBUG(3,("found free connection number %d\n",i));
+ return(i);
+ }
+ i++;
+ if (i == MAX_CONNECTIONS)
+ i = 1;
+ }
+
+ if (!used)
+ {
+ used = !used;
+ goto again;
+ }
+
+ DEBUG(1,("ERROR! Out of connection structures\n"));
+ return(-1);
+}
+
+
+/****************************************************************************
+reply for the core protocol
+****************************************************************************/
+int reply_corep(char *outbuf)
+{
+ int outsize = set_message(outbuf,1,0,True);
+
+ Protocol = PROTOCOL_CORE;
+
+ return outsize;
+}
+
+
+/****************************************************************************
+reply for the coreplus protocol
+****************************************************************************/
+int reply_coreplus(char *outbuf)
+{
+ int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
+ int outsize = set_message(outbuf,13,0,True);
+ SSVAL(outbuf,smb_vwv5,raw); /* tell redirector we support
+ readbraw and writebraw (possibly) */
+ CVAL(outbuf,smb_flg) = 0x81; /* Reply, SMBlockread, SMBwritelock supported */
+ SSVAL(outbuf,smb_vwv1,0x1); /* user level security, don't encrypt */
+
+ Protocol = PROTOCOL_COREPLUS;
+
+ return outsize;
+}
+
+
+/****************************************************************************
+reply for the lanman 1.0 protocol
+****************************************************************************/
+int reply_lanman1(char *outbuf)
+{
+ int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
+ int secword=0;
+ BOOL doencrypt = SMBENCRYPT();
+ time_t t = time(NULL);
+
+ if (lp_security()>=SEC_USER) secword |= 1;
+ if (doencrypt) secword |= 2;
+
+ set_message(outbuf,13,doencrypt?8:0,True);
+ SSVAL(outbuf,smb_vwv1,secword);
+#ifdef SMB_PASSWD
+ /* Create a token value and add it to the outgoing packet. */
+ if (doencrypt)
+ generate_next_challenge(smb_buf(outbuf));
+#endif
+
+ Protocol = PROTOCOL_LANMAN1;
+
+ if (lp_security() == SEC_SERVER && server_cryptkey(outbuf)) {
+ DEBUG(3,("using password server validation\n"));
+#ifdef SMB_PASSWD
+ if (doencrypt) set_challenge(smb_buf(outbuf));
+#endif
+ }
+
+ CVAL(outbuf,smb_flg) = 0x81; /* Reply, SMBlockread, SMBwritelock supported */
+ SSVAL(outbuf,smb_vwv2,maxxmit);
+ SSVAL(outbuf,smb_vwv3,lp_maxmux()); /* maxmux */
+ SSVAL(outbuf,smb_vwv4,1);
+ SSVAL(outbuf,smb_vwv5,raw); /* tell redirector we support
+ readbraw writebraw (possibly) */
+ SIVAL(outbuf,smb_vwv6,getpid());
+ SSVAL(outbuf,smb_vwv10, TimeDiff(t)/60);
+
+ put_dos_date(outbuf,smb_vwv8,t);
+
+ return (smb_len(outbuf)+4);
+}
+
+
+/****************************************************************************
+reply for the lanman 2.0 protocol
+****************************************************************************/
+int reply_lanman2(char *outbuf)
+{
+ int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
+ int secword=0;
+ BOOL doencrypt = SMBENCRYPT();
+ time_t t = time(NULL);
+
+ if (lp_security()>=SEC_USER) secword |= 1;
+ if (doencrypt) secword |= 2;
+
+ set_message(outbuf,13,doencrypt?8:0,True);
+ SSVAL(outbuf,smb_vwv1,secword);
+#ifdef SMB_PASSWD
+ /* Create a token value and add it to the outgoing packet. */
+ if (doencrypt)
+ generate_next_challenge(smb_buf(outbuf));
+#endif
+
+ SIVAL(outbuf,smb_vwv6,getpid());
+
+ Protocol = PROTOCOL_LANMAN2;
+
+ if (lp_security() == SEC_SERVER && server_cryptkey(outbuf)) {
+ DEBUG(3,("using password server validation\n"));
+#ifdef SMB_PASSWD
+ if (doencrypt) set_challenge(smb_buf(outbuf));
+#endif
+ }
+
+ CVAL(outbuf,smb_flg) = 0x81; /* Reply, SMBlockread, SMBwritelock supported */
+ SSVAL(outbuf,smb_vwv2,maxxmit);
+ SSVAL(outbuf,smb_vwv3,lp_maxmux());
+ SSVAL(outbuf,smb_vwv4,1);
+ SSVAL(outbuf,smb_vwv5,raw); /* readbraw and/or writebraw */
+ SSVAL(outbuf,smb_vwv10, TimeDiff(t)/60);
+ put_dos_date(outbuf,smb_vwv8,t);
+
+ return (smb_len(outbuf)+4);
+}
+
+/****************************************************************************
+reply for the nt protocol
+****************************************************************************/
+int reply_nt1(char *outbuf)
+{
+ int capabilities=0x300; /* has dual names + lock_and_read */
+ int secword=0;
+ BOOL doencrypt = SMBENCRYPT();
+
+ if (lp_security()>=SEC_USER) secword |= 1;
+ if (doencrypt) secword |= 2;
+
+ set_message(outbuf,17,doencrypt?8:0,True);
+ CVAL(outbuf,smb_vwv1) = secword;
+#ifdef SMB_PASSWD
+ /* Create a token value and add it to the outgoing packet. */
+ if (doencrypt) {
+ generate_next_challenge(smb_buf(outbuf));
+ /* Tell the nt machine how long the challenge is. */
+ SSVALS(outbuf,smb_vwv16+1,8);
+ }
+#endif
+
+ SIVAL(outbuf,smb_vwv7+1,getpid()); /* session key */
+
+ Protocol = PROTOCOL_NT1;
+
+ if (lp_security() == SEC_SERVER && server_cryptkey(outbuf)) {
+ DEBUG(3,("using password server validation\n"));
+#ifdef SMB_PASSWD
+ if (doencrypt) set_challenge(smb_buf(outbuf));
+#endif
+ }
+
+ if (lp_readraw() && lp_writeraw())
+ capabilities |= 1;
+
+ SSVAL(outbuf,smb_vwv1+1,lp_maxmux()); /* maxmpx */
+ SSVAL(outbuf,smb_vwv2+1,1); /* num vcs */
+ SIVAL(outbuf,smb_vwv3+1,0xFFFF); /* max buffer */
+ SIVAL(outbuf,smb_vwv5+1,0xFFFF); /* raw size */
+ SIVAL(outbuf,smb_vwv9+1,capabilities); /* capabilities */
+ put_long_date(outbuf+smb_vwv11+1,time(NULL));
+ SSVALS(outbuf,smb_vwv15+1,TimeDiff(time(NULL))/60);
+
+ return (smb_len(outbuf)+4);
+}
+
+
+/* these are the protocol lists used for auto architecture detection:
+
+WinNT 3.51:
+protocol [PC NETWORK PROGRAM 1.0]
+protocol [XENIX CORE]
+protocol [MICROSOFT NETWORKS 1.03]
+protocol [LANMAN1.0]
+protocol [Windows for Workgroups 3.1a]
+protocol [LM1.2X002]
+protocol [LANMAN2.1]
+protocol [NT LM 0.12]
+
+Win95:
+protocol [PC NETWORK PROGRAM 1.0]
+protocol [XENIX CORE]
+protocol [MICROSOFT NETWORKS 1.03]
+protocol [LANMAN1.0]
+protocol [Windows for Workgroups 3.1a]
+protocol [LM1.2X002]
+protocol [LANMAN2.1]
+protocol [NT LM 0.12]
+
+OS/2:
+protocol [PC NETWORK PROGRAM 1.0]
+protocol [XENIX CORE]
+protocol [LANMAN1.0]
+protocol [LM1.2X002]
+protocol [LANMAN2.1]
+*/
+
+/*
+ * Modified to recognize the architecture of the remote machine better.
+ *
+ * This appears to be the matrix of which protocol is used by which
+ * MS product.
+ Protocol WfWg Win95 WinNT OS/2
+ PC NETWORK PROGRAM 1.0 1 1 1 1
+ XENIX CORE 2 2
+ MICROSOFT NETWORKS 3.0 2 2
+ DOS LM1.2X002 3 3
+ MICROSOFT NETWORKS 1.03 3
+ DOS LANMAN2.1 4 4
+ LANMAN1.0 4 3
+ Windows for Workgroups 3.1a 5 5 5
+ LM1.2X002 6 4
+ LANMAN2.1 7 5
+ NT LM 0.12 6 8
+ *
+ * tim@fsg.com 09/29/95
+ */
+
+#define ARCH_WFWG 0x3 /* This is a fudge because WfWg is like Win95 */
+#define ARCH_WIN95 0x2
+#define ARCH_OS2 0xC /* Again OS/2 is like NT */
+#define ARCH_WINNT 0x8
+#define ARCH_SAMBA 0x10
+
+#define ARCH_ALL 0x1F
+
+/* List of supported protocols, most desired first */
+struct {
+ char *proto_name;
+ char *short_name;
+ int (*proto_reply_fn)(char *);
+ int protocol_level;
+} supported_protocols[] = {
+ {"NT LANMAN 1.0", "NT1", reply_nt1, PROTOCOL_NT1},
+ {"NT LM 0.12", "NT1", reply_nt1, PROTOCOL_NT1},
+ {"LM1.2X002", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
+ {"Samba", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
+ {"DOS LM1.2X002", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
+ {"LANMAN1.0", "LANMAN1", reply_lanman1, PROTOCOL_LANMAN1},
+ {"MICROSOFT NETWORKS 3.0", "LANMAN1", reply_lanman1, PROTOCOL_LANMAN1},
+ {"MICROSOFT NETWORKS 1.03", "COREPLUS", reply_coreplus, PROTOCOL_COREPLUS},
+ {"PC NETWORK PROGRAM 1.0", "CORE", reply_corep, PROTOCOL_CORE},
+ {NULL,NULL},
+};
+
+
+/****************************************************************************
+ reply to a negprot
+****************************************************************************/
+static int reply_negprot(char *inbuf,char *outbuf)
+{
+ extern fstring remote_arch;
+ int outsize = set_message(outbuf,1,0,True);
+ int Index=0;
+ int choice= -1;
+ int protocol;
+ char *p;
+ int bcc = SVAL(smb_buf(inbuf),-2);
+ int arch = ARCH_ALL;
+
+ p = smb_buf(inbuf)+1;
+ while (p < (smb_buf(inbuf) + bcc))
+ {
+ Index++;
+ DEBUG(3,("Requested protocol [%s]\n",p));
+ if (strcsequal(p,"Windows for Workgroups 3.1a"))
+ arch &= ( ARCH_WFWG | ARCH_WIN95 | ARCH_WINNT );
+ else if (strcsequal(p,"DOS LM1.2X002"))
+ arch &= ( ARCH_WFWG | ARCH_WIN95 );
+ else if (strcsequal(p,"DOS LANMAN2.1"))
+ arch &= ( ARCH_WFWG | ARCH_WIN95 );
+ else if (strcsequal(p,"NT LM 0.12"))
+ arch &= ( ARCH_WIN95 | ARCH_WINNT );
+ else if (strcsequal(p,"LANMAN2.1"))
+ arch &= ( ARCH_WINNT | ARCH_OS2 );
+ else if (strcsequal(p,"LM1.2X002"))
+ arch &= ( ARCH_WINNT | ARCH_OS2 );
+ else if (strcsequal(p,"MICROSOFT NETWORKS 1.03"))
+ arch &= ARCH_WINNT;
+ else if (strcsequal(p,"XENIX CORE"))
+ arch &= ( ARCH_WINNT | ARCH_OS2 );
+ else if (strcsequal(p,"Samba")) {
+ arch = ARCH_SAMBA;
+ break;
+ }
+
+ p += strlen(p) + 2;
+ }
+
+ switch ( arch ) {
+ case ARCH_SAMBA:
+ strcpy(remote_arch,"Samba");
+ break;
+ case ARCH_WFWG:
+ strcpy(remote_arch,"WfWg");
+ break;
+ case ARCH_WIN95:
+ strcpy(remote_arch,"Win95");
+ break;
+ case ARCH_WINNT:
+ strcpy(remote_arch,"WinNT");
+ break;
+ case ARCH_OS2:
+ strcpy(remote_arch,"OS2");
+ break;
+ default:
+ strcpy(remote_arch,"UNKNOWN");
+ break;
+ }
+
+ /* possibly reload - change of architecture */
+ reload_services(True);
+
+ /* a special case to stop password server loops */
+ if (Index == 1 && strequal(remote_machine,myhostname) &&
+ lp_security()==SEC_SERVER)
+ exit_server("Password server loop!");
+
+ /* Check for protocols, most desirable first */
+ for (protocol = 0; supported_protocols[protocol].proto_name; protocol++)
+ {
+ p = smb_buf(inbuf)+1;
+ Index = 0;
+ if (lp_maxprotocol() >= supported_protocols[protocol].protocol_level)
+ while (p < (smb_buf(inbuf) + bcc))
+ {
+ if (strequal(p,supported_protocols[protocol].proto_name))
+ choice = Index;
+ Index++;
+ p += strlen(p) + 2;
+ }
+ if(choice != -1)
+ break;
+ }
+
+ SSVAL(outbuf,smb_vwv0,choice);
+ if(choice != -1) {
+ extern fstring remote_proto;
+ strcpy(remote_proto,supported_protocols[protocol].short_name);
+ reload_services(True);
+ outsize = supported_protocols[protocol].proto_reply_fn(outbuf);
+ DEBUG(3,("Selected protocol %s\n",supported_protocols[protocol].proto_name));
+ }
+ else {
+ DEBUG(0,("No protocol supported !\n"));
+ }
+ SSVAL(outbuf,smb_vwv0,choice);
+
+ DEBUG(5,("%s negprot index=%d\n",timestring(),choice));
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ parse a connect packet
+****************************************************************************/
+void parse_connect(char *buf,char *service,char *user,char *password,int *pwlen,char *dev)
+{
+ char *p = smb_buf(buf) + 1;
+ char *p2;
+
+ DEBUG(4,("parsing connect string %s\n",p));
+
+ p2 = strrchr(p,'\\');
+ if (p2 == NULL)
+ strcpy(service,p);
+ else
+ strcpy(service,p2+1);
+
+ p += strlen(p) + 2;
+
+ strcpy(password,p);
+ *pwlen = strlen(password);
+
+ p += strlen(p) + 2;
+
+ strcpy(dev,p);
+
+ *user = 0;
+ p = strchr(service,'%');
+ if (p != NULL)
+ {
+ *p = 0;
+ strcpy(user,p+1);
+ }
+}
+
+
+/****************************************************************************
+close all open files for a connection
+****************************************************************************/
+static void close_open_files(int cnum)
+{
+ int i;
+ for (i=0;i<MAX_OPEN_FILES;i++)
+ if( Files[i].cnum == cnum && Files[i].open) {
+ close_file(i);
+ }
+}
+
+
+
+/****************************************************************************
+close a cnum
+****************************************************************************/
+void close_cnum(int cnum, int uid)
+{
+ extern struct from_host Client_info;
+
+ DirCacheFlush(SNUM(cnum));
+
+ unbecome_user();
+
+ if (!OPEN_CNUM(cnum))
+ {
+ DEBUG(0,("Can't close cnum %d\n",cnum));
+ return;
+ }
+
+ DEBUG(IS_IPC(cnum)?3:1,("%s %s (%s) closed connection to service %s\n",
+ timestring(),
+ Client_info.name,Client_info.addr,
+ lp_servicename(SNUM(cnum))));
+
+ yield_connection(cnum,
+ lp_servicename(SNUM(cnum)),
+ lp_max_connections(SNUM(cnum)));
+
+ if (lp_status(SNUM(cnum)))
+ yield_connection(cnum,"STATUS.",MAXSTATUS);
+
+ close_open_files(cnum);
+ dptr_closecnum(cnum);
+
+ /* execute any "postexec = " line */
+ if (*lp_postexec(SNUM(cnum)) && become_user(cnum,uid))
+ {
+ pstring cmd;
+ strcpy(cmd,lp_postexec(SNUM(cnum)));
+ standard_sub(cnum,cmd);
+ smbrun(cmd,NULL);
+ unbecome_user();
+ }
+
+ unbecome_user();
+ /* execute any "root postexec = " line */
+ if (*lp_rootpostexec(SNUM(cnum)))
+ {
+ pstring cmd;
+ strcpy(cmd,lp_rootpostexec(SNUM(cnum)));
+ standard_sub(cnum,cmd);
+ smbrun(cmd,NULL);
+ }
+
+ Connections[cnum].open = False;
+ num_connections_open--;
+ if (Connections[cnum].ngroups && Connections[cnum].groups)
+ {
+ if (Connections[cnum].igroups != (int *)Connections[cnum].groups)
+ free(Connections[cnum].groups);
+ free(Connections[cnum].igroups);
+ Connections[cnum].groups = NULL;
+ Connections[cnum].igroups = NULL;
+ Connections[cnum].ngroups = 0;
+ }
+
+ string_set(&Connections[cnum].user,"");
+ string_set(&Connections[cnum].dirpath,"");
+ string_set(&Connections[cnum].connectpath,"");
+}
+
+
+/****************************************************************************
+simple routines to do connection counting
+****************************************************************************/
+BOOL yield_connection(int cnum,char *name,int max_connections)
+{
+ struct connect_record crec;
+ pstring fname;
+ FILE *f;
+ int mypid = getpid();
+ int i;
+
+ DEBUG(3,("Yielding connection to %d %s\n",cnum,name));
+
+ if (max_connections <= 0)
+ return(True);
+
+ bzero(&crec,sizeof(crec));
+
+ strcpy(fname,lp_lockdir());
+ standard_sub(cnum,fname);
+ trim_string(fname,"","/");
+
+ strcat(fname,"/");
+ strcat(fname,name);
+ strcat(fname,".LCK");
+
+ f = fopen(fname,"r+");
+ if (!f)
+ {
+ DEBUG(2,("Coudn't open lock file %s (%s)\n",fname,strerror(errno)));
+ return(False);
+ }
+
+ fseek(f,0,SEEK_SET);
+
+ /* find a free spot */
+ for (i=0;i<max_connections;i++)
+ {
+ if (fread(&crec,sizeof(crec),1,f) != 1)
+ {
+ DEBUG(2,("Entry not found in lock file %s\n",fname));
+ fclose(f);
+ return(False);
+ }
+ if (crec.pid == mypid && crec.cnum == cnum)
+ break;
+ }
+
+ if (crec.pid != mypid || crec.cnum != cnum)
+ {
+ fclose(f);
+ DEBUG(2,("Entry not found in lock file %s\n",fname));
+ return(False);
+ }
+
+ bzero((void *)&crec,sizeof(crec));
+
+ /* remove our mark */
+ if (fseek(f,i*sizeof(crec),SEEK_SET) != 0 ||
+ fwrite(&crec,sizeof(crec),1,f) != 1)
+ {
+ DEBUG(2,("Couldn't update lock file %s (%s)\n",fname,strerror(errno)));
+ fclose(f);
+ return(False);
+ }
+
+ DEBUG(3,("Yield successful\n"));
+
+ fclose(f);
+ return(True);
+}
+
+
+/****************************************************************************
+simple routines to do connection counting
+****************************************************************************/
+BOOL claim_connection(int cnum,char *name,int max_connections,BOOL Clear)
+{
+ struct connect_record crec;
+ pstring fname;
+ FILE *f;
+ int snum = SNUM(cnum);
+ int i,foundi= -1;
+ int total_recs;
+
+ if (max_connections <= 0)
+ return(True);
+
+ DEBUG(5,("trying claim %s %s %d\n",lp_lockdir(),name,max_connections));
+
+ strcpy(fname,lp_lockdir());
+ standard_sub(cnum,fname);
+ trim_string(fname,"","/");
+
+ if (!directory_exist(fname,NULL))
+ mkdir(fname,0755);
+
+ strcat(fname,"/");
+ strcat(fname,name);
+ strcat(fname,".LCK");
+
+ if (!file_exist(fname,NULL))
+ {
+ f = fopen(fname,"w");
+ if (f) fclose(f);
+ }
+
+ total_recs = file_size(fname) / sizeof(crec);
+
+ f = fopen(fname,"r+");
+
+ if (!f)
+ {
+ DEBUG(1,("couldn't open lock file %s\n",fname));
+ return(False);
+ }
+
+ /* find a free spot */
+ for (i=0;i<max_connections;i++)
+ {
+
+ if (i>=total_recs ||
+ fseek(f,i*sizeof(crec),SEEK_SET) != 0 ||
+ fread(&crec,sizeof(crec),1,f) != 1)
+ {
+ if (foundi < 0) foundi = i;
+ break;
+ }
+
+ if (Clear && crec.pid && !process_exists(crec.pid))
+ {
+ fseek(f,i*sizeof(crec),SEEK_SET);
+ bzero((void *)&crec,sizeof(crec));
+ fwrite(&crec,sizeof(crec),1,f);
+ if (foundi < 0) foundi = i;
+ continue;
+ }
+ if (foundi < 0 && (!crec.pid || !process_exists(crec.pid)))
+ {
+ foundi=i;
+ if (!Clear) break;
+ }
+ }
+
+ if (foundi < 0)
+ {
+ DEBUG(3,("no free locks in %s\n",fname));
+ fclose(f);
+ return(False);
+ }
+
+ /* fill in the crec */
+ bzero((void *)&crec,sizeof(crec));
+ crec.magic = 0x280267;
+ crec.pid = getpid();
+ crec.cnum = cnum;
+ crec.uid = Connections[cnum].uid;
+ crec.gid = Connections[cnum].gid;
+ StrnCpy(crec.name,lp_servicename(snum),sizeof(crec.name)-1);
+ crec.start = time(NULL);
+
+ {
+ extern struct from_host Client_info;
+ StrnCpy(crec.machine,Client_info.name,sizeof(crec.machine)-1);
+ StrnCpy(crec.addr,Client_info.addr,sizeof(crec.addr)-1);
+ }
+
+ /* make our mark */
+ if (fseek(f,foundi*sizeof(crec),SEEK_SET) != 0 ||
+ fwrite(&crec,sizeof(crec),1,f) != 1)
+ {
+ fclose(f);
+ return(False);
+ }
+
+ fclose(f);
+ return(True);
+}
+
+#if DUMP_CORE
+/*******************************************************************
+prepare to dump a core file - carefully!
+********************************************************************/
+static BOOL dump_core(void)
+{
+ char *p;
+ pstring dname;
+ strcpy(dname,debugf);
+ if ((p=strrchr(dname,'/'))) *p=0;
+ strcat(dname,"/corefiles");
+ mkdir(dname,0700);
+ sys_chown(dname,getuid(),getgid());
+ chmod(dname,0700);
+ if (chdir(dname)) return(False);
+ umask(~(0700));
+
+#ifndef NO_GETRLIMIT
+#ifdef RLIMIT_CORE
+ {
+ struct rlimit rlp;
+ getrlimit(RLIMIT_CORE, &rlp);
+ rlp.rlim_cur = MAX(4*1024*1024,rlp.rlim_cur);
+ setrlimit(RLIMIT_CORE, &rlp);
+ getrlimit(RLIMIT_CORE, &rlp);
+ DEBUG(3,("Core limits now %d %d\n",rlp.rlim_cur,rlp.rlim_max));
+ }
+#endif
+#endif
+
+
+ DEBUG(0,("Dumping core in %s\n",dname));
+ return(True);
+}
+#endif
+
+/****************************************************************************
+exit the server
+****************************************************************************/
+void exit_server(char *reason)
+{
+ static int firsttime=1;
+ int i;
+
+ if (!firsttime) exit(0);
+ firsttime = 0;
+
+ unbecome_user();
+ DEBUG(2,("Closing connections\n"));
+ for (i=0;i<MAX_CONNECTIONS;i++)
+ if (Connections[i].open)
+ close_cnum(i,-1);
+#ifdef DFS_AUTH
+ if (dcelogin_atmost_once)
+ dfs_unlogin();
+#endif
+ if (!reason) {
+ int oldlevel = DEBUGLEVEL;
+ DEBUGLEVEL = 10;
+ DEBUG(0,("Last message was %s\n",smb_fn_name(last_message)));
+ if (last_inbuf)
+ show_msg(last_inbuf);
+ DEBUGLEVEL = oldlevel;
+ DEBUG(0,("===============================================================\n"));
+#if DUMP_CORE
+ if (dump_core()) return;
+#endif
+ }
+ DEBUG(3,("%s Server exit (%s)\n",timestring(),reason?reason:""));
+ exit(0);
+}
+
+/****************************************************************************
+do some standard substitutions in a string
+****************************************************************************/
+void standard_sub(int cnum,char *s)
+{
+ if (!strchr(s,'%')) return;
+
+ if (VALID_CNUM(cnum))
+ {
+ string_sub(s,"%S",lp_servicename(Connections[cnum].service));
+ string_sub(s,"%P",Connections[cnum].connectpath);
+ string_sub(s,"%u",Connections[cnum].user);
+ if (strstr(s,"%H")) {
+ char *home = get_home_dir(Connections[cnum].user);
+ if (home) string_sub(s,"%H",home);
+ }
+ string_sub(s,"%g",gidtoname(Connections[cnum].gid));
+ }
+ standard_sub_basic(s);
+}
+
+/*
+These flags determine some of the permissions required to do an operation
+
+Note that I don't set NEED_WRITE on some write operations because they
+are used by some brain-dead clients when printing, and I don't want to
+force write permissions on print services.
+*/
+#define AS_USER (1<<0)
+#define NEED_WRITE (1<<1)
+#define TIME_INIT (1<<2)
+#define CAN_IPC (1<<3)
+#define AS_GUEST (1<<5)
+
+
+/*
+ define a list of possible SMB messages and their corresponding
+ functions. Any message that has a NULL function is unimplemented -
+ please feel free to contribute implementations!
+*/
+struct smb_message_struct
+{
+ int code;
+ char *name;
+ int (*fn)();
+ int flags;
+#if PROFILING
+ unsigned long time;
+#endif
+}
+ smb_messages[] = {
+
+ /* CORE PROTOCOL */
+
+ {SMBnegprot,"SMBnegprot",reply_negprot,0},
+ {SMBtcon,"SMBtcon",reply_tcon,0},
+ {SMBtdis,"SMBtdis",reply_tdis,0},
+ {SMBexit,"SMBexit",reply_exit,0},
+ {SMBioctl,"SMBioctl",reply_ioctl,0},
+ {SMBecho,"SMBecho",reply_echo,0},
+ {SMBsesssetupX,"SMBsesssetupX",reply_sesssetup_and_X,0},
+ {SMBtconX,"SMBtconX",reply_tcon_and_X,0},
+ {SMBulogoffX, "SMBulogoffX", reply_ulogoffX, 0},
+ {SMBgetatr,"SMBgetatr",reply_getatr,AS_USER},
+ {SMBsetatr,"SMBsetatr",reply_setatr,AS_USER | NEED_WRITE},
+ {SMBchkpth,"SMBchkpth",reply_chkpth,AS_USER},
+ {SMBsearch,"SMBsearch",reply_search,AS_USER},
+ {SMBopen,"SMBopen",reply_open,AS_USER},
+
+ /* note that SMBmknew and SMBcreate are deliberately overloaded */
+ {SMBcreate,"SMBcreate",reply_mknew,AS_USER},
+ {SMBmknew,"SMBmknew",reply_mknew,AS_USER},
+
+ {SMBunlink,"SMBunlink",reply_unlink,AS_USER | NEED_WRITE},
+ {SMBread,"SMBread",reply_read,AS_USER},
+ {SMBwrite,"SMBwrite",reply_write,AS_USER},
+ {SMBclose,"SMBclose",reply_close,AS_USER},
+ {SMBmkdir,"SMBmkdir",reply_mkdir,AS_USER | NEED_WRITE},
+ {SMBrmdir,"SMBrmdir",reply_rmdir,AS_USER | NEED_WRITE},
+ {SMBdskattr,"SMBdskattr",reply_dskattr,AS_USER},
+ {SMBmv,"SMBmv",reply_mv,AS_USER | NEED_WRITE},
+
+ /* this is a Pathworks specific call, allowing the
+ changing of the root path */
+ {pSETDIR,"pSETDIR",reply_setdir,AS_USER},
+
+ {SMBlseek,"SMBlseek",reply_lseek,AS_USER},
+ {SMBflush,"SMBflush",reply_flush,AS_USER},
+ {SMBctemp,"SMBctemp",reply_ctemp,AS_USER},
+ {SMBsplopen,"SMBsplopen",reply_printopen,AS_USER},
+ {SMBsplclose,"SMBsplclose",reply_printclose,AS_USER},
+ {SMBsplretq,"SMBsplretq",reply_printqueue,AS_USER},
+ {SMBsplwr,"SMBsplwr",reply_printwrite,AS_USER},
+ {SMBlock,"SMBlock",reply_lock,AS_USER},
+ {SMBunlock,"SMBunlock",reply_unlock,AS_USER},
+
+ /* CORE+ PROTOCOL FOLLOWS */
+
+ {SMBreadbraw,"SMBreadbraw",reply_readbraw,AS_USER},
+ {SMBwritebraw,"SMBwritebraw",reply_writebraw,AS_USER},
+ {SMBwriteclose,"SMBwriteclose",reply_writeclose,AS_USER},
+ {SMBlockread,"SMBlockread",reply_lockread,AS_USER},
+ {SMBwriteunlock,"SMBwriteunlock",reply_writeunlock,AS_USER},
+
+ /* LANMAN1.0 PROTOCOL FOLLOWS */
+
+ {SMBreadBmpx,"SMBreadBmpx",reply_readbmpx,AS_USER},
+ {SMBreadBs,"SMBreadBs",NULL,AS_USER},
+ {SMBwriteBmpx,"SMBwriteBmpx",reply_writebmpx,AS_USER},
+ {SMBwriteBs,"SMBwriteBs",reply_writebs,AS_USER},
+ {SMBwritec,"SMBwritec",NULL,AS_USER},
+ {SMBsetattrE,"SMBsetattrE",reply_setattrE,AS_USER | NEED_WRITE},
+ {SMBgetattrE,"SMBgetattrE",reply_getattrE,AS_USER},
+ {SMBtrans,"SMBtrans",reply_trans,AS_USER | CAN_IPC},
+ {SMBtranss,"SMBtranss",NULL,AS_USER | CAN_IPC},
+ {SMBioctls,"SMBioctls",NULL,AS_USER},
+ {SMBcopy,"SMBcopy",reply_copy,AS_USER | NEED_WRITE},
+ {SMBmove,"SMBmove",NULL,AS_USER | NEED_WRITE},
+
+ {SMBopenX,"SMBopenX",reply_open_and_X,AS_USER},
+ {SMBreadX,"SMBreadX",reply_read_and_X,AS_USER},
+ {SMBwriteX,"SMBwriteX",reply_write_and_X,AS_USER},
+ {SMBlockingX,"SMBlockingX",reply_lockingX,AS_USER},
+
+ {SMBffirst,"SMBffirst",reply_search,AS_USER},
+ {SMBfunique,"SMBfunique",reply_search,AS_USER},
+ {SMBfclose,"SMBfclose",reply_fclose,AS_USER},
+
+ /* LANMAN2.0 PROTOCOL FOLLOWS */
+ {SMBfindnclose, "SMBfindnclose", reply_findnclose, AS_USER},
+ {SMBfindclose, "SMBfindclose", reply_findclose,AS_USER},
+ {SMBtrans2, "SMBtrans2", reply_trans2, AS_USER},
+ {SMBtranss2, "SMBtranss2", reply_transs2, AS_USER},
+
+ /* messaging routines */
+ {SMBsends,"SMBsends",reply_sends,AS_GUEST},
+ {SMBsendstrt,"SMBsendstrt",reply_sendstrt,AS_GUEST},
+ {SMBsendend,"SMBsendend",reply_sendend,AS_GUEST},
+ {SMBsendtxt,"SMBsendtxt",reply_sendtxt,AS_GUEST},
+
+ /* NON-IMPLEMENTED PARTS OF THE CORE PROTOCOL */
+
+ {SMBsendb,"SMBsendb",NULL,AS_GUEST},
+ {SMBfwdname,"SMBfwdname",NULL,AS_GUEST},
+ {SMBcancelf,"SMBcancelf",NULL,AS_GUEST},
+ {SMBgetmac,"SMBgetmac",NULL,AS_GUEST}
+ };
+
+/****************************************************************************
+return a string containing the function name of a SMB command
+****************************************************************************/
+char *smb_fn_name(int type)
+{
+ static char *unknown_name = "SMBunknown";
+ static int num_smb_messages =
+ sizeof(smb_messages) / sizeof(struct smb_message_struct);
+ int match;
+
+ for (match=0;match<num_smb_messages;match++)
+ if (smb_messages[match].code == type)
+ break;
+
+ if (match == num_smb_messages)
+ return(unknown_name);
+
+ return(smb_messages[match].name);
+}
+
+
+/****************************************************************************
+do a switch on the message type, and return the response size
+****************************************************************************/
+static int switch_message(int type,char *inbuf,char *outbuf,int size,int bufsize)
+{
+ static int pid= -1;
+ int outsize = 0;
+ static int num_smb_messages =
+ sizeof(smb_messages) / sizeof(struct smb_message_struct);
+ int match;
+
+#if PROFILING
+ struct timeval msg_start_time;
+ struct timeval msg_end_time;
+ static unsigned long total_time = 0;
+
+ GetTimeOfDay(&msg_start_time);
+#endif
+
+ if (pid == -1)
+ pid = getpid();
+
+ errno = 0;
+ last_message = type;
+
+ /* make sure this is an SMB packet */
+ if (strncmp(smb_base(inbuf),"\377SMB",4) != 0)
+ {
+ DEBUG(2,("Non-SMB packet of length %d\n",smb_len(inbuf)));
+ return(-1);
+ }
+
+ for (match=0;match<num_smb_messages;match++)
+ if (smb_messages[match].code == type)
+ break;
+
+ if (match == num_smb_messages)
+ {
+ DEBUG(0,("Unknown message type %d!\n",type));
+ outsize = reply_unknown(inbuf,outbuf);
+ }
+ else
+ {
+ DEBUG(3,("switch message %s (pid %d)\n",smb_messages[match].name,pid));
+ if (smb_messages[match].fn)
+ {
+ int cnum = SVAL(inbuf,smb_tid);
+ int flags = smb_messages[match].flags;
+ int uid = SVAL(inbuf,smb_uid);
+
+ /* does this protocol need to be run as root? */
+ if (!(flags & AS_USER))
+ unbecome_user();
+
+ /* does this protocol need to be run as the connected user? */
+ if ((flags & AS_USER) && !become_user(cnum,uid))
+ return(ERROR(ERRSRV,ERRinvnid));
+
+ /* does it need write permission? */
+ if ((flags & NEED_WRITE) && !CAN_WRITE(cnum))
+ return(ERROR(ERRSRV,ERRaccess));
+
+ /* ipc services are limited */
+ if (IS_IPC(cnum) && (flags & AS_USER) && !(flags & CAN_IPC))
+ return(ERROR(ERRSRV,ERRaccess));
+
+ /* load service specific parameters */
+ if (OPEN_CNUM(cnum) && !become_service(cnum,(flags & AS_USER)?True:False))
+ return(ERROR(ERRSRV,ERRaccess));
+
+ /* does this protocol need to be run as guest? */
+ if ((flags & AS_GUEST) && (!become_guest() || !check_access(-1)))
+ return(ERROR(ERRSRV,ERRaccess));
+
+ last_inbuf = inbuf;
+
+ outsize = smb_messages[match].fn(inbuf,outbuf,size,bufsize);
+ }
+ else
+ {
+ outsize = reply_unknown(inbuf,outbuf);
+ }
+ }
+
+#if PROFILING
+ GetTimeOfDay(&msg_end_time);
+ if (!(smb_messages[match].flags & TIME_INIT))
+ {
+ smb_messages[match].time = 0;
+ smb_messages[match].flags |= TIME_INIT;
+ }
+ {
+ unsigned long this_time =
+ (msg_end_time.tv_sec - msg_start_time.tv_sec)*1e6 +
+ (msg_end_time.tv_usec - msg_start_time.tv_usec);
+ smb_messages[match].time += this_time;
+ total_time += this_time;
+ }
+ DEBUG(2,("TIME %s %d usecs %g pct\n",
+ smb_fn_name(type),smb_messages[match].time,
+ (100.0*smb_messages[match].time) / total_time));
+#endif
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+construct a chained reply and add it to the already made reply
+
+inbuf points to the original message start.
+inbuf2 points to the smb_wct part of the secondary message
+type is the type of the secondary message
+outbuf points to the original outbuffer
+outbuf2 points to the smb_wct field of the new outbuffer
+size is the total length of the incoming message (from inbuf1)
+bufsize is the total buffer size
+
+return how many bytes were added to the response
+****************************************************************************/
+int chain_reply(int type,char *inbuf,char *inbuf2,char *outbuf,char *outbuf2,int size,int bufsize)
+{
+ int outsize = 0;
+ char *ibuf,*obuf;
+ static BOOL in_chain = False;
+ static char *last_outbuf=NULL;
+ BOOL was_inchain = in_chain;
+ int insize_remaining;
+ static int insize_deleted;
+
+
+ chain_size += PTR_DIFF(outbuf2,outbuf) - smb_wct;
+ if (was_inchain)
+ outbuf = last_outbuf;
+ else
+ insize_deleted = 0;
+
+
+ insize_deleted = 0;
+ inbuf2 -= insize_deleted;
+ insize_remaining = size - PTR_DIFF(inbuf2,inbuf);
+ insize_deleted += size - (insize_remaining + smb_wct);
+
+ in_chain = True;
+ last_outbuf = outbuf;
+
+
+ /* allocate some space for the in and out buffers of the chained message */
+ ibuf = (char *)malloc(size + SAFETY_MARGIN);
+ obuf = (char *)malloc(bufsize + SAFETY_MARGIN);
+
+ if (!ibuf || !obuf)
+ {
+ DEBUG(0,("Out of memory in chain reply\n"));
+ return(ERROR(ERRSRV,ERRnoresource));
+ }
+
+ ibuf += SMB_ALIGNMENT;
+ obuf += SMB_ALIGNMENT;
+
+ /* create the in buffer */
+ memcpy(ibuf,inbuf,smb_wct);
+ memcpy(ibuf+smb_wct,inbuf2,insize_remaining);
+ CVAL(ibuf,smb_com) = type;
+
+ /* create the out buffer */
+ bzero(obuf,smb_size);
+
+ set_message(obuf,0,0,True);
+ CVAL(obuf,smb_com) = CVAL(ibuf,smb_com);
+
+ memcpy(obuf+4,ibuf+4,4);
+ CVAL(obuf,smb_rcls) = SUCCESS;
+ CVAL(obuf,smb_reh) = 0;
+ CVAL(obuf,smb_flg) = 0x80 | (CVAL(ibuf,smb_flg) & 0x8); /* bit 7 set
+ means a reply */
+ SSVAL(obuf,smb_flg2,1); /* say we support long filenames */
+ SSVAL(obuf,smb_err,SUCCESS);
+ SSVAL(obuf,smb_tid,SVAL(inbuf,smb_tid));
+ SSVAL(obuf,smb_pid,SVAL(inbuf,smb_pid));
+ SSVAL(obuf,smb_uid,SVAL(inbuf,smb_uid));
+ SSVAL(obuf,smb_mid,SVAL(inbuf,smb_mid));
+
+ DEBUG(3,("Chained message\n"));
+ show_msg(ibuf);
+
+ /* process the request */
+ outsize = switch_message(type,ibuf,obuf,smb_wct+insize_remaining,
+ bufsize-chain_size);
+
+ /* copy the new reply header over the old one, but preserve
+ the smb_com field */
+ memcpy(outbuf+smb_com+1,obuf+smb_com+1,smb_wct-(smb_com+1));
+
+ /* and copy the data from the reply to the right spot */
+ memcpy(outbuf2,obuf+smb_wct,outsize - smb_wct);
+
+ /* free the allocated buffers */
+ if (ibuf) free(ibuf-SMB_ALIGNMENT);
+ if (obuf) free(obuf-SMB_ALIGNMENT);
+
+ in_chain = was_inchain;
+
+ /* return how much extra has been added to the packet */
+ return(outsize - smb_wct);
+}
+
+
+
+/****************************************************************************
+ construct a reply to the incoming packet
+****************************************************************************/
+int construct_reply(char *inbuf,char *outbuf,int size,int bufsize)
+{
+ int type = CVAL(inbuf,smb_com);
+ int outsize = 0;
+ int msg_type = CVAL(inbuf,0);
+
+ smb_last_time = time(NULL);
+
+ chain_size = 0;
+
+ bzero(outbuf,smb_size);
+
+ if (msg_type != 0)
+ return(reply_special(inbuf,outbuf));
+
+ CVAL(outbuf,smb_com) = CVAL(inbuf,smb_com);
+ set_message(outbuf,0,0,True);
+
+ memcpy(outbuf+4,inbuf+4,4);
+ CVAL(outbuf,smb_rcls) = SUCCESS;
+ CVAL(outbuf,smb_reh) = 0;
+ CVAL(outbuf,smb_flg) = 0x80 | (CVAL(inbuf,smb_flg) & 0x8); /* bit 7 set
+ means a reply */
+ SSVAL(outbuf,smb_flg2,1); /* say we support long filenames */
+ SSVAL(outbuf,smb_err,SUCCESS);
+ SSVAL(outbuf,smb_tid,SVAL(inbuf,smb_tid));
+ SSVAL(outbuf,smb_pid,SVAL(inbuf,smb_pid));
+ SSVAL(outbuf,smb_uid,SVAL(inbuf,smb_uid));
+ SSVAL(outbuf,smb_mid,SVAL(inbuf,smb_mid));
+
+ outsize = switch_message(type,inbuf,outbuf,size,bufsize);
+
+ if(outsize > 4)
+ smb_setlen(outbuf,outsize - 4);
+ return(outsize);
+}
+
+
+/****************************************************************************
+ process commands from the client
+****************************************************************************/
+void process(void )
+{
+ static int trans_num = 0;
+ int nread;
+ extern struct from_host Client_info;
+ extern int Client;
+
+ fromhost(Client,&Client_info);
+
+ InBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+ OutBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+ if ((InBuffer == NULL) || (OutBuffer == NULL))
+ return;
+
+ InBuffer += SMB_ALIGNMENT;
+ OutBuffer += SMB_ALIGNMENT;
+
+#if PRIME_NMBD
+ DEBUG(3,("priming nmbd\n"));
+ {
+ struct in_addr ip;
+ ip = *interpret_addr2("localhost");
+ if (zero_ip(ip)) ip = *interpret_addr2("127.0.0.1");
+ *OutBuffer = 0;
+ send_one_packet(OutBuffer,1,ip,137,SOCK_DGRAM);
+ }
+#endif
+
+ last_user.cnum = -1;
+
+ while (True)
+ {
+ int32 len;
+ int msg_type;
+ int msg_flags;
+ int type;
+ int deadtime = lp_deadtime()*60;
+ int counter;
+ int last_keepalive=0;
+
+ if (deadtime <= 0)
+ deadtime = DEFAULT_SMBD_TIMEOUT;
+
+ if (lp_readprediction())
+ do_read_prediction();
+
+ {
+ extern pstring share_del_pending;
+ if (*share_del_pending) {
+ unbecome_user();
+ if (!unlink(share_del_pending))
+ DEBUG(3,("Share file deleted %s\n",share_del_pending));
+ else
+ DEBUG(2,("Share del failed of %s\n",share_del_pending));
+ share_del_pending[0] = 0;
+ }
+ }
+
+ if (share_mode_pending) {
+ unbecome_user();
+ check_share_modes();
+ share_mode_pending=False;
+ }
+
+ errno = 0;
+
+ for (counter=SMBD_SELECT_LOOP;
+ !receive_smb(Client,InBuffer,SMBD_SELECT_LOOP*1000);
+ counter += SMBD_SELECT_LOOP)
+ {
+ int i;
+ time_t t;
+ BOOL allidle = True;
+ extern int keepalive;
+
+ /* check for socket failure */
+ if (errno == EBADF) {
+ DEBUG(3,("%s Bad file descriptor - exiting\n",timestring()));
+ return;
+ }
+
+ t = time(NULL);
+
+ /* become root again if waiting */
+ unbecome_user();
+
+ /* check for smb.conf reload */
+ if (!(counter%SMBD_RELOAD_CHECK))
+ reload_services(True);
+
+ /* check the share modes every 10 secs */
+ if (!(counter%SHARE_MODES_CHECK))
+ check_share_modes();
+
+ /* clean the share modes every 5 minutes */
+ if (!(counter%SHARE_MODES_CLEAN))
+ clean_share_files();
+
+ /* automatic timeout if all connections are closed */
+ if (num_connections_open==0 && counter >= IDLE_CLOSED_TIMEOUT) {
+ DEBUG(2,("%s Closing idle connection\n",timestring()));
+ return;
+ }
+
+ if (keepalive && (counter-last_keepalive)>keepalive) {
+ if (!send_keepalive(Client)) {
+ DEBUG(2,("%s Keepalive failed - exiting\n",timestring()));
+ return;
+ }
+ last_keepalive = counter;
+ }
+
+ /* check for connection timeouts */
+ for (i=0;i<MAX_CONNECTIONS;i++)
+ if (Connections[i].open)
+ {
+ /* close dirptrs on connections that are idle */
+ if ((t-Connections[i].lastused)>DPTR_IDLE_TIMEOUT)
+ dptr_idlecnum(i);
+
+ if (Connections[i].num_files_open > 0 ||
+ (t-Connections[i].lastused)<deadtime)
+ allidle = False;
+ }
+
+ if (allidle && num_connections_open>0) {
+ DEBUG(2,("%s Closing idle connection 2\n",timestring()));
+ return;
+ }
+ }
+
+ msg_type = CVAL(InBuffer,0);
+ msg_flags = CVAL(InBuffer,1);
+ type = CVAL(InBuffer,smb_com);
+
+ len = smb_len(InBuffer);
+
+ DEBUG(6,("got message type 0x%x of len 0x%x\n",msg_type,len));
+
+ nread = len + 4;
+
+ DEBUG(3,("%s Transaction %d of length %d\n",timestring(),trans_num,nread));
+
+#ifdef WITH_VTP
+ if(trans_num == 1 && VT_Check(InBuffer)) {
+ VT_Process();
+ return;
+ }
+#endif
+
+
+ if (msg_type == 0)
+ show_msg(InBuffer);
+
+ nread = construct_reply(InBuffer,OutBuffer,nread,maxxmit);
+
+ if(nread > 0) {
+ if (CVAL(OutBuffer,0) == 0)
+ show_msg(OutBuffer);
+
+ if (nread != smb_len(OutBuffer) + 4)
+ {
+ DEBUG(0,("ERROR: Invalid message response size! %d %d\n",
+ nread,
+ smb_len(OutBuffer)));
+ }
+ else
+ send_smb(Client,OutBuffer);
+ }
+ trans_num++;
+ }
+}
+
+
+/****************************************************************************
+ initialise connect, service and file structs
+****************************************************************************/
+static void init_structs(void )
+{
+ int i;
+ get_myname(myhostname,&myip);
+
+ for (i=0;i<MAX_CONNECTIONS;i++)
+ {
+ Connections[i].open = False;
+ Connections[i].num_files_open=0;
+ Connections[i].lastused=0;
+ Connections[i].used=False;
+ string_init(&Connections[i].user,"");
+ string_init(&Connections[i].dirpath,"");
+ string_init(&Connections[i].connectpath,"");
+ string_init(&Connections[i].origpath,"");
+ }
+
+ for (i=0;i<MAX_OPEN_FILES;i++)
+ {
+ Files[i].open = False;
+ string_init(&Files[i].name,"");
+ }
+
+ init_dptrs();
+}
+
+/****************************************************************************
+usage on the program
+****************************************************************************/
+void usage(char *pname)
+{
+ DEBUG(0,("Incorrect program usage - are you sure the command line is correct?\n"));
+
+ printf("Usage: %s [-D] [-p port] [-d debuglevel] [-l log basename] [-s services file]\n",pname);
+ printf("Version %s\n",VERSION);
+ printf("\t-D become a daemon\n");
+ printf("\t-p port listen on the specified port\n");
+ printf("\t-d debuglevel set the debuglevel\n");
+ printf("\t-l log basename. Basename for log/debug files\n");
+ printf("\t-s services file. Filename of services file\n");
+ printf("\t-P passive only\n");
+ printf("\t-a overwrite log file, don't append\n");
+ printf("\n");
+}
+
+
+/****************************************************************************
+ main program
+****************************************************************************/
+int main(int argc,char *argv[])
+{
+ extern BOOL append_log;
+ /* shall I run as a daemon */
+ BOOL is_daemon = False;
+ int port = 139;
+ int opt;
+ extern char *optarg;
+
+#ifdef NEED_AUTH_PARAMETERS
+ set_auth_parameters(argc,argv);
+#endif
+
+#ifdef SecureWare
+ setluid(0);
+#endif
+
+ append_log = True;
+
+ TimeInit();
+
+ strcpy(debugf,SMBLOGFILE);
+
+ setup_logging(argv[0],False);
+
+ charset_initialise();
+
+ /* make absolutely sure we run as root - to handle cases whre people
+ are crazy enough to have it setuid */
+#ifdef USE_SETRES
+ setresuid(0,0,0);
+#else
+ setuid(0);
+ seteuid(0);
+ setuid(0);
+ seteuid(0);
+#endif
+
+ fault_setup(exit_server);
+
+ umask(0777 & ~DEF_CREATE_MASK);
+
+ initial_uid = geteuid();
+ initial_gid = getegid();
+
+ if (initial_gid != 0 && initial_uid == 0)
+ {
+#ifdef HPUX
+ setresgid(0,0,0);
+#else
+ setgid(0);
+ setegid(0);
+#endif
+ }
+
+ initial_uid = geteuid();
+ initial_gid = getegid();
+
+
+ /* this is for people who can't start the program correctly */
+ while (argc > 1 && (*argv[1] != '-'))
+ {
+ argv++;
+ argc--;
+ }
+
+ while ((opt = getopt(argc, argv, "O:i:l:s:d:Dp:hPa")) != EOF)
+ switch (opt)
+ {
+ case 'O':
+ strcpy(user_socket_options,optarg);
+ break;
+ case 'i':
+ strcpy(scope,optarg);
+ break;
+ case 'P':
+ {
+ extern BOOL passive;
+ passive = True;
+ }
+ break;
+ case 's':
+ strcpy(servicesf,optarg);
+ break;
+ case 'l':
+ strcpy(debugf,optarg);
+ break;
+ case 'a':
+ {
+ extern BOOL append_log;
+ append_log = !append_log;
+ }
+ break;
+ case 'D':
+ is_daemon = True;
+ break;
+ case 'd':
+ if (*optarg == 'A')
+ DEBUGLEVEL = 10000;
+ else
+ DEBUGLEVEL = atoi(optarg);
+ break;
+ case 'p':
+ port = atoi(optarg);
+ break;
+ case 'h':
+ usage(argv[0]);
+ exit(0);
+ break;
+ default:
+ usage(argv[0]);
+ exit(1);
+ }
+
+ reopen_logs();
+
+ DEBUG(2,("%s smbd version %s started\n",timestring(),VERSION));
+ DEBUG(2,("Copyright Andrew Tridgell 1992-1995\n"));
+
+ GetWd(OriginalDir);
+
+#ifndef NO_GETRLIMIT
+#ifdef RLIMIT_NOFILE
+ {
+ struct rlimit rlp;
+ getrlimit(RLIMIT_NOFILE, &rlp);
+ rlp.rlim_cur = (MAX_OPEN_FILES>rlp.rlim_max)? rlp.rlim_max:MAX_OPEN_FILES;
+ setrlimit(RLIMIT_NOFILE, &rlp);
+ getrlimit(RLIMIT_NOFILE, &rlp);
+ DEBUG(3,("Maximum number of open files per session is %d\n",rlp.rlim_cur));
+ }
+#endif
+#endif
+
+
+ DEBUG(2,("uid=%d gid=%d euid=%d egid=%d\n",
+ getuid(),getgid(),geteuid(),getegid()));
+
+ if (sizeof(uint16) < 2 || sizeof(uint32) < 4)
+ {
+ DEBUG(0,("ERROR: Samba is not configured correctly for the word size on your machine\n"));
+ exit(1);
+ }
+
+ init_structs();
+
+ if (!reload_services(False))
+ return(-1);
+
+#ifndef NO_SIGNAL_TEST
+ signal(SIGHUP,SIGNAL_CAST sig_hup);
+#endif
+
+ DEBUG(3,("%s loaded services\n",timestring()));
+
+ if (!is_daemon && !is_a_socket(0))
+ {
+ DEBUG(0,("standard input is not a socket, assuming -D option\n"));
+ is_daemon = True;
+ }
+
+ if (is_daemon)
+ {
+ DEBUG(3,("%s becoming a daemon\n",timestring()));
+ become_daemon();
+ }
+
+ if (open_sockets(is_daemon,port))
+ {
+ /* possibly reload the services file. */
+ reload_services(True);
+
+ maxxmit = MIN(lp_maxxmit(),BUFFER_SIZE);
+
+ if (*lp_rootdir())
+ {
+ if (sys_chroot(lp_rootdir()) == 0)
+ DEBUG(2,("%s changed root to %s\n",timestring(),lp_rootdir()));
+ }
+
+ process();
+ close_sockets();
+ }
+ exit_server("normal exit");
+ return(0);
+}
+
+
diff --git a/source/smbd/smbrun.c b/source/smbd/smbrun.c
new file mode 100644
index 00000000000..df12ae1f85c
--- /dev/null
+++ b/source/smbd/smbrun.c
@@ -0,0 +1,96 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ external program running routine
+ Copyright (C) Andrew Tridgell 1992-1995
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+
+/*******************************************************************
+close the low 3 fd's and open dev/null in their place
+********************************************************************/
+static void close_fds(void)
+{
+ int fd;
+ int i;
+ close(0); close(1); close(2);
+ /* try and use up these file descriptors, so silly
+ library routines writing to stdout etc won't cause havoc */
+ for (i=0;i<3;i++) {
+ fd = open("/dev/null",O_RDWR,0);
+ if (fd < 0) fd = open("/dev/null",O_WRONLY,0);
+ if (fd != i) return;
+ }
+}
+
+
+/*
+This is a wrapper around the system call to allow commands to run correctly
+as non root from a program which is switching between root and non-root
+
+It takes one argument as argv[1] and runs it after becoming a non-root
+user
+*/
+int main(int argc,char *argv[])
+{
+ close_fds();
+
+ if (getuid() != geteuid())
+ {
+ int uid,gid;
+
+ if (getuid() == 0)
+ uid = geteuid();
+ else
+ uid = getuid();
+
+ if (getgid() == 0)
+ gid = getegid();
+ else
+ gid = getgid();
+
+#ifdef USE_SETRES
+ setresgid(0,0,0);
+ setresuid(0,0,0);
+ setresgid(gid,gid,gid);
+ setresuid(uid,uid,uid);
+#else
+ setuid(0);
+ seteuid(0);
+ setgid(gid);
+ setegid(gid);
+ setuid(uid);
+ seteuid(uid);
+#endif
+
+ if (getuid() != uid)
+ return(3);
+ }
+
+ if (geteuid() != getuid())
+ return(1);
+
+ if (argc < 2)
+ return(2);
+
+ /* this is to make sure that the system() call doesn't run forever */
+ alarm(30);
+
+ return(system(argv[1]));
+}
diff --git a/source/smbd/trans2.c b/source/smbd/trans2.c
new file mode 100644
index 00000000000..9d02123cf87
--- /dev/null
+++ b/source/smbd/trans2.c
@@ -0,0 +1,1646 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ SMB transaction2 handling
+ Copyright (C) Jeremy Allison 1994
+
+ Extensively modified by Andrew Tridgell, 1995
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "loadparm.h"
+#include "trans2.h"
+
+extern int DEBUGLEVEL;
+extern int Protocol;
+extern connection_struct Connections[];
+extern files_struct Files[];
+extern BOOL case_sensitive;
+extern int Client;
+
+/****************************************************************************
+ Send the required number of replies back.
+ We assume all fields other than the data fields are
+ set correctly for the type of call.
+ HACK ! Always assumes smb_setup field is zero.
+****************************************************************************/
+static int send_trans2_replies(char *outbuf, int bufsize, char *params,
+ int paramsize, char *pdata, int datasize)
+{
+ /* As we are using a protocol > LANMAN1 then the maxxmit
+ variable must have been set in the sessetupX call.
+ This takes precedence over the max_xmit field in the
+ global struct. These different max_xmit variables should
+ be merged as this is now too confusing */
+
+ extern int maxxmit;
+ int data_to_send = datasize;
+ int params_to_send = paramsize;
+ int useable_space;
+ char *pp = params;
+ char *pd = pdata;
+ int params_sent_thistime, data_sent_thistime, total_sent_thistime;
+ int alignment_offset = 1;
+
+ /* Initially set the wcnt area to be 10 - this is true for all
+ trans2 replies */
+ set_message(outbuf,10,0,True);
+
+ /* If there genuinely are no parameters or data to send just send
+ the empty packet */
+ if(params_to_send == 0 && data_to_send == 0)
+ {
+ send_smb(Client,outbuf);
+ return 0;
+ }
+
+ /* Space is bufsize minus Netbios over TCP header minus SMB header */
+ /* The + 1 is to align the param and data bytes on an even byte
+ boundary. NT 4.0 Beta needs this to work correctly. */
+ useable_space = bufsize - ((smb_buf(outbuf)+alignment_offset) - outbuf);
+ useable_space = MIN(useable_space, maxxmit); /* XXX is this needed? correct? */
+
+ while( params_to_send || data_to_send)
+ {
+ /* Calculate whether we will totally or partially fill this packet */
+ total_sent_thistime = params_to_send + data_to_send + alignment_offset;
+ total_sent_thistime = MIN(total_sent_thistime, useable_space);
+
+ set_message(outbuf, 10, total_sent_thistime, True);
+
+ /* Set total params and data to be sent */
+ SSVAL(outbuf,smb_tprcnt,paramsize);
+ SSVAL(outbuf,smb_tdrcnt,datasize);
+
+ /* Calculate how many parameters and data we can fit into
+ this packet. Parameters get precedence */
+
+ params_sent_thistime = MIN(params_to_send,useable_space);
+ data_sent_thistime = useable_space - params_sent_thistime;
+ data_sent_thistime = MIN(data_sent_thistime,data_to_send);
+
+ SSVAL(outbuf,smb_prcnt, params_sent_thistime);
+ if(params_sent_thistime == 0)
+ {
+ SSVAL(outbuf,smb_proff,0);
+ SSVAL(outbuf,smb_prdisp,0);
+ } else {
+ /* smb_proff is the offset from the start of the SMB header to the
+ parameter bytes, however the first 4 bytes of outbuf are
+ the Netbios over TCP header. Thus use smb_base() to subtract
+ them from the calculation */
+ SSVAL(outbuf,smb_proff,((smb_buf(outbuf)+alignment_offset) - smb_base(outbuf)));
+ /* Absolute displacement of param bytes sent in this packet */
+ SSVAL(outbuf,smb_prdisp,pp - params);
+ }
+
+ SSVAL(outbuf,smb_drcnt, data_sent_thistime);
+ if(data_sent_thistime == 0)
+ {
+ SSVAL(outbuf,smb_droff,0);
+ SSVAL(outbuf,smb_drdisp, 0);
+ } else {
+ /* The offset of the data bytes is the offset of the
+ parameter bytes plus the number of parameters being sent this time */
+ SSVAL(outbuf,smb_droff,((smb_buf(outbuf)+alignment_offset) -
+ smb_base(outbuf)) + params_sent_thistime);
+ SSVAL(outbuf,smb_drdisp, pd - pdata);
+ }
+
+ /* Copy the param bytes into the packet */
+ if(params_sent_thistime)
+ memcpy((smb_buf(outbuf)+alignment_offset),pp,params_sent_thistime);
+ /* Copy in the data bytes */
+ if(data_sent_thistime)
+ memcpy(smb_buf(outbuf)+alignment_offset+params_sent_thistime,pd,data_sent_thistime);
+
+ DEBUG(9,("t2_rep: params_sent_thistime = %d, data_sent_thistime = %d, useable_space = %d\n",
+ params_sent_thistime, data_sent_thistime, useable_space));
+ DEBUG(9,("t2_rep: params_to_send = %d, data_to_send = %d, paramsize = %d, datasize = %d\n",
+ params_to_send, data_to_send, paramsize, datasize));
+
+ /* Send the packet */
+ send_smb(Client,outbuf);
+
+ pp += params_sent_thistime;
+ pd += data_sent_thistime;
+
+ params_to_send -= params_sent_thistime;
+ data_to_send -= data_sent_thistime;
+
+ /* Sanity check */
+ if(params_to_send < 0 || data_to_send < 0)
+ {
+ DEBUG(2,("send_trans2_replies failed sanity check pts = %d, dts = %d\n!!!",
+ params_to_send, data_to_send));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+/****************************************************************************
+ reply to a TRANSACT2_OPEN
+****************************************************************************/
+static int call_trans2open(char *inbuf, char *outbuf, int bufsize, int cnum,
+ char **pparams, char **ppdata)
+{
+ char *params = *pparams;
+ int16 open_mode = SVAL(params, 2);
+ int16 open_attr = SVAL(params,6);
+#if 0
+ BOOL return_additional_info = BITSETW(params,0);
+ int16 open_sattr = SVAL(params, 4);
+ time_t open_time = make_unix_date3(params+8);
+#endif
+ int16 open_ofun = SVAL(params,12);
+ int32 open_size = IVAL(params,14);
+ char *pname = &params[28];
+ int16 namelen = strlen(pname)+1;
+
+ pstring fname;
+ int fnum = -1;
+ int unixmode;
+ int size=0,fmode=0,mtime=0,rmode;
+ int32 inode = 0;
+ struct stat sbuf;
+ int smb_action = 0;
+
+ StrnCpy(fname,pname,namelen);
+
+ DEBUG(3,("trans2open %s cnum=%d mode=%d attr=%d ofun=%d size=%d\n",
+ fname,cnum,open_mode, open_attr, open_ofun, open_size));
+
+ /* XXXX we need to handle passed times, sattr and flags */
+
+ unix_convert(fname,cnum);
+
+ fnum = find_free_file();
+ if (fnum < 0)
+ return(ERROR(ERRSRV,ERRnofids));
+
+ if (!check_name(fname,cnum))
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ unixmode = unix_mode(cnum,open_attr | aARCH);
+
+
+ open_file_shared(fnum,cnum,fname,open_mode,open_ofun,unixmode,
+ &rmode,&smb_action);
+
+ if (!Files[fnum].open)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+
+ if (fstat(Files[fnum].fd,&sbuf) != 0) {
+ close_file(fnum);
+ return(ERROR(ERRDOS,ERRnoaccess));
+ }
+
+ size = sbuf.st_size;
+ fmode = dos_mode(cnum,fname,&sbuf);
+ mtime = sbuf.st_mtime;
+ inode = sbuf.st_ino;
+ if (fmode & aDIR) {
+ close_file(fnum);
+ return(ERROR(ERRDOS,ERRnoaccess));
+ }
+
+ /* Realloc the size of parameters and data we will return */
+ params = *pparams = Realloc(*pparams, 28);
+ if(params == NULL)
+ return(ERROR(ERRDOS,ERRnomem));
+
+ bzero(params,28);
+ SSVAL(params,0,fnum);
+ SSVAL(params,2,fmode);
+ put_dos_date2(params,4, mtime);
+ SIVAL(params,8, size);
+ SSVAL(params,12,rmode);
+
+ SSVAL(params,18,smb_action);
+ SIVAL(params,20,inode);
+
+ /* Send the required number of replies */
+ send_trans2_replies(outbuf, bufsize, params, 28, *ppdata, 0);
+
+ return -1;
+}
+
+/****************************************************************************
+ get a level dependent lanman2 dir entry.
+****************************************************************************/
+static int get_lanman2_dir_entry(int cnum,char *path_mask,int dirtype,int info_level,
+ int requires_resume_key,
+ BOOL dont_descend,char **ppdata,
+ char *base_data, int space_remaining,
+ BOOL *out_of_space,
+ int *last_name_off)
+{
+ char *dname;
+ BOOL found = False;
+ struct stat sbuf;
+ pstring mask;
+ pstring pathreal;
+ pstring fname;
+ BOOL matched;
+ char *p, *pdata = *ppdata;
+ int reskey=0, prev_dirpos=0;
+ int mode=0;
+ uint32 size=0,len;
+ uint32 mdate=0, adate=0, cdate=0;
+ char *name_ptr;
+ BOOL isrootdir = (strequal(Connections[cnum].dirpath,"./") ||
+ strequal(Connections[cnum].dirpath,".") ||
+ strequal(Connections[cnum].dirpath,"/"));
+ BOOL was_8_3;
+
+ *fname = 0;
+ *out_of_space = False;
+
+ if (!Connections[cnum].dirptr)
+ return(False);
+
+ p = strrchr(path_mask,'/');
+ if(p != NULL)
+ {
+ if(p[1] == '\0')
+ strcpy(mask,"*.*");
+ else
+ strcpy(mask, p+1);
+ }
+ else
+ strcpy(mask, path_mask);
+
+ while (!found)
+ {
+ /* Needed if we run out of space */
+ prev_dirpos = TellDir(Connections[cnum].dirptr);
+ dname = ReadDirName(Connections[cnum].dirptr);
+
+ reskey = TellDir(Connections[cnum].dirptr);
+
+ DEBUG(6,("get_lanman2_dir_entry:readdir on dirptr 0x%x now at offset %d\n",
+ Connections[cnum].dirptr,TellDir(Connections[cnum].dirptr)));
+
+ if (!dname)
+ return(False);
+
+ matched = False;
+
+ strcpy(fname,dname);
+
+ if(mask_match(fname, mask, case_sensitive, True))
+ {
+ BOOL isdots = (strequal(fname,"..") || strequal(fname,"."));
+ if (dont_descend && !isdots)
+ continue;
+
+ if (isrootdir && isdots)
+ continue;
+
+ strcpy(pathreal,Connections[cnum].dirpath);
+ strcat(pathreal,"/");
+ strcat(pathreal,fname);
+ if (sys_stat(pathreal,&sbuf) != 0)
+ {
+ DEBUG(5,("get_lanman2_dir_entry:Couldn't stat [%s] (%s)\n",pathreal,strerror(errno)));
+ continue;
+ }
+
+ mode = dos_mode(cnum,pathreal,&sbuf);
+
+ if (((mode & ~dirtype) & (aHIDDEN | aSYSTEM | aDIR)) != 0)
+ {
+ DEBUG(5,("[%s] attribs didn't match %x\n",fname,dirtype));
+ continue;
+ }
+ size = sbuf.st_size;
+ mdate = sbuf.st_mtime;
+ adate = sbuf.st_atime;
+ cdate = sbuf.st_ctime;
+ if(mode & aDIR)
+ size = 0;
+
+ DEBUG(5,("get_lanman2_dir_entry found %s fname=%s\n",pathreal,fname));
+
+ found = True;
+ }
+ }
+
+
+#ifndef KANJI
+ unix2dos_format(fname, True);
+#endif
+
+ p = pdata;
+ name_ptr = p;
+
+ name_map_mangle(fname,False,SNUM(cnum));
+
+ switch (info_level)
+ {
+ case 1:
+ if(requires_resume_key) {
+ SIVAL(p,0,reskey);
+ p += 4;
+ }
+ put_dos_date2(p,l1_fdateCreation,cdate);
+ put_dos_date2(p,l1_fdateLastAccess,adate);
+ put_dos_date2(p,l1_fdateLastWrite,mdate);
+ SIVAL(p,l1_cbFile,size);
+ SIVAL(p,l1_cbFileAlloc,ROUNDUP(size,1024));
+ SSVAL(p,l1_attrFile,mode);
+ SCVAL(p,l1_cchName,strlen(fname));
+ strcpy(p + l1_achName, fname);
+ name_ptr = p + l1_achName;
+ p += l1_achName + strlen(fname) + 1;
+ break;
+
+ case 2:
+ /* info_level 2 */
+ if(requires_resume_key) {
+ SIVAL(p,0,reskey);
+ p += 4;
+ }
+ put_dos_date2(p,l2_fdateCreation,cdate);
+ put_dos_date2(p,l2_fdateLastAccess,adate);
+ put_dos_date2(p,l2_fdateLastWrite,mdate);
+ SIVAL(p,l2_cbFile,size);
+ SIVAL(p,l2_cbFileAlloc,ROUNDUP(size,1024));
+ SSVAL(p,l2_attrFile,mode);
+ SIVAL(p,l2_cbList,0); /* No extended attributes */
+ SCVAL(p,l2_cchName,strlen(fname));
+ strcpy(p + l2_achName, fname);
+ name_ptr = p + l2_achName;
+ p += l2_achName + strlen(fname) + 1;
+ break;
+
+ case 3:
+ SIVAL(p,0,reskey);
+ put_dos_date2(p,4,cdate);
+ put_dos_date2(p,8,adate);
+ put_dos_date2(p,12,mdate);
+ SIVAL(p,16,size);
+ SIVAL(p,20,ROUNDUP(size,1024));
+ SSVAL(p,24,mode);
+ SIVAL(p,26,4);
+ CVAL(p,30) = strlen(fname);
+ strcpy(p+31, fname);
+ name_ptr = p+31;
+ p += 31 + strlen(fname) + 1;
+ break;
+
+ case 4:
+ if(requires_resume_key) {
+ SIVAL(p,0,reskey);
+ p += 4;
+ }
+ SIVAL(p,0,33+strlen(fname)+1);
+ put_dos_date2(p,4,cdate);
+ put_dos_date2(p,8,adate);
+ put_dos_date2(p,12,mdate);
+ SIVAL(p,16,size);
+ SIVAL(p,20,ROUNDUP(size,1024));
+ SSVAL(p,24,mode);
+ CVAL(p,32) = strlen(fname);
+ strcpy(p + 33, fname);
+ name_ptr = p+33;
+ p += 33 + strlen(fname) + 1;
+ break;
+
+ case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
+ was_8_3 = is_8_3(fname);
+ len = 94+strlen(fname);
+ len = (len + 3) & ~3;
+ SIVAL(p,0,len); p += 4;
+ SIVAL(p,0,reskey); p += 4;
+ put_long_date(p,cdate); p += 8;
+ put_long_date(p,adate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ SIVAL(p,0,size); p += 8;
+ SIVAL(p,0,size); p += 8;
+ SIVAL(p,0,mode); p += 4;
+ SIVAL(p,0,strlen(fname)); p += 4;
+ SIVAL(p,0,0); p += 4;
+ if (!was_8_3) {
+#ifndef KANJI
+ strcpy(p+2,unix2dos_format(fname,False));
+#else
+ strcpy(p+2,fname);
+#endif
+ if (!name_map_mangle(p+2,True,SNUM(cnum)))
+ (p+2)[12] = 0;
+ } else
+ *(p+2) = 0;
+ strupper(p+2);
+ SSVAL(p,0,strlen(p+2));
+ p += 2 + 24;
+ /* name_ptr = p; */
+ strcpy(p,fname); p += strlen(p);
+ p = pdata + len;
+ break;
+
+ case SMB_FIND_FILE_DIRECTORY_INFO:
+ len = 64+strlen(fname);
+ len = (len + 3) & ~3;
+ SIVAL(p,0,len); p += 4;
+ SIVAL(p,0,reskey); p += 4;
+ put_long_date(p,cdate); p += 8;
+ put_long_date(p,adate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ SIVAL(p,0,size); p += 8;
+ SIVAL(p,0,size); p += 8;
+ SIVAL(p,0,mode); p += 4;
+ SIVAL(p,0,strlen(fname)); p += 4;
+ strcpy(p,fname);
+ p = pdata + len;
+ break;
+
+
+ case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
+ len = 68+strlen(fname);
+ len = (len + 3) & ~3;
+ SIVAL(p,0,len); p += 4;
+ SIVAL(p,0,reskey); p += 4;
+ put_long_date(p,cdate); p += 8;
+ put_long_date(p,adate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ put_long_date(p,mdate); p += 8;
+ SIVAL(p,0,size); p += 8;
+ SIVAL(p,0,size); p += 8;
+ SIVAL(p,0,mode); p += 4;
+ SIVAL(p,0,strlen(fname)); p += 4;
+ SIVAL(p,0,0); p += 4;
+ strcpy(p,fname);
+ p = pdata + len;
+ break;
+
+ case SMB_FIND_FILE_NAMES_INFO:
+ len = 12+strlen(fname);
+ len = (len + 3) & ~3;
+ SIVAL(p,0,len); p += 4;
+ SIVAL(p,0,reskey); p += 4;
+ SIVAL(p,0,strlen(fname)); p += 4;
+ strcpy(p,fname);
+ p = pdata + len;
+ break;
+
+ default:
+ return(False);
+ }
+
+
+ if (PTR_DIFF(p,pdata) > space_remaining) {
+ /* Move the dirptr back to prev_dirpos */
+ SeekDir(Connections[cnum].dirptr, prev_dirpos);
+ *out_of_space = True;
+ DEBUG(9,("get_lanman2_dir_entry: out of space\n"));
+ return False; /* Not finished - just out of space */
+ }
+
+ /* Setup the last_filename pointer, as an offset from base_data */
+ *last_name_off = PTR_DIFF(name_ptr,base_data);
+ /* Advance the data pointer to the next slot */
+ *ppdata = p;
+ return(found);
+}
+
+/****************************************************************************
+ reply to a TRANS2_FINDFIRST
+****************************************************************************/
+static int call_trans2findfirst(char *inbuf, char *outbuf, int bufsize, int cnum,
+ char **pparams, char **ppdata)
+{
+ /* We must be careful here that we don't return more than the
+ allowed number of data bytes. If this means returning fewer than
+ maxentries then so be it. We assume that the redirector has
+ enough room for the fixed number of parameter bytes it has
+ requested. */
+ uint32 max_data_bytes = SVAL(inbuf, smb_mdrcnt);
+ char *params = *pparams;
+ char *pdata = *ppdata;
+ int dirtype = SVAL(params,0);
+ int maxentries = SVAL(params,2);
+ BOOL close_after_first = BITSETW(params+4,0);
+ BOOL close_if_end = BITSETW(params+4,1);
+ BOOL requires_resume_key = BITSETW(params+4,2);
+ int info_level = SVAL(params,6);
+ pstring directory;
+ pstring mask;
+ char *p, *wcard;
+ int last_name_off=0;
+ int dptr_num = -1;
+ int numentries = 0;
+ int i;
+ BOOL finished = False;
+ BOOL dont_descend = False;
+ BOOL out_of_space = False;
+ int space_remaining;
+
+ *directory = *mask = 0;
+
+ DEBUG(3,("call_trans2findfirst: dirtype = %d, maxentries = %d, close_after_first=%d, close_if_end = %d requires_resume_key = %d level = %d, max_data_bytes = %d\n",
+ dirtype, maxentries, close_after_first, close_if_end, requires_resume_key,
+ info_level, max_data_bytes));
+
+ switch (info_level)
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case SMB_FIND_FILE_DIRECTORY_INFO:
+ case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
+ case SMB_FIND_FILE_NAMES_INFO:
+ case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
+ break;
+ default:
+ return(ERROR(ERRDOS,ERRunknownlevel));
+ }
+
+ strcpy(directory, params + 12); /* Complete directory path with
+ wildcard mask appended */
+
+ DEBUG(5,("path=%s\n",directory));
+
+ unix_convert(directory,cnum);
+ if(!check_name(directory,cnum)) {
+ return(ERROR(ERRDOS,ERRbadpath));
+ }
+
+ p = strrchr(directory,'/');
+ if(p == NULL) {
+ strcpy(mask,directory);
+ strcpy(directory,"./");
+ } else {
+ strcpy(mask,p+1);
+ *p = 0;
+ }
+
+ DEBUG(5,("dir=%s, mask = %s\n",directory, mask));
+
+ pdata = *ppdata = Realloc(*ppdata, max_data_bytes + 1024);
+ if(!*ppdata)
+ return(ERROR(ERRDOS,ERRnomem));
+ bzero(pdata,max_data_bytes);
+
+ /* Realloc the params space */
+ params = *pparams = Realloc(*pparams, 10);
+ if(params == NULL)
+ return(ERROR(ERRDOS,ERRnomem));
+
+ dptr_num = dptr_create(cnum,directory, True ,SVAL(inbuf,smb_pid));
+ if (dptr_num < 0)
+ return(ERROR(ERRDOS,ERRbadpath));
+
+ /* convert the formatted masks */
+ {
+ p = mask;
+ while (*p) {
+ if (*p == '<') *p = '*';
+ if (*p == '>') *p = '?';
+ if (*p == '"') *p = '.';
+ p++;
+ }
+ }
+
+ /* a special case for 16 bit apps */
+ if (strequal(mask,"????????.???")) strcpy(mask,"*");
+
+ /* handle broken clients that send us old 8.3 format */
+ string_sub(mask,"????????","*");
+ string_sub(mask,".???",".*");
+
+ /* Save the wildcard match and attribs we are using on this directory -
+ needed as lanman2 assumes these are being saved between calls */
+
+ if(!(wcard = strdup(mask))) {
+ dptr_close(dptr_num);
+ return(ERROR(ERRDOS,ERRnomem));
+ }
+
+ dptr_set_wcard(dptr_num, wcard);
+ dptr_set_attr(dptr_num, dirtype);
+
+ DEBUG(4,("dptr_num is %d, wcard = %s, attr = %d\n",dptr_num, wcard, dirtype));
+
+ /* We don't need to check for VOL here as this is returned by
+ a different TRANS2 call. */
+
+ DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n",
+ Connections[cnum].dirpath,lp_dontdescend(SNUM(cnum))));
+ if (in_list(Connections[cnum].dirpath,lp_dontdescend(SNUM(cnum)),case_sensitive))
+ dont_descend = True;
+
+ p = pdata;
+ space_remaining = max_data_bytes;
+ out_of_space = False;
+
+ for (i=0;(i<maxentries) && !finished && !out_of_space;i++)
+ {
+
+ /* this is a heuristic to avoid seeking the dirptr except when
+ absolutely necessary. It allows for a filename of about 40 chars */
+ if (space_remaining < DIRLEN_GUESS && numentries > 0)
+ {
+ out_of_space = True;
+ finished = False;
+ }
+ else
+ {
+ finished =
+ !get_lanman2_dir_entry(cnum,mask,dirtype,info_level,
+ requires_resume_key,dont_descend,
+ &p,pdata,space_remaining, &out_of_space,
+ &last_name_off);
+ }
+
+ if (finished && out_of_space)
+ finished = False;
+
+ if (!finished && !out_of_space)
+ numentries++;
+ space_remaining = max_data_bytes - PTR_DIFF(p,pdata);
+ }
+
+ /* Check if we can close the dirptr */
+ if(close_after_first || (finished && close_if_end))
+ {
+ dptr_close(dptr_num);
+ DEBUG(5,("call_trans2findfirst - (2) closing dptr_num %d\n", dptr_num));
+ dptr_num = -1;
+ }
+
+ /* At this point pdata points to numentries directory entries. */
+
+ /* Set up the return parameter block */
+ SSVAL(params,0,dptr_num);
+ SSVAL(params,2,numentries);
+ SSVAL(params,4,finished);
+ SSVAL(params,6,0); /* Never an EA error */
+ SSVAL(params,8,last_name_off);
+
+ send_trans2_replies( outbuf, bufsize, params, 10, pdata, PTR_DIFF(p,pdata));
+
+ if ((! *directory) && dptr_path(dptr_num))
+ sprintf(directory,"(%s)",dptr_path(dptr_num));
+
+ DEBUG(4,("%s %s mask=%s directory=%s cnum=%d dirtype=%d numentries=%d\n",
+ timestring(),
+ smb_fn_name(CVAL(inbuf,smb_com)),
+ mask,directory,cnum,dirtype,numentries));
+
+ return(-1);
+}
+
+
+/****************************************************************************
+ reply to a TRANS2_FINDNEXT
+****************************************************************************/
+static int call_trans2findnext(char *inbuf, char *outbuf, int length, int bufsize,
+ int cnum, char **pparams, char **ppdata)
+{
+ /* We must be careful here that we don't return more than the
+ allowed number of data bytes. If this means returning fewer than
+ maxentries then so be it. We assume that the redirector has
+ enough room for the fixed number of parameter bytes it has
+ requested. */
+ int max_data_bytes = SVAL(inbuf, smb_mdrcnt);
+ char *params = *pparams;
+ char *pdata = *ppdata;
+ int16 dptr_num = SVAL(params,0);
+ int maxentries = SVAL(params,2);
+ uint16 info_level = SVAL(params,4);
+ uint32 resume_key = IVAL(params,6);
+ BOOL close_after_request = BITSETW(params+10,0);
+ BOOL close_if_end = BITSETW(params+10,1);
+ BOOL requires_resume_key = BITSETW(params+10,2);
+ BOOL continue_bit = BITSETW(params+10,3);
+ pstring mask;
+ pstring directory;
+ char *p;
+ uint16 dirtype;
+ int numentries = 0;
+ int i, last_name_off=0;
+ BOOL finished = False;
+ BOOL dont_descend = False;
+ BOOL out_of_space = False;
+ int space_remaining;
+
+ *mask = *directory = 0;
+
+ DEBUG(3,("call_trans2findnext: dirhandle = %d, max_data_bytes = %d, maxentries = %d, close_after_request=%d, close_if_end = %d requires_resume_key = %d resume_key = %d continue=%d level = %d\n",
+ dptr_num, max_data_bytes, maxentries, close_after_request, close_if_end,
+ requires_resume_key, resume_key, continue_bit, info_level));
+
+ switch (info_level)
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case SMB_FIND_FILE_DIRECTORY_INFO:
+ case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
+ case SMB_FIND_FILE_NAMES_INFO:
+ case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
+ break;
+ default:
+ return(ERROR(ERRDOS,ERRunknownlevel));
+ }
+
+ pdata = *ppdata = Realloc( *ppdata, max_data_bytes + 1024);
+ if(!*ppdata)
+ return(ERROR(ERRDOS,ERRnomem));
+ bzero(pdata,max_data_bytes);
+
+ /* Realloc the params space */
+ params = *pparams = Realloc(*pparams, 6*SIZEOFWORD);
+ if(!params)
+ return(ERROR(ERRDOS,ERRnomem));
+
+ /* Check that the dptr is valid */
+ if(!(Connections[cnum].dirptr = dptr_fetch_lanman2(params, dptr_num)))
+ return(ERROR(ERRDOS,ERRnofiles));
+
+ string_set(&Connections[cnum].dirpath,dptr_path(dptr_num));
+
+ /* Get the wildcard mask from the dptr */
+ if((p = dptr_wcard(dptr_num))== NULL) {
+ DEBUG(2,("dptr_num %d has no wildcard\n", dptr_num));
+ return (ERROR(ERRDOS,ERRnofiles));
+ }
+ strcpy(mask, p);
+ strcpy(directory,Connections[cnum].dirpath);
+
+ /* Get the attr mask from the dptr */
+ dirtype = dptr_attr(dptr_num);
+
+ DEBUG(3,("dptr_num is %d, mask = %s, attr = %x, dirptr=(0x%X,%d)\n",
+ dptr_num, mask, dirtype,
+ Connections[cnum].dirptr,
+ TellDir(Connections[cnum].dirptr)));
+
+ /* We don't need to check for VOL here as this is returned by
+ a different TRANS2 call. */
+
+ DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n",Connections[cnum].dirpath,lp_dontdescend(SNUM(cnum))));
+ if (in_list(Connections[cnum].dirpath,lp_dontdescend(SNUM(cnum)),case_sensitive))
+ dont_descend = True;
+
+ p = pdata;
+ space_remaining = max_data_bytes;
+ out_of_space = False;
+
+ /* If we have a resume key - seek to the correct position. */
+ if(requires_resume_key && !continue_bit)
+ SeekDir(Connections[cnum].dirptr, resume_key);
+
+ for (i=0;(i<(int)maxentries) && !finished && !out_of_space ;i++)
+ {
+ /* this is a heuristic to avoid seeking the dirptr except when
+ absolutely necessary. It allows for a filename of about 40 chars */
+ if (space_remaining < DIRLEN_GUESS && numentries > 0)
+ {
+ out_of_space = True;
+ finished = False;
+ }
+ else
+ {
+ finished =
+ !get_lanman2_dir_entry(cnum,mask,dirtype,info_level,
+ requires_resume_key,dont_descend,
+ &p,pdata,space_remaining, &out_of_space,
+ &last_name_off);
+ }
+
+ if (finished && out_of_space)
+ finished = False;
+
+ if (!finished && !out_of_space)
+ numentries++;
+ space_remaining = max_data_bytes - PTR_DIFF(p,pdata);
+ }
+
+ /* Check if we can close the dirptr */
+ if(close_after_request || (finished && close_if_end))
+ {
+ dptr_close(dptr_num); /* This frees up the saved mask */
+ DEBUG(5,("call_trans2findnext: closing dptr_num = %d\n", dptr_num));
+ dptr_num = -1;
+ }
+
+
+ /* Set up the return parameter block */
+ SSVAL(params,0,numentries);
+ SSVAL(params,2,finished);
+ SSVAL(params,4,0); /* Never an EA error */
+ SSVAL(params,6,last_name_off);
+
+ send_trans2_replies( outbuf, bufsize, params, 8, pdata, PTR_DIFF(p,pdata));
+
+ if ((! *directory) && dptr_path(dptr_num))
+ sprintf(directory,"(%s)",dptr_path(dptr_num));
+
+ DEBUG(3,("%s %s mask=%s directory=%s cnum=%d dirtype=%d numentries=%d\n",
+ timestring(),
+ smb_fn_name(CVAL(inbuf,smb_com)),
+ mask,directory,cnum,dirtype,numentries));
+
+ return(-1);
+}
+
+/****************************************************************************
+ reply to a TRANS2_QFSINFO (query filesystem info)
+****************************************************************************/
+static int call_trans2qfsinfo(char *inbuf, char *outbuf, int length, int bufsize,
+ int cnum, char **pparams, char **ppdata)
+{
+ char *pdata = *ppdata;
+ char *params = *pparams;
+ uint16 info_level = SVAL(params,0);
+ int data_len;
+ struct stat st;
+ char *vname = volume_label(SNUM(cnum));
+
+ DEBUG(3,("call_trans2qfsinfo: cnum = %d, level = %d\n", cnum, info_level));
+
+ if(sys_stat(".",&st)!=0) {
+ DEBUG(2,("call_trans2qfsinfo: stat of . failed (%s)\n", strerror(errno)));
+ return (ERROR(ERRSRV,ERRinvdevice));
+ }
+
+ pdata = *ppdata = Realloc(*ppdata, 1024); bzero(pdata,1024);
+
+ switch (info_level)
+ {
+ case 1:
+ {
+ int dfree,dsize,bsize;
+ data_len = 18;
+ sys_disk_free(".",&bsize,&dfree,&dsize);
+ SIVAL(pdata,l1_idFileSystem,st.st_dev);
+ SIVAL(pdata,l1_cSectorUnit,bsize/512);
+ SIVAL(pdata,l1_cUnit,dsize);
+ SIVAL(pdata,l1_cUnitAvail,dfree);
+ SSVAL(pdata,l1_cbSector,512);
+ DEBUG(5,("call_trans2qfsinfo : bsize=%d, id=%x, cSectorUnit=%d, cUnit=%d, cUnitAvail=%d, cbSector=%d\n",
+ bsize, st.st_dev, bsize/512, dsize, dfree, 512));
+ break;
+ }
+ case 2:
+ {
+ /* Return volume name */
+ int volname_len = MIN(strlen(vname),11);
+ data_len = l2_vol_szVolLabel + volname_len + 1;
+ put_dos_date2(pdata,l2_vol_fdateCreation,st.st_ctime);
+ SCVAL(pdata,l2_vol_cch,volname_len);
+ StrnCpy(pdata+l2_vol_szVolLabel,vname,volname_len);
+ DEBUG(5,("call_trans2qfsinfo : time = %x, namelen = %d, name = %s\n",st.st_ctime,volname_len,
+ pdata+l2_vol_szVolLabel));
+ break;
+ }
+ case SMB_QUERY_FS_ATTRIBUTE_INFO:
+ data_len = 12 + 2*strlen(FSTYPE_STRING);
+ SIVAL(pdata,0,0x4006); /* FS ATTRIBUTES == long filenames supported? */
+ SIVAL(pdata,4,128); /* Max filename component length */
+ SIVAL(pdata,8,2*strlen(FSTYPE_STRING));
+ PutUniCode(pdata+12,FSTYPE_STRING);
+ break;
+ case SMB_QUERY_FS_LABEL_INFO:
+ data_len = 4 + strlen(vname);
+ SIVAL(pdata,0,strlen(vname));
+ strcpy(pdata+4,vname);
+ break;
+ case SMB_QUERY_FS_VOLUME_INFO:
+ data_len = 17 + strlen(vname);
+ SIVAL(pdata,12,strlen(vname));
+ strcpy(pdata+17,vname);
+ break;
+ case SMB_QUERY_FS_SIZE_INFO:
+ {
+ int dfree,dsize,bsize;
+ data_len = 24;
+ sys_disk_free(".",&bsize,&dfree,&dsize);
+ SIVAL(pdata,0,dsize);
+ SIVAL(pdata,8,dfree);
+ SIVAL(pdata,16,bsize/512);
+ SIVAL(pdata,20,512);
+ }
+ break;
+ case SMB_QUERY_FS_DEVICE_INFO:
+ data_len = 8;
+ SIVAL(pdata,0,0); /* dev type */
+ SIVAL(pdata,4,0); /* characteristics */
+ break;
+ default:
+ return(ERROR(ERRDOS,ERRunknownlevel));
+ }
+
+
+ send_trans2_replies( outbuf, bufsize, params, 0, pdata, data_len);
+
+ DEBUG(4,("%s %s info_level =%d\n",timestring(),smb_fn_name(CVAL(inbuf,smb_com)), info_level));
+
+ return -1;
+}
+
+/****************************************************************************
+ reply to a TRANS2_SETFSINFO (set filesystem info)
+****************************************************************************/
+static int call_trans2setfsinfo(char *inbuf, char *outbuf, int length, int bufsize,
+ int cnum, char **pparams, char **ppdata)
+{
+ /* Just say yes we did it - there is nothing that
+ can be set here so it doesn't matter. */
+ int outsize;
+ DEBUG(3,("call_trans2setfsinfo\n"));
+
+ if (!CAN_WRITE(cnum))
+ return(ERROR(ERRSRV,ERRaccess));
+
+ outsize = set_message(outbuf,10,0,True);
+
+ return outsize;
+}
+
+/****************************************************************************
+ reply to a TRANS2_QFILEINFO (query file info by fileid)
+****************************************************************************/
+static int call_trans2qfilepathinfo(char *inbuf, char *outbuf, int length,
+ int bufsize,int cnum,
+ char **pparams,char **ppdata,
+ int total_data)
+{
+ char *params = *pparams;
+ char *pdata = *ppdata;
+ uint16 tran_call = SVAL(inbuf, smb_setup0);
+ uint16 info_level;
+ int mode=0;
+ int size=0;
+ unsigned int data_size;
+ struct stat sbuf;
+ pstring fname1;
+ char *fname;
+ char *p;
+ int l,pos;
+
+
+ if (tran_call == TRANSACT2_QFILEINFO) {
+ int16 fnum = SVAL(params,0);
+ info_level = SVAL(params,2);
+
+ CHECK_FNUM(fnum,cnum);
+ CHECK_ERROR(fnum);
+
+ fname = Files[fnum].name;
+ if (fstat(Files[fnum].fd,&sbuf) != 0) {
+ DEBUG(3,("fstat of fnum %d failed (%s)\n",fnum, strerror(errno)));
+ return(UNIXERROR(ERRDOS,ERRbadfid));
+ }
+ pos = lseek(Files[fnum].fd,0,SEEK_CUR);
+ } else {
+ /* qpathinfo */
+ info_level = SVAL(params,0);
+ fname = &fname1[0];
+ strcpy(fname,&params[6]);
+ unix_convert(fname,cnum);
+ if (!check_name(fname,cnum) || sys_stat(fname,&sbuf)) {
+ DEBUG(3,("fileinfo of %s failed (%s)\n",fname,strerror(errno)));
+ return(UNIXERROR(ERRDOS,ERRbadpath));
+ }
+ pos = 0;
+ }
+
+
+ DEBUG(3,("call_trans2qfilepathinfo %s level=%d call=%d total_data=%d\n",
+ fname,info_level,tran_call,total_data));
+
+ p = strrchr(fname,'/');
+ if (!p)
+ p = fname;
+ else
+ p++;
+ l = strlen(p);
+ mode = dos_mode(cnum,fname,&sbuf);
+ size = sbuf.st_size;
+ if (mode & aDIR) size = 0;
+
+ params = *pparams = Realloc(*pparams,2); bzero(params,2);
+ data_size = 1024;
+ pdata = *ppdata = Realloc(*ppdata, data_size);
+
+ if (total_data > 0 && IVAL(pdata,0) == total_data) {
+ /* uggh, EAs for OS2 */
+ DEBUG(4,("Rejecting EA request with total_data=%d\n",total_data));
+#if 0
+ SSVAL(params,0,ERROR_EAS_NOT_SUPPORTED);
+ send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
+ return(-1);
+#else
+ return(ERROR(ERRDOS,ERROR_EAS_NOT_SUPPORTED));
+#endif
+ }
+
+ bzero(pdata,data_size);
+
+ switch (info_level)
+ {
+ case 1:
+ case 2:
+ data_size = (info_level==1?22:26);
+ put_dos_date2(pdata,l1_fdateCreation,sbuf.st_ctime);
+ put_dos_date2(pdata,l1_fdateLastAccess,sbuf.st_atime);
+ put_dos_date2(pdata,l1_fdateLastWrite,sbuf.st_mtime);
+ SIVAL(pdata,l1_cbFile,size);
+ SIVAL(pdata,l1_cbFileAlloc,ROUNDUP(size,1024));
+ SSVAL(pdata,l1_attrFile,mode);
+ SIVAL(pdata,l1_attrFile+2,4); /* this is what OS2 does */
+ break;
+
+ case 3:
+ data_size = 24;
+ put_dos_date2(pdata,0,sbuf.st_ctime);
+ put_dos_date2(pdata,4,sbuf.st_atime);
+ put_dos_date2(pdata,8,sbuf.st_mtime);
+ SIVAL(pdata,12,size);
+ SIVAL(pdata,16,ROUNDUP(size,1024));
+ SIVAL(pdata,20,mode);
+ break;
+
+ case 4:
+ data_size = 4;
+ SIVAL(pdata,0,data_size);
+ break;
+
+ case 6:
+ return(ERROR(ERRDOS,ERRbadfunc)); /* os/2 needs this */
+
+ case SMB_QUERY_FILE_BASIC_INFO:
+ data_size = 36;
+ put_long_date(pdata,sbuf.st_ctime);
+ put_long_date(pdata+8,sbuf.st_atime);
+ put_long_date(pdata+16,sbuf.st_mtime);
+ put_long_date(pdata+24,sbuf.st_mtime);
+ SIVAL(pdata,32,mode);
+ break;
+
+ case SMB_QUERY_FILE_STANDARD_INFO:
+ data_size = 22;
+ SIVAL(pdata,0,size);
+ SIVAL(pdata,8,size);
+ SIVAL(pdata,16,sbuf.st_nlink);
+ CVAL(pdata,20) = 0;
+ CVAL(pdata,21) = (mode&aDIR)?1:0;
+ break;
+
+ case SMB_QUERY_FILE_EA_INFO:
+ data_size = 4;
+ break;
+
+ case SMB_QUERY_FILE_NAME_INFO:
+ case SMB_QUERY_FILE_ALT_NAME_INFO:
+ data_size = 4 + l;
+ SIVAL(pdata,0,l);
+ strcpy(pdata+4,fname);
+ break;
+ case SMB_QUERY_FILE_ALLOCATION_INFO:
+ case SMB_QUERY_FILE_END_OF_FILEINFO:
+ data_size = 8;
+ SIVAL(pdata,0,size);
+ break;
+
+ case SMB_QUERY_FILE_ALL_INFO:
+ put_long_date(pdata,sbuf.st_ctime);
+ put_long_date(pdata+8,sbuf.st_atime);
+ put_long_date(pdata+16,sbuf.st_mtime);
+ put_long_date(pdata+24,sbuf.st_mtime);
+ SIVAL(pdata,32,mode);
+ pdata += 40;
+ SIVAL(pdata,0,size);
+ SIVAL(pdata,8,size);
+ SIVAL(pdata,16,sbuf.st_nlink);
+ CVAL(pdata,20) = 0;
+ CVAL(pdata,21) = (mode&aDIR)?1:0;
+ pdata += 24;
+ pdata += 8; /* index number */
+ pdata += 4; /* EA info */
+ if (mode & aRONLY)
+ SIVAL(pdata,0,0xA9);
+ else
+ SIVAL(pdata,0,0xd01BF);
+ pdata += 4;
+ SIVAL(pdata,0,pos); /* current offset */
+ pdata += 8;
+ SIVAL(pdata,0,mode); /* is this the right sort of mode info? */
+ pdata += 4;
+ pdata += 4; /* alignment */
+ SIVAL(pdata,0,l);
+ strcpy(pdata+4,fname);
+ pdata += 4 + l;
+ data_size = PTR_DIFF(pdata,(*ppdata));
+ break;
+
+ case SMB_QUERY_FILE_STREAM_INFO:
+ data_size = 24 + l;
+ SIVAL(pdata,0,pos);
+ SIVAL(pdata,4,size);
+ SIVAL(pdata,12,size);
+ SIVAL(pdata,20,l);
+ strcpy(pdata+24,fname);
+ break;
+ default:
+ return(ERROR(ERRDOS,ERRunknownlevel));
+ }
+
+ send_trans2_replies( outbuf, bufsize, params, 2, *ppdata, data_size);
+
+ return(-1);
+}
+
+/****************************************************************************
+ reply to a TRANS2_SETFILEINFO (set file info by fileid)
+****************************************************************************/
+static int call_trans2setfilepathinfo(char *inbuf, char *outbuf, int length,
+ int bufsize, int cnum, char **pparams,
+ char **ppdata, int total_data)
+{
+ char *params = *pparams;
+ char *pdata = *ppdata;
+ uint16 tran_call = SVAL(inbuf, smb_setup0);
+ uint16 info_level;
+ int mode=0;
+ int size=0;
+ struct utimbuf tvs;
+ struct stat st;
+ pstring fname1;
+ char *fname;
+ int fd = -1;
+
+ if (!CAN_WRITE(cnum))
+ return(ERROR(ERRSRV,ERRaccess));
+
+ if (tran_call == TRANSACT2_SETFILEINFO) {
+ int16 fnum = SVAL(params,0);
+ info_level = SVAL(params,2);
+
+ CHECK_FNUM(fnum,cnum);
+ CHECK_ERROR(fnum);
+
+ fname = Files[fnum].name;
+ fd = Files[fnum].fd;
+
+ if(fstat(fd,&st)!=0) {
+ DEBUG(3,("fstat of %s failed (%s)\n", fname, strerror(errno)));
+ return(ERROR(ERRDOS,ERRbadpath));
+ }
+ } else {
+ /* set path info */
+ info_level = SVAL(params,0);
+ fname = fname1;
+ strcpy(fname,&params[6]);
+ unix_convert(fname,cnum);
+ if(!check_name(fname, cnum))
+ return(ERROR(ERRDOS,ERRbadpath));
+
+ if(sys_stat(fname,&st)!=0) {
+ DEBUG(3,("stat of %s failed (%s)\n", fname, strerror(errno)));
+ return(ERROR(ERRDOS,ERRbadpath));
+ }
+ }
+
+ DEBUG(3,("call_trans2setfilepathinfo(%d) %s info_level=%d totdata=%d\n",
+ tran_call,fname,info_level,total_data));
+
+ /* Realloc the parameter and data sizes */
+ params = *pparams = Realloc(*pparams,2); SSVAL(params,0,0);
+ if(params == NULL)
+ return(ERROR(ERRDOS,ERRnomem));
+
+ size = st.st_size;
+ tvs.modtime = st.st_mtime;
+ tvs.actime = st.st_atime;
+ mode = dos_mode(cnum,fname,&st);
+
+ if (total_data > 0 && IVAL(pdata,0) == total_data) {
+ /* uggh, EAs for OS2 */
+ DEBUG(4,("Rejecting EA request with total_data=%d\n",total_data));
+ SSVAL(params,0,ERROR_EAS_NOT_SUPPORTED);
+
+ send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
+
+ return(-1);
+ }
+
+ switch (info_level)
+ {
+ case 1:
+ tvs.actime = make_unix_date2(pdata+l1_fdateLastAccess);
+ tvs.modtime = make_unix_date2(pdata+l1_fdateLastWrite);
+ mode = SVAL(pdata,l1_attrFile);
+ size = IVAL(pdata,l1_cbFile);
+ break;
+
+ case 2:
+ tvs.actime = make_unix_date2(pdata+l1_fdateLastAccess);
+ tvs.modtime = make_unix_date2(pdata+l1_fdateLastWrite);
+ mode = SVAL(pdata,l1_attrFile);
+ size = IVAL(pdata,l1_cbFile);
+ break;
+
+ case 3:
+ tvs.actime = make_unix_date2(pdata+8);
+ tvs.modtime = make_unix_date2(pdata+12);
+ size = IVAL(pdata,16);
+ mode = IVAL(pdata,24);
+ break;
+
+ case 4:
+ tvs.actime = make_unix_date2(pdata+8);
+ tvs.modtime = make_unix_date2(pdata+12);
+ size = IVAL(pdata,16);
+ mode = IVAL(pdata,24);
+ break;
+
+ case SMB_SET_FILE_BASIC_INFO:
+ pdata += 8; /* create time */
+ tvs.actime = interpret_long_date(pdata); pdata += 8;
+ tvs.modtime=MAX(interpret_long_date(pdata),interpret_long_date(pdata+8));
+ pdata += 16;
+ mode = IVAL(pdata,0);
+ break;
+
+ case SMB_SET_FILE_END_OF_FILE_INFO:
+ if (IVAL(pdata,4) != 0) /* more than 32 bits? */
+ return(ERROR(ERRDOS,ERRunknownlevel));
+ size = IVAL(pdata,0);
+ break;
+
+ case SMB_SET_FILE_DISPOSITION_INFO: /* not supported yet */
+ case SMB_SET_FILE_ALLOCATION_INFO: /* not supported yet */
+ default:
+ return(ERROR(ERRDOS,ERRunknownlevel));
+ }
+
+
+ if (!tvs.actime) tvs.actime = st.st_atime;
+ if (!tvs.modtime) tvs.modtime = st.st_mtime;
+ if (!size) size = st.st_size;
+
+ /* Try and set the times, size and mode of this file - if they are different
+ from the current values */
+ if(st.st_mtime != tvs.modtime || st.st_atime != tvs.actime) {
+ if(sys_utime(fname, &tvs)!=0)
+ return(ERROR(ERRDOS,ERRnoaccess));
+ }
+ if(mode != dos_mode(cnum,fname,&st) && dos_chmod(cnum,fname,mode,NULL)) {
+ DEBUG(2,("chmod of %s failed (%s)\n", fname, strerror(errno)));
+ return(ERROR(ERRDOS,ERRnoaccess));
+ }
+ if(size != st.st_size) {
+ if (fd == -1) {
+ fd = sys_open(fname,O_RDWR,0);
+ if (fd == -1)
+ return(ERROR(ERRDOS,ERRbadpath));
+ set_filelen(fd, size);
+ close(fd);
+ } else {
+ set_filelen(fd, size);
+ }
+ }
+
+ SSVAL(params,0,0);
+
+ send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
+
+ return(-1);
+}
+
+/****************************************************************************
+ reply to a TRANS2_MKDIR (make directory with extended attributes).
+****************************************************************************/
+static int call_trans2mkdir(char *inbuf, char *outbuf, int length, int bufsize,
+ int cnum, char **pparams, char **ppdata)
+{
+ char *params = *pparams;
+ pstring directory;
+ int ret = -1;
+
+ if (!CAN_WRITE(cnum))
+ return(ERROR(ERRSRV,ERRaccess));
+
+ strcpy(directory, &params[4]);
+
+ DEBUG(3,("call_trans2mkdir : name = %s\n", directory));
+
+ unix_convert(directory,cnum);
+ if (check_name(directory,cnum))
+ ret = sys_mkdir(directory,unix_mode(cnum,aDIR));
+
+ if(ret < 0)
+ {
+ DEBUG(5,("call_trans2mkdir error (%s)\n", strerror(errno)));
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ /* Realloc the parameter and data sizes */
+ params = *pparams = Realloc(*pparams,2);
+ if(params == NULL)
+ return(ERROR(ERRDOS,ERRnomem));
+
+ SSVAL(params,0,0);
+
+ send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0);
+
+ return(-1);
+}
+
+/****************************************************************************
+ reply to a TRANS2_FINDNOTIFYFIRST (start monitoring a directory for changes)
+ We don't actually do this - we just send a null response.
+****************************************************************************/
+static int call_trans2findnotifyfirst(char *inbuf, char *outbuf, int length, int bufsize,
+ int cnum, char **pparams, char **ppdata)
+{
+ static uint16 fnf_handle = 257;
+ char *params = *pparams;
+ uint16 info_level = SVAL(params,4);
+
+ DEBUG(3,("call_trans2findnotifyfirst - info_level %d\n", info_level));
+
+ switch (info_level)
+ {
+ case 1:
+ case 2:
+ break;
+ default:
+ return(ERROR(ERRDOS,ERRunknownlevel));
+ }
+
+ /* Realloc the parameter and data sizes */
+ params = *pparams = Realloc(*pparams,6);
+ if(params == NULL)
+ return(ERROR(ERRDOS,ERRnomem));
+
+ SSVAL(params,0,fnf_handle);
+ SSVAL(params,2,0); /* No changes */
+ SSVAL(params,4,0); /* No EA errors */
+
+ fnf_handle++;
+
+ if(fnf_handle == 0)
+ fnf_handle = 257;
+
+ send_trans2_replies(outbuf, bufsize, params, 6, *ppdata, 0);
+
+ return(-1);
+}
+
+/****************************************************************************
+ reply to a TRANS2_FINDNOTIFYNEXT (continue monitoring a directory for
+ changes). Currently this does nothing.
+****************************************************************************/
+static int call_trans2findnotifynext(char *inbuf, char *outbuf, int length, int bufsize,
+ int cnum, char **pparams, char **ppdata)
+{
+ char *params = *pparams;
+
+ DEBUG(3,("call_trans2findnotifynext\n"));
+
+ /* Realloc the parameter and data sizes */
+ params = *pparams = Realloc(*pparams,4);
+ if(params == NULL)
+ return(ERROR(ERRDOS,ERRnomem));
+
+ SSVAL(params,0,0); /* No changes */
+ SSVAL(params,2,0); /* No EA errors */
+
+ send_trans2_replies(outbuf, bufsize, params, 4, *ppdata, 0);
+
+ return(-1);
+}
+
+/****************************************************************************
+ reply to a SMBfindclose (stop trans2 directory search)
+****************************************************************************/
+int reply_findclose(char *inbuf,char *outbuf,int length,int bufsize)
+{
+ int cnum;
+ int outsize = 0;
+ uint16 dptr_num=SVAL(inbuf,smb_vwv0);
+
+ cnum = SVAL(inbuf,smb_tid);
+
+ DEBUG(3,("reply_findclose, cnum = %d, dptr_num = %d\n", cnum, dptr_num));
+
+ dptr_close(dptr_num);
+
+ outsize = set_message(outbuf,0,0,True);
+
+ DEBUG(3,("%s SMBfindclose cnum=%d, dptr_num = %d\n",timestring(),cnum,dptr_num));
+
+ return(outsize);
+}
+
+/****************************************************************************
+ reply to a SMBfindnclose (stop FINDNOTIFYFIRST directory search)
+****************************************************************************/
+int reply_findnclose(char *inbuf,char *outbuf,int length,int bufsize)
+{
+ int cnum;
+ int outsize = 0;
+ int dptr_num= -1;
+
+ cnum = SVAL(inbuf,smb_tid);
+ dptr_num = SVAL(inbuf,smb_vwv0);
+
+ DEBUG(3,("reply_findnclose, cnum = %d, dptr_num = %d\n", cnum, dptr_num));
+
+ /* We never give out valid handles for a
+ findnotifyfirst - so any dptr_num is ok here.
+ Just ignore it. */
+
+ outsize = set_message(outbuf,0,0,True);
+
+ DEBUG(3,("%s SMB_findnclose cnum=%d, dptr_num = %d\n",timestring(),cnum,dptr_num));
+
+ return(outsize);
+}
+
+
+/****************************************************************************
+ reply to a SMBtranss2 - just ignore it!
+****************************************************************************/
+int reply_transs2(char *inbuf,char *outbuf,int length,int bufsize)
+{
+ DEBUG(4,("Ignoring transs2 of length %d\n",length));
+ return(-1);
+}
+
+/****************************************************************************
+ reply to a SMBtrans2
+****************************************************************************/
+int reply_trans2(char *inbuf,char *outbuf,int length,int bufsize)
+{
+ int outsize = 0;
+ int cnum = SVAL(inbuf,smb_tid);
+ unsigned int total_params = SVAL(inbuf, smb_tpscnt);
+ unsigned int total_data =SVAL(inbuf, smb_tdscnt);
+#if 0
+ unsigned int max_param_reply = SVAL(inbuf, smb_mprcnt);
+ unsigned int max_data_reply = SVAL(inbuf, smb_mdrcnt);
+ unsigned int max_setup_fields = SVAL(inbuf, smb_msrcnt);
+ BOOL close_tid = BITSETW(inbuf+smb_flags,0);
+ BOOL no_final_response = BITSETW(inbuf+smb_flags,1);
+ int32 timeout = IVALS(inbuf,smb_timeout);
+#endif
+ unsigned int suwcnt = SVAL(inbuf, smb_suwcnt);
+ unsigned int tran_call = SVAL(inbuf, smb_setup0);
+ char *params = NULL, *data = NULL;
+ int num_params, num_params_sofar, num_data, num_data_sofar;
+
+ outsize = set_message(outbuf,0,0,True);
+
+ /* All trans2 messages we handle have smb_sucnt == 1 - ensure this
+ is so as a sanity check */
+ if(suwcnt != 1 )
+ {
+ DEBUG(2,("Invalid smb_sucnt in trans2 call\n"));
+ return(ERROR(ERRSRV,ERRerror));
+ }
+
+ /* Allocate the space for the maximum needed parameters and data */
+ if (total_params > 0)
+ params = (char *)malloc(total_params);
+ if (total_data > 0)
+ data = (char *)malloc(total_data);
+
+ if ((total_params && !params) || (total_data && !data))
+ {
+ DEBUG(2,("Out of memory in reply_trans2\n"));
+ return(ERROR(ERRDOS,ERRnomem));
+ }
+
+ /* Copy the param and data bytes sent with this request into
+ the params buffer */
+ num_params = num_params_sofar = SVAL(inbuf,smb_pscnt);
+ num_data = num_data_sofar = SVAL(inbuf, smb_dscnt);
+
+ memcpy( params, smb_base(inbuf) + SVAL(inbuf, smb_psoff), num_params);
+ memcpy( data, smb_base(inbuf) + SVAL(inbuf, smb_dsoff), num_data);
+
+ if(num_data_sofar < total_data || num_params_sofar < total_params)
+ {
+ /* We need to send an interim response then receive the rest
+ of the parameter/data bytes */
+ outsize = set_message(outbuf,0,0,True);
+ send_smb(Client,outbuf);
+
+ while( num_data_sofar < total_data || num_params_sofar < total_params)
+ {
+ receive_smb(Client,inbuf, 0);
+
+ /* Ensure this is still a trans2 packet (sanity check) */
+ if(CVAL(inbuf, smb_com) != SMBtranss2)
+ {
+ outsize = set_message(outbuf,0,0,True);
+ DEBUG(2,("Invalid secondary trans2 packet\n"));
+ free(params);
+ free(data);
+ return(ERROR(ERRSRV,ERRerror));
+ }
+
+ /* Revise total_params and total_data in case they have changed downwards */
+ total_params = SVAL(inbuf, smb_tpscnt);
+ total_data = SVAL(inbuf, smb_tdscnt);
+ num_params_sofar += (num_params = SVAL(inbuf,smb_spscnt));
+ num_data_sofar += ( num_data = SVAL(inbuf, smb_sdscnt));
+ memcpy( &params[ SVAL(inbuf, smb_spsdisp)],
+ smb_base(inbuf) + SVAL(inbuf, smb_spsoff), num_params);
+ memcpy( &data[SVAL(inbuf, smb_sdsdisp)],
+ smb_base(inbuf)+ SVAL(inbuf, smb_sdsoff), num_data);
+ }
+ }
+
+ if (Protocol >= PROTOCOL_NT1) {
+ uint16 flg2 = SVAL(outbuf,smb_flg2);
+ SSVAL(outbuf,smb_flg2,flg2 | 0x40); /* IS_LONG_NAME */
+ }
+
+ /* Now we must call the relevant TRANS2 function */
+ switch(tran_call)
+ {
+ case TRANSACT2_OPEN:
+ outsize = call_trans2open(inbuf, outbuf, bufsize, cnum, &params, &data);
+ break;
+ case TRANSACT2_FINDFIRST:
+ outsize = call_trans2findfirst(inbuf, outbuf, bufsize, cnum, &params, &data);
+ break;
+ case TRANSACT2_FINDNEXT:
+ outsize = call_trans2findnext(inbuf, outbuf, length, bufsize, cnum, &params, &data);
+ break;
+ case TRANSACT2_QFSINFO:
+ outsize = call_trans2qfsinfo(inbuf, outbuf, length, bufsize, cnum, &params, &data);
+ break;
+ case TRANSACT2_SETFSINFO:
+ outsize = call_trans2setfsinfo(inbuf, outbuf, length, bufsize, cnum, &params, &data);
+ break;
+ case TRANSACT2_QPATHINFO:
+ case TRANSACT2_QFILEINFO:
+ outsize = call_trans2qfilepathinfo(inbuf, outbuf, length, bufsize, cnum, &params, &data, total_data);
+ break;
+ case TRANSACT2_SETPATHINFO:
+ case TRANSACT2_SETFILEINFO:
+ outsize = call_trans2setfilepathinfo(inbuf, outbuf, length, bufsize, cnum, &params, &data, total_data);
+ break;
+ case TRANSACT2_FINDNOTIFYFIRST:
+ outsize = call_trans2findnotifyfirst(inbuf, outbuf, length, bufsize, cnum, &params, &data);
+ break;
+ case TRANSACT2_FINDNOTIFYNEXT:
+ outsize = call_trans2findnotifynext(inbuf, outbuf, length, bufsize, cnum, &params, &data);
+ break;
+ case TRANSACT2_MKDIR:
+ outsize = call_trans2mkdir(inbuf, outbuf, length, bufsize, cnum, &params, &data);
+ break;
+ default:
+ /* Error in request */
+ DEBUG(2,("%s Unknown request %d in trans2 call\n",timestring(), tran_call));
+ if(params)
+ free(params);
+ if(data)
+ free(data);
+ return (ERROR(ERRSRV,ERRerror));
+ }
+
+ /* As we do not know how many data packets will need to be
+ returned here the various call_trans2xxxx calls
+ must send their own. Thus a call_trans2xxx routine only
+ returns a value other than -1 when it wants to send
+ an error packet.
+ */
+
+ if(params)
+ free(params);
+ if(data)
+ free(data);
+ return outsize; /* If a correct response was needed the call_trans2xxx
+ calls have already sent it. If outsize != -1 then it is
+ returning an error packet. */
+}
diff --git a/source/smbd/vt_mode.c b/source/smbd/vt_mode.c
new file mode 100644
index 00000000000..83b62a38ac2
--- /dev/null
+++ b/source/smbd/vt_mode.c
@@ -0,0 +1,496 @@
+/* vt_mode.c */
+/*
+support vtp-sessions
+
+written by Christian A. Lademann <cal@zls.com>
+*/
+
+/*
+02.05.95:cal:ported to samba-1.9.13
+*/
+
+#define __vt_mode_c__
+
+
+/* #include <stdio.h> */
+/* #include <fcntl.h> */
+/* #include <sys/types.h> */
+/* #include <unistd.h> */
+/* #include <signal.h> */
+/* #include <errno.h> */
+/* #include <ctype.h> */
+/* #include <utmp.h> */
+/* #include <sys/param.h> */
+/* #include <sys/ioctl.h> */
+/* #include <stdlib.h> */
+/* #include <string.h> */
+
+#include "includes.h"
+#include "vt_mode.h"
+#include <utmp.h>
+
+#ifdef SCO
+ extern char *strdup();
+#endif
+
+extern int Client;
+
+#ifdef LINUX
+# define HAS_VTY
+#endif
+
+#ifdef SCO
+# define HAS_PTY
+# define HAS_VTY
+
+# include <sys/tty.h>
+#endif
+
+extern int DEBUGLEVEL;
+extern char *InBuffer, *OutBuffer;
+extern int done_become_user;
+
+char master_name [64], slave_name [64];
+int master, slave, i, o, e;
+
+int ms_type = MS_NONE,
+ ms_poll = 0;
+
+
+/*
+VT_Check: test incoming packet for "vtp" or "iVT1\0"
+*/
+int VT_Check(buffer)
+char *buffer;
+{
+ DEBUG(3,("Checking packet: <%10s...>\n", buffer+4));
+ if((strncmp(buffer+4, "vtp", 3) == 0 && smb_len(buffer) == 3) || (strncmp(buffer+4, "iVT1\0", 5) == 0 && smb_len(buffer) == 5))
+ return(1);
+ else
+ return(0);
+}
+
+
+/*
+VT_Start_utmp: prepare /etc/utmp for /bin/login
+*/
+VT_Start_utmp()
+{
+ struct utmp u, *v;
+ char *tt;
+
+
+ setutent();
+
+ strcpy(u.ut_line, VT_Line);
+
+ if((v = getutline(&u)) == NULL) {
+ if(strncmp(VT_Line, "tty", 3) == 0)
+ tt = VT_Line + 3;
+ else if(strlen(VT_Line) > 4)
+ tt = VT_Line + strlen(VT_Line) - 4;
+ else
+ tt = VT_Line;
+
+ strcpy(u.ut_id, tt);
+ u.ut_time = time((time_t*)0);
+ }
+
+ strcpy(u.ut_user, "LOGIN");
+ strcpy(u.ut_line, VT_Line);
+ u.ut_pid = getpid();
+ u.ut_type = LOGIN_PROCESS;
+ pututline(&u);
+
+ endutent();
+
+ return(0);
+}
+
+
+/*
+VT_Stop_utmp: prepare /etc/utmp for other processes
+*/
+VT_Stop_utmp()
+{
+ struct utmp u, *v;
+
+
+ if(VT_Line != NULL) {
+ setutent();
+
+ strcpy(u.ut_line, VT_Line);
+
+ if((v = getutline(&u)) != NULL) {
+ strcpy(v->ut_user, "");
+ v->ut_type = DEAD_PROCESS;
+ v->ut_time = time((time_t*)0);
+ pututline(v);
+ }
+
+ endutent();
+ }
+
+ return(0);
+}
+
+
+/*
+VT_AtExit: Things to do when the program exits
+*/
+void VT_AtExit()
+{
+ if(VT_ChildPID > 0) {
+ kill(VT_ChildPID, SIGHUP);
+ (void)wait(NULL);
+ }
+
+ VT_Stop_utmp();
+}
+
+
+/*
+VT_SigCLD: signalhandler for SIGCLD: set flag if child-process died
+*/
+void VT_SigCLD(sig)
+int sig;
+{
+ if(wait(NULL) == VT_ChildPID)
+ VT_ChildDied = True;
+ else
+ signal(SIGCLD, VT_SigCLD);
+}
+
+
+/*
+VT_SigEXIT: signalhandler for signals that cause the process to exit
+*/
+void VT_SigEXIT(sig)
+int sig;
+{
+ VT_AtExit();
+
+ exit(1);
+}
+
+
+/*
+VT_Start: initialize vt-specific data, alloc pty, spawn shell and send ACK
+*/
+int VT_Start()
+{
+ char OutBuf [64], *X, *Y;
+
+
+ ms_type = MS_NONE;
+ master = slave = -1;
+
+#ifdef HAS_VTY
+#ifdef LINUX
+# define MASTER_TMPL "/dev/pty "
+# define SLAVE_TMPL "/dev/tty "
+# define LETTER1 "pqrs"
+# define POS1 8
+# define LETTER2 "0123456789abcdef"
+# define POS2 9
+#endif
+
+#ifdef SCO
+# define MASTER_TMPL "/dev/ptyp_ "
+# define SLAVE_TMPL "/dev/ttyp_ "
+# define LETTER1 "0123456"
+# define POS1 10
+# define LETTER2 "0123456789abcdef"
+# define POS2 11
+#endif
+
+ if(ms_poll == MS_VTY || ms_poll == 0) {
+ strcpy(master_name, MASTER_TMPL);
+ strcpy(slave_name, SLAVE_TMPL);
+
+ for(X = LETTER1; *X && master < 0; X++)
+ for(Y = LETTER2; *Y && master < 0; Y++) {
+ master_name [POS1] = *X;
+ master_name [POS2] = *Y;
+ if((master = open(master_name, O_RDWR)) >= 0) {
+ slave_name [POS1] = *X;
+ slave_name [POS2] = *Y;
+ if((slave = open(slave_name, O_RDWR)) < 0)
+ close(master);
+ }
+ }
+
+ if(master >= 0 && slave >= 0)
+ ms_type = MS_VTY;
+ }
+
+# undef MASTER_TMPL
+# undef SLAVE_TMPL
+# undef LETTER1
+# undef LETTER2
+# undef POS1
+# undef POS2
+#endif
+
+
+#ifdef HAS_PTY
+#ifdef SCO
+# define MASTER_TMPL "/dev/ptyp%d"
+# define SLAVE_TMPL "/dev/ttyp%d"
+# define MIN_I 0
+# define MAX_I 63
+#endif
+
+ if(ms_poll == MS_PTY || ms_poll == 0) {
+ int i;
+
+ for(i = MIN_I; i <= MAX_I && master < 0; i++) {
+ sprintf(master_name, MASTER_TMPL, i);
+ if((master = open(master_name, O_RDWR)) >= 0) {
+ sprintf(slave_name, SLAVE_TMPL, i);
+ if((slave = open(slave_name, O_RDWR)) < 0)
+ close(master);
+ }
+ }
+
+ if(master >= 0 && slave >= 0)
+ ms_type = MS_PTY;
+ }
+
+# undef MASTER_TMPL
+# undef SLAVE_TMPL
+# undef MIN_I
+# undef MAX_I
+#endif
+
+
+ if(! ms_type)
+ return(-1);
+
+ VT_Line = strdup(strrchr(slave_name, '/') + 1);
+
+ switch((VT_ChildPID = fork())) {
+ case -1:
+ return(-1);
+ break;
+
+ case 0:
+#ifdef SCO
+ setsid();
+#endif
+ close(0);
+ close(1);
+ close(2);
+
+ i = open(slave_name, O_RDWR);
+ o = open(slave_name, O_RDWR);
+ e = open(slave_name, O_RDWR);
+
+#ifdef LINUX
+ setsid();
+ if (ioctl(slave, TIOCSCTTY, (char *)NULL) == -1)
+ exit(1);
+#endif
+#ifdef SCO
+ tcsetpgrp(0, getpid());
+#endif
+
+ VT_Start_utmp();
+
+ system("stty sane");
+ execlp("/bin/login", "login", "-c", (char*)0);
+ exit(1);
+ break;
+
+ default:
+ VT_Mode = True;
+ VT_Status = VT_OPEN;
+ VT_ChildDied = False;
+ VT_Fd = master;
+
+ signal(SIGCLD, VT_SigCLD);
+
+ signal(SIGHUP, VT_SigEXIT);
+ signal(SIGTERM, VT_SigEXIT);
+ signal(SIGINT, VT_SigEXIT);
+ signal(SIGQUIT, VT_SigEXIT);
+
+ memset(OutBuf, 0, sizeof(OutBuf));
+ OutBuf [4] = 0x06;
+ _smb_setlen(OutBuf, 1);
+
+ send_smb(Client,OutBuf);
+
+ return(0);
+ break;
+ }
+}
+
+
+/*
+VT_Output: transport data from socket to pty
+*/
+int VT_Output(Buffer)
+char *Buffer;
+{
+ int i, len, nb;
+
+
+ if(VT_Status != VT_OPEN)
+ return(-1);
+
+ len = smb_len(Buffer);
+
+ nb = write(VT_Fd, Buffer + 4, len);
+
+ return((nb == len) ? 0 : -1);
+}
+
+
+/*
+VT_Input: transport data from pty to socket
+*/
+int VT_Input(Buffer, Size)
+char *Buffer;
+int Size;
+{
+ int len;
+
+
+ if(VT_Status != VT_OPEN)
+ return(-1);
+
+ memset(Buffer, 0, Size);
+ len = read(VT_Fd, Buffer + 4, MIN(VT_MAXREAD, Size));
+
+ _smb_setlen(Buffer, len);
+
+ return(len + 4);
+}
+
+
+/*
+VT_Process: main loop while in vt-mode
+*/
+void VT_Process()
+{
+ static int trans_num = 0;
+ extern int Client;
+ int nread;
+
+
+ VT_Start();
+
+ atexit(VT_AtExit);
+
+ while (True) {
+ int32 len;
+ int msg_type;
+ int msg_flags;
+ int counter;
+ int last_keepalive=0;
+ struct fd_set si;
+ struct timeval to, *top;
+ int n, ret, t;
+
+
+ errno = 0;
+ t = SMBD_SELECT_LOOP*1000;
+
+
+ FD_ZERO(&si);
+ FD_SET(Client, &si);
+
+ FD_SET(VT_Fd, &si);
+
+ if(t >= 0) {
+ to.tv_sec = t / 1000;
+ to.tv_usec = t - (to.tv_sec * 1000);
+
+ top = &to;
+ } else
+ top = NULL;
+
+ if(VT_ChildDied)
+ goto leave_VT_Process;
+
+ n = select(MAX(VT_Fd, Client) + 1, &si, NULL, NULL, top);
+
+ if(VT_ChildDied)
+ goto leave_VT_Process;
+
+ if(n == 0) {
+ int i;
+ time_t t;
+ BOOL allidle = True;
+ extern int keepalive;
+
+ counter += SMBD_SELECT_LOOP;
+
+ t = time(NULL);
+
+ if (keepalive && (counter-last_keepalive)>keepalive) {
+ if (!send_keepalive(Client))
+ goto leave_VT_Process;
+ last_keepalive = counter;
+ }
+ } else if(n > 0) {
+ counter = 0;
+
+ if(FD_ISSET(VT_Fd, &si)) {
+ /* got input from vt */
+ nread = VT_Input(OutBuffer, MIN(BUFFER_SIZE,lp_maxxmit()));
+
+ if(nread > 0)
+ send_smb(Client,OutBuffer);
+ }
+
+ if(FD_ISSET(Client, &si)) {
+ /* got input from socket */
+
+ if(receive_smb(Client,InBuffer, 0)) {
+ msg_type = CVAL(InBuffer,0);
+ msg_flags = CVAL(InBuffer,1);
+
+ len = smb_len(InBuffer);
+
+ DEBUG(6,("got message type 0x%x of len 0x%x\n",msg_type,len));
+
+ nread = len + 4;
+
+ DEBUG(3,("%s Transaction %d of length %d\n",timestring(),trans_num,nread));
+
+ if(msg_type == 0)
+ VT_Output(InBuffer);
+ else {
+ nread = construct_reply(InBuffer,OutBuffer,nread,MIN(BUFFER_SIZE,lp_maxxmit()));
+
+ if(nread > 0) {
+ if (nread != smb_len(OutBuffer) + 4) {
+ DEBUG(0,("ERROR: Invalid message response size! %d %d\n",
+ nread,
+ smb_len(OutBuffer)));
+ } else
+ send_smb(Client,OutBuffer);
+ }
+ }
+ } else
+ if(errno == EBADF)
+ goto leave_VT_Process;
+ }
+ }
+
+ trans_num++;
+ }
+
+ leave_VT_Process:
+/*
+ if(VT_ChildPID > 0)
+ kill(VT_ChildPID, SIGHUP);
+
+ VT_Stop_utmp(VT_Line);
+ return;
+*/
+ close_sockets();
+ exit(0);
+}