From 0e8fd3398771da2f016d72830179507f3edda51b Mon Sep 17 00:00:00 2001 From: Samba Release Account Date: Sat, 4 May 1996 07:50:46 +0000 Subject: Initial version imported to CVS (This used to be commit 291551d80711daab7b7581720bcd9a08d6096517) --- source3/smbd/chgpasswd.c | 376 ++++ source3/smbd/dir.c | 955 ++++++++++ source3/smbd/ipc.c | 2779 ++++++++++++++++++++++++++++++ source3/smbd/mangle.c | 610 +++++++ source3/smbd/message.c | 204 +++ source3/smbd/password.c | 1416 +++++++++++++++ source3/smbd/reply.c | 3210 ++++++++++++++++++++++++++++++++++ source3/smbd/server.c | 4300 ++++++++++++++++++++++++++++++++++++++++++++++ source3/smbd/smbrun.c | 96 ++ source3/smbd/trans2.c | 1646 ++++++++++++++++++ source3/smbd/vt_mode.c | 496 ++++++ 11 files changed, 16088 insertions(+) create mode 100644 source3/smbd/chgpasswd.c create mode 100644 source3/smbd/dir.c create mode 100644 source3/smbd/ipc.c create mode 100644 source3/smbd/mangle.c create mode 100644 source3/smbd/message.c create mode 100644 source3/smbd/password.c create mode 100644 source3/smbd/reply.c create mode 100644 source3/smbd/server.c create mode 100644 source3/smbd/smbrun.c create mode 100644 source3/smbd/trans2.c create mode 100644 source3/smbd/vt_mode.c (limited to 'source3/smbd') diff --git a/source3/smbd/chgpasswd.c b/source3/smbd/chgpasswd.c new file mode 100644 index 0000000000..dc0514c1ed --- /dev/null +++ b/source3/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/source3/smbd/dir.c b/source3/smbd/dir.c new file mode 100644 index 0000000000..ac6f918b9d --- /dev/null +++ b/source3/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= 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= MAXDIR) + dptr_idleoldest(); + + for (i=0;ipos = 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 +#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/source3/smbd/ipc.c b/source3/smbd/ipc.c new file mode 100644 index 0000000000..8852e57e8b --- /dev/null +++ b/source3/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;ibuflen; + 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 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 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;itm_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: <> + ****************************************************************************/ +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= 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: + or +****************************************************************************/ +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 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 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 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,""); + p2 = skip_string(p2,1); + SIVAL(p,26,PTR_DIFF(p2,p)); /* user_comment */ + strcpy(p2,""); + p2 = skip_string(p2,1); + SIVAL(p,30,PTR_DIFF(p2,p)); /* full name */ + strcpy(p2,""); + 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,""); + 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 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/source3/smbd/mangle.c b/source3/smbd/mangle.c new file mode 100644 index 0000000000..8f1490c528 --- /dev/null +++ b/source3/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;iuid = -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; iufld.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/source3/smbd/reply.c b/source3/smbd/reply.c new file mode 100644 index 0000000000..b7b51775bb --- /dev/null +++ b/source3/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= 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;iwr_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;i0?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 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/source3/smbd/server.c b/source3/smbd/server.c new file mode 100644 index 0000000000..5d8facef33 --- /dev/null +++ b/source3/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 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;iuid) 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 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 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 && iread_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=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=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 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;iDPTR_IDLE_TIMEOUT) + dptr_idlecnum(i); + + if (Connections[i].num_files_open > 0 || + (t-Connections[i].lastused)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 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/source3/smbd/smbrun.c b/source3/smbd/smbrun.c new file mode 100644 index 0000000000..df12ae1f85 --- /dev/null +++ b/source3/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/source3/smbd/trans2.c b/source3/smbd/trans2.c new file mode 100644 index 0000000000..9d02123cf8 --- /dev/null +++ b/source3/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 = ¶ms[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 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,¶ms[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,¶ms[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, ¶ms[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( ¶ms[ 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, ¶ms, &data); + break; + case TRANSACT2_FINDFIRST: + outsize = call_trans2findfirst(inbuf, outbuf, bufsize, cnum, ¶ms, &data); + break; + case TRANSACT2_FINDNEXT: + outsize = call_trans2findnext(inbuf, outbuf, length, bufsize, cnum, ¶ms, &data); + break; + case TRANSACT2_QFSINFO: + outsize = call_trans2qfsinfo(inbuf, outbuf, length, bufsize, cnum, ¶ms, &data); + break; + case TRANSACT2_SETFSINFO: + outsize = call_trans2setfsinfo(inbuf, outbuf, length, bufsize, cnum, ¶ms, &data); + break; + case TRANSACT2_QPATHINFO: + case TRANSACT2_QFILEINFO: + outsize = call_trans2qfilepathinfo(inbuf, outbuf, length, bufsize, cnum, ¶ms, &data, total_data); + break; + case TRANSACT2_SETPATHINFO: + case TRANSACT2_SETFILEINFO: + outsize = call_trans2setfilepathinfo(inbuf, outbuf, length, bufsize, cnum, ¶ms, &data, total_data); + break; + case TRANSACT2_FINDNOTIFYFIRST: + outsize = call_trans2findnotifyfirst(inbuf, outbuf, length, bufsize, cnum, ¶ms, &data); + break; + case TRANSACT2_FINDNOTIFYNEXT: + outsize = call_trans2findnotifynext(inbuf, outbuf, length, bufsize, cnum, ¶ms, &data); + break; + case TRANSACT2_MKDIR: + outsize = call_trans2mkdir(inbuf, outbuf, length, bufsize, cnum, ¶ms, &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/source3/smbd/vt_mode.c b/source3/smbd/vt_mode.c new file mode 100644 index 0000000000..83b62a38ac --- /dev/null +++ b/source3/smbd/vt_mode.c @@ -0,0 +1,496 @@ +/* vt_mode.c */ +/* +support vtp-sessions + +written by Christian A. Lademann +*/ + +/* +02.05.95:cal:ported to samba-1.9.13 +*/ + +#define __vt_mode_c__ + + +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ + +#include "includes.h" +#include "vt_mode.h" +#include + +#ifdef SCO + extern char *strdup(); +#endif + +extern int Client; + +#ifdef LINUX +# define HAS_VTY +#endif + +#ifdef SCO +# define HAS_PTY +# define HAS_VTY + +# include +#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); +} -- cgit