diff options
Diffstat (limited to 'source3/smbd')
54 files changed, 34919 insertions, 13314 deletions
diff --git a/source3/smbd/.cvsignore b/source3/smbd/.cvsignore new file mode 100644 index 0000000000..5f2a5c4cf7 --- /dev/null +++ b/source3/smbd/.cvsignore @@ -0,0 +1,2 @@ +*.po +*.po32 diff --git a/source3/smbd/blocking.c b/source3/smbd/blocking.c new file mode 100644 index 0000000000..d4a53d9a6d --- /dev/null +++ b/source3/smbd/blocking.c @@ -0,0 +1,635 @@ +/* + Unix SMB/CIFS implementation. + Blocking Locking functions + Copyright (C) Jeremy Allison 1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + 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" + +extern char *OutBuffer; + +/**************************************************************************** + This is the structure to queue to implement blocking locks. + notify. It consists of the requesting SMB and the expiry time. +*****************************************************************************/ + +typedef struct { + ubi_slNode msg_next; + int com_type; + files_struct *fsp; + time_t expire_time; + int lock_num; + char *inbuf; + int length; +} blocking_lock_record; + +static ubi_slList blocking_lock_queue = { NULL, (ubi_slNodePtr)&blocking_lock_queue, 0}; + +/**************************************************************************** + Destructor for the above structure. +****************************************************************************/ + +static void free_blocking_lock_record(blocking_lock_record *blr) +{ + SAFE_FREE(blr->inbuf); + SAFE_FREE(blr); +} + +/**************************************************************************** + Get the files_struct given a particular queued SMB. +*****************************************************************************/ + +static files_struct *get_fsp_from_pkt(char *inbuf) +{ + switch(CVAL(inbuf,smb_com)) { + case SMBlock: + case SMBlockread: + return file_fsp(inbuf,smb_vwv0); + case SMBlockingX: + return file_fsp(inbuf,smb_vwv2); + default: + DEBUG(0,("get_fsp_from_pkt: PANIC - unknown type on blocking lock queue - exiting.!\n")); + exit_server("PANIC - unknown type on blocking lock queue"); + } + return NULL; /* Keep compiler happy. */ +} + +/**************************************************************************** + Determine if this is a secondary element of a chained SMB. + **************************************************************************/ + +static BOOL in_chained_smb(void) +{ + return (chain_size != 0); +} + +/**************************************************************************** + Function to push a blocking lock request onto the lock queue. +****************************************************************************/ + +BOOL push_blocking_lock_request( char *inbuf, int length, int lock_timeout, int lock_num) +{ + blocking_lock_record *blr; + + if(in_chained_smb() ) { + DEBUG(0,("push_blocking_lock_request: cannot queue a chained request (currently).\n")); + return False; + } + + /* + * Now queue an entry on the blocking lock queue. We setup + * the expiration time here. + */ + + if((blr = (blocking_lock_record *)malloc(sizeof(blocking_lock_record))) == NULL) { + DEBUG(0,("push_blocking_lock_request: Malloc fail !\n" )); + return False; + } + + if((blr->inbuf = (char *)malloc(length)) == NULL) { + DEBUG(0,("push_blocking_lock_request: Malloc fail (2)!\n" )); + SAFE_FREE(blr); + return False; + } + + blr->com_type = CVAL(inbuf,smb_com); + blr->fsp = get_fsp_from_pkt(inbuf); + blr->expire_time = (lock_timeout == -1) ? (time_t)-1 : time(NULL) + (time_t)lock_timeout; + blr->lock_num = lock_num; + memcpy(blr->inbuf, inbuf, length); + blr->length = length; + + ubi_slAddTail(&blocking_lock_queue, blr); + + + DEBUG(3,("push_blocking_lock_request: lock request length=%d blocked with expiry time %d (+%d) \ +for fnum = %d, name = %s\n", length, (int)blr->expire_time, lock_timeout, + blr->fsp->fnum, blr->fsp->fsp_name )); + + return True; +} + +/**************************************************************************** + Return a smd with a given size. +*****************************************************************************/ + +static void send_blocking_reply(char *outbuf, int outsize) +{ + if(outsize > 4) + smb_setlen(outbuf,outsize - 4); + + if (!send_smb(smbd_server_fd(),outbuf)) + exit_server("send_blocking_reply: send_smb failed."); +} + +/**************************************************************************** + Return a lockingX success SMB. +*****************************************************************************/ + +static void reply_lockingX_success(blocking_lock_record *blr) +{ + char *outbuf = OutBuffer; + int bufsize = BUFFER_SIZE; + char *inbuf = blr->inbuf; + int outsize = 0; + + construct_reply_common(inbuf, outbuf); + set_message(outbuf,2,0,True); + + /* + * As this message is a lockingX call we must handle + * any following chained message correctly. + * This is normally handled in construct_reply(), + * but as that calls switch_message, we can't use + * that here and must set up the chain info manually. + */ + + outsize = chain_reply(inbuf,outbuf,blr->length,bufsize); + + outsize += chain_size; + + send_blocking_reply(outbuf,outsize); +} + +/**************************************************************************** + Return a generic lock fail error blocking call. +*****************************************************************************/ + +static void generic_blocking_lock_error(blocking_lock_record *blr, NTSTATUS status) +{ + char *outbuf = OutBuffer; + char *inbuf = blr->inbuf; + construct_reply_common(inbuf, outbuf); + + /* whenever a timeout is given w2k maps LOCK_NOT_GRANTED to + FILE_LOCK_CONFLICT! (tridge) */ + if (NT_STATUS_EQUAL(status, NT_STATUS_LOCK_NOT_GRANTED)) { + status = NT_STATUS_FILE_LOCK_CONFLICT; + } + + ERROR_NT(status); + if (!send_smb(smbd_server_fd(),outbuf)) + exit_server("generic_blocking_lock_error: send_smb failed."); +} + +/**************************************************************************** + Return a lock fail error for a lockingX call. Undo all the locks we have + obtained first. +*****************************************************************************/ + +static void reply_lockingX_error(blocking_lock_record *blr, NTSTATUS status) +{ + char *inbuf = blr->inbuf; + files_struct *fsp = blr->fsp; + connection_struct *conn = conn_find(SVAL(inbuf,smb_tid)); + uint16 num_ulocks = SVAL(inbuf,smb_vwv6); + SMB_BIG_UINT count = (SMB_BIG_UINT)0, offset = (SMB_BIG_UINT) 0; + uint16 lock_pid; + unsigned char locktype = CVAL(inbuf,smb_vwv3); + BOOL large_file_format = (locktype & LOCKING_ANDX_LARGE_FILES); + char *data; + int i; + + data = smb_buf(inbuf) + ((large_file_format ? 20 : 10)*num_ulocks); + + /* + * Data now points at the beginning of the list + * of smb_lkrng structs. + */ + + /* + * Ensure we don't do a remove on the lock that just failed, + * as under POSIX rules, if we have a lock already there, we + * will delete it (and we shouldn't) ..... + */ + + for(i = blr->lock_num - 1; i >= 0; i--) { + BOOL err; + + lock_pid = get_lock_pid( data, i, large_file_format); + count = get_lock_count( data, i, large_file_format); + offset = get_lock_offset( data, i, large_file_format, &err); + + /* + * We know err cannot be set as if it was the lock + * request would never have been queued. JRA. + */ + + do_unlock(fsp,conn,lock_pid,count,offset); + } + + generic_blocking_lock_error(blr, status); +} + +/**************************************************************************** + Return a lock fail error. +*****************************************************************************/ + +static void blocking_lock_reply_error(blocking_lock_record *blr, NTSTATUS status) +{ + switch(blr->com_type) { + case SMBlock: + case SMBlockread: + generic_blocking_lock_error(blr, status); + break; + case SMBlockingX: + reply_lockingX_error(blr, status); + break; + default: + DEBUG(0,("blocking_lock_reply_error: PANIC - unknown type on blocking lock queue - exiting.!\n")); + exit_server("PANIC - unknown type on blocking lock queue"); + } +} + +/**************************************************************************** + Attempt to finish off getting all pending blocking locks for a lockread call. + Returns True if we want to be removed from the list. +*****************************************************************************/ + +static BOOL process_lockread(blocking_lock_record *blr) +{ + char *outbuf = OutBuffer; + char *inbuf = blr->inbuf; + ssize_t nread = -1; + char *data, *p; + int outsize = 0; + SMB_OFF_T startpos; + size_t numtoread; + NTSTATUS status; + connection_struct *conn = conn_find(SVAL(inbuf,smb_tid)); + files_struct *fsp = blr->fsp; + + numtoread = SVAL(inbuf,smb_vwv1); + startpos = IVAL(inbuf,smb_vwv2); + + numtoread = MIN(BUFFER_SIZE-outsize,numtoread); + data = smb_buf(outbuf) + 3; + + status = do_lock_spin( fsp, conn, SVAL(inbuf,smb_pid), (SMB_BIG_UINT)numtoread, + (SMB_BIG_UINT)startpos, READ_LOCK); + if (NT_STATUS_V(status)) { + if ((errno != EACCES) && (errno != EAGAIN)) { + /* + * We have other than a "can't get lock" POSIX + * error. Send an error. + * Return True so we get dequeued. + */ + generic_blocking_lock_error(blr, status); + return True; + } + + /* + * Still waiting for lock.... + */ + + DEBUG(10,("process_lockread: failed to get lock for file = %s. Still waiting....\n", + fsp->fsp_name)); + return False; + } + + nread = read_file(fsp,data,startpos,numtoread); + + if (nread < 0) { + generic_blocking_lock_error(blr,NT_STATUS_ACCESS_DENIED); + return True; + } + + construct_reply_common(inbuf, outbuf); + outsize = set_message(outbuf,5,0,True); + + outsize += nread; + SSVAL(outbuf,smb_vwv0,nread); + SSVAL(outbuf,smb_vwv5,nread+3); + p = smb_buf(outbuf); + *p++ = 1; + SSVAL(p,0,nread); p += 2; + set_message_end(outbuf, p+nread); + + DEBUG(3, ( "process_lockread file = %s, fnum=%d num=%d nread=%d\n", + fsp->fsp_name, fsp->fnum, (int)numtoread, (int)nread ) ); + + send_blocking_reply(outbuf,outsize); + return True; +} + +/**************************************************************************** + Attempt to finish off getting all pending blocking locks for a lock call. + Returns True if we want to be removed from the list. +*****************************************************************************/ + +static BOOL process_lock(blocking_lock_record *blr) +{ + char *outbuf = OutBuffer; + char *inbuf = blr->inbuf; + int outsize; + SMB_OFF_T count = 0, offset = 0; + NTSTATUS status; + connection_struct *conn = conn_find(SVAL(inbuf,smb_tid)); + files_struct *fsp = blr->fsp; + + count = IVAL(inbuf,smb_vwv1); + offset = IVAL(inbuf,smb_vwv3); + + errno = 0; + status = do_lock_spin(fsp, conn, SVAL(inbuf,smb_pid), (SMB_BIG_UINT)count, + (SMB_BIG_UINT)offset, WRITE_LOCK); + if (NT_STATUS_IS_ERR(status)) { + if((errno != EACCES) && (errno != EAGAIN)) { + /* + * We have other than a "can't get lock" POSIX + * error. Send an error. + * Return True so we get dequeued. + */ + + blocking_lock_reply_error(blr, status); + return True; + } + /* + * Still can't get the lock - keep waiting. + */ + DEBUG(10,("process_lock: failed to get lock for file = %s. Still waiting....\n", + fsp->fsp_name)); + return False; + } + + /* + * Success - we got the lock. + */ + + DEBUG(3,("process_lock : file=%s fnum=%d offset=%.0f count=%.0f\n", + fsp->fsp_name, fsp->fnum, (double)offset, (double)count)); + + construct_reply_common(inbuf, outbuf); + outsize = set_message(outbuf,0,0,True); + send_blocking_reply(outbuf,outsize); + return True; +} + +/**************************************************************************** + Attempt to finish off getting all pending blocking locks for a lockingX call. + Returns True if we want to be removed from the list. +*****************************************************************************/ + +static BOOL process_lockingX(blocking_lock_record *blr) +{ + char *inbuf = blr->inbuf; + unsigned char locktype = CVAL(inbuf,smb_vwv3); + files_struct *fsp = blr->fsp; + connection_struct *conn = conn_find(SVAL(inbuf,smb_tid)); + uint16 num_ulocks = SVAL(inbuf,smb_vwv6); + uint16 num_locks = SVAL(inbuf,smb_vwv7); + SMB_BIG_UINT count = (SMB_BIG_UINT)0, offset = (SMB_BIG_UINT)0; + uint16 lock_pid; + BOOL large_file_format = (locktype & LOCKING_ANDX_LARGE_FILES); + char *data; + NTSTATUS status = NT_STATUS_OK; + + data = smb_buf(inbuf) + ((large_file_format ? 20 : 10)*num_ulocks); + + /* + * Data now points at the beginning of the list + * of smb_lkrng structs. + */ + + for(; blr->lock_num < num_locks; blr->lock_num++) { + BOOL err; + + lock_pid = get_lock_pid( data, blr->lock_num, large_file_format); + count = get_lock_count( data, blr->lock_num, large_file_format); + offset = get_lock_offset( data, blr->lock_num, large_file_format, &err); + + /* + * We know err cannot be set as if it was the lock + * request would never have been queued. JRA. + */ + errno = 0; + status = do_lock_spin(fsp,conn,lock_pid,count,offset, + ((locktype & 1) ? READ_LOCK : WRITE_LOCK)); + if (NT_STATUS_IS_ERR(status)) break; + } + + if(blr->lock_num == num_locks) { + /* + * Success - we got all the locks. + */ + + DEBUG(3,("process_lockingX file = %s, fnum=%d type=%d num_locks=%d\n", + fsp->fsp_name, fsp->fnum, (unsigned int)locktype, num_locks) ); + + reply_lockingX_success(blr); + return True; + } else if ((errno != EACCES) && (errno != EAGAIN)) { + /* + * We have other than a "can't get lock" POSIX + * error. Free any locks we had and return an error. + * Return True so we get dequeued. + */ + + blocking_lock_reply_error(blr, status); + return True; + } + + /* + * Still can't get all the locks - keep waiting. + */ + + DEBUG(10,("process_lockingX: only got %d locks of %d needed for file %s, fnum = %d. \ +Waiting....\n", + blr->lock_num, num_locks, fsp->fsp_name, fsp->fnum)); + + return False; +} + +/**************************************************************************** + Process a blocking lock SMB. + Returns True if we want to be removed from the list. +*****************************************************************************/ + +static BOOL blocking_lock_record_process(blocking_lock_record *blr) +{ + switch(blr->com_type) { + case SMBlock: + return process_lock(blr); + case SMBlockread: + return process_lockread(blr); + case SMBlockingX: + return process_lockingX(blr); + default: + DEBUG(0,("blocking_lock_record_process: PANIC - unknown type on blocking lock queue - exiting.!\n")); + exit_server("PANIC - unknown type on blocking lock queue"); + } + return False; /* Keep compiler happy. */ +} + +/**************************************************************************** + Delete entries by fnum from the blocking lock pending queue. +*****************************************************************************/ + +void remove_pending_lock_requests_by_fid(files_struct *fsp) +{ + blocking_lock_record *blr = (blocking_lock_record *)ubi_slFirst( &blocking_lock_queue ); + blocking_lock_record *prev = NULL; + + while(blr != NULL) { + if(blr->fsp->fnum == fsp->fnum) { + + DEBUG(10,("remove_pending_lock_requests_by_fid - removing request type %d for \ +file %s fnum = %d\n", blr->com_type, fsp->fsp_name, fsp->fnum )); + + free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev)); + blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue)); + continue; + } + + prev = blr; + blr = (blocking_lock_record *)ubi_slNext(blr); + } +} + +/**************************************************************************** + Delete entries by mid from the blocking lock pending queue. Always send reply. +*****************************************************************************/ + +void remove_pending_lock_requests_by_mid(int mid) +{ + blocking_lock_record *blr = (blocking_lock_record *)ubi_slFirst( &blocking_lock_queue ); + blocking_lock_record *prev = NULL; + + while(blr != NULL) { + if(SVAL(blr->inbuf,smb_mid) == mid) { + files_struct *fsp = blr->fsp; + + DEBUG(10,("remove_pending_lock_requests_by_mid - removing request type %d for \ +file %s fnum = %d\n", blr->com_type, fsp->fsp_name, fsp->fnum )); + + blocking_lock_reply_error(blr,NT_STATUS_CANCELLED); + free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev)); + blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue)); + continue; + } + + prev = blr; + blr = (blocking_lock_record *)ubi_slNext(blr); + } +} + +/**************************************************************************** + Return True if the blocking lock queue has entries. +*****************************************************************************/ + +BOOL blocking_locks_pending(void) +{ + blocking_lock_record *blr = (blocking_lock_record *)ubi_slFirst( &blocking_lock_queue ); + return (blr == NULL ? False : True); +} + +/**************************************************************************** + Process the blocking lock queue. Note that this is only called as root. +*****************************************************************************/ + +void process_blocking_lock_queue(time_t t) +{ + blocking_lock_record *blr = (blocking_lock_record *)ubi_slFirst( &blocking_lock_queue ); + blocking_lock_record *prev = NULL; + + if(blr == NULL) + return; + + /* + * Go through the queue and see if we can get any of the locks. + */ + + while(blr != NULL) { + connection_struct *conn = NULL; + uint16 vuid; + files_struct *fsp = NULL; + + /* + * Ensure we don't have any old chain_fsp values + * sitting around.... + */ + chain_size = 0; + file_chain_reset(); + fsp = blr->fsp; + + conn = conn_find(SVAL(blr->inbuf,smb_tid)); + vuid = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID : + SVAL(blr->inbuf,smb_uid); + + DEBUG(5,("process_blocking_lock_queue: examining pending lock fnum = %d for file %s\n", + fsp->fnum, fsp->fsp_name )); + + if((blr->expire_time != -1) && (blr->expire_time > t)) { + /* + * Lock expired - throw away all previously + * obtained locks and return lock error. + */ + DEBUG(5,("process_blocking_lock_queue: pending lock fnum = %d for file %s timed out.\n", + fsp->fnum, fsp->fsp_name )); + + blocking_lock_reply_error(blr,NT_STATUS_ACCESS_DENIED); + free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev)); + blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue)); + continue; + } + + if(!change_to_user(conn,vuid)) { + DEBUG(0,("process_blocking_lock_queue: Unable to become user vuid=%d.\n", + vuid )); + /* + * Remove the entry and return an error to the client. + */ + blocking_lock_reply_error(blr,NT_STATUS_ACCESS_DENIED); + free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev)); + blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue)); + continue; + } + + if(!set_current_service(conn,True)) { + DEBUG(0,("process_blocking_lock_queue: Unable to become service Error was %s.\n", strerror(errno) )); + /* + * Remove the entry and return an error to the client. + */ + blocking_lock_reply_error(blr,NT_STATUS_ACCESS_DENIED); + free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev)); + blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue)); + change_to_root_user(); + continue; + } + + /* + * Go through the remaining locks and try and obtain them. + * The call returns True if all locks were obtained successfully + * and False if we still need to wait. + */ + + if(blocking_lock_record_process(blr)) { + free_blocking_lock_record((blocking_lock_record *)ubi_slRemNext( &blocking_lock_queue, prev)); + blr = (blocking_lock_record *)(prev ? ubi_slNext(prev) : ubi_slFirst(&blocking_lock_queue)); + change_to_root_user(); + continue; + } + + change_to_root_user(); + + /* + * Move to the next in the list. + */ + prev = blr; + blr = (blocking_lock_record *)ubi_slNext(blr); + } +} diff --git a/source3/smbd/build_options.c b/source3/smbd/build_options.c new file mode 100644 index 0000000000..1d18f534b1 --- /dev/null +++ b/source3/smbd/build_options.c @@ -0,0 +1,536 @@ +/* + Unix SMB/CIFS implementation. + Build Options for Samba Suite + Copyright (C) Vance Lankhaar <vlankhaar@hotmail.com> 2001 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "build_env.h" +#include "dynconfig.h" + +static void output(BOOL screen, char *format, ...) PRINTF_ATTRIBUTE(2,3); + +/* +#define OUTPUT(x) snprintf(outstring,sizeof(outstring),x); output(screen,outstring); +*/ +/**************************************************************************** +helper function for build_options +****************************************************************************/ +static void output(BOOL screen, char *format, ...) +{ + char *ptr; + va_list ap; + + va_start(ap, format); + vasprintf(&ptr,format,ap); + va_end(ap); + + if (screen) { + d_printf("%s", ptr); + } else { + DEBUG(4,("%s", ptr)); + } + + SAFE_FREE(ptr); +} + +/**************************************************************************** +options set at build time for the samba suite +****************************************************************************/ +void build_options(BOOL screen) +{ + if ((DEBUGLEVEL < 4) && (!screen)) { + return; + } + +#ifdef _BUILD_ENV_H + /* Output information about the build environment */ + output(screen,"Build environment:\n"); + output(screen," Built by: %s@%s\n",BUILD_ENV_USER,BUILD_ENV_HOST); + output(screen," Built on: %s\n",BUILD_ENV_DATE); + + output(screen," Built using: %s\n",BUILD_ENV_COMPILER); + output(screen," Build host: %s\n",BUILD_ENV_UNAME); + output(screen," SRCDIR: %s\n",BUILD_ENV_SRCDIR); + output(screen," BUILDDIR: %s\n",BUILD_ENV_BUILDDIR); + + +#endif + + /* Output various options (most correspond to --with options) */ + output(screen,"\nBuild options:\n"); +#ifdef WITH_SMBWRAPPER + output(screen," WITH_SMBWRAPPER\n"); +#endif +#ifdef WITH_AFS + output(screen," WITH_AFS\n"); +#endif +#ifdef WITH_DFS + output(screen," WITH_DFS\n"); +#endif +#ifdef KRB4_AUTH + output(screen," KRB4_AUTH"); +#endif +#ifdef HAVE_KRB5 + output(screen," HAVE_KRB5"); +#endif +#ifdef WITH_AUTOMOUNT + output(screen," WITH_AUTOMOUNT\n"); +#endif +#ifdef WITH_SMBMOUNT + output(screen," WITH_SMBMOUNT\n"); +#endif +#ifdef WITH_PAM + output(screen," WITH_PAM\n"); +#endif +#ifdef WITH_TDB_SAM + output(screen," WITH_TDB_SAM\n"); +#endif +#ifdef WITH_LDAP_SAM + output(screen," WITH_LDAP_SAM\n"); +#endif +#ifdef WITH_SMBPASSWD_SAM + output(screen," WITH_SMBPASSWD_SAM\n"); +#endif +#ifdef WITH_NISPLUS_SAM + output(screen," WITH_NISPLUS_SAM\n"); +#endif +#ifdef WITH_NISPLUS_HOME + output(screen," WITH_NISPLUS_HOME\n"); +#endif +#ifdef WITH_SSL + output(screen," WITH_SSL\n"); +#endif +#ifdef SSL_DIR + output(screen," SSL_DIR: %s\n",SSL_DIR); +#endif +#ifdef WITH_SYSLOG + output(screen," WITH_SYSLOG\n"); +#endif +#ifdef WITH_PROFILE + output(screen," WITH_PROFILE\n"); +#endif +#ifdef WITH_QUOTAS + output(screen," WITH_QUOTAS\n"); +#endif +#ifdef WITH_VFS + output(screen," WITH_VFS\n"); +#endif +#ifdef USE_SPINLOCKS + output(screen," USE_SPINLOCKS\n"); +#endif +#ifdef SPARC_SPINLOCKS + output(screen," SPARC_SPINLOCKS\n"); +#endif +#ifdef INTEL_SPINLOCKS + output(screen," INTEL_SPINLOCKS\n"); +#endif +#ifdef MIPS_SPINLOCKS + output(screen," MIPS_SPINLOCKS\n"); +#endif +#ifdef POWERPC_SPINLOCKS + output(screen," POWERPC_SPINLOCKS\n"); +#endif +#ifdef HAVE_UNIXWARE_ACLS + output(screen," HAVE_UNIXWARE_ACLS\n"); +#endif +#ifdef HAVE_SOLARIS_ACLS + output(screen," HAVE_SOLARIS_ACLS\n"); +#endif +#ifdef HAVE_IRIX_ACLS + output(screen," HAVE_IRIX_ACLS\n"); +#endif +#ifdef HAVE_AIX_ACLS + output(screen," HAVE_AIX_ACLS\n"); +#endif +#ifdef HAVE_POSIX_ACLS + output(screen," HAVE_POSIX_ACLS\n"); +#endif +#ifdef HAVE_TRU64_ACLS + output(screen," HAVE_TRU64_ACLS\n"); +#endif + +#ifdef HAVE_ACL_GET_PERM_NP + output(screen," HAVE_ACL_GET_PERM_NP\n"); +#endif +#ifdef HAVE_NO_ACLS + output(screen," HAVE_NO_ACLS\n"); +#endif +#ifdef HAVE_LIBREADLINE + output(screen," HAVE_LIBREADLINE\n"); +#endif +#ifdef WITH_LIBICONV + output(screen," WITH_LIBICONV: %s\n",WITH_LIBICONV); +#endif + + + /* Output various paths to files and directories */ + output(screen,"\nPaths:\n"); + output(screen," CONFIGFILE: %s\n", dyn_CONFIGFILE); +#ifdef PRIVATE_DIR + output(screen," PRIVATE_DIR: %s\n",PRIVATE_DIR); +#endif +#ifdef LMHOSTSFILE + output(screen," LMHOSTSFILE: %s\n",LMHOSTSFILE); +#endif + output(screen," SBINDIR: %s\n", dyn_SBINDIR); + output(screen," BINDIR: %s\n", dyn_BINDIR); + output(screen," LOCKDIR: %s\n",dyn_LOCKDIR); + output(screen," DRIVERFILE: %s\n", dyn_DRIVERFILE); + output(screen," LOGFILEBASE: %s\n", dyn_LOGFILEBASE); + + /*Output various other options (most map to defines in the configure script*/ + output(screen,"\nOther Build Options:\n"); +#ifdef HAVE_VOLATILE + output(screen," HAVE_VOLATILE\n"); +#endif +#ifdef HAVE_SHADOW_H + output(screen," HAVE_SHADOW_H\n"); +#endif +#ifdef HAVE_CRYPT + output(screen," HAVE_CRYPT\n"); +#endif +#ifdef USE_BOTH_CRYPT_CALLS + output(screen," USE_BOTH_CRYPT_CALLS\n"); +#endif +#ifdef HAVE_TRUNCATED_SALT + output(screen," HAVE_TRUNCATED_SALT\n"); +#endif +#ifdef HAVE_CUPS + output(screen," HAVE_CUPS\n"); +#endif +#ifdef HAVE_CUPS_CUPS_H + output(screen," HAVE_CUPS_CUPS_H\n"); +#endif +#ifdef HAVE_CUPS_LANGUAGE_H + output(screen," HAVE_CUPS_LANGUAGE_H\n"); +#endif +#ifdef HAVE_LIBDL + output(screen," HAVE_LIBDL\n"); +#endif +#ifdef HAVE_UNIXSOCKET + output(screen," HAVE_UNIXSOCKET\n"); +#endif +#ifdef HAVE_SOCKLEN_T_TYPE + output(screen," HAVE_SOCKLEN_T_TYPE\n"); +#endif +#ifdef HAVE_SIG_ATOMIC_T_TYPE + output(screen," HAVE_SIG_ATOMIC_T_TYPE\n"); +#endif +#ifdef HAVE_SETRESUID + output(screen," HAVE_SETRESUID\n"); +#endif +#ifdef HAVE_SETRESGID + output(screen," HAVE_SETRESGID\n"); +#endif +#ifdef HAVE_CONNECT + output(screen," HAVE_CONNECT\n"); +#endif +#ifdef HAVE_YP_GET_DEFAULT_DOMAIN + output(screen," HAVE_YP_GET_DEFAULT_DOMAIN\n"); +#endif +#ifdef HAVE_STAT64 + output(screen," HAVE_STAT64\n"); +#endif +#ifdef HAVE_LSTAT64 + output(screen," HAVE_LSTAT64\n"); +#endif +#ifdef HAVE_FSTAT64 + output(screen," HAVE_FSTAT64\n"); +#endif +#ifdef HAVE_STRCASECMP + output(screen," HAVE_STRCASECMP\n"); +#endif +#ifdef HAVE_MEMSET + output(screen," HAVE_MEMSET\n"); +#endif +#ifdef HAVE_LONGLONG + output(screen," HAVE_LONGLONG\n"); +#endif +#ifdef COMPILER_SUPPORTS_LL + output(screen," COMPILER_SUPPORTS_LL\n"); +#endif +#ifdef SIZEOF_OFF_T + output(screen," SIZEOF_OFF_T: %d\n",SIZEOF_OFF_T); +#endif +#ifdef HAVE_OFF64_T + output(screen," HAVE_OFF64_T\n"); +#endif +#ifdef SIZEOF_INO_T + output(screen," SIZEOF_INO_T: %d\n",SIZEOF_INO_T); +#endif +#ifdef HAVE_INO64_T + output(screen," HAVE_INO64_T\n"); +#endif +#ifdef HAVE_STRUCT_DIRENT64 + output(screen," HAVE_STRUCT_DIRENT64\n"); +#endif +#ifdef HAVE_UNSIGNED_CHAR + output(screen," HAVE_UNSIGNED_CHAR\n"); +#endif +#ifdef HAVE_SOCK_SIN_LEN + output(screen," HAVE_SOCK_SIN_LEN\n"); +#endif +#ifdef SEEKDIR_RETURNS_VOID + output(screen," SEEKDIR_RETURNS_VOID\n"); +#endif +#ifdef HAVE_FILE_MACRO + output(screen," HAVE_FILE_MACRO\n"); +#endif +#ifdef HAVE_FUNCTION_MACRO + output(screen," HAVE_FUNCTION_MACRO\n"); +#endif +#ifdef HAVE_GETTIMEOFDAY + output(screen," HAVE_GETTIMEOFDAY\n"); +#endif +#ifdef HAVE_C99_VSNPRINTF + output(screen," HAVE_C99_VSNPRINTF\n"); +#endif +#ifdef HAVE_BROKEN_READDIR + output(screen," HAVE_BROKEN_READDIR\n"); +#endif +#ifdef HAVE_NATIVE_ICONV + output(screen," HAVE_NATIVE_ICONV\n"); +#endif +#ifdef HAVE_KERNEL_OPLOCKS_LINUX + output(screen," HAVE_KERNEL_OPLOCKS_LINUX\n"); +#endif +#ifdef HAVE_KERNEL_CHANGE_NOTIFY + output(screen," HAVE_KERNEL_CHANGE_NOTIFY\n"); +#endif +#ifdef HAVE_KERNEL_SHARE_MODES + output(screen," HAVE_KERNEL_SHARE_MODES\n"); +#endif +#ifdef HAVE_KERNEL_OPLOCKS_IRIX + output(screen," HAVE_KERNEL_OPLOCKS_IRIX\n"); +#endif +#ifdef HAVE_IRIX_SPECIFIC_CAPABILITIES + output(screen," HAVE_IRIX_SPECIFIC_CAPABILITIES\n"); +#endif +#ifdef HAVE_INT16_FROM_RPC_RPC_H + output(screen," HAVE_INT16_FROM_RPC_RPC_H\n"); +#endif +#ifdef HAVE_UINT16_FROM_RPC_RPC_H + output(screen," HAVE_UINT16_FROM_RPC_RPC_H\n"); +#endif +#ifdef HAVE_INT32_FROM_RPC_RPC_H + output(screen," HAVE_INT16_FROM_RPC_RPC_H\n"); +#endif +#ifdef HAVE_UINT32_FROM_RPC_RPC_H + output(screen," HAVE_UINT32_FROM_RPC_RPC_H\n"); +#endif +#ifdef HAVE_RPC_AUTH_ERROR_CONFLICT + output(screen," HAVE_RPC_AUTH_ERROR_CONFLICT\n"); +#endif +#ifdef HAVE_FTRUNCATE_EXTEND + output(screen," HAVE_FTRUNCATE_EXTEND\n"); +#endif +#ifdef HAVE_WORKING_AF_LOCAL + output(screen," HAVE_WORKING_AF_LOCAL\n"); +#endif +#ifdef HAVE_BROKEN_GETGROUPS + output(screen," HAVE_BROKEN_GETGROUPS\n"); +#endif +#ifdef REPLACE_GETPASS + output(screen," REPLACE_GETPASS\n"); +#endif +#ifdef REPLACE_INET_NTOA + output(screen," REPLACE_INET_NTOA\n"); +#endif +#ifdef HAVE_SECURE_MKSTEMP + output(screen," HAVE_SECURE_MKSTEMP\n"); +#endif +#ifdef SYSCONF_SC_NGROUPS_MAX + output(screen," SYSCONF_SC_NGROUPS_MAX\n"); +#endif +#ifdef HAVE_IFACE_AIX + output(screen," HAVE_IFACE_AIX\n"); +#endif +#ifdef HAVE_IFACE_IFCONF + output(screen," HAVE_IFACE_IFCONF\n"); +#endif +#ifdef HAVE_IFACE_IFREQ + output(screen," HAVE_IFACE_IFREQ\n"); +#endif +#ifdef USE_SETRESUID + output(screen," USE_SETRESUID\n"); +#endif +#ifdef USE_SETRESGID + output(screen," USE_SETREUID\n"); +#endif +#ifdef USE_SETEUID + output(screen," USE_SETEUID\n"); +#endif +#ifdef USE_SETUIDX + output(screen," USE_SETUIDX\n"); +#endif +#ifdef HAVE_MMAP + output(screen," HAVE_MMAP\n"); +#endif +#ifdef MMAP_BLACKLIST + output(screen," MMAP_BLACKLIST\n"); +#endif +#ifdef FTRUNCATE_NEEDS_ROOT + output(screen," FTRUNCATE_NEEDS_ROOT\n"); +#endif +#ifdef HAVE_FCNTL_LOCK + output(screen," HAVE_FCNTL_LOCK\n"); +#endif +#ifdef HAVE_BROKEN_FCNTL64_LOCKS + output(screen," HAVE_BROKEN_FCNTL64_LOCKS\n"); +#endif +#ifdef HAVE_STRUCT_FLOCK64 + output(screen," HAVE_STRUCT_FLOCK64\n"); +#endif +#ifdef BROKEN_NISPLUS_INCLUDE_FILES + output(screen," BROKEN_NISPLUS_INCLUDE_FILES\n"); +#endif +#ifdef HAVE_LIBPAM + output(screen," HAVE_LIBPAM\n"); +#endif +#ifdef STAT_STATVFS64 + output(screen," STAT_STATVFS64\n"); +#endif +#ifdef STAT_STATVFS + output(screen," STAT_STATVFS\n"); +#endif +#ifdef STAT_STATFS3_OSF1 + output(screen," STAT_STATFS3_OSF1\n"); +#endif +#ifdef STAT_STATFS2_BSIZE + output(screen," STAT_STATFS2_BSIZE\n"); +#endif +#ifdef STAT_STATFS4 + output(screen," STAT_STATFS4\n"); +#endif +#ifdef STAT_STATFS2_FSIZE + output(screen," STAT_STATFS2_FSIZE\n"); +#endif +#ifdef STAT_STATFS2_FS_DATA + output(screen," STAT_STATFS2_FS_DATA\n"); +#endif +#ifdef HAVE_EXPLICIT_LARGEFILE_SUPPORT + output(screen," HAVE_EXPLICIT_LARGEFILE_SUPPORT\n"); +#endif + +#ifdef WITH_UTMP + /* Output UTMP Stuff */ + output(screen,"\nUTMP Related:\n"); + output(screen," WITH_UTMP\n"); + +#ifdef HAVE_UTIMBUF + output(screen," HAVE_UTIMBUF\n"); +#endif +#ifdef HAVE_UT_UT_NAME + output(screen," HAVE_UT_UT_NAME\n"); +#endif +#ifdef HAVE_UT_UT_USER + output(screen," HAVE_UT_UT_USER\n"); +#endif +#ifdef HAVE_UT_UT_ID + output(screen," HAVE_UT_UT_ID\n"); +#endif +#ifdef HAVE_UT_UT_HOST + output(screen," HAVE_UT_UT_HOST\n"); +#endif +#ifdef HAVE_UT_UT_TIME + output(screen," HAVE_UT_UT_TIME\n"); +#endif +#ifdef HAVE_UT_UT_TV + output(screen," HAVE_UT_UT_TV\n"); +#endif +#ifdef HAVE_UT_UT_TYPE + output(screen," HAVE_UT_UT_TYPE\n"); +#endif +#ifdef HAVE_UT_UT_PID + output(screen," HAVE_UT_UT_PID\n"); +#endif +#ifdef HAVE_UT_UT_EXIT + output(screen," HAVE_UT_UT_EXIT\n"); +#endif +#ifdef HAVE_UT_UT_ADDR + output(screen," HAVE_UT_UT_ADDR\n"); +#endif +#ifdef PUTUTLINE_RETURNS_UTMP + output(screen," PUTUTLINE_RETURNS_UTMP\n"); +#endif +#ifdef HAVE_UX_UT_SYSLEN + output(screen," HAVE_UX_UT_SYSLEN\n"); +#endif +#endif /* WITH_UTMP */ + + /* Output Build OS */ + output(screen,"\nBuilt for host os:\n"); +#ifdef LINUX + output(screen," LINUX\n"); +#endif +#ifdef SUNOS5 + output(screen," SUNOS5\n"); +#endif +#ifdef SUNOS4 + output(screen," SUNOS4\n"); +#endif + /* BSD Isn't Defined in the configure script, but there is something about it in include/config.h.in (and I guess acconfig.h) */ +#ifdef BSD + output(screen," BSD\n"); +#endif +#ifdef IRIX + output(screen," IRIX\n"); +#endif +#ifdef IRIX6 + output(screen," IRIX6\n"); +#endif +#ifdef AIX + output(screen," AIX\n"); +#endif +#ifdef HPUX + output(screen," HPUX\n"); +#endif +#ifdef QNX + output(screen," QNX\n"); +#endif +#ifdef OSF1 + output(screen," OSF1\n"); +#endif +#ifdef SCO + output(screen," SCO\n"); +#endif +#ifdef UNIXWARE + output(screen," UNIXWARE\n"); +#endif +#ifdef NEXT2 + output(screen," NEXT2\n"); +#endif +#ifdef RELIANTUNIX + output(screen," RELIANTUNIX\n"); +#endif + + /* Output the sizes of the various types */ + output(screen,"\nType sizes:\n"); + output(screen," sizeof(char): %d\n",sizeof(char)); + output(screen," sizeof(int): %d\n",sizeof(int)); + output(screen," sizeof(long): %d\n",sizeof(long)); + output(screen," sizeof(uint8): %d\n",sizeof(uint8)); + output(screen," sizeof(uint16): %d\n",sizeof(uint16)); + output(screen," sizeof(uint32): %d\n",sizeof(uint32)); + output(screen," sizeof(short): %d\n",sizeof(short)); + output(screen," sizeof(void*): %d\n",sizeof(void*)); +} + + + diff --git a/source3/smbd/change_trust_pw.c b/source3/smbd/change_trust_pw.c new file mode 100644 index 0000000000..0d80d5718f --- /dev/null +++ b/source3/smbd/change_trust_pw.c @@ -0,0 +1,151 @@ +/* + * Unix SMB/CIFS implementation. + * Periodic Trust account password changing. + * Copyright (C) Andrew Tridgell 1992-1997, + * Copyright (C) Luke Kenneth Casson Leighton 1996-1997, + * Copyright (C) Paul Ashton 1997. + * Copyright (C) Jeremy Allison 1998. + * Copyright (C) Andrew Bartlett 2001. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "includes.h" + +extern pstring global_myname; + +/********************************************************* + Change the domain password on the PDC. +**********************************************************/ + +static NTSTATUS modify_trust_password( char *domain, char *remote_machine, + unsigned char orig_trust_passwd_hash[16]) +{ + struct cli_state *cli; + DOM_SID domain_sid; + struct in_addr dest_ip; + NTSTATUS nt_status; + + /* + * Ensure we have the domain SID for this domain. + */ + + if (!secrets_fetch_domain_sid(domain, &domain_sid)) { + DEBUG(0, ("domain_client_validate: unable to fetch domain sid.\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + if(!resolve_name( remote_machine, &dest_ip, 0x20)) { + DEBUG(0,("modify_trust_password: Can't resolve address for %s\n", remote_machine)); + return NT_STATUS_UNSUCCESSFUL; + } + + if (!NT_STATUS_IS_OK(cli_full_connection(&cli, global_myname, remote_machine, + &dest_ip, 0, + "IPC$", "IPC", + "", "", + "", 0))) { + DEBUG(0,("modify_trust_password: Connection to %s failed!\n", remote_machine)); + return NT_STATUS_UNSUCCESSFUL; + } + + /* + * Ok - we have an anonymous connection to the IPC$ share. + * Now start the NT Domain stuff :-). + */ + + if(cli_nt_session_open(cli, PIPE_NETLOGON) == False) { + DEBUG(0,("modify_trust_password: unable to open the domain client session to \ +machine %s. Error was : %s.\n", remote_machine, cli_errstr(cli))); + cli_nt_session_close(cli); + cli_ulogoff(cli); + cli_shutdown(cli); + return NT_STATUS_UNSUCCESSFUL; + } + + nt_status = trust_pw_change_and_store_it(cli, cli->mem_ctx, + orig_trust_passwd_hash); + + cli_nt_session_close(cli); + cli_ulogoff(cli); + cli_shutdown(cli); + return nt_status; +} + +/************************************************************************ + Change the trust account password for a domain. +************************************************************************/ + +NTSTATUS change_trust_account_password( char *domain, char *remote_machine_list) +{ + fstring remote_machine; + unsigned char old_trust_passwd_hash[16]; + time_t lct; + NTSTATUS res = NT_STATUS_UNSUCCESSFUL; + + if(!secrets_fetch_trust_account_password(domain, old_trust_passwd_hash, &lct)) { + DEBUG(0,("change_trust_account_password: unable to read the machine \ +account password for domain %s.\n", domain)); + return NT_STATUS_UNSUCCESSFUL; + } + + while(remote_machine_list && + next_token(&remote_machine_list, remote_machine, + LIST_SEP, sizeof(remote_machine))) { + strupper(remote_machine); + if(strequal(remote_machine, "*")) { + + /* + * We have been asked to dynamcially determine the IP addresses of the PDC. + */ + + struct in_addr *ip_list = NULL; + int count = 0; + int i; + + /* Use the PDC *only* for this. */ + if(!get_dc_list(True, domain, &ip_list, &count)) + continue; + + /* + * Try and connect to the PDC/BDC list in turn as an IP + * address used as a string. + */ + + for(i = 0; i < count; i++) { + fstring dc_name; + if(!lookup_dc_name(global_myname, domain, &ip_list[i], dc_name)) + continue; + if(NT_STATUS_IS_OK(res = modify_trust_password( domain, dc_name, + old_trust_passwd_hash))) + break; + } + + SAFE_FREE(ip_list); + + } else { + res = modify_trust_password( domain, remote_machine, + old_trust_passwd_hash); + } + + } + + if (!NT_STATUS_IS_OK(res)) { + DEBUG(0,("%s : change_trust_account_password: Failed to change password for \ +domain %s.\n", timestring(False), domain)); + } + + return res; +} diff --git a/source3/smbd/chgpasswd.c b/source3/smbd/chgpasswd.c index dc0514c1ed..eed535cf11 100644 --- a/source3/smbd/chgpasswd.c +++ b/source3/smbd/chgpasswd.c @@ -1,8 +1,28 @@ -/* 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. +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Andrew Tridgell 1992-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + 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. */ +/* 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 @@ -27,350 +47,891 @@ */ #include "includes.h" -#include "loadparm.h" -extern int DEBUGLEVEL; +extern struct passdb_ops pdb_ops; -#ifdef ALLOW_CHANGE_PASSWORD +static BOOL check_oem_password(const char *user, + uchar * lmdata, const uchar * lmhash, + const uchar * ntdata, const uchar * nthash, + SAM_ACCOUNT **hnd, char *new_passwd, + int new_passwd_size); -#define MINPASSWDLENGTH 5 -#define BUFSIZE 512 +#if ALLOW_CHANGE_PASSWORD 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); + int master; + static fstring line; + DIR *dirp; + char *dpname; + +#if defined(HAVE_GRANTPT) + /* Try to open /dev/ptmx. If that fails, fall through to old method. */ + if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 0) + { + grantpt(master); + unlockpt(master); + *slave = (char *)ptsname(master); + if (*slave == NULL) + { + DEBUG(0, + ("findpty: Unable to create master/slave pty pair.\n")); + /* Stop fd leak on error. */ + close(master); + return -1; + } + else + { + DEBUG(10, + ("findpty: Allocated slave pty %s\n", *slave)); + return (master); + } + } +#endif /* HAVE_GRANTPT */ + + fstrcpy(line, "/dev/ptyXX"); + + dirp = opendir("/dev"); + if (!dirp) + return (-1); + while ((dpname = readdirname(dirp)) != NULL) + { + if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5) + { + DEBUG(3, + ("pty: try to open %s, line was %s\n", dpname, + line)); + line[8] = dpname[3]; + line[9] = dpname[4]; + if ((master = sys_open(line, O_RDWR, 0)) >= 0) + { + DEBUG(3, ("pty: opened %s\n", line)); + line[5] = 't'; + *slave = line; + closedir(dirp); + return (master); + } + } + } + closedir(dirp); + return (-1); } -static int dochild(int master,char *slavedev, char *name, char *passwordprogram) +static int dochild(int master, const char *slavedev, const struct passwd *pass, + const char *passwordprogram, BOOL as_root) { - 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); + int slave; + struct termios stermios; + gid_t gid; + uid_t uid; + + if (pass == NULL) + { + DEBUG(0, + ("dochild: user doesn't exist in the UNIX password database.\n")); + return False; + } + + gid = pass->pw_gid; + uid = pass->pw_uid; + + gain_root_privilege(); + + /* 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 = sys_open(slavedev, O_RDWR, 0)) < 0) + { + DEBUG(3, ("More weirdness, could not open %s\n", slavedev)); + return (False); + } +#ifdef I_PUSH + ioctl(slave, I_PUSH, "ptem"); + ioctl(slave, I_PUSH, "ldterm"); +#elif defined(TIOCSCTTY) + if (ioctl(slave, TIOCSCTTY, 0) < 0) + { + DEBUG(3, ("Error in ioctl call for slave pty\n")); + /* return(False); */ + } #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); + /* 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 */ + if (!as_root) + { + become_user_permanently(uid, gid); + } + + DEBUG(10, + ("Invoking '%s' as password change program.\n", + passwordprogram)); + + /* 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) +static int expect(int master, char *issue, char *expected) { - 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); - } - } + pstring buffer; + int attempts, timeout, nread, len; + BOOL match = False; + + for (attempts = 0; attempts < 2; attempts++) { + if (!strequal(issue, ".")) { + if (lp_passwd_chat_debug()) + DEBUG(100, ("expect: sending [%s]\n", issue)); + + if ((len = write(master, issue, strlen(issue))) != strlen(issue)) { + DEBUG(2,("expect: (short) write returned %d\n", len )); + return False; + } + } + + if (strequal(expected, ".")) + return True; + + timeout = 2000; + nread = 0; + buffer[nread] = 0; + + while ((len = read_with_timeout(master, buffer + nread, 1, + sizeof(buffer) - nread - 1, + timeout)) > 0) { + nread += len; + buffer[nread] = 0; + + { + /* Eat leading/trailing whitespace before match. */ + pstring str; + pstrcpy( str, buffer); + trim_string( str, " ", " "); + + if ((match = (unix_wild_match(expected, str) == 0))) + timeout = 200; + } + } + + if (lp_passwd_chat_debug()) + DEBUG(100, ("expect: expected [%s] received [%s] match %s\n", + expected, buffer, match ? "yes" : "no" )); + + if (match) + break; + + if (len < 0) { + DEBUG(2, ("expect: %s\n", strerror(errno))); + return False; + } + } + + DEBUG(10,("expect: returning %s\n", match ? "True" : "False" )); + return match; } 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"); + all_string_sub(buf, "\\n", "\n", 0); + all_string_sub(buf, "\\r", "\r", 0); + all_string_sub(buf, "\\s", " ", 0); + all_string_sub(buf, "\\t", "\t", 0); +} + +static int talktochild(int master, char *seq) +{ + int count = 0; + fstring issue, expected; + + fstrcpy(issue, "."); + + while (next_token(&seq, expected, NULL, sizeof(expected))) + { + pwd_sub(expected); + count++; + + if (!expect(master, issue, expected)) + { + DEBUG(3, ("Response %d incorrect\n", count)); + return False; + } + + if (!next_token(&seq, issue, NULL, sizeof(issue))) + fstrcpy(issue, "."); + + pwd_sub(issue); + } + if (!strequal(issue, ".")) { + /* we have one final issue to send */ + fstrcpy(expected, "."); + if (!expect(master, issue, expected)) + return False; + } + + return (count > 0); } -static void writestring(int fd,char *s) +static BOOL chat_with_program(char *passwordprogram, struct passwd *pass, + char *chatsequence, BOOL as_root) { - int l; - - l = strlen (s); - write (fd, s, l); + char *slavedev; + int master; + pid_t pid, wpid; + int wstat; + BOOL chstat = False; + + if (pass == NULL) + { + DEBUG(0, + ("chat_with_program: user doesn't exist in the UNIX password database.\n")); + return False; + } + + /* allocate a pseudo-terminal device */ + if ((master = findpty(&slavedev)) < 0) + { + DEBUG(3, + ("Cannot Allocate pty for password change: %s\n", + pass->pw_name)); + return (False); + } + + /* + * We need to temporarily stop CatchChild from eating + * SIGCLD signals as it also eats the exit status code. JRA. + */ + + CatchChildLeaveStatus(); + + if ((pid = sys_fork()) < 0) + { + DEBUG(3, + ("Cannot fork() child for password change: %s\n", + pass->pw_name)); + close(master); + CatchChild(); + 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", + pass->pw_name)); + kill(pid, SIGKILL); /* be sure to end this process */ + } + + while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) + { + if (errno == EINTR) + { + errno = 0; + continue; + } + break; + } + + if (wpid < 0) + { + DEBUG(3, ("The process is no longer waiting!\n\n")); + close(master); + CatchChild(); + return (False); + } + + /* + * Go back to ignoring children. + */ + CatchChild(); + + close(master); + + 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 */ + + /* + * Lose any oplock capabilities. + */ + oplock_set_capability(False, False); + + /* make sure it doesn't freeze */ + alarm(20); + + if (as_root) + become_root(); + + DEBUG(3, + ("Dochild for user %s (uid=%d,gid=%d)\n", pass->pw_name, + (int)getuid(), (int)getgid())); + chstat = + dochild(master, slavedev, pass, passwordprogram, + as_root); + + if (as_root) + unbecome_root(); + + /* + * The child should never return from dochild() .... + */ + + DEBUG(0, + ("chat_with_program: Error: dochild() returned %d\n", + chstat)); + exit(1); + } + + if (chstat) + DEBUG(3, + ("Password change %ssuccessful for user %s\n", + (chstat ? "" : "un"), pass->pw_name)); + return (chstat); } -static int talktochild(int master, char *chatsequence) +BOOL chgpasswd(const char *name, const char *oldpass, const char *newpass, BOOL as_root) { - char buf[BUFSIZE]; - int count=0; - char *ptr=chatsequence; - fstring chatbuf; + pstring passwordprogram; + pstring chatsequence; + size_t i; + size_t len; - *buf = 0; - sleep(1); + struct passwd *pass; - while (next_token(&ptr,chatbuf,NULL)) { - BOOL ok=True; - count++; - pwd_sub(chatbuf); - if (!strequal(chatbuf,".")) - ok = expect(master,chatbuf,buf); + DEBUG(3, ("Password change for user: %s\n", name)); #if DEBUG_PASSWORD - DEBUG(100,("chatbuf=[%s] responsebuf=[%s]\n",chatbuf,buf)); -#endif - - if (!ok) { - DEBUG(3,("response %d incorrect\n",count)); - return(False); - } + DEBUG(100, ("Passwords: old=%s new=%s\n", oldpass, newpass)); +#endif - if (!next_token(&ptr,chatbuf,NULL)) break; - pwd_sub(chatbuf); - if (!strequal(chatbuf,".")) - writestring(master,chatbuf); + /* Take the passed information and test it for minimum criteria */ + /* Minimum password length */ + if (strlen(newpass) < lp_min_passwd_length()) { + /* too short, must be at least MINPASSWDLENGTH */ + DEBUG(0, ("Password Change: user %s, New password is shorter than minimum password length = %d\n", + name, lp_min_passwd_length())); + 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 */ + } + + /* + * Check the old and new passwords don't contain any control + * characters. + */ + + len = strlen(oldpass); + for (i = 0; i < len; i++) { + if (iscntrl((int)oldpass[i])) { + DEBUG(0, + ("chat_with_program: oldpass contains control characters (disallowed).\n")); + return False; + } + } + + len = strlen(newpass); + for (i = 0; i < len; i++) { + if (iscntrl((int)newpass[i])) { + DEBUG(0, + ("chat_with_program: newpass contains control characters (disallowed).\n")); + return False; + } + } + + pass = Get_Pwnam(name); + +#ifdef WITH_PAM + if (lp_pam_password_change()) { + BOOL ret; + + if (as_root) + become_root(); + + if (pass) { + ret = smb_pam_passchange(pass->pw_name, oldpass, newpass); + } else { + ret = smb_pam_passchange(name, oldpass, newpass); + } + + if (as_root) + unbecome_root(); + + return ret; + } +#endif -#if DEBUG_PASSWORD - DEBUG(100,("sendbuf=[%s]\n",chatbuf)); -#endif - } + /* A non-PAM password change just doen't make sense without a valid local user */ + + if (pass == NULL) + { + DEBUG(0, + ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", + name)); + return False; + } + + pstrcpy(passwordprogram, lp_passwd_program()); + pstrcpy(chatsequence, lp_passwd_chat()); + + if (!*chatsequence) { + DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n")); + return (False); + } + + if (!*passwordprogram) { + DEBUG(2, ("chgpasswd: Null password program - no password changing\n")); + return (False); + } + + if (as_root) { + /* The password program *must* contain the user name to work. Fail if not. */ + if (strstr(passwordprogram, "%u") == NULL) { + DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \ +the string %%u, and the given string %s does not.\n", passwordprogram )); + return False; + } + } + + pstring_sub(passwordprogram, "%u", name); + /* note that we do NOT substitute the %o and %n in the password program + as this would open up a security hole where the user could use + a new password containing shell escape characters */ + + pstring_sub(chatsequence, "%u", name); + all_string_sub(chatsequence, "%o", oldpass, sizeof(pstring)); + all_string_sub(chatsequence, "%n", newpass, sizeof(pstring)); + return (chat_with_program + (passwordprogram, pass, chatsequence, as_root)); +} - if (count<1) return(False); +#else /* ALLOW_CHANGE_PASSWORD */ - return (True); +BOOL chgpasswd(const char *name, const char *oldpass, const char *newpass, BOOL as_root) +{ + DEBUG(0, ("Password changing not compiled in (user=%s)\n", name)); + return (False); } +#endif /* ALLOW_CHANGE_PASSWORD */ +/*********************************************************** + Code to check the lanman hashed password. +************************************************************/ -BOOL chat_with_program(char *passwordprogram,char *name,char *chatsequence) +BOOL check_lanman_password(char *user, uchar * pass1, + uchar * pass2, SAM_ACCOUNT **hnd) { - 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); + uchar unenc_new_pw[16]; + uchar unenc_old_pw[16]; + SAM_ACCOUNT *sampass = NULL; + uint16 acct_ctrl; + const uint8 *lanman_pw; + BOOL ret; + + become_root(); + ret = pdb_getsampwnam(sampass, user); + unbecome_root(); + + if (ret == False) { + DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n")); + pdb_free_sam(&sampass); + return False; + } + + acct_ctrl = pdb_get_acct_ctrl (sampass); + lanman_pw = pdb_get_lanman_passwd (sampass); + + if (acct_ctrl & ACB_DISABLED) { + DEBUG(0,("check_lanman_password: account %s disabled.\n", user)); + pdb_free_sam(&sampass); + return False; + } + + if (lanman_pw == NULL) { + if (acct_ctrl & ACB_PWNOTREQ) { + /* this saves the pointer for the caller */ + *hnd = sampass; + return True; + } else { + DEBUG(0, ("check_lanman_password: no lanman password !\n")); + pdb_free_sam(&sampass); + return False; + } + } + + /* Get the new lanman hash. */ + D_P16(lanman_pw, pass2, unenc_new_pw); + + /* Use this to get the old lanman hash. */ + D_P16(unenc_new_pw, pass1, unenc_old_pw); + + /* Check that the two old passwords match. */ + if (memcmp(lanman_pw, unenc_old_pw, 16)) { + DEBUG(0,("check_lanman_password: old password doesn't match.\n")); + pdb_free_sam(&sampass); + return False; + } + + /* this saves the pointer for the caller */ + *hnd = sampass; + return True; } +/*********************************************************** + Code to change the lanman hashed password. + It nulls out the NT hashed password as it will + no longer be valid. +************************************************************/ + +BOOL change_lanman_password(SAM_ACCOUNT *sampass, uchar * pass1, + uchar * pass2) +{ + static uchar null_pw[16]; + uchar unenc_new_pw[16]; + BOOL ret; + uint16 acct_ctrl; + const uint8 *pwd; + + if (sampass == NULL) { + DEBUG(0,("change_lanman_password: no smb password entry.\n")); + return False; + } + + acct_ctrl = pdb_get_acct_ctrl(sampass); + pwd = pdb_get_lanman_passwd(sampass); + + if (acct_ctrl & ACB_DISABLED) { + DEBUG(0,("change_lanman_password: account %s disabled.\n", + pdb_get_username(sampass))); + return False; + } + + if (pwd == NULL) { + if (acct_ctrl & ACB_PWNOTREQ) { + uchar no_pw[14]; + memset(no_pw, '\0', 14); + E_P16(no_pw, null_pw); + + /* Get the new lanman hash. */ + D_P16(null_pw, pass2, unenc_new_pw); + } else { + DEBUG(0,("change_lanman_password: no lanman password !\n")); + return False; + } + } else { + /* Get the new lanman hash. */ + D_P16(pwd, pass2, unenc_new_pw); + } + + if (!pdb_set_lanman_passwd(sampass, unenc_new_pw)) { + return False; + } + + if (!pdb_set_nt_passwd (sampass, NULL)) { + return False; /* We lose the NT hash. Sorry. */ + } + + if (!pdb_set_pass_changed_now (sampass)) { + pdb_free_sam(&sampass); + /* Not quite sure what this one qualifies as, but this will do */ + return False; + } + + /* Now flush the sam_passwd struct to persistent storage */ + become_root(); + ret = pdb_update_sam_account (sampass); + unbecome_root(); + + return ret; +} -BOOL chgpasswd(char *name,char *oldpass,char *newpass) +/*********************************************************** + Code to check and change the OEM hashed password. +************************************************************/ +BOOL pass_oem_change(char *user, + uchar * lmdata, uchar * lmhash, + uchar * ntdata, uchar * nthash) { - pstring passwordprogram; - pstring chatsequence; + fstring new_passwd; + const char *unix_user; + SAM_ACCOUNT *sampass = NULL; + BOOL ret = check_oem_password(user, lmdata, lmhash, ntdata, nthash, + &sampass, new_passwd, sizeof(new_passwd)); - strlower(name); - DEBUG(3,("Password change for user: %s\n",name)); + /* + * At this point we have the new case-sensitive plaintext + * password in the fstring new_passwd. If we wanted to synchronise + * with UNIX passwords we would call a UNIX password changing + * function here. However it would have to be done as root + * as the plaintext of the old users password is not + * available. JRA. + */ -#if DEBUG_PASSWORD - DEBUG(100,("Passwords: old=%s new=%s\n",oldpass,newpass)); -#endif + unix_user = pdb_get_username(sampass); - /* 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 ((ret) && (unix_user) && (*unix_user) && lp_unix_password_sync()) + ret = chgpasswd(unix_user, "", new_passwd, True); - if (!*chatsequence) { - DEBUG(2,("Null chat sequence - no password changing\n")); - return(False); - } + if (ret) + ret = change_oem_password(sampass, new_passwd); - if (!*passwordprogram) { - DEBUG(2,("Null password program - no password changing\n")); - return(False); - } + memset(new_passwd, 0, sizeof(new_passwd)); - string_sub(passwordprogram,"%u",name); - string_sub(passwordprogram,"%o",oldpass); - string_sub(passwordprogram,"%n",newpass); + pdb_free_sam(&sampass); - string_sub(chatsequence,"%u",name); - string_sub(chatsequence,"%o",oldpass); - string_sub(chatsequence,"%n",newpass); - return(chat_with_program(passwordprogram,name,chatsequence)); + return ret; } -#else -BOOL chgpasswd(char *name,char *oldpass,char *newpass) +/*********************************************************** + Code to check the OEM hashed password. + + this function ignores the 516 byte nt OEM hashed password + but does use the lm OEM password to check the nt hashed-hash. + +************************************************************/ +static BOOL check_oem_password(const char *user, + uchar * lmdata, const uchar * lmhash, + const uchar * ntdata, const uchar * nthash, + SAM_ACCOUNT **hnd, char *new_passwd, + int new_passwd_size) { - DEBUG(0,("Password changing not compiled in (user=%s)\n",name)); - return(False); -} + static uchar null_pw[16]; + static uchar null_ntpw[16]; + SAM_ACCOUNT *sampass = NULL; + const uint8 *lanman_pw, *nt_pw; + uint16 acct_ctrl; + int new_pw_len; + uchar new_ntp16[16]; + uchar unenc_old_ntpw[16]; + uchar new_p16[16]; + uchar unenc_old_pw[16]; + char no_pw[2]; + BOOL ret; + + BOOL nt_pass_set = (ntdata != NULL && nthash != NULL); + + pdb_init_sam(&sampass); + + become_root(); + ret = pdb_getsampwnam(sampass, user); + unbecome_root(); + + if (ret == False) { + DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n")); + return False; + } + + *hnd = sampass; + + acct_ctrl = pdb_get_acct_ctrl(sampass); + + if (acct_ctrl & ACB_DISABLED) { + DEBUG(0,("check_lanman_password: account %s disabled.\n", user)); + return False; + } + + /* construct a null password (in case one is needed */ + no_pw[0] = 0; + no_pw[1] = 0; + nt_lm_owf_gen(no_pw, null_ntpw, null_pw); + + /* save pointers to passwords so we don't have to keep looking them up */ + lanman_pw = pdb_get_lanman_passwd(sampass); + nt_pw = pdb_get_nt_passwd (sampass); + + /* check for null passwords */ + if (lanman_pw == NULL) { + if (!(acct_ctrl & ACB_PWNOTREQ)) { + DEBUG(0,("check_oem_password: no lanman password !\n")); + return False; + } + } + + if (pdb_get_nt_passwd(sampass) == NULL && nt_pass_set) { + if (!(acct_ctrl & ACB_PWNOTREQ)) { + DEBUG(0,("check_oem_password: no ntlm password !\n")); + return False; + } + } + + /* + * Call the hash function to get the new password. + */ + SamOEMhash( lmdata, lanman_pw, 516); + + /* + * The length of the new password is in the last 4 bytes of + * the data buffer. + */ + + new_pw_len = IVAL(lmdata, 512); + if (new_pw_len < 0 || new_pw_len > new_passwd_size - 1) { + DEBUG(0,("check_oem_password: incorrect password length (%d).\n", new_pw_len)); + return False; + } + + if (nt_pass_set) { + /* + * nt passwords are in unicode + */ + pull_ucs2(NULL, new_passwd, + (const smb_ucs2_t *)&lmdata[512 - new_pw_len], + new_passwd_size, new_pw_len, 0); + } else { + memcpy(new_passwd, &lmdata[512 - new_pw_len], new_pw_len); + new_passwd[new_pw_len] = 0; + } + + /* + * To ensure we got the correct new password, hash it and + * use it as a key to test the passed old password. + */ + + nt_lm_owf_gen(new_passwd, new_ntp16, new_p16); + + if (!nt_pass_set) + { + /* + * Now use new_p16 as the key to see if the old + * password matches. + */ + D_P16(new_p16, lmhash, unenc_old_pw); + + if (memcmp(lanman_pw, unenc_old_pw, 16)) + { + DEBUG(0,("check_oem_password: old lm password doesn't match.\n")); + return False; + } + +#ifdef DEBUG_PASSWORD + DEBUG(100, + ("check_oem_password: password %s ok\n", new_passwd)); +#endif + return True; + } + + /* + * Now use new_p16 as the key to see if the old + * password matches. + */ + D_P16(new_ntp16, lmhash, unenc_old_pw); + D_P16(new_ntp16, nthash, unenc_old_ntpw); + + if (memcmp(lanman_pw, unenc_old_pw, 16)) + { + DEBUG(0,("check_oem_password: old lm password doesn't match.\n")); + return False; + } + + if (memcmp(nt_pw, unenc_old_ntpw, 16)) + { + DEBUG(0,("check_oem_password: old nt password doesn't match.\n")); + return False; + } +#ifdef DEBUG_PASSWORD + DEBUG(100, ("check_oem_password: password %s ok\n", new_passwd)); #endif + return True; +} + +/*********************************************************** + Code to change the oem password. Changes both the lanman + and NT hashes. +************************************************************/ + +BOOL change_oem_password(SAM_ACCOUNT *hnd, char *new_passwd) +{ + BOOL ret; + + if (!pdb_set_plaintext_passwd (hnd, new_passwd)) { + return False; + } + + /* Now write it into the file. */ + become_root(); + ret = pdb_update_sam_account (hnd); + unbecome_root(); + + return ret; +} + + + diff --git a/source3/smbd/close.c b/source3/smbd/close.c new file mode 100644 index 0000000000..dc52327cb4 --- /dev/null +++ b/source3/smbd/close.c @@ -0,0 +1,274 @@ +/* + Unix SMB/CIFS implementation. + file closing + Copyright (C) Andrew Tridgell 1992-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + 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" + +/**************************************************************************** +run a file if it is a magic script +****************************************************************************/ +static void check_magic(files_struct *fsp,connection_struct *conn) +{ + if (!*lp_magicscript(SNUM(conn))) + return; + + DEBUG(5,("checking magic for %s\n",fsp->fsp_name)); + + { + char *p; + if (!(p = strrchr_m(fsp->fsp_name,'/'))) + p = fsp->fsp_name; + else + p++; + + if (!strequal(lp_magicscript(SNUM(conn)),p)) + return; + } + + { + int ret; + pstring magic_output; + pstring fname; + SMB_STRUCT_STAT st; + int tmp_fd, outfd; + + pstrcpy(fname,fsp->fsp_name); + if (*lp_magicoutput(SNUM(conn))) + pstrcpy(magic_output,lp_magicoutput(SNUM(conn))); + else + slprintf(magic_output,sizeof(fname)-1, "%s.out",fname); + + chmod(fname,0755); + ret = smbrun(fname,&tmp_fd); + DEBUG(3,("Invoking magic command %s gave %d\n",fname,ret)); + unlink(fname); + if (ret != 0 || tmp_fd == -1) { + if (tmp_fd != -1) + close(tmp_fd); + return; + } + outfd = open(magic_output, O_CREAT|O_EXCL|O_RDWR, 0600); + if (outfd == -1) { + close(tmp_fd); + return; + } + + if (sys_fstat(tmp_fd,&st) == -1) { + close(tmp_fd); + close(outfd); + return; + } + + transfer_file(tmp_fd,outfd,(SMB_OFF_T)st.st_size); + close(tmp_fd); + close(outfd); + } +} + +/**************************************************************************** + Common code to close a file or a directory. +****************************************************************************/ + +static int close_filestruct(files_struct *fsp) +{ + connection_struct *conn = fsp->conn; + int ret = 0; + + if (fsp->fd != -1) { + if(flush_write_cache(fsp, CLOSE_FLUSH) == -1) + ret = -1; + + delete_write_cache(fsp); + } + + fsp->is_directory = False; + + conn->num_files_open--; + SAFE_FREE(fsp->wbmpx_ptr); + + return ret; +} + +/**************************************************************************** + Close a file. + + If normal_close is 1 then this came from a normal SMBclose (or equivalent) + operation otherwise it came as the result of some other operation such as + the closing of the connection. In the latter case printing and + magic scripts are not run. +****************************************************************************/ + +static int close_normal_file(files_struct *fsp, BOOL normal_close) +{ + share_mode_entry *share_entry = NULL; + size_t share_entry_count = 0; + BOOL delete_on_close = False; + connection_struct *conn = fsp->conn; + int err = 0; + int err1 = 0; + + remove_pending_lock_requests_by_fid(fsp); + + /* + * If we're flushing on a close we can get a write + * error here, we must remember this. + */ + + if (close_filestruct(fsp) == -1) + err1 = -1; + + if (fsp->print_file) { + print_fsp_end(fsp, normal_close); + file_free(fsp); + return 0; + } + + /* + * Lock the share entries, and determine if we should delete + * on close. If so delete whilst the lock is still in effect. + * This prevents race conditions with the file being created. JRA. + */ + + lock_share_entry_fsp(fsp); + share_entry_count = del_share_mode(fsp, &share_entry); + + DEBUG(10,("close_normal_file: share_entry_count = %d for file %s\n", + share_entry_count, fsp->fsp_name )); + + /* + * We delete on close if it's the last open, and the + * delete on close flag was set in the entry we just deleted. + */ + + if ((share_entry_count == 0) && share_entry && + GET_DELETE_ON_CLOSE_FLAG(share_entry->share_mode) ) + delete_on_close = True; + + SAFE_FREE(share_entry); + + /* + * NT can set delete_on_close of the last open + * reference to a file. + */ + + if (normal_close && delete_on_close) { + DEBUG(5,("close_file: file %s. Delete on close was set - deleting file.\n", + fsp->fsp_name)); + if(fsp->conn->vfs_ops.unlink(conn,fsp->fsp_name) != 0) { + /* + * This call can potentially fail as another smbd may have + * had the file open with delete on close set and deleted + * it when its last reference to this file went away. Hence + * we log this but not at debug level zero. + */ + + DEBUG(5,("close_file: file %s. Delete on close was set and unlink failed \ +with error %s\n", fsp->fsp_name, strerror(errno) )); + } + } + + unlock_share_entry_fsp(fsp); + + if(EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) + release_file_oplock(fsp); + + locking_close_file(fsp); + + err = fd_close(conn, fsp); + + /* check for magic scripts */ + if (normal_close) { + check_magic(fsp,conn); + } + + /* + * Ensure pending modtime is set after close. + */ + + if(fsp->pending_modtime) { + int saved_errno = errno; + set_filetime(conn, fsp->fsp_name, fsp->pending_modtime); + errno = saved_errno; + } + + DEBUG(2,("%s closed file %s (numopen=%d) %s\n", + conn->user,fsp->fsp_name, + conn->num_files_open, err ? strerror(err) : "")); + + if (fsp->fsp_name) + string_free(&fsp->fsp_name); + + file_free(fsp); + + if (err == -1 || err1 == -1) + return -1; + else + return 0; +} + +/**************************************************************************** + Close a directory opened by an NT SMB call. +****************************************************************************/ + +static int close_directory(files_struct *fsp, BOOL normal_close) +{ + remove_pending_change_notify_requests_by_fid(fsp); + + /* + * NT can set delete_on_close of the last open + * reference to a directory also. + */ + + if (normal_close && fsp->directory_delete_on_close) { + BOOL ok = rmdir_internals(fsp->conn, fsp->fsp_name); + DEBUG(5,("close_directory: %s. Delete on close was set - deleting directory %s.\n", + fsp->fsp_name, ok ? "succeeded" : "failed" )); + + /* + * Ensure we remove any change notify requests that would + * now fail as the directory has been deleted. + */ + + if(ok) + remove_pending_change_notify_requests_by_filename(fsp); + } + + /* + * Do the code common to files and directories. + */ + close_filestruct(fsp); + + if (fsp->fsp_name) + string_free(&fsp->fsp_name); + + file_free(fsp); + + return 0; +} + +/**************************************************************************** + Close a directory opened by an NT SMB call. +****************************************************************************/ + +int close_file(files_struct *fsp, BOOL normal_close) +{ + if(fsp->is_directory) + return close_directory(fsp, normal_close); + return close_normal_file(fsp, normal_close); +} diff --git a/source3/smbd/conn.c b/source3/smbd/conn.c new file mode 100644 index 0000000000..f552d4a224 --- /dev/null +++ b/source3/smbd/conn.c @@ -0,0 +1,224 @@ +/* + Unix SMB/CIFS implementation. + Manage connections_struct structures + Copyright (C) Andrew Tridgell 1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + 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" + +/* set these to define the limits of the server. NOTE These are on a + per-client basis. Thus any one machine can't connect to more than + MAX_CONNECTIONS services, but any number of machines may connect at + one time. */ +#define MAX_CONNECTIONS 128 + +static connection_struct *Connections; + +/* number of open connections */ +static struct bitmap *bmap; +static int num_open; + +/**************************************************************************** +init the conn structures +****************************************************************************/ +void conn_init(void) +{ + bmap = bitmap_allocate(MAX_CONNECTIONS); +} + +/**************************************************************************** +return the number of open connections +****************************************************************************/ +int conn_num_open(void) +{ + return num_open; +} + + +/**************************************************************************** +check if a snum is in use +****************************************************************************/ +BOOL conn_snum_used(int snum) +{ + connection_struct *conn; + for (conn=Connections;conn;conn=conn->next) { + if (conn->service == snum) { + return(True); + } + } + return(False); +} + + +/**************************************************************************** +find a conn given a cnum +****************************************************************************/ +connection_struct *conn_find(int cnum) +{ + int count=0; + connection_struct *conn; + + for (conn=Connections;conn;conn=conn->next,count++) { + if (conn->cnum == cnum) { + if (count > 10) { + DLIST_PROMOTE(Connections, conn); + } + return conn; + } + } + + return NULL; +} + + +/**************************************************************************** + find first available connection slot, starting from a random position. +The randomisation stops problems with the server dieing and clients +thinking the server is still available. +****************************************************************************/ +connection_struct *conn_new(void) +{ + connection_struct *conn; + int i; + + i = bitmap_find(bmap, 1); + + if (i == -1) { + DEBUG(1,("ERROR! Out of connection structures\n")); + return NULL; + } + + conn = (connection_struct *)malloc(sizeof(*conn)); + if (!conn) return NULL; + + ZERO_STRUCTP(conn); + conn->cnum = i; + + bitmap_set(bmap, i); + + num_open++; + + string_set(&conn->user,""); + string_set(&conn->dirpath,""); + string_set(&conn->connectpath,""); + string_set(&conn->origpath,""); + + DLIST_ADD(Connections, conn); + + return conn; +} + +/**************************************************************************** +close all conn structures +****************************************************************************/ +void conn_close_all(void) +{ + connection_struct *conn, *next; + for (conn=Connections;conn;conn=next) { + next=conn->next; + close_cnum(conn, (uint16)-1); + } +} + +/**************************************************************************** +idle inactive connections +****************************************************************************/ +BOOL conn_idle_all(time_t t, int deadtime) +{ + BOOL allidle = True; + connection_struct *conn, *next; + + for (conn=Connections;conn;conn=next) { + next=conn->next; + /* close dirptrs on connections that are idle */ + if ((t-conn->lastused) > DPTR_IDLE_TIMEOUT) + dptr_idlecnum(conn); + + if (conn->num_files_open > 0 || + (t-conn->lastused)<deadtime) + allidle = False; + } + + return allidle; +} + +/**************************************************************************** + Free a conn structure. +****************************************************************************/ + +void conn_free(connection_struct *conn) +{ + /* Free vfs_connection_struct */ + + if (conn->dl_handle != NULL) { + /* Close dlopen() handle */ + dlclose(conn->dl_handle); + } + + DLIST_REMOVE(Connections, conn); + + if (conn->ngroups && conn->groups) { + SAFE_FREE(conn->groups); + conn->ngroups = 0; + } + + delete_nt_token(&conn->nt_user_token); + free_namearray(conn->veto_list); + free_namearray(conn->hide_list); + free_namearray(conn->veto_oplock_list); + + string_free(&conn->user); + string_free(&conn->dirpath); + string_free(&conn->connectpath); + string_free(&conn->origpath); + + bitmap_clear(bmap, conn->cnum); + num_open--; + + ZERO_STRUCTP(conn); + SAFE_FREE(conn); +} + + +/**************************************************************************** +receive a smbcontrol message to forcibly unmount a share +the message contains just a share name and all instances of that +share are unmounted +the special sharename '*' forces unmount of all shares +****************************************************************************/ +void msg_force_tdis(int msg_type, pid_t pid, void *buf, size_t len) +{ + connection_struct *conn, *next; + fstring sharename; + + fstrcpy(sharename, buf); + + if (strcmp(sharename, "*") == 0) { + DEBUG(1,("Forcing close of all shares\n")); + conn_close_all(); + return; + } + + for (conn=Connections;conn;conn=next) { + next=conn->next; + if (strequal(lp_servicename(conn->service), sharename)) { + DEBUG(1,("Forcing close of share %s cnum=%d\n", + sharename, conn->cnum)); + close_cnum(conn, (uint16)-1); + } + } +} diff --git a/source3/smbd/connection.c b/source3/smbd/connection.c new file mode 100644 index 0000000000..c9815dbf8c --- /dev/null +++ b/source3/smbd/connection.c @@ -0,0 +1,189 @@ +/* + Unix SMB/CIFS implementation. + connection claim routines + Copyright (C) Andrew Tridgell 1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + 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" + +extern fstring remote_machine; +static TDB_CONTEXT *tdb; + +/**************************************************************************** + Return the connection tdb context (used for message send all). +****************************************************************************/ + +TDB_CONTEXT *conn_tdb_ctx(void) +{ + return tdb; +} + +/**************************************************************************** + Delete a connection record. +****************************************************************************/ + +BOOL yield_connection(connection_struct *conn,char *name) +{ + struct connections_key key; + TDB_DATA kbuf; + + if (!tdb) return False; + + DEBUG(3,("Yielding connection to %s\n",name)); + + ZERO_STRUCT(key); + key.pid = sys_getpid(); + key.cnum = conn?conn->cnum:-1; + fstrcpy(key.name, name); + + kbuf.dptr = (char *)&key; + kbuf.dsize = sizeof(key); + + if (tdb_delete(tdb, kbuf) != 0) { + int dbg_lvl = (!conn && (tdb_error(tdb) == TDB_ERR_NOEXIST)) ? 3 : 0; + DEBUG(dbg_lvl,("yield_connection: tdb_delete for name %s failed with error %s.\n", + name, tdb_errorstr(tdb) )); + return (False); + } + + return(True); +} + +struct count_stat { + pid_t mypid; + int curr_connections; + char *name; + BOOL Clear; +}; + +/**************************************************************************** + Count the entries belonging to a service in the connection db. +****************************************************************************/ + +static int count_fn( TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *udp) +{ + struct connections_data crec; + struct count_stat *cs = (struct count_stat *)udp; + + if (dbuf.dsize != sizeof(crec)) + return 0; + + memcpy(&crec, dbuf.dptr, sizeof(crec)); + + if (crec.cnum == -1) + return 0; + + /* If the pid was not found delete the entry from connections.tdb */ + + if (cs->Clear && !process_exists(crec.pid) && (errno == ESRCH)) { + DEBUG(2,("pid %u doesn't exist - deleting connections %d [%s]\n", + (unsigned int)crec.pid, crec.cnum, crec.name)); + if (tdb_delete(the_tdb, kbuf) != 0) + DEBUG(0,("count_fn: tdb_delete failed with error %s\n", tdb_errorstr(tdb) )); + return 0; + } + + if (strequal(crec.name, cs->name)) + cs->curr_connections++; + + return 0; +} + +/**************************************************************************** + Claim an entry in the connections database. +****************************************************************************/ + +BOOL claim_connection(connection_struct *conn,char *name,int max_connections,BOOL Clear) +{ + struct connections_key key; + struct connections_data crec; + TDB_DATA kbuf, dbuf; + + if (!tdb) { + tdb = tdb_open_log(lock_path("connections.tdb"), 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT, + O_RDWR | O_CREAT, 0644); + } + if (!tdb) + return False; + + /* + * Enforce the max connections parameter. + */ + + if (max_connections > 0) { + struct count_stat cs; + + cs.mypid = sys_getpid(); + cs.curr_connections = 0; + cs.name = lp_servicename(SNUM(conn)); + cs.Clear = Clear; + + /* + * This has a race condition, but locking the chain before hand is worse + * as it leads to deadlock. + */ + + if (tdb_traverse(tdb, count_fn, &cs) == -1) { + DEBUG(0,("claim_connection: traverse of connections.tdb failed with error %s.\n", + tdb_errorstr(tdb) )); + return False; + } + + if (cs.curr_connections >= max_connections) { + DEBUG(1,("claim_connection: Max connections (%d) exceeded for %s\n", + max_connections, name )); + return False; + } + } + + DEBUG(5,("claiming %s %d\n",name,max_connections)); + + ZERO_STRUCT(key); + key.pid = sys_getpid(); + key.cnum = conn?conn->cnum:-1; + fstrcpy(key.name, name); + + kbuf.dptr = (char *)&key; + kbuf.dsize = sizeof(key); + + /* fill in the crec */ + ZERO_STRUCT(crec); + crec.magic = 0x280267; + crec.pid = sys_getpid(); + crec.cnum = conn?conn->cnum:-1; + if (conn) { + crec.uid = conn->uid; + crec.gid = conn->gid; + StrnCpy(crec.name, + lp_servicename(SNUM(conn)),sizeof(crec.name)-1); + } + crec.start = time(NULL); + + StrnCpy(crec.machine,remote_machine,sizeof(crec.machine)-1); + StrnCpy(crec.addr,conn?conn->client_address:client_addr(),sizeof(crec.addr)-1); + + dbuf.dptr = (char *)&crec; + dbuf.dsize = sizeof(crec); + + if (tdb_store(tdb, kbuf, dbuf, TDB_REPLACE) != 0) { + DEBUG(0,("claim_connection: tdb_store failed with error %s.\n", + tdb_errorstr(tdb) )); + return False; + } + + return True; +} diff --git a/source3/smbd/dfree.c b/source3/smbd/dfree.c new file mode 100644 index 0000000000..71b3f2bf77 --- /dev/null +++ b/source3/smbd/dfree.c @@ -0,0 +1,164 @@ +/* + Unix SMB/CIFS implementation. + functions to calculate the free disk space + Copyright (C) Andrew Tridgell 1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + 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" + +/**************************************************************************** +normalise for DOS usage +****************************************************************************/ +static void disk_norm(BOOL small_query, SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize) +{ + /* check if the disk is beyond the max disk size */ + SMB_BIG_UINT 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(small_query) { + /* + * Force max to fit in 16 bit fields. + */ + if (*bsize > (WORDMAX*512)) { + *bsize = (WORDMAX*512); + if (*dsize > WORDMAX) + *dsize = WORDMAX; + if (*dfree > WORDMAX) + *dfree = WORDMAX; + break; + } + } + } +} + + + +/**************************************************************************** + return number of 1K blocks available on a path and total number +****************************************************************************/ + +static SMB_BIG_UINT disk_free(const char *path, BOOL small_query, + SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize) +{ + int dfree_retval; + SMB_BIG_UINT dfree_q = 0; + SMB_BIG_UINT bsize_q = 0; + SMB_BIG_UINT dsize_q = 0; + char *dfree_command; + + (*dfree) = (*dsize) = 0; + (*bsize) = 512; + + /* + * If external disk calculation specified, use it. + */ + + dfree_command = lp_dfree_command(); + if (dfree_command && *dfree_command) { + char *p; + char **lines; + pstring syscmd; + + slprintf(syscmd, sizeof(syscmd)-1, "%s %s", dfree_command, path); + DEBUG (3, ("disk_free: Running command %s\n", syscmd)); + + lines = file_lines_pload(syscmd, NULL); + if (lines) { + char *line = lines[0]; + + DEBUG (3, ("Read input from dfree, \"%s\"\n", line)); + + *dsize = (SMB_BIG_UINT)strtoul(line, &p, 10); + while (p && *p & isspace(*p)) + p++; + if (p && *p) + *dfree = (SMB_BIG_UINT)strtoul(p, &p, 10); + while (p && *p & isspace(*p)) + p++; + if (p && *p) + *bsize = (SMB_BIG_UINT)strtoul(p, NULL, 10); + else + *bsize = 1024; + file_lines_free(lines); + DEBUG (3, ("Parsed output of dfree, dsize=%u, dfree=%u, bsize=%u\n", + (unsigned int)*dsize, (unsigned int)*dfree, (unsigned int)*bsize)); + + if (!*dsize) + *dsize = 2048; + if (!*dfree) + *dfree = 1024; + } else { + DEBUG (0, ("disk_free: sys_popen() failed for command %s. Error was : %s\n", + syscmd, strerror(errno) )); + sys_fsusage(path, dfree, dsize); + } + } else + sys_fsusage(path, dfree, dsize); + + if (disk_quotas(path, &bsize_q, &dfree_q, &dsize_q)) { + (*bsize) = bsize_q; + (*dfree) = MIN(*dfree,dfree_q); + (*dsize) = MIN(*dsize,dsize_q); + } + + /* FIXME : Any reason for this assumption ? */ + if (*bsize < 256) { + DEBUG(5,("disk_free:Warning: bsize == %d < 256 . Changing to assumed correct bsize = 512\n",(int)*bsize)); + *bsize = 512; + } + + if ((*dsize)<1) { + static int done; + if (!done) { + DEBUG(0,("WARNING: dfree is broken on this system\n")); + done=1; + } + *dsize = 20*1024*1024/(*bsize); + *dfree = MAX(1,*dfree); + } + + disk_norm(small_query,bsize,dfree,dsize); + + if ((*bsize) < 1024) { + dfree_retval = (*dfree)/(1024/(*bsize)); + } else { + dfree_retval = ((*bsize)/1024)*(*dfree); + } + + return(dfree_retval); +} + + +/**************************************************************************** +wrap it to get filenames right +****************************************************************************/ +SMB_BIG_UINT sys_disk_free(const char *path, BOOL small_query, + SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize) +{ + return disk_free(path,small_query, bsize,dfree,dsize); +} diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c index ac6f918b9d..f56e0e9ef0 100644 --- a/source3/smbd/dir.c +++ b/source3/smbd/dir.c @@ -1,8 +1,7 @@ /* - Unix SMB/Netbios implementation. - Version 1.9. + Unix SMB/CIFS implementation. Directory handling routines - Copyright (C) Andrew Tridgell 1992-1995 + Copyright (C) Andrew Tridgell 1992-1998 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,351 +19,505 @@ */ #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]; - +typedef struct _dptr_struct { + struct _dptr_struct *next, *prev; + int dnum; + uint16 spid; + connection_struct *conn; + void *ptr; + BOOL expect_close; + char *wcard; /* Field only used for trans2_ searches */ + uint16 attr; /* Field only used for trans2_ searches */ + char *path; +} dptr_struct; + +static struct bitmap *dptr_bmap; +static dptr_struct *dirptrs; static int dptrs_open = 0; +#define INVALID_DPTR_KEY (-3) + /**************************************************************************** -initialise the dir array + Initialise the dir bitmap. ****************************************************************************/ + void init_dptrs(void) { static BOOL dptrs_init=False; - int i; - if (dptrs_init) return; - for (i=0;i<NUMDIRPTRS;i++) - { - dirptrs[i].valid = False; - dirptrs[i].wcard = NULL; - dirptrs[i].ptr = NULL; - string_init(&dirptrs[i].path,""); - } + if (dptrs_init) + return; + + dptr_bmap = bitmap_allocate(MAX_DIRECTORY_HANDLES); + + if (!dptr_bmap) + exit_server("out of memory in init_dptrs"); + dptrs_init = True; } /**************************************************************************** -idle a dptr - the directory is closed but the control info is kept + Idle a dptr - the directory is closed but the control info is kept. ****************************************************************************/ -static void dptr_idle(int key) + +static void dptr_idle(dptr_struct *dptr) { - if (dirptrs[key].valid && dirptrs[key].ptr) { - DEBUG(4,("Idling dptr key %d\n",key)); + if (dptr->ptr) { + DEBUG(4,("Idling dptr dnum %d\n",dptr->dnum)); dptrs_open--; - CloseDir(dirptrs[key].ptr); - dirptrs[key].ptr = NULL; - } + CloseDir(dptr->ptr); + dptr->ptr = NULL; + } } /**************************************************************************** -idle the oldest dptr + Idle the oldest dptr. ****************************************************************************/ + static void dptr_idleoldest(void) { - int i; - uint32 old=dircounter+1; - int oldi= -1; - for (i=0;i<NUMDIRPTRS;i++) - if (dirptrs[i].valid && dirptrs[i].ptr && dirptrs[i].lastused < old) { - old = dirptrs[i].lastused; - oldi = i; + dptr_struct *dptr; + + /* + * Go to the end of the list. + */ + for(dptr = dirptrs; dptr && dptr->next; dptr = dptr->next) + ; + + if(!dptr) { + DEBUG(0,("No dptrs available to idle ?\n")); + return; + } + + /* + * Idle the oldest pointer. + */ + + for(; dptr; dptr = dptr->prev) { + if (dptr->ptr) { + dptr_idle(dptr); + return; } - if (oldi != -1) - dptr_idle(oldi); - else - DEBUG(0,("No dptrs available to idle??\n")); + } } /**************************************************************************** -get the dir ptr for a dir index + Get the dptr_struct for a dir index. ****************************************************************************/ -static void *dptr_get(int key,uint32 lastused) -{ - if (dirptrs[key].valid) { - if (lastused) dirptrs[key].lastused = lastused; - if (!dirptrs[key].ptr) { - if (dptrs_open >= MAXDIR) - dptr_idleoldest(); - DEBUG(4,("Reopening dptr key %d\n",key)); - if ((dirptrs[key].ptr = OpenDir(dirptrs[key].path))) - dptrs_open++; + +static dptr_struct *dptr_get(int key, BOOL forclose) +{ + dptr_struct *dptr; + + for(dptr = dirptrs; dptr; dptr = dptr->next) { + if(dptr->dnum == key) { + if (!forclose && !dptr->ptr) { + if (dptrs_open >= MAX_OPEN_DIRECTORIES) + dptr_idleoldest(); + DEBUG(4,("Reopening dptr key %d\n",key)); + if ((dptr->ptr = OpenDir(dptr->conn, dptr->path, True))) + dptrs_open++; + } + DLIST_PROMOTE(dirptrs,dptr); + return dptr; } - return(dirptrs[key].ptr); } return(NULL); } /**************************************************************************** -get the dir path for a dir index + Get the dptr ptr for a dir index. +****************************************************************************/ + +static void *dptr_ptr(int key) +{ + dptr_struct *dptr = dptr_get(key, False); + + if (dptr) + return(dptr->ptr); + return(NULL); +} + +/**************************************************************************** + Get the dir path for a dir index. ****************************************************************************/ + char *dptr_path(int key) { - if (dirptrs[key].valid) - return(dirptrs[key].path); + dptr_struct *dptr = dptr_get(key, False); + + if (dptr) + return(dptr->path); return(NULL); } /**************************************************************************** -get the dir wcard for a dir index (lanman2 specific) + Get the dir wcard for a dir index (lanman2 specific). ****************************************************************************/ + char *dptr_wcard(int key) { - if (dirptrs[key].valid) - return(dirptrs[key].wcard); + dptr_struct *dptr = dptr_get(key, False); + + if (dptr) + return(dptr->wcard); return(NULL); } /**************************************************************************** -set the dir wcard for a dir index (lanman2 specific) -Returns 0 on ok, 1 on fail. + 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; + dptr_struct *dptr = dptr_get(key, False); + + if (dptr) { + dptr->wcard = wcard; return True; } return False; } /**************************************************************************** -set the dir attrib for a dir index (lanman2 specific) -Returns 0 on ok, 1 on fail. + 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; + dptr_struct *dptr = dptr_get(key, False); + + if (dptr) { + dptr->attr = attr; return True; } return False; } /**************************************************************************** -get the dir attrib for a dir index (lanman2 specific) + Get the dir attrib for a dir index (lanman2 specific) ****************************************************************************/ + uint16 dptr_attr(int key) { - if (dirptrs[key].valid) - return(dirptrs[key].attr); + dptr_struct *dptr = dptr_get(key, False); + + if (dptr) + return(dptr->attr); return(0); } /**************************************************************************** -close a dptr + Close a dptr (internal func). +****************************************************************************/ + +static void dptr_close_internal(dptr_struct *dptr) +{ + DEBUG(4,("closing dptr key %d\n",dptr->dnum)); + + DLIST_REMOVE(dirptrs, dptr); + + /* + * Free the dnum in the bitmap. Remember the dnum value is always + * biased by one with respect to the bitmap. + */ + + if(bitmap_query( dptr_bmap, dptr->dnum - 1) != True) { + DEBUG(0,("dptr_close_internal : Error - closing dnum = %d and bitmap not set !\n", + dptr->dnum )); + } + + bitmap_clear(dptr_bmap, dptr->dnum - 1); + + if (dptr->ptr) { + CloseDir(dptr->ptr); + dptrs_open--; + } + + /* Lanman 2 specific code */ + SAFE_FREE(dptr->wcard); + string_set(&dptr->path,""); + SAFE_FREE(dptr); +} + +/**************************************************************************** + Close a dptr given a key. ****************************************************************************/ -void dptr_close(int key) + +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--; + dptr_struct *dptr; + + if(*key == INVALID_DPTR_KEY) + return; + + /* OS/2 seems to use -1 to indicate "close all directories" */ + if (*key == -1) { + dptr_struct *next; + for(dptr = dirptrs; dptr; dptr = next) { + next = dptr->next; + dptr_close_internal(dptr); } - /* Lanman 2 specific code */ - if (dirptrs[key].wcard) - free(dirptrs[key].wcard); - dirptrs[key].valid = False; - string_set(&dirptrs[key].path,""); + *key = INVALID_DPTR_KEY; + return; } + + dptr = dptr_get(*key, True); + + if (!dptr) { + DEBUG(0,("Invalid key %d given to dptr_close\n", *key)); + return; + } + + dptr_close_internal(dptr); + + *key = INVALID_DPTR_KEY; } /**************************************************************************** -close all dptrs for a cnum + Close all dptrs for a cnum. ****************************************************************************/ -void dptr_closecnum(int cnum) + +void dptr_closecnum(connection_struct *conn) { - int i; - for (i=0;i<NUMDIRPTRS;i++) - if (dirptrs[i].valid && dirptrs[i].cnum == cnum) - dptr_close(i); + dptr_struct *dptr, *next; + for(dptr = dirptrs; dptr; dptr = next) { + next = dptr->next; + if (dptr->conn == conn) + dptr_close_internal(dptr); + } } /**************************************************************************** -idle all dptrs for a cnum + Idle all dptrs for a cnum. ****************************************************************************/ -void dptr_idlecnum(int cnum) + +void dptr_idlecnum(connection_struct *conn) { - int i; - for (i=0;i<NUMDIRPTRS;i++) - if (dirptrs[i].valid && dirptrs[i].cnum == cnum && dirptrs[i].ptr) - dptr_idle(i); + dptr_struct *dptr; + for(dptr = dirptrs; dptr; dptr = dptr->next) { + if (dptr->conn == conn && dptr->ptr) + dptr_idle(dptr); + } } /**************************************************************************** -close a dptr that matches a given path, only if it matches the pid also + Close a dptr that matches a given path, only if it matches the spid also. ****************************************************************************/ -void dptr_closepath(char *path,int pid) + +void dptr_closepath(char *path,uint16 spid) { - int i; - for (i=0;i<NUMDIRPTRS;i++) - if (dirptrs[i].valid && pid == dirptrs[i].pid && - strequal(dirptrs[i].path,path)) - dptr_close(i); + dptr_struct *dptr, *next; + for(dptr = dirptrs; dptr; dptr = next) { + next = dptr->next; + if (spid == dptr->spid && strequal(dptr->path,path)) + dptr_close_internal(dptr); + } } /**************************************************************************** - start a directory listing + Start a directory listing. ****************************************************************************/ -static BOOL start_dir(int cnum,char *directory) + +static BOOL start_dir(connection_struct *conn,char *directory) { - DEBUG(5,("start_dir cnum=%d dir=%s\n",cnum,directory)); + DEBUG(5,("start_dir dir=%s\n",directory)); - if (!check_name(directory,cnum)) + if (!check_name(directory,conn)) return(False); if (! *directory) directory = "."; - Connections[cnum].dirptr = OpenDir(directory); - if (Connections[cnum].dirptr) { + conn->dirptr = OpenDir(conn, directory, True); + if (conn->dirptr) { dptrs_open++; - string_set(&Connections[cnum].dirpath,directory); + string_set(&conn->dirpath,directory); return(True); } return(False); } +/**************************************************************************** + Try and close the oldest handle not marked for + expect close in the hope that the client has + finished with that one. +****************************************************************************/ + +static void dptr_close_oldest(BOOL old) +{ + dptr_struct *dptr; + + /* + * Go to the end of the list. + */ + for(dptr = dirptrs; dptr && dptr->next; dptr = dptr->next) + ; + + if(!dptr) { + DEBUG(0,("No old dptrs available to close oldest ?\n")); + return; + } + + /* + * If 'old' is true, close the oldest oldhandle dnum (ie. 1 < dnum < 256) that + * does not have expect_close set. If 'old' is false, close + * one of the new dnum handles. + */ + + for(; dptr; dptr = dptr->prev) { + if ((old && (dptr->dnum < 256) && !dptr->expect_close) || + (!old && (dptr->dnum > 255))) { + dptr_close_internal(dptr); + return; + } + } +} /**************************************************************************** -create a new dir ptr + Create a new dir ptr. If the flag old_handle is true then we must allocate + from the bitmap range 0 - 255 as old SMBsearch directory handles are only + one byte long. If old_handle is false we allocate from the range + 256 - MAX_DIRECTORY_HANDLES. We bias the number we return by 1 to ensure + a directory handle is never zero. All the above is folklore taught to + me at Andrew's knee.... :-) :-). JRA. ****************************************************************************/ -int dptr_create(int cnum,char *path, BOOL expect_close,int pid) + +int dptr_create(connection_struct *conn,char *path, BOOL old_handle, BOOL expect_close,uint16 spid) { - int i; - uint32 old; - int oldi; + dptr_struct *dptr; - if (!start_dir(cnum,path)) - return(-1); + if (!start_dir(conn,path)) + return(-2); /* Code to say use a unix error return code. */ - if (dptrs_open >= MAXDIR) + if (dptrs_open >= MAX_OPEN_DIRECTORIES) dptr_idleoldest(); - for (i=0;i<NUMDIRPTRS;i++) - if (!dirptrs[i].valid) - break; - if (i == NUMDIRPTRS) i = -1; + dptr = (dptr_struct *)malloc(sizeof(dptr_struct)); + if(!dptr) { + DEBUG(0,("malloc fail in dptr_create.\n")); + return -1; + } + + ZERO_STRUCTP(dptr); + if(old_handle) { - /* as a 2nd option, grab the oldest not marked for expect_close */ - if (i == -1) { - old=dircounter+1; - oldi= -1; - for (i=0;i<NUMDIRPTRS;i++) - if (!dirptrs[i].expect_close && dirptrs[i].lastused < old) { - old = dirptrs[i].lastused; - oldi = i; + /* + * This is an old-style SMBsearch request. Ensure the + * value we return will fit in the range 1-255. + */ + + dptr->dnum = bitmap_find(dptr_bmap, 0); + + if(dptr->dnum == -1 || dptr->dnum > 254) { + + /* + * Try and close the oldest handle not marked for + * expect close in the hope that the client has + * finished with that one. + */ + + dptr_close_oldest(True); + + /* Now try again... */ + dptr->dnum = bitmap_find(dptr_bmap, 0); + + if(dptr->dnum == -1 || dptr->dnum > 254) { + DEBUG(0,("dptr_create: returned %d: Error - all old dirptrs in use ?\n", dptr->dnum)); + SAFE_FREE(dptr); + return -1; } - i = oldi; - } + } + } else { + + /* + * This is a new-style trans2 request. Allocate from + * a range that will return 256 - MAX_DIRECTORY_HANDLES. + */ + + dptr->dnum = bitmap_find(dptr_bmap, 255); + + if(dptr->dnum == -1 || dptr->dnum < 255) { + + /* + * Try and close the oldest handle close in the hope that + * the client has finished with that one. This will only + * happen in the case of the Win98 client bug where it leaks + * directory handles. + */ + + dptr_close_oldest(False); - /* a 3rd option - grab the oldest one */ - if (i == -1) { - old=dircounter+1; - oldi= -1; - for (i=0;i<NUMDIRPTRS;i++) - if (dirptrs[i].lastused < old) { - old = dirptrs[i].lastused; - oldi = i; + /* Now try again... */ + dptr->dnum = bitmap_find(dptr_bmap, 255); + + if(dptr->dnum == -1 || dptr->dnum < 255) { + DEBUG(0,("dptr_create: returned %d: Error - all new dirptrs in use ?\n", dptr->dnum)); + SAFE_FREE(dptr); + return -1; } - i = oldi; + } } - if (i == -1) { - DEBUG(0,("Error - all dirptrs in use??\n")); - return(-1); - } + bitmap_set(dptr_bmap, dptr->dnum); + + dptr->dnum += 1; /* Always bias the dnum by one - no zero dnums allowed. */ - if (dirptrs[i].valid) - dptr_close(i); + dptr->ptr = conn->dirptr; + string_set(&dptr->path,path); + dptr->conn = conn; + dptr->spid = spid; + dptr->expect_close = expect_close; + dptr->wcard = NULL; /* Only used in lanman2 searches */ + dptr->attr = 0; /* Only used in lanman2 searches */ - dirptrs[i].ptr = Connections[cnum].dirptr; - string_set(&dirptrs[i].path,path); - dirptrs[i].lastused = dircounter++; - dirptrs[i].finished = False; - dirptrs[i].cnum = cnum; - dirptrs[i].pid = pid; - dirptrs[i].expect_close = expect_close; - dirptrs[i].wcard = NULL; /* Only used in lanman2 searches */ - dirptrs[i].attr = 0; /* Only used in lanman2 searches */ - dirptrs[i].valid = True; + DLIST_ADD(dirptrs, dptr); DEBUG(3,("creating new dirptr %d for path %s, expect_close = %d\n", - i,path,expect_close)); + dptr->dnum,path,expect_close)); - return(i); + return(dptr->dnum); } -#define DPTR_MASK ((uint32)(((uint32)1)<<31)) - /**************************************************************************** -fill the 5 byte server reserved dptr field + Fill the 5 byte server reserved dptr field. ****************************************************************************/ + BOOL dptr_fill(char *buf1,unsigned int key) { unsigned char *buf = (unsigned char *)buf1; - void *p = dptr_get(key,0); + void *p = dptr_ptr(key); uint32 offset; if (!p) { DEBUG(1,("filling null dirptr %d\n",key)); return(False); } offset = TellDir(p); - DEBUG(6,("fill on key %d dirptr 0x%x now at %d\n",key,p,offset)); + DEBUG(6,("fill on key %u dirptr 0x%lx now at %d\n",key, + (long)p,(int)offset)); buf[0] = key; SIVAL(buf,1,offset | DPTR_MASK); return(True); } - /**************************************************************************** -return True is the offset is at zero + Fetch the dir ptr and seek it given the 5 byte server field. ****************************************************************************/ -BOOL dptr_zero(char *buf) -{ - return((IVAL(buf,1)&~DPTR_MASK) == 0); -} -/**************************************************************************** -fetch the dir ptr and seek it given the 5 byte server field -****************************************************************************/ void *dptr_fetch(char *buf,int *num) { unsigned int key = *(unsigned char *)buf; - void *p = dptr_get(key,dircounter++); + void *p = dptr_ptr(key); uint32 offset; if (!p) { DEBUG(3,("fetched null dirptr %d\n",key)); @@ -379,101 +532,117 @@ void *dptr_fetch(char *buf,int *num) } /**************************************************************************** -fetch the dir ptr and seek it given the lanman2 parameter block + Fetch the dir ptr. ****************************************************************************/ -void *dptr_fetch_lanman2(char *params,int dptr_num) + +void *dptr_fetch_lanman2(int dptr_num) { - void *p = dptr_get(dptr_num,dircounter++); - uint32 resume_key = SVAL(params,6); - BOOL uses_resume_key = BITSETW(params+10,2); - BOOL continue_bit = BITSETW(params+10,3); + void *p = dptr_ptr(dptr_num); if (!p) { DEBUG(3,("fetched null dirptr %d\n",dptr_num)); return(NULL); } - if(uses_resume_key && !continue_bit) - SeekDir(p,resume_key); DEBUG(3,("fetching dirptr %d for path %s\n",dptr_num,dptr_path(dptr_num))); return(p); } /**************************************************************************** - get a directory entry + Check a filetype for being valid. +****************************************************************************/ + +BOOL dir_check_ftype(connection_struct *conn,int mode,SMB_STRUCT_STAT *st,int dirtype) +{ + if (((mode & ~dirtype) & (aHIDDEN | aSYSTEM | aDIR)) != 0) + return False; + return True; +} + +/**************************************************************************** + Get an 8.3 directory entry. ****************************************************************************/ -BOOL get_dir_entry(int cnum,char *mask,int dirtype,char *fname,int *size,int *mode,time_t *date,BOOL check_descend) + +BOOL get_dir_entry(connection_struct *conn,char *mask,int dirtype,char *fname, + SMB_OFF_T *size,int *mode,time_t *date,BOOL check_descend) { char *dname; BOOL found = False; - struct stat sbuf; + SMB_STRUCT_STAT sbuf; pstring path; pstring pathreal; BOOL isrootdir; pstring filename; - BOOL matched; + BOOL needslash; *path = *pathreal = *filename = 0; - isrootdir = (strequal(Connections[cnum].dirpath,"./") || - strequal(Connections[cnum].dirpath,".") || - strequal(Connections[cnum].dirpath,"/")); + isrootdir = (strequal(conn->dirpath,"./") || + strequal(conn->dirpath,".") || + strequal(conn->dirpath,"/")); - if (!Connections[cnum].dirptr) + needslash = ( conn->dirpath[strlen(conn->dirpath) -1] != '/'); + + if (!conn->dirptr) return(False); - + while (!found) - { - dname = ReadDirName(Connections[cnum].dirptr); + { + dname = ReadDirName(conn->dirptr); - DEBUG(6,("readdir on dirptr 0x%x now at offset %d\n", - Connections[cnum].dirptr,TellDir(Connections[cnum].dirptr))); + DEBUG(6,("readdir on dirptr 0x%lx now at offset %d\n", + (long)conn->dirptr,TellDir(conn->dirptr))); - if (dname == NULL) - return(False); + if (dname == NULL) + return(False); - matched = False; - - strcpy(filename,dname); - - if ((strcmp(filename,mask) == 0) || - (name_map_mangle(filename,True,SNUM(cnum)) && - mask_match(filename,mask,False,False))) - { - if (isrootdir && (strequal(filename,"..") || strequal(filename,"."))) - continue; - - strcpy(fname,filename); - *path = 0; - strcpy(path,Connections[cnum].dirpath); - strcat(path,"/"); - strcpy(pathreal,path); - strcat(path,fname); - strcat(pathreal,dname); - if (sys_stat(pathreal,&sbuf) != 0) - { - DEBUG(5,("Couldn't stat 1 [%s]\n",path)); - continue; - } + pstrcpy(filename,dname); + + /* notice the special *.* handling. This appears to be the only difference + between the wildcard handling in this routine and in the trans2 routines. + see masktest for a demo + */ + if ((strcmp(mask,"*.*") == 0) || + mask_match(filename,mask,False) || + (mangle_map(filename,True,False,SNUM(conn)) && + mask_match(filename,mask,False))) + { + if (isrootdir && (strequal(filename,"..") || strequal(filename,"."))) + continue; + + if (!mangle_is_8_3(filename, False)) { + mangle_map(filename,True,False,SNUM(conn)); + } - if (check_descend && - !strequal(fname,".") && !strequal(fname,"..")) - continue; + pstrcpy(fname,filename); + *path = 0; + pstrcpy(path,conn->dirpath); + if(needslash) + pstrcat(path,"/"); + pstrcpy(pathreal,path); + pstrcat(path,fname); + pstrcat(pathreal,dname); + if (conn->vfs_ops.stat(conn, pathreal, &sbuf) != 0) + { + DEBUG(5,("Couldn't stat 1 [%s]. Error = %s\n",path, strerror(errno) )); + continue; + } - *mode = dos_mode(cnum,pathreal,&sbuf); + *mode = dos_mode(conn,pathreal,&sbuf); - if (((*mode & ~dirtype) & (aHIDDEN | aSYSTEM | aDIR)) != 0) - { - DEBUG(5,("[%s] attribs didn't match %x\n",filename,dirtype)); - continue; - } - *size = sbuf.st_size; - *date = sbuf.st_mtime; + if (!dir_check_ftype(conn,*mode,&sbuf,dirtype)) + { + DEBUG(5,("[%s] attribs didn't match %x\n",filename,dirtype)); + continue; + } + + *size = sbuf.st_size; + *date = sbuf.st_mtime; - DEBUG(5,("get_dir_entry found %s fname=%s\n",pathreal,fname)); + DEBUG(3,("get_dir_entry mask=[%s] found %s fname=%s\n",mask, pathreal,fname)); - found = True; - } + found = True; } + } return(found); } @@ -490,27 +659,109 @@ typedef struct } Dir; + +/******************************************************************* +check to see if a user can read a file. This is only approximate, +it is used as part of the "hide unreadable" option. Don't +use it for anything security sensitive +********************************************************************/ + +static BOOL user_can_read_file(connection_struct *conn, char *name) +{ + extern struct current_user current_user; + SMB_STRUCT_STAT ste; + SEC_DESC *psd = NULL; + size_t sd_size; + files_struct *fsp; + int smb_action; + int access_mode; + NTSTATUS status; + uint32 access_granted; + + ZERO_STRUCT(ste); + + /* if we can't stat it does not show it */ + if (vfs_stat(conn, name, &ste) != 0) + return False; + + /* Pseudo-open the file (note - no fd's created). */ + + if(S_ISDIR(ste.st_mode)) + fsp = open_directory(conn, name, &ste, 0, SET_DENY_MODE(DENY_NONE), (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), + unix_mode(conn,aRONLY|aDIR, name), &smb_action); + else + fsp = open_file_shared1(conn, name, &ste, FILE_READ_ATTRIBUTES, SET_DENY_MODE(DENY_NONE), + (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), 0, 0, &access_mode, &smb_action); + + if (!fsp) + return False; + + /* Get NT ACL -allocated in main loop talloc context. No free needed here. */ + sd_size = conn->vfs_ops.fget_nt_acl(fsp, fsp->fd, &psd); + close_file(fsp, False); + + /* No access if SD get failed. */ + if (!sd_size) + return False; + + return se_access_check(psd, current_user.nt_user_token, FILE_READ_DATA, + &access_granted, &status); +} + /******************************************************************* -open a directory + Open a directory. ********************************************************************/ -void *OpenDir(char *name) + +void *OpenDir(connection_struct *conn, char *name, BOOL use_veto) { Dir *dirp; char *n; - void *p = sys_opendir(name); + DIR *p = conn->vfs_ops.opendir(conn,name); int used=0; if (!p) return(NULL); dirp = (Dir *)malloc(sizeof(Dir)); if (!dirp) { - closedir(p); + DEBUG(0,("Out of memory in OpenDir\n")); + conn->vfs_ops.closedir(conn,p); return(NULL); } dirp->pos = dirp->numentries = dirp->mallocsize = 0; dirp->data = dirp->current = NULL; - while ((n = readdirname(p))) { - int l = strlen(n)+1; + while (True) + { + int l; + + if (used == 0) { + n = "."; + } else if (used == 2) { + n = ".."; + } else { + n = vfs_readdirname(conn, p); + if (n == NULL) + break; + if ((strcmp(".",n) == 0) ||(strcmp("..",n) == 0)) + continue; + } + + l = strlen(n)+1; + + /* If it's a vetoed file, pretend it doesn't even exist */ + if (use_veto && conn && IS_VETO_PATH(conn, n)) continue; + + /* Honour _hide unreadable_ option */ + if (conn && lp_hideunreadable(SNUM(conn))) { + char *entry; + int ret=0; + + if (asprintf(&entry, "%s/%s/%s", conn->origpath, name, n) > 0) { + ret = user_can_read_file(conn, entry); + SAFE_FREE(entry); + } + if (!ret) continue; + } + if (used + l > dirp->mallocsize) { int s = MAX(used+l,used+2000); char *r; @@ -523,30 +774,31 @@ void *OpenDir(char *name) dirp->mallocsize = s; dirp->current = dirp->data; } - strcpy(dirp->data+used,n); + pstrcpy(dirp->data+used,n); used += l; dirp->numentries++; } - closedir(p); + conn->vfs_ops.closedir(conn,p); return((void *)dirp); } /******************************************************************* -close a directory + Close a directory. ********************************************************************/ + void CloseDir(void *p) { - Dir *dirp = (Dir *)p; - if (!dirp) return; - if (dirp->data) free(dirp->data); - free(dirp); + if (!p) return; + SAFE_FREE(((Dir *)p)->data); + SAFE_FREE(p); } /******************************************************************* -read from a directory + Read from a directory. ********************************************************************/ + char *ReadDirName(void *p) { char *ret; @@ -563,8 +815,9 @@ char *ReadDirName(void *p) /******************************************************************* -seek a dir + Seek a dir. ********************************************************************/ + BOOL SeekDir(void *p,int pos) { Dir *dirp = (Dir *)p; @@ -582,8 +835,9 @@ BOOL SeekDir(void *p,int pos) } /******************************************************************* -tell a dir position + Tell a dir position. ********************************************************************/ + int TellDir(void *p) { Dir *dirp = (Dir *)p; @@ -593,363 +847,113 @@ int TellDir(void *p) return(dirp->pos); } +/******************************************************************************* + This section manages a global directory cache. + (It should probably be split into a separate module. crh) +********************************************************************************/ + +typedef struct { + ubi_dlNode node; + char *path; + char *name; + char *dname; + int snum; +} dir_cache_entry; + +static ubi_dlNewList( dir_cache ); + +/***************************************************************************** + Add an entry to the directory cache. + Input: path - + name - + dname - + snum - + Output: None. +*****************************************************************************/ + +void DirCacheAdd( char *path, char *name, char *dname, int snum ) +{ + int pathlen; + int namelen; + dir_cache_entry *entry; + + /* Allocate the structure & string space in one go so that it can be freed + * in one call to free(). + */ + pathlen = strlen( path ) +1; /* Bytes required to store path (with nul). */ + namelen = strlen( name ) +1; /* Bytes required to store name (with nul). */ + entry = (dir_cache_entry *)malloc( sizeof( dir_cache_entry ) + + pathlen + + namelen + + strlen( dname ) +1 ); + if( NULL == entry ) /* Not adding to the cache is not fatal, */ + return; /* so just return as if nothing happened. */ + + /* Set pointers correctly and load values. */ + entry->path = pstrcpy( (char *)&entry[1], path); + entry->name = pstrcpy( &(entry->path[pathlen]), name); + entry->dname = pstrcpy( &(entry->name[namelen]), dname); + entry->snum = snum; + + /* Add the new entry to the linked list. */ + (void)ubi_dlAddHead( dir_cache, entry ); + DEBUG( 4, ("Added dir cache entry %s %s -> %s\n", path, name, dname ) ); + + /* Free excess cache entries. */ + while( DIRCACHESIZE < dir_cache->count ) + safe_free( ubi_dlRemTail( dir_cache ) ); -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++; - } } +/***************************************************************************** + Search for an entry to the directory cache. + Input: path - + name - + snum - + Output: The dname string of the located entry, or NULL if the entry was + not found. -/******************************************************************* -check for an entry in the directory cache -********************************************************************/ -char *DirCacheCheck(char *path,char *name,int snum) + Notes: This uses a linear search, which is is okay because of + the small size of the cache. Use a splay tree or hash + for large caches. +*****************************************************************************/ + +char *DirCacheCheck( char *path, char *name, int snum ) { - struct dir_cache *entry; + dir_cache_entry *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); + for( entry = (dir_cache_entry *)ubi_dlFirst( dir_cache ); + NULL != entry; + entry = (dir_cache_entry *)ubi_dlNext( entry ) ) + { + if( entry->snum == snum + && 0 == strcmp( name, entry->name ) + && 0 == strcmp( path, entry->path ) ) + { + 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 -********************************************************************/ +/***************************************************************************** + Remove all cache entries which have an snum that matches the input. + Input: snum - + Output: None. +*****************************************************************************/ + void DirCacheFlush(int snum) { - struct dir_cache *entry,*next; - - for (entry=dir_cache; entry; entry=next) { - if (entry->snum == snum) { - free(entry->path); - free(entry->dname); - free(entry->name); - next = entry->next; - if (entry->prev) entry->prev->next = entry->next; - if (entry->next) entry->next->prev = entry->prev; - if (dir_cache == entry) dir_cache = entry->next; - free(entry); - } else { - next = entry->next; - } - } -} - - -#ifdef REPLACE_GETWD -/* This is getcwd.c from bash. It is needed in Interactive UNIX. To - * add support for another OS you need to determine which of the - * conditional compilation macros you need to define. All the options - * are defined for Interactive UNIX. - */ -#ifdef ISC -#define HAVE_UNISTD_H -#define USGr3 -#define USG -#endif - -#if defined (HAVE_UNISTD_H) -# include <unistd.h> -#endif - -#if defined (__STDC__) -# define CONST const -# define PTR void * -#else /* !__STDC__ */ -# define CONST -# define PTR char * -#endif /* !__STDC__ */ - -#if !defined (PATH_MAX) -# if defined (MAXPATHLEN) -# define PATH_MAX MAXPATHLEN -# else /* !MAXPATHLEN */ -# define PATH_MAX 1024 -# endif /* !MAXPATHLEN */ -#endif /* !PATH_MAX */ - -#if defined (_POSIX_VERSION) || defined (USGr3) || defined (HAVE_DIRENT_H) -# if !defined (HAVE_DIRENT) -# define HAVE_DIRENT -# endif /* !HAVE_DIRENT */ -#endif /* _POSIX_VERSION || USGr3 || HAVE_DIRENT_H */ - -#if defined (HAVE_DIRENT) -# define D_NAMLEN(d) (strlen ((d)->d_name)) -#else -# define D_NAMLEN(d) ((d)->d_namlen) -#endif /* ! (_POSIX_VERSION || USGr3) */ - -#if defined (USG) || defined (USGr3) -# define d_fileno d_ino -#endif - -#if !defined (alloca) -extern char *alloca (); -#endif /* alloca */ - -/* Get the pathname of the current working directory, - and put it in SIZE bytes of BUF. Returns NULL if the - directory couldn't be determined or SIZE was too small. - If successful, returns BUF. In GNU, if BUF is NULL, - an array is allocated with `malloc'; the array is SIZE - bytes long, unless SIZE <= 0, in which case it is as - big as necessary. */ -#if defined (__STDC__) -char * -getcwd (char *buf, size_t size) -#else /* !__STDC__ */ -char * -getcwd (buf, size) - char *buf; - int size; -#endif /* !__STDC__ */ -{ - static CONST char dots[] - = "../../../../../../../../../../../../../../../../../../../../../../../\ -../../../../../../../../../../../../../../../../../../../../../../../../../../\ -../../../../../../../../../../../../../../../../../../../../../../../../../.."; - CONST char *dotp, *dotlist; - size_t dotsize; - dev_t rootdev, thisdev; - ino_t rootino, thisino; - char path[PATH_MAX + 1]; - register char *pathp; - char *pathbuf; - size_t pathsize; - struct stat st; - - if (buf != NULL && size == 0) - { - errno = EINVAL; - return ((char *)NULL); - } - - pathsize = sizeof (path); - pathp = &path[pathsize]; - *--pathp = '\0'; - pathbuf = path; - - if (stat (".", &st) < 0) - return ((char *)NULL); - thisdev = st.st_dev; - thisino = st.st_ino; - - if (stat ("/", &st) < 0) - return ((char *)NULL); - rootdev = st.st_dev; - rootino = st.st_ino; - - dotsize = sizeof (dots) - 1; - dotp = &dots[sizeof (dots)]; - dotlist = dots; - while (!(thisdev == rootdev && thisino == rootino)) - { - register DIR *dirstream; - register struct dirent *d; - dev_t dotdev; - ino_t dotino; - char mount_point; - int namlen; - - /* Look at the parent directory. */ - if (dotp == dotlist) - { - /* My, what a deep directory tree you have, Grandma. */ - char *new; - if (dotlist == dots) - { - new = malloc (dotsize * 2 + 1); - if (new == NULL) - goto lose; - memcpy (new, dots, dotsize); - } - else - { - new = realloc ((PTR) dotlist, dotsize * 2 + 1); - if (new == NULL) - goto lose; - } - memcpy (&new[dotsize], new, dotsize); - dotp = &new[dotsize]; - dotsize *= 2; - new[dotsize] = '\0'; - dotlist = new; + dir_cache_entry *entry; + ubi_dlNodePtr next; + + for(entry = (dir_cache_entry *)ubi_dlFirst( dir_cache ); + NULL != entry; ) { + next = ubi_dlNext( entry ); + if( entry->snum == snum ) + safe_free( ubi_dlRemThis( dir_cache, entry ) ); + entry = (dir_cache_entry *)next; } - - 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/dosmode.c b/source3/smbd/dosmode.c new file mode 100644 index 0000000000..dcffe3aa90 --- /dev/null +++ b/source3/smbd/dosmode.c @@ -0,0 +1,337 @@ +/* + Unix SMB/CIFS implementation. + dos mode handling functions + Copyright (C) Andrew Tridgell 1992-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + 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" + +/**************************************************************************** + change a dos mode to a unix mode + base permission for files: + if inheriting + apply read/write bits from parent directory. + else + 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 + if !inheriting { + Then apply create mask, + then add force bits. + } + base permission for directories: + dos directory is represented in unix by unix's dir bit and the exec bit + if !inheriting { + Then apply create mask, + then add force bits. + } +****************************************************************************/ +mode_t unix_mode(connection_struct *conn,int dosmode,const char *fname) +{ + mode_t result = (S_IRUSR | S_IRGRP | S_IROTH); + mode_t dir_mode = 0; /* Mode of the parent directory if inheriting. */ + + if ( !IS_DOS_READONLY(dosmode) ) + result |= (S_IWUSR | S_IWGRP | S_IWOTH); + + if (fname && lp_inherit_perms(SNUM(conn))) { + char *dname; + SMB_STRUCT_STAT sbuf; + + dname = parent_dirname(fname); + DEBUG(2,("unix_mode(%s) inheriting from %s\n",fname,dname)); + if (vfs_stat(conn,dname,&sbuf) != 0) { + DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",fname,dname,strerror(errno))); + return(0); /* *** shouldn't happen! *** */ + } + + /* Save for later - but explicitly remove setuid bit for safety. */ + dir_mode = sbuf.st_mode & ~S_ISUID; + DEBUG(2,("unix_mode(%s) inherit mode %o\n",fname,(int)dir_mode)); + /* Clear "result" */ + result = 0; + } + + if (IS_DOS_DIR(dosmode)) { + /* We never make directories read only for the owner as under DOS a user + can always create a file in a read-only directory. */ + result |= (S_IFDIR | S_IWUSR); + + if (dir_mode) { + /* Inherit mode of parent directory. */ + result |= dir_mode; + } else { + /* Provisionally add all 'x' bits */ + result |= (S_IXUSR | S_IXGRP | S_IXOTH); + + /* Apply directory mask */ + result &= lp_dir_mask(SNUM(conn)); + /* Add in force bits */ + result |= lp_force_dir_mode(SNUM(conn)); + } + } else { + if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode)) + result |= S_IXUSR; + + if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode)) + result |= S_IXGRP; + + if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode)) + result |= S_IXOTH; + + if (dir_mode) { + /* Inherit 666 component of parent directory mode */ + result |= dir_mode + & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH); + } else { + /* Apply mode mask */ + result &= lp_create_mask(SNUM(conn)); + /* Add in force bits */ + result |= lp_force_create_mode(SNUM(conn)); + } + } + + DEBUG(3,("unix_mode(%s) returning 0%o\n",fname,(int)result )); + return(result); +} + + +/**************************************************************************** + change a unix mode to a dos mode +****************************************************************************/ +int dos_mode(connection_struct *conn,char *path,SMB_STRUCT_STAT *sbuf) +{ + int result = 0; + + DEBUG(8,("dos_mode: %s\n", path)); + + if ((sbuf->st_mode & S_IWUSR) == 0) + result |= aRONLY; + + if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0)) + result |= aARCH; + + if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0)) + result |= aSYSTEM; + + if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0)) + result |= aHIDDEN; + + if (S_ISDIR(sbuf->st_mode)) + result = aDIR | (result & aRONLY); + +#ifdef S_ISLNK +#if LINKS_READ_ONLY + if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode)) + result |= aRONLY; +#endif +#endif + + /* hide files with a name starting with a . */ + if (lp_hide_dot_files(SNUM(conn))) + { + char *p = strrchr_m(path,'/'); + if (p) + p++; + else + p = path; + + if (p[0] == '.' && p[1] != '.' && p[1] != 0) + result |= aHIDDEN; + } + + /* Optimization : Only call is_hidden_path if it's not already + hidden. */ + if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) + { + result |= aHIDDEN; + } + + DEBUG(8,("dos_mode returning ")); + + if (result & aHIDDEN) DEBUG(8, ("h")); + if (result & aRONLY ) DEBUG(8, ("r")); + if (result & aSYSTEM) DEBUG(8, ("s")); + if (result & aDIR ) DEBUG(8, ("d")); + if (result & aARCH ) DEBUG(8, ("a")); + + DEBUG(8,("\n")); + + return(result); +} + +/******************************************************************* +chmod a file - but preserve some bits +********************************************************************/ +int file_chmod(connection_struct *conn,char *fname,int dosmode,SMB_STRUCT_STAT *st) +{ + SMB_STRUCT_STAT st1; + int mask=0; + mode_t tmp; + mode_t unixmode; + int ret = -1; + + if (!st) { + st = &st1; + if (vfs_stat(conn,fname,st)) + return(-1); + } + + if (S_ISDIR(st->st_mode)) + dosmode |= aDIR; + else + dosmode &= ~aDIR; + + if (dos_mode(conn,fname,st) == dosmode) + return(0); + + unixmode = unix_mode(conn,dosmode,fname); + + /* 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(conn)) + mask |= S_IXUSR; + if (!MAP_SYSTEM(conn)) + mask |= S_IXGRP; + if (!MAP_HIDDEN(conn)) + 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 + whilst adding in the new w bits, if the new mode is not rdonly */ + if (!IS_DOS_READONLY(dosmode)) { + unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)); + } + + if ((ret = vfs_chmod(conn,fname,unixmode)) == 0) + return 0; + + if((errno != EPERM) && (errno != EACCES)) + return -1; + + if(!lp_dos_filemode(SNUM(conn))) + return -1; + + /* We want DOS semantics, ie allow non owner with write permission to change the + bits on a file. Just like file_utime below. + */ + + /* Check if we have write access. */ + if (CAN_WRITE(conn)) { + /* + * We need to open the file with write access whilst + * still in our current user context. This ensures we + * are not violating security in doing the fchmod. + * This file open does *not* break any oplocks we are + * holding. We need to review this.... may need to + * break batch oplocks open by others. JRA. + */ + files_struct *fsp = open_file_fchmod(conn,fname,st); + if (!fsp) + return -1; + become_root(); + ret = conn->vfs_ops.fchmod(fsp, fsp->fd, unixmode); + unbecome_root(); + close_file_fchmod(fsp); + } + + return( ret ); +} + + +/******************************************************************* +Wrapper around dos_utime that possibly allows DOS semantics rather +than POSIX. +*******************************************************************/ +int file_utime(connection_struct *conn, char *fname, struct utimbuf *times) +{ + extern struct current_user current_user; + SMB_STRUCT_STAT sb; + int ret = -1; + + errno = 0; + + if(conn->vfs_ops.utime(conn,fname, times) == 0) + return 0; + + if((errno != EPERM) && (errno != EACCES)) + return -1; + + if(!lp_dos_filetimes(SNUM(conn))) + return -1; + + /* We have permission (given by the Samba admin) to + break POSIX semantics and allow a user to change + the time on a file they don't own but can write to + (as DOS does). + */ + + if(vfs_stat(conn,fname,&sb) != 0) + return -1; + + /* Check if we have write access. */ + if (CAN_WRITE(conn)) { + if (((sb.st_mode & S_IWOTH) || + conn->admin_user || + ((sb.st_mode & S_IWUSR) && current_user.uid==sb.st_uid) || + ((sb.st_mode & S_IWGRP) && + in_group(sb.st_gid,current_user.gid, + current_user.ngroups,current_user.groups)))) { + /* We are allowed to become root and change the filetime. */ + become_root(); + ret = conn->vfs_ops.utime(conn,fname, times); + unbecome_root(); + } + } + + return ret; +} + +/******************************************************************* +Change a filetime - possibly allowing DOS semantics. +*******************************************************************/ +BOOL set_filetime(connection_struct *conn, char *fname, time_t mtime) +{ + struct utimbuf times; + + if (null_mtime(mtime)) return(True); + + times.modtime = times.actime = mtime; + + if (file_utime(conn, fname, ×)) { + DEBUG(4,("set_filetime(%s) failed: %s\n",fname,strerror(errno))); + return False; + } + + return(True); +} diff --git a/source3/smbd/error.c b/source3/smbd/error.c new file mode 100644 index 0000000000..2f993fb41e --- /dev/null +++ b/source3/smbd/error.c @@ -0,0 +1,129 @@ +/* + Unix SMB/CIFS implementation. + error packet handling + Copyright (C) Andrew Tridgell 1992-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + 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" + +/* these can be set by some functions to override the error codes */ +int unix_ERR_class=SMB_SUCCESS; +int unix_ERR_code=0; + +/* From lib/error.c */ +extern struct unix_error_map unix_dos_nt_errmap[]; + +/**************************************************************************** + Create an error packet from a cached error. +****************************************************************************/ + +int cached_error_packet(char *outbuf,files_struct *fsp,int line,const char *file) +{ + write_bmpx_struct *wbmpx = fsp->wbmpx_ptr; + + int32 eclass = wbmpx->wr_errclass; + int32 err = wbmpx->wr_error; + + /* We can now delete the auxiliary struct */ + free((char *)wbmpx); + fsp->wbmpx_ptr = NULL; + return error_packet(outbuf,NT_STATUS_OK,eclass,err,line,file); +} + +/**************************************************************************** + Create an error packet from errno. +****************************************************************************/ + +int unix_error_packet(char *outbuf,int def_class,uint32 def_code, + int line, const char *file) +{ + int eclass=def_class; + int ecode=def_code; + NTSTATUS ntstatus = NT_STATUS_OK; + int i=0; + + if (unix_ERR_class != SMB_SUCCESS) { + eclass = unix_ERR_class; + ecode = unix_ERR_code; + unix_ERR_class = SMB_SUCCESS; + unix_ERR_code = 0; + } else { + while (unix_dos_nt_errmap[i].dos_class != 0) { + if (unix_dos_nt_errmap[i].unix_error == errno) { + eclass = unix_dos_nt_errmap[i].dos_class; + ecode = unix_dos_nt_errmap[i].dos_code; + ntstatus = unix_dos_nt_errmap[i].nt_error; + break; + } + i++; + } + } + + return error_packet(outbuf,ntstatus,eclass,ecode,line,file); +} + + +/**************************************************************************** + Create an error packet. Normally called using the ERROR() macro. +****************************************************************************/ + +int error_packet(char *outbuf,NTSTATUS ntstatus, + uint8 eclass,uint32 ecode,int line, const char *file) +{ + int outsize = set_message(outbuf,0,0,True); + extern uint32 global_client_caps; + + if (errno != 0) + DEBUG(3,("error string = %s\n",strerror(errno))); + + /* + * We can explicitly force 32 bit error codes even when the + * parameter "nt status" is set to no by pre-setting the + * FLAGS2_32_BIT_ERROR_CODES bit in the smb_flg2 outbuf. + * This is to allow work arounds for client bugs that are needed + * when talking with clients that normally expect nt status codes. JRA. + */ + + if ((lp_nt_status_support() || (SVAL(outbuf,smb_flg2) & FLAGS2_32_BIT_ERROR_CODES)) && (global_client_caps & CAP_STATUS32)) { + if (NT_STATUS_V(ntstatus) == 0 && eclass) + ntstatus = dos_to_ntstatus(eclass, ecode); + SIVAL(outbuf,smb_rcls,NT_STATUS_V(ntstatus)); + SSVAL(outbuf,smb_flg2, SVAL(outbuf,smb_flg2)|FLAGS2_32_BIT_ERROR_CODES); + DEBUG(3,("error packet at %s(%d) cmd=%d (%s) %s\n", + file, line, + (int)CVAL(outbuf,smb_com), + smb_fn_name(CVAL(outbuf,smb_com)), + nt_errstr(ntstatus))); + return outsize; + } + + if (eclass == 0 && NT_STATUS_V(ntstatus)) + ntstatus_to_dos(ntstatus, &eclass, &ecode); + + SSVAL(outbuf,smb_flg2, SVAL(outbuf,smb_flg2)&~FLAGS2_32_BIT_ERROR_CODES); + SSVAL(outbuf,smb_rcls,eclass); + SSVAL(outbuf,smb_err,ecode); + + DEBUG(3,("error packet at %s(%d) cmd=%d (%s) eclass=%d ecode=%d\n", + file, line, + (int)CVAL(outbuf,smb_com), + smb_fn_name(CVAL(outbuf,smb_com)), + eclass, + ecode)); + + return outsize; +} diff --git a/source3/smbd/fileio.c b/source3/smbd/fileio.c new file mode 100644 index 0000000000..addbcb0b3c --- /dev/null +++ b/source3/smbd/fileio.c @@ -0,0 +1,659 @@ +/* + Unix SMB/CIFS implementation. + read/write to a files_struct + Copyright (C) Andrew Tridgell 1992-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + 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" + +static BOOL setup_write_cache(files_struct *, SMB_OFF_T); + +/**************************************************************************** +seek a file. Try to avoid the seek if possible +****************************************************************************/ + +SMB_OFF_T seek_file(files_struct *fsp,SMB_OFF_T pos) +{ + SMB_OFF_T offset = 0; + SMB_OFF_T seek_ret; + + if (fsp->print_file && lp_postscript(fsp->conn->service)) + offset = 3; + + seek_ret = fsp->conn->vfs_ops.lseek(fsp,fsp->fd,pos+offset,SEEK_SET); + + if(seek_ret == -1) { + DEBUG(0,("seek_file: sys_lseek failed. Error was %s\n", strerror(errno) )); + fsp->pos = -1; + return -1; + } + + fsp->pos = seek_ret - offset; + + DEBUG(10,("seek_file: requested pos = %.0f, new pos = %.0f\n", + (double)(pos+offset), (double)fsp->pos )); + + return(fsp->pos); +} + +/**************************************************************************** + Read from write cache if we can. +****************************************************************************/ + + +BOOL read_from_write_cache(files_struct *fsp,char *data,SMB_OFF_T pos,size_t n) +{ + write_cache *wcp = fsp->wcp; + + if(!wcp) + return False; + + if(n > wcp->data_size || pos < wcp->offset || pos + n > wcp->offset + wcp->data_size) + return False; + + memcpy(data, wcp->data + (pos - wcp->offset), n); + + DO_PROFILE_INC(writecache_read_hits); + + return True; +} + +/**************************************************************************** +read from a file +****************************************************************************/ + +ssize_t read_file(files_struct *fsp,char *data,SMB_OFF_T pos,size_t n) +{ + ssize_t ret=0,readret; + + /* you can't read from print files */ + if (fsp->print_file) + return -1; + + /* + * Serve from write cache if we can. + */ + + if(read_from_write_cache(fsp, data, pos, n)) + return n; + + flush_write_cache(fsp, READ_FLUSH); + + if (seek_file(fsp,pos) == -1) { + DEBUG(3,("read_file: Failed to seek to %.0f\n",(double)pos)); + return(ret); + } + + if (n > 0) { +#ifdef DMF_FIX + int numretries = 3; +tryagain: + readret = fsp->conn->vfs_ops.read(fsp,fsp->fd,data,n); + if (readret == -1) { + if ((errno == EAGAIN) && numretries) { + DEBUG(3,("read_file EAGAIN retry in 10 seconds\n")); + (void)sleep(10); + --numretries; + goto tryagain; + } + return -1; + } +#else /* NO DMF fix. */ + readret = fsp->conn->vfs_ops.read(fsp,fsp->fd,data,n); + if (readret == -1) + return -1; +#endif + if (readret > 0) + ret += readret; + } + + return(ret); +} + +/* how many write cache buffers have been allocated */ +static unsigned int allocated_write_caches; + +/**************************************************************************** + *Really* write to a file. +****************************************************************************/ + +static ssize_t real_write_file(files_struct *fsp,char *data,SMB_OFF_T pos, size_t n) +{ + if ((pos != -1) && (seek_file(fsp,pos) == -1)) + return -1; + + return vfs_write_data(fsp,data,n); +} + +/**************************************************************************** +write to a file +****************************************************************************/ + +ssize_t write_file(files_struct *fsp, char *data, SMB_OFF_T pos, size_t n) +{ + write_cache *wcp = fsp->wcp; + ssize_t total_written = 0; + int write_path = -1; + + if (fsp->print_file) { + return print_job_write(fsp->print_jobid, data, n); + } + + if (!fsp->can_write) { + errno = EPERM; + return(0); + } + + if (!fsp->modified) { + SMB_STRUCT_STAT st; + fsp->modified = True; + + if (fsp->conn->vfs_ops.fstat(fsp,fsp->fd,&st) == 0) { + int dosmode = dos_mode(fsp->conn,fsp->fsp_name,&st); + fsp->size = st.st_size; + if (MAP_ARCHIVE(fsp->conn) && !IS_DOS_ARCHIVE(dosmode)) { + file_chmod(fsp->conn,fsp->fsp_name,dosmode | aARCH,&st); + } + + /* + * If this is the first write and we have an exclusive oplock then setup + * the write cache. + */ + + if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && !wcp) { + setup_write_cache(fsp, st.st_size); + wcp = fsp->wcp; + } + } + } + +#ifdef WITH_PROFILE + DO_PROFILE_INC(writecache_total_writes); + if (!fsp->oplock_type) { + DO_PROFILE_INC(writecache_non_oplock_writes); + } +#endif + + /* + * If this file is level II oplocked then we need + * to grab the shared memory lock and inform all + * other files with a level II lock that they need + * to flush their read caches. We keep the lock over + * the shared memory area whilst doing this. + */ + + release_level_2_oplocks_on_change(fsp); + +#ifdef WITH_PROFILE + if (profile_p && profile_p->writecache_total_writes % 500 == 0) { + DEBUG(3,("WRITECACHE: initwrites=%u abutted=%u total=%u \ +nonop=%u allocated=%u active=%u direct=%u perfect=%u readhits=%u\n", + profile_p->writecache_init_writes, + profile_p->writecache_abutted_writes, + profile_p->writecache_total_writes, + profile_p->writecache_non_oplock_writes, + profile_p->writecache_allocated_write_caches, + profile_p->writecache_num_write_caches, + profile_p->writecache_direct_writes, + profile_p->writecache_num_perfect_writes, + profile_p->writecache_read_hits )); + + DEBUG(3,("WRITECACHE: Flushes SEEK=%d, READ=%d, WRITE=%d, READRAW=%d, OPLOCK=%d, CLOSE=%d, SYNC=%d\n", + profile_p->writecache_flushed_writes[SEEK_FLUSH], + profile_p->writecache_flushed_writes[READ_FLUSH], + profile_p->writecache_flushed_writes[WRITE_FLUSH], + profile_p->writecache_flushed_writes[READRAW_FLUSH], + profile_p->writecache_flushed_writes[OPLOCK_RELEASE_FLUSH], + profile_p->writecache_flushed_writes[CLOSE_FLUSH], + profile_p->writecache_flushed_writes[SYNC_FLUSH] )); + } +#endif + + if(!wcp) { + DO_PROFILE_INC(writecache_direct_writes); + total_written = real_write_file(fsp, data, pos, n); + if ((total_written != -1) && (pos + total_written > fsp->size)) + fsp->size = pos + total_written; + return total_written; + } + + DEBUG(9,("write_file(fd=%d pos=%.0f size=%u) wcp->offset=%.0f wcp->data_size=%u\n", + fsp->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigned int)wcp->data_size)); + + /* + * If we have active cache and it isn't contiguous then we flush. + * NOTE: There is a small problem with running out of disk .... + */ + + if (wcp->data_size) { + + BOOL cache_flush_needed = False; + + if ((pos >= wcp->offset) && (pos <= wcp->offset + wcp->data_size)) { + + /* + * Start of write overlaps or abutts the existing data. + */ + + size_t data_used = MIN((wcp->alloc_size - (pos - wcp->offset)), n); + + memcpy(wcp->data + (pos - wcp->offset), data, data_used); + + /* + * Update the current buffer size with the new data. + */ + + if(pos + data_used > wcp->offset + wcp->data_size) + wcp->data_size = pos + data_used - wcp->offset; + + /* + * Update the file size if changed. + */ + + if (wcp->offset + wcp->data_size > wcp->file_size) + fsp->size = wcp->file_size = wcp->offset + wcp->data_size; + + /* + * If we used all the data then + * return here. + */ + + if(n == data_used) + return n; + else + cache_flush_needed = True; + + /* + * Move the start of data forward by the amount used, + * cut down the amount left by the same amount. + */ + + data += data_used; + pos += data_used; + n -= data_used; + + DO_PROFILE_INC(writecache_abutted_writes); + total_written = data_used; + + write_path = 1; + + } else if ((pos < wcp->offset) && (pos + n > wcp->offset) && + (pos + n <= wcp->offset + wcp->alloc_size)) { + + /* + * End of write overlaps the existing data. + */ + + size_t data_used = pos + n - wcp->offset; + + memcpy(wcp->data, data + n - data_used, data_used); + + /* + * Update the current buffer size with the new data. + */ + + if(pos + n > wcp->offset + wcp->data_size) + wcp->data_size = pos + n - wcp->offset; + + /* + * Update the file size if changed. + */ + + if (wcp->offset + wcp->data_size > wcp->file_size) + fsp->size = wcp->file_size = wcp->offset + wcp->data_size; + + /* + * We don't need to move the start of data, but we + * cut down the amount left by the amount used. + */ + + n -= data_used; + + /* + * We cannot have used all the data here. + */ + + cache_flush_needed = True; + + DO_PROFILE_INC(writecache_abutted_writes); + total_written = data_used; + + write_path = 2; + + } else if ( (pos >= wcp->file_size) && + (wcp->offset + wcp->data_size == wcp->file_size) && + (pos > wcp->offset + wcp->data_size) && + (pos < wcp->offset + wcp->alloc_size) ) { + + /* + * Non-contiguous write part of which fits within + * the cache buffer and is extending the file + * and the cache contents reflect the current + * data up to the current end of the file. + */ + + size_t data_used; + + if(pos + n <= wcp->offset + wcp->alloc_size) + data_used = n; + else + data_used = wcp->offset + wcp->alloc_size - pos; + + /* + * Fill in the non-continuous area with zeros. + */ + + memset(wcp->data + wcp->data_size, '\0', + pos - (wcp->offset + wcp->data_size) ); + + memcpy(wcp->data + (pos - wcp->offset), data, data_used); + + /* + * Update the current buffer size with the new data. + */ + + if(pos + data_used > wcp->offset + wcp->data_size) + wcp->data_size = pos + data_used - wcp->offset; + + /* + * Update the file size if changed. + */ + + if (wcp->offset + wcp->data_size > wcp->file_size) + fsp->size = wcp->file_size = wcp->offset + wcp->data_size; + + /* + * If we used all the data then + * return here. + */ + + if(n == data_used) + return n; + else + cache_flush_needed = True; + + /* + * Move the start of data forward by the amount used, + * cut down the amount left by the same amount. + */ + + data += data_used; + pos += data_used; + n -= data_used; + + DO_PROFILE_INC(writecache_abutted_writes); + total_written = data_used; + + write_path = 3; + + } else { + + /* + * Write is bigger than buffer, or there is no overlap on the + * low or high ends. + */ + + DEBUG(9,("write_file: non cacheable write : fd = %d, pos = %.0f, len = %u, current cache pos = %.0f \ +len = %u\n",fsp->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigned int)wcp->data_size )); + + /* + * Update the file size if needed. + */ + + if(pos + n > wcp->file_size) + fsp->size = wcp->file_size = pos + n; + + /* + * If write would fit in the cache, and is larger than + * the data already in the cache, flush the cache and + * preferentially copy the data new data into it. Otherwise + * just write the data directly. + */ + + if ( n <= wcp->alloc_size && n > wcp->data_size) { + cache_flush_needed = True; + } else { + ssize_t ret = real_write_file(fsp, data, pos, n); + + DO_PROFILE_INC(writecache_direct_writes); + if (ret == -1) + return ret; + + if (pos + ret > wcp->file_size) + fsp->size = wcp->file_size = pos + ret; + + return ret; + } + + write_path = 4; + + } + + if(wcp->data_size > wcp->file_size) + fsp->size = wcp->file_size = wcp->data_size; + + if (cache_flush_needed) { + DEBUG(3,("WRITE_FLUSH:%d: due to noncontinuous write: fd = %d, size = %.0f, pos = %.0f, \ +n = %u, wcp->offset=%.0f, wcp->data_size=%u\n", + write_path, fsp->fd, (double)wcp->file_size, (double)pos, (unsigned int)n, + (double)wcp->offset, (unsigned int)wcp->data_size )); + + flush_write_cache(fsp, WRITE_FLUSH); + } + } + + /* + * If the write request is bigger than the cache + * size, write it all out. + */ + + if (n > wcp->alloc_size ) { + ssize_t ret = real_write_file(fsp, data, pos, n); + if (ret == -1) + return -1; + + if (pos + ret > wcp->file_size) + fsp->size = wcp->file_size = pos + n; + + DO_PROFILE_INC(writecache_direct_writes); + return total_written + n; + } + + /* + * If there's any data left, cache it. + */ + + if (n) { +#ifdef WITH_PROFILE + if (wcp->data_size) { + DO_PROFILE_INC(writecache_abutted_writes); + } else { + DO_PROFILE_INC(writecache_init_writes); + } +#endif + memcpy(wcp->data+wcp->data_size, data, n); + if (wcp->data_size == 0) { + wcp->offset = pos; + DO_PROFILE_INC(writecache_num_write_caches); + } + wcp->data_size += n; + + /* + * Update the file size if changed. + */ + + if (wcp->offset + wcp->data_size > wcp->file_size) + fsp->size = wcp->file_size = wcp->offset + wcp->data_size; + DEBUG(9,("wcp->offset = %.0f wcp->data_size = %u cache return %u\n", + (double)wcp->offset, (unsigned int)wcp->data_size, (unsigned int)n)); + + total_written += n; + return total_written; /* .... that's a write :) */ + } + + return total_written; +} + +/**************************************************************************** + Delete the write cache structure. +****************************************************************************/ + +void delete_write_cache(files_struct *fsp) +{ + write_cache *wcp; + + if(!fsp) + return; + + if(!(wcp = fsp->wcp)) + return; + + DO_PROFILE_DEC(writecache_allocated_write_caches); + allocated_write_caches--; + + SMB_ASSERT(wcp->data_size == 0); + + SAFE_FREE(wcp->data); + SAFE_FREE(fsp->wcp); + + DEBUG(10,("delete_write_cache: File %s deleted write cache\n", fsp->fsp_name )); + +} + +/**************************************************************************** + Setup the write cache structure. +****************************************************************************/ + +static BOOL setup_write_cache(files_struct *fsp, SMB_OFF_T file_size) +{ + ssize_t alloc_size = lp_write_cache_size(SNUM(fsp->conn)); + write_cache *wcp; + + if (allocated_write_caches >= MAX_WRITE_CACHES) + return False; + + if(alloc_size == 0 || fsp->wcp) + return False; + + if((wcp = (write_cache *)malloc(sizeof(write_cache))) == NULL) { + DEBUG(0,("setup_write_cache: malloc fail.\n")); + return False; + } + + wcp->file_size = file_size; + wcp->offset = 0; + wcp->alloc_size = alloc_size; + wcp->data_size = 0; + if((wcp->data = malloc(wcp->alloc_size)) == NULL) { + DEBUG(0,("setup_write_cache: malloc fail for buffer size %u.\n", + (unsigned int)wcp->alloc_size )); + SAFE_FREE(wcp); + return False; + } + + memset(wcp->data, '\0', wcp->alloc_size ); + + fsp->wcp = wcp; + DO_PROFILE_INC(writecache_allocated_write_caches); + allocated_write_caches++; + + DEBUG(10,("setup_write_cache: File %s allocated write cache size %u\n", + fsp->fsp_name, wcp->alloc_size )); + + return True; +} + +/**************************************************************************** + Cope with a size change. +****************************************************************************/ + +void set_filelen_write_cache(files_struct *fsp, SMB_OFF_T file_size) +{ + fsp->size = file_size; + if(fsp->wcp) { + /* The cache *must* have been flushed before we do this. */ + if (fsp->wcp->data_size != 0) { + pstring msg; + slprintf(msg, sizeof(msg)-1, "set_filelen_write_cache: size change \ +on file %s with write cache size = %u\n", fsp->fsp_name, fsp->wcp->data_size ); + smb_panic(msg); + } + fsp->wcp->file_size = file_size; + } +} + +/******************************************************************* + Flush a write cache struct to disk. +********************************************************************/ + +ssize_t flush_write_cache(files_struct *fsp, enum flush_reason_enum reason) +{ + write_cache *wcp = fsp->wcp; + size_t data_size; + ssize_t ret; + + if(!wcp || !wcp->data_size) + return 0; + + data_size = wcp->data_size; + wcp->data_size = 0; + + DO_PROFILE_DEC_INC(writecache_num_write_caches,writecache_flushed_writes[reason]); + + DEBUG(9,("flushing write cache: fd = %d, off=%.0f, size=%u\n", + fsp->fd, (double)wcp->offset, (unsigned int)data_size)); + +#ifdef WITH_PROFILE + if(data_size == wcp->alloc_size) + DO_PROFILE_INC(writecache_num_perfect_writes); +#endif + + ret = real_write_file(fsp, wcp->data, wcp->offset, data_size); + + /* + * Ensure file size if kept up to date if write extends file. + */ + + if ((ret != -1) && (wcp->offset + ret > wcp->file_size)) + wcp->file_size = wcp->offset + ret; + + return ret; +} + +/******************************************************************* +sync a file +********************************************************************/ + +void sync_file(connection_struct *conn, files_struct *fsp) +{ + if(lp_strict_sync(SNUM(conn)) && fsp->fd != -1) { + flush_write_cache(fsp, SYNC_FLUSH); + conn->vfs_ops.fsync(fsp,fsp->fd); + } +} + +/************************************************************ + Perform a stat whether a valid fd or not. +************************************************************/ + +int fsp_stat(files_struct *fsp, SMB_STRUCT_STAT *pst) +{ + if (fsp->fd == -1) + return vfs_stat(fsp->conn, fsp->fsp_name, pst); + else + return vfs_fstat(fsp,fsp->fd, pst); +} diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c new file mode 100644 index 0000000000..cb6a6d31a4 --- /dev/null +++ b/source3/smbd/filename.c @@ -0,0 +1,503 @@ +/* + Unix SMB/CIFS implementation. + filename handling routines + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Jeremy Allison 1999-200 + Copyright (C) Ying Chen 2000 + + 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. +*/ + +/* + * New hash table stat cache code added by Ying Chen. + */ + +#include "includes.h" + +extern BOOL case_sensitive; +extern BOOL case_preserve; +extern BOOL short_case_preserve; +extern fstring remote_machine; +extern BOOL use_mangled_map; + +static BOOL scan_directory(char *path, char *name,connection_struct *conn,BOOL docache); + +/**************************************************************************** + 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, int snum) +{ + pstring tmpname; + if (mangle_is_8_3(name2, True)) { + return False; + } + + pstrcpy(tmpname, name2); + return mangle_map(tmpname, True, False, snum) && + strequal(name1, tmpname); +} + + +/**************************************************************************** +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 + +If the saved_last_component != 0, then the unmodified last component +of the pathname is returned there. This is used in an exceptional +case in reply_mv (so far). If saved_last_component == 0 then nothing +is returned there. + +The bad_path arg is set to True if the filename walk failed. This is +used to pick the correct error code to return between ENOENT and ENOTDIR +as Windows applications depend on ERRbadpath being returned if a component +of a pathname does not exist. + +On exit from unix_convert, if *pst was not null, then the file stat +struct will be returned if the file exists and was found, if not this +stat struct will be filled with zeros (and this can be detected by checking +for nlinks = 0, which can never be true for any file). +****************************************************************************/ + +BOOL unix_convert(pstring name,connection_struct *conn,char *saved_last_component, + BOOL *bad_path, SMB_STRUCT_STAT *pst) +{ + SMB_STRUCT_STAT st; + char *start, *end; + pstring dirpath; + pstring orig_path; + BOOL component_was_mangled = False; + BOOL name_has_wildcard = False; + + ZERO_STRUCTP(pst); + + *dirpath = 0; + *bad_path = False; + if(saved_last_component) + *saved_last_component = 0; + + if (conn->printer) { + /* we don't ever use the filenames on a printer share as a + filename - so don't convert them */ + return True; + } + + DEBUG(5, ("unix_convert called on file \"%s\"\n", name)); + + /* + * Convert to basic unix format - removing \ chars and cleaning it up. + */ + + unix_format(name); + unix_clean_name(name); + + /* + * Names must be relative to the root of the service - trim any leading /. + * also trim trailing /'s. + */ + + trim_string(name,"/","/"); + + /* + * If we trimmed down to a single '\0' character + * then we should use the "." directory to avoid + * searching the cache, but not if we are in a + * printing share. + */ + + if (!*name) { + name[0] = '.'; + name[1] = '\0'; + } + + /* + * Ensure saved_last_component is valid even if file exists. + */ + + if(saved_last_component) { + end = strrchr_m(name, '/'); + if(end) + pstrcpy(saved_last_component, end + 1); + else + pstrcpy(saved_last_component, name); + } + + if (!case_sensitive && + (!case_preserve || (mangle_is_8_3(name, False) && !short_case_preserve))) + strnorm(name); + + /* + * If we trimmed down to a single '\0' character + * then we will be using the "." directory. + * As we know this is valid we can return true here. + */ + + if(!*name) + return(True); + + start = name; + while (strncmp(start,"./",2) == 0) + start += 2; + + pstrcpy(orig_path, name); + + if(stat_cache_lookup(conn, name, dirpath, &start, &st)) { + *pst = st; + return True; + } + + /* + * stat the name - if it exists then we are all done! + */ + +/* ZZZ: stat1 */ if (vfs_stat(conn,name,&st) == 0) { + stat_cache_add(orig_path, name); + DEBUG(5,("conversion finished %s -> %s\n",orig_path, name)); + *pst = st; + return(True); + } + + DEBUG(5,("unix_convert begin: name = %s, dirpath = %s, start = %s\n", + name, dirpath, start)); + + /* + * A special case - if we don't have any mangling chars and are case + * sensitive then searching won't help. + */ + + if (case_sensitive && !mangle_is_mangled(name) && + !lp_strip_dot() && !use_mangled_map) + return(False); + + name_has_wildcard = ms_has_wild(start); + + /* + * is_mangled() was changed to look at an entire pathname, not + * just a component. JRA. + */ + + if (mangle_is_mangled(start)) + component_was_mangled = True; + + /* + * Now we need to recursively match the name against the real + * directory structure. + */ + + /* + * 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_m(start, '/'); + + /* + * Chop the name at this point. + */ + if (end) + *end = 0; + + if(saved_last_component != 0) + pstrcpy(saved_last_component, end ? end + 1 : start); + + /* + * Check if the name exists up to this point. + */ + + if (vfs_stat(conn,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; + + /* Stat failed - ensure we don't use it. */ + ZERO_STRUCT(st); + *rest = 0; + + /* + * Remember the rest of the pathname so it can be restored + * later. + */ + + if (end) + pstrcpy(rest,end+1); + + /* + * Try to find this part of the path in the directory. + */ + + if (ms_has_wild(start) || !scan_directory(dirpath, start, conn, end?True:False)) { + if (end) { + /* + * An intermediate part of the name can't be found. + */ + DEBUG(5,("Intermediate not found %s\n",start)); + *end = '/'; + + /* + * We need to return the fact that the intermediate + * name resolution failed. This is used to return an + * error of ERRbadpath rather than ERRbadfile. Some + * Windows applications depend on the difference between + * these two errors. + */ + *bad_path = True; + 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 (mangle_is_mangled(start)) { + mangle_check_cache( start ); + } + + DEBUG(5,("New file %s\n",start)); + return(True); + } + + /* + * Restore the rest of the string. If the string was mangled the size + * may have changed. + */ + if (end) { + end = start + strlen(start); + pstrcat(start,"/"); + pstrcat(start,rest); + *end = '\0'; + } + } /* end else */ + + /* + * Add to the dirpath that we have resolved so far. + */ + if (*dirpath) + pstrcat(dirpath,"/"); + + pstrcat(dirpath,start); + + /* + * Don't cache a name with mangled or wildcard components + * as this can change the size. + */ + + if(!component_was_mangled && !name_has_wildcard) + stat_cache_add(orig_path, dirpath); + + /* + * Restore the / that we wiped out earlier. + */ + if (end) + *end = '/'; + } + + /* + * Don't cache a name with mangled or wildcard components + * as this can change the size. + */ + + if(!component_was_mangled && !name_has_wildcard) + stat_cache_add(orig_path, name); + + /* + * If we ended up resolving the entire path then return a valid + * stat struct if we got one. + */ + + if (VALID_STAT(st) && (strlen(orig_path) == strlen(name))) + *pst = st; + + /* + * The name has been resolved. + */ + + DEBUG(5,("conversion finished %s -> %s\n",orig_path, name)); + return(True); +} + + +/**************************************************************************** +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,connection_struct *conn) +{ + BOOL ret; + + errno = 0; + + if (IS_VETO_PATH(conn, name)) { + DEBUG(5,("file path name %s vetoed\n",name)); + return(0); + } + + ret = reduce_name(conn,name,conn->connectpath,lp_widelinks(SNUM(conn))); + + /* Check if we are allowing users to follow symlinks */ + /* Patch from David Clerc <David.Clerc@cui.unige.ch> + University of Geneva */ + +#ifdef S_ISLNK + if (!lp_symlinks(SNUM(conn))) { + SMB_STRUCT_STAT statbuf; + if ( (conn->vfs_ops.lstat(conn,name,&statbuf) != -1) && + (S_ISLNK(statbuf.st_mode)) ) { + DEBUG(3,("check_name: denied: file path name %s is a symlink\n",name)); + ret=0; + } + } +#endif + + if (!ret) + DEBUG(5,("check_name on %s failed\n",name)); + + return(ret); +} + + +/**************************************************************************** +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,connection_struct *conn,BOOL docache) +{ + void *cur_dir; + char *dname; + BOOL mangled; + pstring name2; + + mangled = mangle_is_mangled(name); + + /* handle null paths */ + if (*path == 0) + path = "."; + + if (docache && (dname = DirCacheCheck(path,name,SNUM(conn)))) { + pstrcpy(name, dname); + return(True); + } + + /* + * The incoming name can be mangled, and if we de-mangle it + * here it will not compare correctly against the filename (name2) + * read from the directory and then mangled by the mangle_map() + * call. We need to mangle both names or neither. + * (JRA). + */ + if (mangled) + mangled = !mangle_check_cache( name ); + + /* open the directory */ + if (!(cur_dir = OpenDir(conn, path, True))) { + 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; + + pstrcpy(name2,dname); + if (!mangle_map(name2,False,True,SNUM(conn))) + continue; + + if ((mangled && mangled_equal(name,name2,SNUM(conn))) || fname_equal(name, dname)) { + /* we've found the file, change it's name and return */ + if (docache) + DirCacheAdd(path,name,dname,SNUM(conn)); + pstrcpy(name, dname); + CloseDir(cur_dir); + return(True); + } + } + + CloseDir(cur_dir); + return(False); +} diff --git a/source3/smbd/files.c b/source3/smbd/files.c new file mode 100644 index 0000000000..c055fb54eb --- /dev/null +++ b/source3/smbd/files.c @@ -0,0 +1,400 @@ +/* + Unix SMB/CIFS implementation. + Files[] structure handling + Copyright (C) Andrew Tridgell 1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + 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" + +static int real_max_open_files; + +#define VALID_FNUM(fnum) (((fnum) >= 0) && ((fnum) < real_max_open_files)) + +#define FILE_HANDLE_OFFSET 0x1000 + +static struct bitmap *file_bmap; + +static files_struct *Files; + +/* a fsp to use when chaining */ +static files_struct *chain_fsp = NULL; +/* a fsp to use to save when breaking an oplock. */ +static files_struct *oplock_save_chain_fsp = NULL; + +static int files_used; + +/**************************************************************************** + Return a unique number identifying this fsp over the life of this pid. +****************************************************************************/ + +static unsigned long get_gen_count(void) +{ + static unsigned long file_gen_counter; + + if ((++file_gen_counter) == 0) + return ++file_gen_counter; + return file_gen_counter; +} + +/**************************************************************************** + Find first available file slot. +****************************************************************************/ + +files_struct *file_new(connection_struct *conn) +{ + int i; + static int first_file; + files_struct *fsp, *next; + + /* we want to give out file handles differently on each new + connection because of a common bug in MS clients where they try to + reuse a file descriptor from an earlier smb connection. This code + increases the chance that the errant client will get an error rather + than causing corruption */ + if (first_file == 0) { + first_file = (sys_getpid() ^ (int)time(NULL)) % real_max_open_files; + } + + i = bitmap_find(file_bmap, first_file); + if (i == -1) { + /* + * Before we give up, go through the open files + * and see if there are any files opened with a + * batch oplock. If so break the oplock and then + * re-use that entry (if it becomes closed). + * This may help as NT/95 clients tend to keep + * files batch oplocked for quite a long time + * after they have finished with them. + */ + for (fsp=Files;fsp;fsp=next) { + next=fsp->next; + if (attempt_close_oplocked_file(fsp)) { + return file_new(conn); + } + } + + DEBUG(0,("ERROR! Out of file structures\n")); + unix_ERR_class = ERRSRV; + unix_ERR_code = ERRnofids; + return NULL; + } + + fsp = (files_struct *)malloc(sizeof(*fsp)); + if (!fsp) { + unix_ERR_class = ERRSRV; + unix_ERR_code = ERRnofids; + return NULL; + } + + ZERO_STRUCTP(fsp); + fsp->fd = -1; + fsp->conn = conn; + fsp->file_id = get_gen_count(); + GetTimeOfDay(&fsp->open_time); + + first_file = (i+1) % real_max_open_files; + + bitmap_set(file_bmap, i); + files_used++; + + fsp->fnum = i + FILE_HANDLE_OFFSET; + string_set(&fsp->fsp_name,""); + + DLIST_ADD(Files, fsp); + + DEBUG(5,("allocated file structure %d, fnum = %d (%d used)\n", + i, fsp->fnum, files_used)); + + chain_fsp = fsp; + + return fsp; +} + +/**************************************************************************** + Close all open files for a connection. +****************************************************************************/ + +void file_close_conn(connection_struct *conn) +{ + files_struct *fsp, *next; + + for (fsp=Files;fsp;fsp=next) { + next = fsp->next; + if (fsp->conn == conn) { + close_file(fsp,False); + } + } +} + +/**************************************************************************** + Initialise file structures. +****************************************************************************/ + +#define MAX_OPEN_FUDGEFACTOR 10 + +void file_init(void) +{ + int request_max_open_files = lp_max_open_files(); + int real_lim; + + /* + * Set the max_open files to be the requested + * max plus a fudgefactor to allow for the extra + * fd's we need such as log files etc... + */ + real_lim = set_maxfiles(request_max_open_files + MAX_OPEN_FUDGEFACTOR); + + real_max_open_files = real_lim - MAX_OPEN_FUDGEFACTOR; + + if(real_max_open_files != request_max_open_files) { + DEBUG(1,("file_init: Information only: requested %d \ +open files, %d are available.\n", request_max_open_files, real_max_open_files)); + } + + file_bmap = bitmap_allocate(real_max_open_files); + + if (!file_bmap) { + exit_server("out of memory in file_init"); + } + + /* + * Ensure that pipe_handle_oppset is set correctly. + */ + set_pipe_handle_offset(real_max_open_files); +} + +/**************************************************************************** + Close files open by a specified vuid. +****************************************************************************/ + +void file_close_user(int vuid) +{ + files_struct *fsp, *next; + + for (fsp=Files;fsp;fsp=next) { + next=fsp->next; + if (fsp->vuid == vuid) { + close_file(fsp,False); + } + } +} + +/**************************************************************************** + Find a fsp given a file descriptor. +****************************************************************************/ + +files_struct *file_find_fd(int fd) +{ + int count=0; + files_struct *fsp; + + for (fsp=Files;fsp;fsp=fsp->next,count++) { + if (fsp->fd == fd) { + if (count > 10) { + DLIST_PROMOTE(Files, fsp); + } + return fsp; + } + } + + return NULL; +} + +/**************************************************************************** + Find a fsp given a device, inode and file_id. +****************************************************************************/ + +files_struct *file_find_dif(SMB_DEV_T dev, SMB_INO_T inode, unsigned long file_id) +{ + int count=0; + files_struct *fsp; + + for (fsp=Files;fsp;fsp=fsp->next,count++) { + if (fsp->fd != -1 && + fsp->dev == dev && + fsp->inode == inode && + fsp->file_id == file_id ) { + if (count > 10) { + DLIST_PROMOTE(Files, fsp); + } + return fsp; + } + } + + return NULL; +} + +/**************************************************************************** + Check if an fsp still exists. +****************************************************************************/ + +files_struct *file_find_fsp(files_struct *orig_fsp) +{ + files_struct *fsp; + + for (fsp=Files;fsp;fsp=fsp->next) { + if (fsp == orig_fsp) + return fsp; + } + + return NULL; +} + +/**************************************************************************** + Find the first fsp given a device and inode. +****************************************************************************/ + +files_struct *file_find_di_first(SMB_DEV_T dev, SMB_INO_T inode) +{ + files_struct *fsp; + + for (fsp=Files;fsp;fsp=fsp->next) { + if ( fsp->fd != -1 && + fsp->dev == dev && + fsp->inode == inode ) + return fsp; + } + + return NULL; +} + +/**************************************************************************** + Find the next fsp having the same device and inode. +****************************************************************************/ + +files_struct *file_find_di_next(files_struct *start_fsp) +{ + files_struct *fsp; + + for (fsp = start_fsp->next;fsp;fsp=fsp->next) { + if ( fsp->fd != -1 && + fsp->dev == start_fsp->dev && + fsp->inode == start_fsp->inode ) + return fsp; + } + + return NULL; +} + +/**************************************************************************** + Find a fsp that is open for printing. +****************************************************************************/ + +files_struct *file_find_print(void) +{ + files_struct *fsp; + + for (fsp=Files;fsp;fsp=fsp->next) { + if (fsp->print_file) return fsp; + } + + return NULL; +} + +/**************************************************************************** + Sync open files on a connection. +****************************************************************************/ + +void file_sync_all(connection_struct *conn) +{ + files_struct *fsp, *next; + + for (fsp=Files;fsp;fsp=next) { + next=fsp->next; + if ((conn == fsp->conn) && (fsp->fd != -1)) { + sync_file(conn,fsp); + } + } +} + +/**************************************************************************** + Free up a fsp. +****************************************************************************/ + +void file_free(files_struct *fsp) +{ + DLIST_REMOVE(Files, fsp); + + string_free(&fsp->fsp_name); + + bitmap_clear(file_bmap, fsp->fnum - FILE_HANDLE_OFFSET); + files_used--; + + DEBUG(5,("freed files structure %d (%d used)\n", + fsp->fnum, files_used)); + + /* this is paranoia, just in case someone tries to reuse the + information */ + ZERO_STRUCTP(fsp); + + if (fsp == chain_fsp) chain_fsp = NULL; + + SAFE_FREE(fsp); +} + +/**************************************************************************** + Get a fsp from a packet given the offset of a 16 bit fnum. +****************************************************************************/ + +files_struct *file_fsp(char *buf, int where) +{ + int fnum, count=0; + files_struct *fsp; + + if (chain_fsp) + return chain_fsp; + + fnum = SVAL(buf, where); + + for (fsp=Files;fsp;fsp=fsp->next, count++) { + if (fsp->fnum == fnum) { + chain_fsp = fsp; + if (count > 10) { + DLIST_PROMOTE(Files, fsp); + } + return fsp; + } + } + return NULL; +} + +/**************************************************************************** + Reset the chained fsp - done at the start of a packet reply. +****************************************************************************/ + +void file_chain_reset(void) +{ + chain_fsp = NULL; +} + +/**************************************************************************** +Save the chained fsp - done when about to do an oplock break. +****************************************************************************/ + +void file_chain_save(void) +{ + oplock_save_chain_fsp = chain_fsp; +} + +/**************************************************************************** +Restore the chained fsp - done after an oplock break. +****************************************************************************/ + +void file_chain_restore(void) +{ + chain_fsp = oplock_save_chain_fsp; +} diff --git a/source3/smbd/groupname.c b/source3/smbd/groupname.c new file mode 100644 index 0000000000..812488571a --- /dev/null +++ b/source3/smbd/groupname.c @@ -0,0 +1,238 @@ +/* + Unix SMB/CIFS implementation. + Groupname handling + Copyright (C) Jeremy Allison 1998. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + 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. +*/ + +#ifdef USING_GROUPNAME_MAP + +#include "includes.h" +extern DOM_SID global_sam_sid; + +/************************************************************************** + Groupname map functionality. The code loads a groupname map file and + (currently) loads it into a linked list. This is slow and memory + hungry, but can be changed into a more efficient storage format + if the demands on it become excessive. +***************************************************************************/ + +typedef struct groupname_map { + ubi_slNode next; + + char *windows_name; + DOM_SID windows_sid; + char *unix_name; + gid_t unix_gid; +} groupname_map_entry; + +static ubi_slList groupname_map_list; + +/************************************************************************** + Delete all the entries in the groupname map list. +***************************************************************************/ + +static void delete_groupname_map_list(void) +{ + groupname_map_entry *gmep; + + while((gmep = (groupname_map_entry *)ubi_slRemHead( &groupname_map_list )) != NULL) { + SAFE_FREE(gmep->windows_name); + SAFE_FREE(gmep->unix_name); + SAFE_FREE(gmep); + } +} + +/************************************************************************** + Load a groupname map file. Sets last accessed timestamp. +***************************************************************************/ + +void load_groupname_map(void) +{ + static time_t groupmap_file_last_modified = (time_t)0; + static BOOL initialized = False; + char *groupname_map_file = lp_groupname_map(); + SMB_STRUCT_STAT st; + char **lines; + int i; + groupname_map_entry *new_ep; + + if(!initialized) { + ubi_slInitList( &groupname_map_list ); + initialized = True; + } + + if (!*groupname_map_file) + return; + + if(sys_stat(groupname_map_file, &st) != 0) { + DEBUG(0, ("load_groupname_map: Unable to stat file %s. Error was %s\n", + groupname_map_file, strerror(errno) )); + return; + } + + /* + * Check if file has changed. + */ + if( st.st_mtime <= groupmap_file_last_modified) + return; + + groupmap_file_last_modified = st.st_mtime; + + /* + * Load the file. + */ + + lines = file_lines_load(groupname_map_file,NULL,False); + if (!lines) { + DEBUG(0,("load_groupname_map: can't open groupname map %s. Error was %s\n", + groupname_map_file, strerror(errno))); + return; + } + file_lines_slashcont(lines); + + /* + * Throw away any previous list. + */ + delete_groupname_map_list(); + + DEBUG(4,("load_groupname_map: Scanning groupname map %s\n",groupname_map_file)); + + for (i=0; lines[i]; i++) { + pstring unixname; + pstring windows_name; + gid_t gid; + DOM_SID tmp_sid; + char *s = lines[i]; + + DEBUG(10,("load_groupname_map: Read line |%s|\n", s)); + + if (!*s || strchr_m("#;",*s)) + continue; + + if(!next_token(&s,unixname, "\t\n\r=", sizeof(unixname))) + continue; + + if(!next_token(&s,windows_name, "\t\n\r=", sizeof(windows_name))) + continue; + + trim_string(unixname, " ", " "); + trim_string(windows_name, " ", " "); + + if (!*windows_name) + continue; + + if(!*unixname) + continue; + + DEBUG(5,("load_groupname_map: unixname = %s, windowsname = %s.\n", + unixname, windows_name)); + + /* + * Attempt to get the unix gid_t for this name. + */ + + if ((gid = nametogid(unixname)) == (gid_t)-1) + DEBUG(0,("load_groupname_map: nametogid for group %s failed.\ +Error was %s.\n", unixname, strerror(errno) )); + continue; + } + + /* + * Now map to an NT SID. + */ + + if(!lookup_wellknown_sid_from_name(windows_name, &tmp_sid)) { + /* + * It's not a well known name, convert the UNIX gid_t + * to a rid within this domain SID. + */ + tmp_sid = global_sam_sid; + tmp_sid.sub_auths[tmp_sid.num_auths++] = + pdb_gid_to_group_rid(gid); + } + + /* + * Create the list entry and add it onto the list. + */ + + if((new_ep = (groupname_map_entry *)malloc( sizeof(groupname_map_entry) ))== NULL) { + DEBUG(0,("load_groupname_map: malloc fail for groupname_map_entry.\n")); + fclose(fp); + return; + } + + new_ep->unix_gid = gid; + new_ep->windows_sid = tmp_sid; + new_ep->windows_name = strdup( windows_name ); + new_ep->unix_name = strdup( unixname ); + + if(new_ep->windows_name == NULL || new_ep->unix_name == NULL) { + DEBUG(0,("load_groupname_map: malloc fail for names in groupname_map_entry.\n")); + fclose(fp); + SAFE_FREE(new_ep->windows_name); + SAFE_FREE(new_ep->unix_name); + SAFE_FREE(new_ep); + file_lines_free(lines); + return; + } + memset((char *)&new_ep->next, '\0', sizeof(new_ep->next) ); + + ubi_slAddHead( &groupname_map_list, (ubi_slNode *)new_ep); + } + + DEBUG(10,("load_groupname_map: Added %ld entries to groupname map.\n", + ubi_slCount(&groupname_map_list))); + + file_lines_free(lines); +} + +/*********************************************************** + Lookup a SID entry by gid_t. +************************************************************/ + +void map_gid_to_sid( gid_t gid, DOM_SID *psid) +{ + groupname_map_entry *gmep; + + /* + * Initialize and load if not already loaded. + */ + load_groupname_map(); + + for( gmep = (groupname_map_entry *)ubi_slFirst( &groupname_map_list); + gmep; gmep = (groupname_map_entry *)ubi_slNext( gmep )) { + + if( gmep->unix_gid == gid) { + *psid = gmep->windows_sid; + DEBUG(7,("map_gid_to_sid: Mapping unix group %s to windows group %s.\n", + gmep->unix_name, gmep->windows_name )); + return; + } + } + + /* + * If there's no map, convert the UNIX gid_t + * to a rid within this domain SID. + */ + *psid = global_sam_sid; + psid->sub_auths[psid->num_auths++] = pdb_gid_to_group_rid(gid); + + return; +} +#else /* USING_GROUPNAME_MAP */ + void load_groupname_map(void) {;} +#endif /* USING_GROUPNAME_MAP */ diff --git a/source3/smbd/ipc.c b/source3/smbd/ipc.c index 8852e57e8b..c2f3b7b2f0 100644 --- a/source3/smbd/ipc.c +++ b/source3/smbd/ipc.c @@ -1,8 +1,10 @@ /* - Unix SMB/Netbios implementation. - Version 1.9. + Unix SMB/CIFS implementation. Inter-process communication and named pipe handling - Copyright (C) Andrew Tridgell 1992-1995 + Copyright (C) Andrew Tridgell 1992-1998 + + SMB Version handling + Copyright (C) John H Terpstra 1995-1998 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -24,2756 +26,495 @@ */ #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 int max_send; 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]; -} +extern int smb_read_error; /******************************************************************* - 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); -} + copies parameters and data, as needed, into the smb buffer + *both* the data and params sections should be aligned. this + is fudged in the rpc pipes by + at present, only the data section is. this may be a possible + cause of some of the ipc problems being experienced. lkcl26dec97 -/**************************************************************************** - send a trans reply - ****************************************************************************/ -static void send_trans_reply(char *outbuf,char *data,char *param,uint16 *setup, - int ldata,int lparam,int lsetup) -{ - int i; - int this_ldata,this_lparam; - int tot_data=0,tot_param=0; - int align; - - this_lparam = MIN(lparam,maxxmit - (500+lsetup*SIZEOFWORD)); /* hack */ - this_ldata = MIN(ldata,maxxmit - (500+lsetup*SIZEOFWORD+this_lparam)); - - align = (this_lparam%4); - - set_message(outbuf,10+lsetup,align+this_ldata+this_lparam,True); - if (this_lparam) - memcpy(smb_buf(outbuf),param,this_lparam); - if (this_ldata) - memcpy(smb_buf(outbuf)+this_lparam+align,data,this_ldata); - - SSVAL(outbuf,smb_vwv0,lparam); - SSVAL(outbuf,smb_vwv1,ldata); - SSVAL(outbuf,smb_vwv3,this_lparam); - SSVAL(outbuf,smb_vwv4,smb_offset(smb_buf(outbuf),outbuf)); - SSVAL(outbuf,smb_vwv5,0); - SSVAL(outbuf,smb_vwv6,this_ldata); - SSVAL(outbuf,smb_vwv7,smb_offset(smb_buf(outbuf)+this_lparam+align,outbuf)); - SSVAL(outbuf,smb_vwv8,0); - SSVAL(outbuf,smb_vwv9,lsetup); - for (i=0;i<lsetup;i++) - SSVAL(outbuf,smb_vwv10+i*SIZEOFWORD,setup[i]); - - show_msg(outbuf); - send_smb(Client,outbuf); - - tot_data = this_ldata; - tot_param = this_lparam; - - while (tot_data < ldata || tot_param < lparam) - { - this_lparam = MIN(lparam-tot_param,maxxmit - 500); /* hack */ - this_ldata = MIN(ldata-tot_data,maxxmit - (500+this_lparam)); - - align = (this_lparam%4); - - set_message(outbuf,10,this_ldata+this_lparam+align,False); - if (this_lparam) - memcpy(smb_buf(outbuf),param+tot_param,this_lparam); - if (this_ldata) - memcpy(smb_buf(outbuf)+this_lparam+align,data+tot_data,this_ldata); - - SSVAL(outbuf,smb_vwv3,this_lparam); - SSVAL(outbuf,smb_vwv4,smb_offset(smb_buf(outbuf),outbuf)); - SSVAL(outbuf,smb_vwv5,tot_param); - SSVAL(outbuf,smb_vwv6,this_ldata); - SSVAL(outbuf,smb_vwv7,smb_offset(smb_buf(outbuf)+this_lparam+align,outbuf)); - SSVAL(outbuf,smb_vwv8,tot_data); - SSVAL(outbuf,smb_vwv9,0); - - show_msg(outbuf); - send_smb(Client,outbuf); - - tot_data += this_ldata; - tot_param += this_lparam; - } -} - + ******************************************************************/ - -/**************************************************************************** - get a print queue - ****************************************************************************/ - -struct pack_desc { - char* format; /* formatstring for structure */ - char* subformat; /* subformat for structure */ - char* base; /* baseaddress of buffer */ - int buflen; /* remaining size for fixed part; on init: length of base */ - int subcount; /* count of substructures */ - char* structbuf; /* pointer into buffer for remaining fixed part */ - int stringlen; /* remaining size for variable part */ - char* stringbuf; /* pointer into buffer for remaining variable part */ - int neededlen; /* total needed size */ - int usedlen; /* total used size (usedlen <= neededlen and usedlen <= buflen) */ - char* curpos; /* current position; pointer into format or subformat */ - int errcode; -}; - -static int get_counter(char** p) +static void copy_trans_params_and_data(char *outbuf, int align, + char *rparam, int param_offset, int param_len, + char *rdata, int data_offset, int data_len) { - int i, n; - if (!p || !(*p)) return(1); - if (!isdigit(**p)) return 1; - for (n = 0;;) { - i = **p; - if (isdigit(i)) - n = 10 * n + (i - '0'); - else - return n; - (*p)++; - } -} + char *copy_into = smb_buf(outbuf)+1; -static int getlen(char* p) -{ - int n = 0; - if (!p) return(0); - while (*p) { - switch( *p++ ) { - case 'W': /* word (2 byte) */ - n += 2; - break; - case 'N': /* count of substructures (word) at end */ - n += 2; - break; - case 'D': /* double word (4 byte) */ - case 'z': /* offset to zero terminated string (4 byte) */ - case 'l': /* offset to user data (4 byte) */ - n += 4; - break; - case 'b': /* offset to data (with counter) (4 byte) */ - n += 4; - get_counter(&p); - break; - case 'B': /* byte (with optional counter) */ - n += get_counter(&p); - break; - } - } - return n; -} + if(param_len < 0) + param_len = 0; -static BOOL init_package(struct pack_desc* p, int count, int subcount) -{ - int n = p->buflen; - int i; - - if (!p->format || !p->base) return(False); - - i = count * getlen(p->format); - if (p->subformat) i += subcount * getlen(p->subformat); - p->structbuf = p->base; - p->neededlen = 0; - p->usedlen = 0; - p->subcount = 0; - p->curpos = p->format; - if (i > n) { - i = n = 0; - p->errcode = NERR_BufTooSmall; - } - - p->errcode = NERR_Success; - p->buflen = i; - n -= i; - p->stringbuf = p->base + i; - p->stringlen = n; - return(p->errcode == NERR_Success); -} - -#ifdef __STDC__ -static int package(struct pack_desc* p, ...) -{ -#else -static int package(va_alist) -va_dcl -{ - struct pack_desc* p; -#endif - va_list args; - int needed=0, stringneeded; - char* str=NULL; - int is_string=0, stringused; - int32 temp; - -#ifdef __STDC__ - va_start(args,p); -#else - va_start(args); - p = va_arg(args,struct pack_desc *); -#endif - - if (!*p->curpos) { - if (!p->subcount) - p->curpos = p->format; - else { - p->curpos = p->subformat; - p->subcount--; - } - } -#if CHECK_TYPES - str = va_arg(args,char*); - if (strncmp(str,p->curpos,strlen(str)) != 0) { - DEBUG(2,("type error in package: %s instead of %*s\n",str, - strlen(str),p->curpos)); - va_end(args); -#if AJT - ajt_panic(); -#endif - return 0; - } -#endif - stringneeded = -1; - - if (!p->curpos) return(0); - - switch( *p->curpos++ ) { - case 'W': /* word (2 byte) */ - needed = 2; - temp = va_arg(args,int); - if (p->buflen >= needed) SSVAL(p->structbuf,0,temp); - break; - case 'N': /* count of substructures (word) at end */ - needed = 2; - p->subcount = va_arg(args,int); - if (p->buflen >= needed) SSVAL(p->structbuf,0,p->subcount); - break; - case 'D': /* double word (4 byte) */ - needed = 4; - temp = va_arg(args,int); - if (p->buflen >= needed) SIVAL(p->structbuf,0,temp); - break; - case 'B': /* byte (with optional counter) */ - needed = get_counter(&p->curpos); - { - char *s = va_arg(args,char*); - if (p->buflen >= needed) StrnCpy(p->structbuf,s?s:"",needed); - } - break; - case 'z': /* offset to zero terminated string (4 byte) */ - str = va_arg(args,char*); - stringneeded = (str ? strlen(str)+1 : 0); - is_string = 1; - break; - case 'l': /* offset to user data (4 byte) */ - str = va_arg(args,char*); - stringneeded = va_arg(args,int); - is_string = 0; - break; - case 'b': /* offset to data (with counter) (4 byte) */ - str = va_arg(args,char*); - stringneeded = get_counter(&p->curpos); - is_string = 0; - break; - } - va_end(args); - if (stringneeded >= 0) { - needed = 4; - if (p->buflen >= needed) { - stringused = stringneeded; - if (stringused > p->stringlen) { - stringused = (is_string ? p->stringlen : 0); - if (p->errcode == NERR_Success) p->errcode = ERROR_MORE_DATA; - } - if (!stringused) - SIVAL(p->structbuf,0,0); - else { - SIVAL(p->structbuf,0,PTR_DIFF(p->stringbuf,p->base)); - memcpy(p->stringbuf,str?str:"",stringused); - if (is_string) p->stringbuf[stringused-1] = '\0'; - p->stringbuf += stringused; - p->stringlen -= stringused; - p->usedlen += stringused; - } - } - p->neededlen += stringneeded; - } - p->neededlen += needed; - if (p->buflen >= needed) { - p->structbuf += needed; - p->buflen -= needed; - p->usedlen += needed; - } - else { - if (p->errcode == NERR_Success) p->errcode = NERR_BufTooSmall; - } - return 1; -} + if(data_len < 0) + data_len = 0; -#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 + DEBUG(5,("copy_trans_params_and_data: params[%d..%d] data[%d..%d]\n", + param_offset, param_offset + param_len, + data_offset , data_offset + data_len)); -static void PACKI(struct pack_desc* desc,char *t,int v) -{ - PACK(desc,t,v); -} + if (param_len) + memcpy(copy_into, &rparam[param_offset], param_len); -static void PACKS(struct pack_desc* desc,char *t,char *v) -{ - PACK(desc,t,v); -} + copy_into += param_len + align; -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 */ + if (data_len ) + memcpy(copy_into, &rdata[data_offset], data_len); } -static int check_printq_info(struct pack_desc* desc, - int uLevel, const char* id1, const char* id2) -{ - desc->subformat = NULL; - switch( uLevel ) { - case 0: - desc->format = "B13"; - break; - case 1: - desc->format = "B13BWWWzzzzzWW"; - break; - case 2: - desc->format = "B13BWWWzzzzzWN"; - desc->subformat = "WB21BB16B10zWWzDDz"; - break; - case 3: - desc->format = "zWWWWzzzzWWzzl"; - break; - case 4: - desc->format = "zWWWWzzzzWNzzl"; - desc->subformat = "WWzWWDDzz"; - break; - case 5: - desc->format = "z"; - break; - default: return False; - } - if (strcmp(desc->format,id1) != 0) return False; - if (desc->subformat && strcmp(desc->subformat,id2) != 0) return False; - return True; -} - -static void fill_printjob_info(int cnum, int snum, int uLevel, - struct pack_desc* desc, - print_queue_struct* queue, int n) -{ - time_t t = queue->time; - - /* the client expects localtime */ - t += GMT_TO_LOCAL*TimeDiff(t); - - PACKI(desc,"W",((snum%0xFF)<<8) | (queue->job%0xFF)); /* uJobId */ - if (uLevel == 1) { - PACKS(desc,"B21",queue->user); /* szUserName */ - PACKS(desc,"B",""); /* pad */ - PACKS(desc,"B16",""); /* szNotifyName */ - PACKS(desc,"B10","PM_Q_RAW"); /* szDataType */ - PACKS(desc,"z",""); /* pszParms */ - PACKI(desc,"W",n+1); /* uPosition */ - PACKI(desc,"W",queue->status); /* fsStatus */ - PACKS(desc,"z",""); /* pszStatus */ - PACKI(desc,"D",queue->time); /* ulSubmitted */ - PACKI(desc,"D",queue->size); /* ulSize */ - PACKS(desc,"z",queue->file); /* pszComment */ - } - if (uLevel == 2 || uLevel == 3) { - PACKI(desc,"W",queue->priority); /* uPriority */ - PACKS(desc,"z",queue->user); /* pszUserName */ - PACKI(desc,"W",n+1); /* uPosition */ - PACKI(desc,"W",queue->status); /* fsStatus */ - PACKI(desc,"D",queue->time); /* ulSubmitted */ - PACKI(desc,"D",queue->size); /* ulSize */ - PACKS(desc,"z","Samba"); /* pszComment */ - PACKS(desc,"z",queue->file); /* pszDocument */ - if (uLevel == 3) { - PACKS(desc,"z",""); /* pszNotifyName */ - PACKS(desc,"z","PM_Q_RAW"); /* pszDataType */ - PACKS(desc,"z",""); /* pszParms */ - PACKS(desc,"z",""); /* pszStatus */ - PACKS(desc,"z",SERVICE(snum)); /* pszQueue */ - PACKS(desc,"z","lpd"); /* pszQProcName */ - PACKS(desc,"z",""); /* pszQProcParms */ - PACKS(desc,"z","NULL"); /* pszDriverName */ - PackDriverData(desc); /* pDriverData */ - PACKS(desc,"z",""); /* pszPrinterName */ - } - } -} - -static void fill_printq_info(int cnum, int snum, int uLevel, - struct pack_desc* desc, - int count, print_queue_struct* queue, - print_status_struct* status) -{ - if (uLevel < 3) { - PACKS(desc,"B13",SERVICE(snum)); - } else { - PACKS(desc,"z",Expand(cnum,snum,SERVICE(snum))); - } - if (uLevel == 1 || uLevel == 2) { - PACKS(desc,"B",""); /* alignment */ - PACKI(desc,"W",5); /* priority */ - PACKI(desc,"W",0); /* start time */ - PACKI(desc,"W",0); /* until time */ - PACKS(desc,"z",""); /* pSepFile */ - PACKS(desc,"z","lpd"); /* pPrProc */ - PACKS(desc,"z",SERVICE(snum)); /* pDestinations */ - PACKS(desc,"z",""); /* pParms */ - if (snum < 0) { - PACKS(desc,"z","UNKNOWN PRINTER"); - PACKI(desc,"W",LPSTAT_ERROR); - } - else if (!status || !status->message[0]) { - PACKS(desc,"z",Expand(cnum,snum,lp_comment(snum))); - PACKI(desc,"W",LPSTAT_OK); /* status */ - } else { - PACKS(desc,"z",status->message); - PACKI(desc,"W",status->status); /* status */ - } - PACKI(desc,(uLevel == 1 ? "W" : "N"),count); - } - if (uLevel == 3 || uLevel == 4) { - PACKI(desc,"W",5); /* uPriority */ - PACKI(desc,"W",0); /* uStarttime */ - PACKI(desc,"W",0); /* uUntiltime */ - PACKI(desc,"W",5); /* pad1 */ - PACKS(desc,"z",""); /* pszSepFile */ - PACKS(desc,"z","lpd"); /* pszPrProc */ - PACKS(desc,"z",""); /* pszParms */ - if (!status || !status->message[0]) { - PACKS(desc,"z",Expand(cnum,snum,lp_comment(snum))); /* pszComment */ - PACKI(desc,"W",LPSTAT_OK); /* fsStatus */ - } else { - PACKS(desc,"z",status->message); /* pszComment */ - PACKI(desc,"W",status->status); /* fsStatus */ - } - PACKI(desc,(uLevel == 3 ? "W" : "N"),count); /* cJobs */ - PACKS(desc,"z",SERVICE(snum)); /* pszPrinters */ - PACKS(desc,"z","NULL"); /* pszDriverName */ - PackDriverData(desc); /* pDriverData */ - } - if (uLevel == 2 || uLevel == 4) { - int i; - for (i=0;i<count;i++) - fill_printjob_info(cnum,snum,uLevel == 2 ? 1 : 2,desc,&queue[i],i); - } - - DEBUG(3,("fill_printq_info on <%s> gave %d entries\n",SERVICE(snum),count)); -} - -static BOOL api_DosPrintQGetInfo(int cnum,int uid, char *param,char *data, - int mdrcnt,int mprcnt, - char **rdata,char **rparam, - int *rdata_len,int *rparam_len) -{ - char *str1 = param+2; - char *str2 = skip_string(str1,1); - char *p = skip_string(str2,1); - char *QueueName = p; - int uLevel,cbBuf; - int count=0; - int snum; - char* str3; - struct pack_desc desc; - print_queue_struct *queue=NULL; - print_status_struct status; - - bzero(&status,sizeof(status)); - bzero(&desc,sizeof(desc)); - - p = skip_string(p,1); - uLevel = SVAL(p,0); - cbBuf = SVAL(p,2); - str3 = p + 4; - - if ((p = strchr(QueueName,'%'))) *p = 0; - - DEBUG(3,("PrintQueue uLevel=%d name=%s\n",uLevel,QueueName)); - - /* check it's a supported varient */ - if (!prefix_ok(str1,"zWrLh")) return False; - if (!check_printq_info(&desc,uLevel,str2,str3)) return False; - - snum = lp_servicenumber(QueueName); - if (snum < 0 && pcap_printername_ok(QueueName,NULL)) { - int pnum = lp_servicenumber(PRINTERS_NAME); - if (pnum >= 0) { - lp_add_printer(QueueName,pnum); - snum = lp_servicenumber(QueueName); - } - } - - if (snum < 0 || !VALID_SNUM(snum)) return(False); - - count = get_printqueue(snum,cnum,&queue,&status); - if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt); - desc.base = *rdata; - desc.buflen = mdrcnt; - if (init_package(&desc,1,count)) { - desc.subcount = count; - fill_printq_info(cnum,snum,uLevel,&desc,count,queue,&status); - } - - *rdata_len = desc.usedlen; - - *rparam_len = 6; - *rparam = REALLOC(*rparam,*rparam_len); - SSVALS(*rparam,0,desc.errcode); - SSVAL(*rparam,2,0); - SSVAL(*rparam,4,desc.neededlen); - - DEBUG(4,("printqgetinfo: errorcode %d\n",desc.errcode)); - - if (queue) free(queue); - - return(True); -} - - /**************************************************************************** - view list of all print jobs on all queues - ****************************************************************************/ -static BOOL api_DosPrintQEnum(int cnum, int uid, char* param, char* data, - int mdrcnt, int mprcnt, - char **rdata, char** rparam, - int *rdata_len, int *rparam_len) -{ - char *param_format = param+2; - char *output_format1 = skip_string(param_format,1); - char *p = skip_string(output_format1,1); - int uLevel = SVAL(p,0); - char *output_format2 = p + 4; - int services = lp_numservices(); - int i, n; - struct pack_desc desc; - print_queue_struct **queue = NULL; - print_status_struct *status = NULL; - int* subcntarr = NULL; - int queuecnt, subcnt=0, succnt=0; - - bzero(&desc,sizeof(desc)); - - DEBUG(3,("DosPrintQEnum uLevel=%d\n",uLevel)); - - if (prefix_ok(param_format,"WrLeh")) return False; - if (!check_printq_info(&desc,uLevel,output_format1,output_format2)) - return False; - queuecnt = 0; - for (i = 0; i < services; i++) - if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i)) - queuecnt++; - if (uLevel > 0) { - queue = (print_queue_struct**)malloc(queuecnt*sizeof(print_queue_struct*)); - memset(queue,0,queuecnt*sizeof(print_queue_struct*)); - status = (print_status_struct*)malloc(queuecnt*sizeof(print_status_struct)); - memset(status,0,queuecnt*sizeof(print_status_struct)); - subcntarr = (int*)malloc(queuecnt*sizeof(int)); - subcnt = 0; - n = 0; - for (i = 0; i < services; i++) - if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i)) { - subcntarr[n] = get_printqueue(i,cnum,&queue[n],&status[n]); - subcnt += subcntarr[n]; - n++; - } - } - if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt); - desc.base = *rdata; - desc.buflen = mdrcnt; - - if (init_package(&desc,queuecnt,subcnt)) { - n = 0; - succnt = 0; - for (i = 0; i < services; i++) - if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i)) { - fill_printq_info(cnum,i,uLevel,&desc,subcntarr[n],queue[n],&status[n]); - n++; - if (desc.errcode == NERR_Success) succnt = n; - } - } - - if (subcntarr) free(subcntarr); - - *rdata_len = desc.usedlen; - *rparam_len = 8; - *rparam = REALLOC(*rparam,*rparam_len); - SSVALS(*rparam,0,desc.errcode); - SSVAL(*rparam,2,0); - SSVAL(*rparam,4,succnt); - SSVAL(*rparam,6,queuecnt); - - for (i = 0; i < queuecnt; i++) { - if (queue && queue[i]) free(queue[i]); - } - - if (queue) free(queue); - if (status) free(status); - - return True; -} + Send a trans reply. + ****************************************************************************/ -/**************************************************************************** - get info level for a server list query - ****************************************************************************/ -static BOOL check_server_info(int uLevel, char* id) +void send_trans_reply(char *outbuf, + char *rparam, int rparam_len, + char *rdata, int rdata_len, + BOOL buffer_too_large) { - 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; -} + int this_ldata,this_lparam; + int tot_data_sent = 0; + int tot_param_sent = 0; + int align; -/* 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 */ -}; + int ldata = rdata ? rdata_len : 0; + int lparam = rparam ? rparam_len : 0; -/******************************************************************* - 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! */ -} + if (buffer_too_large) + DEBUG(5,("send_trans_reply: buffer %d too large\n", ldata )); -/******************************************************************* - 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; + this_lparam = MIN(lparam,max_send - 500); /* hack */ + this_ldata = MIN(ldata,max_send - (500+this_lparam)); - if (!servers || num_servers == 0) return (False); + align = ((this_lparam)%4); - for (count = 0; count < num_servers; count++) { - struct srv_info_struct *s; + if (buffer_too_large) { + ERROR_NT(STATUS_BUFFER_OVERFLOW); + } - s = &servers[count]; + set_message(outbuf,10,1+align+this_ldata+this_lparam,True); - if (strequal(name, s->name)) { - StrnCpy(domain, s->domain, sizeof(pstring)-1); - return (True); - } - } - return (False); -} + copy_trans_params_and_data(outbuf, align, + rparam, tot_param_sent, this_lparam, + rdata, tot_data_sent, 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)+1,outbuf)); + SSVAL(outbuf,smb_vwv5,0); + SSVAL(outbuf,smb_vwv6,this_ldata); + SSVAL(outbuf,smb_vwv7,smb_offset(smb_buf(outbuf)+1+this_lparam+align,outbuf)); + SSVAL(outbuf,smb_vwv8,0); + SSVAL(outbuf,smb_vwv9,0); -/******************************************************************* - 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); -} + show_msg(outbuf); + if (!send_smb(smbd_server_fd(),outbuf)) + exit_server("send_trans_reply: send_smb failed."); -/******************************************************************* - 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; -} + tot_data_sent = this_ldata; + tot_param_sent = this_lparam; - -/**************************************************************************** - view list of servers available (or possibly domains). The info is - extracted from lists saved by nmbd on the local host - ****************************************************************************/ -static BOOL api_RNetServerEnum(int cnum, int uid, char *param, char *data, - int mdrcnt, int mprcnt, char **rdata, - char **rparam, int *rdata_len, int *rparam_len) -{ - char *str1 = param+2; - char *str2 = skip_string(str1,1); - char *p = skip_string(str2,1); - int uLevel = SVAL(p,0); - int buf_len = SVAL(p,2); - uint32 servertype = IVAL(p,4); - char *p2; - int data_len, fixed_len, string_len; - int f_len, s_len; - struct srv_info_struct *servers=NULL; - int counted=0,total=0; - int i; - fstring domain; - BOOL domain_request = (servertype & SV_TYPE_DOMAIN_ENUM) && - !(servertype == SV_TYPE_ALL); - - domain[0] = 0; - p += 8; - - if (!prefix_ok(str1,"WrLehD")) return False; - if (!check_server_info(uLevel,str2)) return False; - - DEBUG(4, ("server request level: %s\n", str2)); - - if (strcmp(str1, "WrLehDO") == 0) - { - /* asking for servers. we will have to work out which workgroup was - requested, as we maintain lists for multiple workgroups */ - } - else if (strcmp(str1, "WrLehDz") == 0) - { - /* asking for a specific workgroup */ - StrnCpy(domain, p, sizeof(fstring)-1); - } - - if (lp_browse_list()) - { - total = get_server_info(servertype,&servers); - } - - if (!domain[0] && !domain_request) { - extern fstring remote_machine; - /* must be a server request with an assumed domain. find a domain */ - - if (find_server(servers, total, domain, remote_machine)) { - DEBUG(4, ("No domain specified: using %s for %s\n", - domain, remote_machine)); - } else { - /* default to soemthing sensible */ - strcpy(domain,my_workgroup()); - } - } - - data_len = fixed_len = string_len = 0; - - for (i=0;i<total;i++) - if (filter_server_info(&servers[i],domain)) { - data_len += fill_srv_info(&servers[i],uLevel,0,&f_len,0,&s_len,0); - if (data_len <= buf_len) + while (tot_data_sent < ldata || tot_param_sent < lparam) { - counted++; - fixed_len += f_len; - string_len += s_len; - } - } + this_lparam = MIN(lparam-tot_param_sent, max_send - 500); /* hack */ + this_ldata = MIN(ldata -tot_data_sent, max_send - (500+this_lparam)); - *rdata_len = fixed_len + string_len; - *rdata = REALLOC(*rdata,*rdata_len); - bzero(*rdata,*rdata_len); - - p2 = (*rdata) + fixed_len; /* auxilliary data (strings) will go here */ - p = *rdata; - f_len = fixed_len; - s_len = string_len; - - { - int count2 = counted; - for (i = 0; i < total && count2;i++) { - if (filter_server_info(&servers[i],domain)) { - fill_srv_info(&servers[i],uLevel,&p,&f_len,&p2,&s_len,*rdata); - count2--; - } - } - } - - *rparam_len = 8; - *rparam = REALLOC(*rparam,*rparam_len); - SSVAL(*rparam,0,NERR_Success); - SSVAL(*rparam,2,0); - SSVAL(*rparam,4,counted); - SSVAL(*rparam,6,total); + if(this_lparam < 0) + this_lparam = 0; - if (servers) free(servers); + if(this_ldata < 0) + this_ldata = 0; - DEBUG(3,("NetServerEnum domain = %s uLevel=%d counted=%d total=%d\n", - domain,uLevel,counted,total)); - - return(True); -} + align = (this_lparam%4); + set_message(outbuf,10,1+this_ldata+this_lparam+align,False); -/**************************************************************************** - get info about a share - ****************************************************************************/ -static BOOL check_share_info(int uLevel, char* id) -{ - switch( uLevel ) { - case 0: - if (strcmp(id,"B13") != 0) return False; - break; - case 1: - if (strcmp(id,"B13BWz") != 0) return False; - break; - case 2: - if (strcmp(id,"B13BWzWWWzB9B") != 0) return False; - break; - case 91: - if (strcmp(id,"B13BWzWWWzB9BB9BWzWWzWW") != 0) return False; - break; - default: return False; - } - return True; -} + copy_trans_params_and_data(outbuf, align, + rparam, tot_param_sent, this_lparam, + rdata, tot_data_sent, this_ldata); -static int fill_share_info(int cnum, int snum, int uLevel, - char** buf, int* buflen, - char** stringbuf, int* stringspace, char* baseaddr) -{ - int struct_len; - char* p; - char* p2; - int l2; - int len; - - switch( uLevel ) { - case 0: struct_len = 13; break; - case 1: struct_len = 20; break; - case 2: struct_len = 40; break; - case 91: struct_len = 68; break; - default: return -1; - } - - - if (!buf) - { - len = 0; - if (uLevel > 0) len += StrlenExpanded(cnum,snum,lp_comment(snum)); - if (uLevel > 1) len += strlen(lp_pathname(snum)) + 1; - if (buflen) *buflen = struct_len; - if (stringspace) *stringspace = len; - return struct_len + len; - } - - len = struct_len; - p = *buf; - if ((*buflen) < struct_len) return -1; - if (stringbuf) - { - p2 = *stringbuf; - l2 = *stringspace; - } - else - { - p2 = p + struct_len; - l2 = (*buflen) - struct_len; - } - if (!baseaddr) baseaddr = p; - - StrnCpy(p,lp_servicename(snum),13); - - if (uLevel > 0) - { - int type; - CVAL(p,13) = 0; - type = STYPE_DISKTREE; - if (lp_print_ok(snum)) type = STYPE_PRINTQ; - if (strequal("IPC$",lp_servicename(snum))) type = STYPE_IPC; - SSVAL(p,14,type); /* device type */ - SIVAL(p,16,PTR_DIFF(p2,baseaddr)); - len += CopyExpanded(cnum,snum,&p2,lp_comment(snum),&l2); - } - - if (uLevel > 1) - { - SSVAL(p,20,ACCESS_READ|ACCESS_WRITE|ACCESS_CREATE); /* permissions */ - SSVALS(p,22,-1); /* max uses */ - SSVAL(p,24,1); /* current uses */ - SIVAL(p,26,PTR_DIFF(p2,baseaddr)); /* local pathname */ - len += CopyAndAdvance(&p2,lp_pathname(snum),&l2); - memset(p+30,0,SHPWLEN+2); /* passwd (reserved), pad field */ - } - - if (uLevel > 2) - { - memset(p+40,0,SHPWLEN+2); - SSVAL(p,50,0); - SIVAL(p,52,0); - SSVAL(p,56,0); - SSVAL(p,58,0); - SIVAL(p,60,0); - SSVAL(p,64,0); - SSVAL(p,66,0); - } - - if (stringbuf) - { - (*buf) = p + struct_len; - (*buflen) -= struct_len; - (*stringbuf) = p2; - (*stringspace) = l2; - } - else - { - (*buf) = p2; - (*buflen) -= len; - } - return len; -} + SSVAL(outbuf,smb_vwv3,this_lparam); + SSVAL(outbuf,smb_vwv4,smb_offset(smb_buf(outbuf)+1,outbuf)); + SSVAL(outbuf,smb_vwv5,tot_param_sent); + SSVAL(outbuf,smb_vwv6,this_ldata); + SSVAL(outbuf,smb_vwv7,smb_offset(smb_buf(outbuf)+1+this_lparam+align,outbuf)); + SSVAL(outbuf,smb_vwv8,tot_data_sent); + SSVAL(outbuf,smb_vwv9,0); -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); -} + show_msg(outbuf); + if (!send_smb(smbd_server_fd(),outbuf)) + exit_server("send_trans_reply: send_smb failed."); -/**************************************************************************** - view list of shares available - ****************************************************************************/ -static BOOL api_RNetShareEnum(int cnum,int uid, char *param,char *data, - int mdrcnt,int mprcnt, - char **rdata,char **rparam, - int *rdata_len,int *rparam_len) -{ - char *str1 = param+2; - char *str2 = skip_string(str1,1); - char *p = skip_string(str2,1); - int uLevel = SVAL(p,0); - int buf_len = SVAL(p,2); - char *p2; - int count=lp_numservices(); - int total=0,counted=0; - int i; - int data_len, fixed_len, string_len; - int f_len, s_len; - - if (!prefix_ok(str1,"WrLeh")) return False; - if (!check_share_info(uLevel,str2)) return False; - - data_len = fixed_len = string_len = 0; - for (i=0;i<count;i++) - if (lp_browseable(i) && lp_snum_ok(i)) - { - total++; - data_len += fill_share_info(cnum,i,uLevel,0,&f_len,0,&s_len,0); - if (data_len <= buf_len) - { - counted++; - fixed_len += f_len; - string_len += s_len; - } - } - *rdata_len = fixed_len + string_len; - *rdata = REALLOC(*rdata,*rdata_len); - memset(*rdata,0,*rdata_len); - - p2 = (*rdata) + fixed_len; /* auxillery data (strings) will go here */ - p = *rdata; - f_len = fixed_len; - s_len = string_len; - for (i = 0; i < count;i++) - if (lp_browseable(i) && lp_snum_ok(i)) - if (fill_share_info(cnum,i,uLevel,&p,&f_len,&p2,&s_len,*rdata) < 0) - break; - - *rparam_len = 8; - *rparam = REALLOC(*rparam,*rparam_len); - SSVAL(*rparam,0,NERR_Success); - SSVAL(*rparam,2,0); - SSVAL(*rparam,4,counted); - SSVAL(*rparam,6,total); - - DEBUG(3,("RNetShareEnum gave %d entries of %d (%d %d %d %d)\n", - counted,total,uLevel, - buf_len,*rdata_len,mdrcnt)); - return(True); + tot_data_sent += this_ldata; + tot_param_sent += this_lparam; + } } - - /**************************************************************************** - get the time of day info - ****************************************************************************/ -static BOOL api_NetRemoteTOD(int cnum,int uid, char *param,char *data, - int mdrcnt,int mprcnt, - char **rdata,char **rparam, - int *rdata_len,int *rparam_len) -{ - char *p; - *rparam_len = 4; - *rparam = REALLOC(*rparam,*rparam_len); - - *rdata_len = 21; - *rdata = REALLOC(*rdata,*rdata_len); - - SSVAL(*rparam,0,NERR_Success); - SSVAL(*rparam,2,0); /* converter word */ - - p = *rdata; - - { - struct tm *t; - time_t unixdate = time(NULL); - - put_dos_date3(p,0,unixdate); /* this is the time that is looked at - by NT in a "net time" operation, - it seems to ignore the one below */ - - /* the client expects to get localtime, not GMT, in this bit - (I think, this needs testing) */ - t = LocalTime(&unixdate,GMT_TO_LOCAL); - - SIVAL(p,4,0); /* msecs ? */ - CVAL(p,8) = t->tm_hour; - CVAL(p,9) = t->tm_min; - CVAL(p,10) = t->tm_sec; - CVAL(p,11) = 0; /* hundredths of seconds */ - SSVALS(p,12,TimeDiff(unixdate)/60); /* timezone in minutes from GMT */ - SSVAL(p,14,10000); /* timer interval in 0.0001 of sec */ - CVAL(p,16) = t->tm_mday; - CVAL(p,17) = t->tm_mon + 1; - SSVAL(p,18,1900+t->tm_year); - CVAL(p,20) = t->tm_wday; - } - - - return(True); -} + Start the first part of an RPC reply which began with an SMBtrans request. +****************************************************************************/ -/**************************************************************************** - 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) +static BOOL api_rpc_trans_reply(char *outbuf, smb_np_struct *p) { - char *p = skip_string(param+2,2); - fstring user; - fstring pass1,pass2; - - strcpy(user,p); + BOOL is_data_outstanding; + char *rdata = malloc(p->max_trans_reply); + int data_len; - 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 */ + if(rdata == NULL) { + DEBUG(0,("api_rpc_trans_reply: malloc fail.\n")); + return False; + } - DEBUG(3,("Set password for <%s>\n",user)); + if((data_len = read_from_pipe( p, rdata, p->max_trans_reply, + &is_data_outstanding)) < 0) { + SAFE_FREE(rdata); + return False; + } - if (!password_ok(user,pass1,strlen(pass1),NULL,False) || - !chgpasswd(user,pass1,pass2)) - SSVAL(*rparam,0,NERR_badpass); + send_trans_reply(outbuf, NULL, 0, rdata, data_len, is_data_outstanding); - bzero(pass1,sizeof(fstring)); - bzero(pass2,sizeof(fstring)); - - return(True); + SAFE_FREE(rdata); + return True; } /**************************************************************************** - delete a print job - Form: <W> <> - ****************************************************************************/ -static BOOL api_RDosPrintJobDel(int cnum,int uid, char *param,char *data, - int mdrcnt,int mprcnt, - char **rdata,char **rparam, - int *rdata_len,int *rparam_len) -{ - int function = SVAL(param,0); - char *str1 = param+2; - char *str2 = skip_string(str1,1); - char *p = skip_string(str2,1); - int jobid = (SVAL(p,0)&0xFF); /* the snum and jobid are encoded - by the print queue api */ - int snum = (SVAL(p,0)>>8); - int i, count; - - - /* check it's a supported varient */ - if (!(strcsequal(str1,"W") && strcsequal(str2,""))) - return(False); - - *rparam_len = 4; - *rparam = REALLOC(*rparam,*rparam_len); - - *rdata_len = 0; - - SSVAL(*rparam,0,NERR_Success); - - if (snum >= 0 && VALID_SNUM(snum)) - { - print_queue_struct *queue=NULL; - lpq_reset(snum); - count = get_printqueue(snum,cnum,&queue,NULL); - - for (i=0;i<count;i++) - if ((queue[i].job%0xFF) == jobid) - { - switch (function) { - case 81: /* delete */ - DEBUG(3,("Deleting queue entry %d\n",queue[i].job)); - del_printqueue(cnum,snum,queue[i].job); - break; - case 82: /* pause */ - case 83: /* resume */ - DEBUG(3,("%s queue entry %d\n", - (function==82?"pausing":"resuming"),queue[i].job)); - status_printjob(cnum,snum,queue[i].job, - (function==82?LPQ_PAUSED:LPQ_QUEUED)); - break; - } - break; - } - - if (i==count) - SSVAL(*rparam,0,NERR_JobNotFound); - - if (queue) free(queue); - } - - SSVAL(*rparam,2,0); /* converter word */ - - return(True); -} + WaitNamedPipeHandleState +****************************************************************************/ -static BOOL api_WPrintQueuePurge(int cnum,int uid, char *param,char *data, - int mdrcnt,int mprcnt, - char **rdata,char **rparam, - int *rdata_len,int *rparam_len) +static BOOL api_WNPHS(char *outbuf, smb_np_struct *p, char *param, int param_len) { - char *str1 = param+2; - char *str2 = skip_string(str1,1); - char *QueueName = skip_string(str2,1); - int snum; - - /* check it's a supported varient */ - if (!(strcsequal(str1,"z") && strcsequal(str2,""))) - return(False); - - *rparam_len = 4; - *rparam = REALLOC(*rparam,*rparam_len); - - *rdata_len = 0; - - SSVAL(*rparam,0,NERR_Success); - SSVAL(*rparam,2,0); /* converter word */ - - snum = lp_servicenumber(QueueName); - if (snum < 0 && pcap_printername_ok(QueueName,NULL)) { - int pnum = lp_servicenumber(PRINTERS_NAME); - if (pnum >= 0) { - lp_add_printer(QueueName,pnum); - snum = lp_servicenumber(QueueName); - } - } - - if (snum >= 0 && VALID_SNUM(snum)) { - print_queue_struct *queue=NULL; - int i, count; - lpq_reset(snum); - - count = get_printqueue(snum,cnum,&queue,NULL); - for (i = 0; i < count; i++) - del_printqueue(cnum,snum,queue[i].job); - - if (queue) free(queue); - } - - DEBUG(3,("Print queue purge, queue=%s\n",QueueName)); - - return(True); -} + uint16 priority; + if (!param || param_len < 2) + return False; -/**************************************************************************** - set the property of a print job (undocumented?) - ? function = 0xb -> set name of print job - ? function = 0x6 -> move print job up/down - Form: <WWsTP> <WWzWWDDzzzzzzzzzzlz> - or <WWsTP> <WB21BB16B10zWWzDDz> -****************************************************************************/ -static int check_printjob_info(struct pack_desc* desc, - int uLevel, char* id) -{ - desc->subformat = NULL; - switch( uLevel ) { - case 0: desc->format = "W"; break; - case 1: desc->format = "WB21BB16B10zWWzDDz"; break; - case 2: desc->format = "WWzWWDDzz"; break; - case 3: desc->format = "WWzWWDDzzzzzzzzzzlz"; break; - default: return False; - } - if (strcmp(desc->format,id) != 0) return False; - return True; -} + priority = SVAL(param,0); + DEBUG(4,("WaitNamedPipeHandleState priority %x\n", priority)); -static BOOL api_PrintJobInfo(int cnum,int uid,char *param,char *data, - int mdrcnt,int mprcnt, - char **rdata,char **rparam, - int *rdata_len,int *rparam_len) -{ - struct pack_desc desc; - char *str1 = param+2; - char *str2 = skip_string(str1,1); - char *p = skip_string(str2,1); - int jobid = (SVAL(p,0)&0xFF); /* the snum and jobid are encoded - by the print queue api */ - int snum = (SVAL(p,0)>>8); - int uLevel = SVAL(p,2); - int function = SVAL(p,4); /* what is this ?? */ - int i; - char *s = data; - - *rparam_len = 4; - *rparam = REALLOC(*rparam,*rparam_len); - - *rdata_len = 0; - - /* check it's a supported varient */ - if ((strcmp(str1,"WWsTP")) || (!check_printjob_info(&desc,uLevel,str2))) - return(False); - - switch (function) { - case 0x6: /* change job place in the queue, data gives the new place */ - if (snum >= 0 && VALID_SNUM(snum)) - { - print_queue_struct *queue=NULL; - int count; - - lpq_reset(snum); - count = get_printqueue(snum,cnum,&queue,NULL); - for (i=0;i<count;i++) /* find job */ - if ((queue[i].job%0xFF) == jobid) break; - - if (i==count) { - desc.errcode=NERR_JobNotFound; - if (queue) free(queue); + if (wait_rpc_pipe_hnd_state(p, priority)) { + /* now send the reply */ + send_trans_reply(outbuf, NULL, 0, NULL, 0, False); + return True; } - else { - desc.errcode=NERR_Success; - i++; -#if 0 - { - int place= SVAL(data,0); - /* we currently have no way of doing this. Can any unix do it? */ - if (i < place) /* move down */; - else if (i > place ) /* move up */; - } -#endif - desc.errcode=NERR_notsupported; /* not yet supported */ - if (queue) free(queue); - } - } - else desc.errcode=NERR_JobNotFound; - break; - case 0xb: /* change print job name, data gives the name */ - /* jobid, snum should be zero */ - if (isalpha(*s)) - { - pstring name; - int l = 0; - while (l<64 && *s) - { - if (isalnum(*s) || strchr("-._",*s)) - name[l++] = *s; - s++; - } - name[l] = 0; - - DEBUG(3,("Setting print name to %s\n",name)); - - for (i=0;i<MAX_OPEN_FILES;i++) - if (Files[i].open && Files[i].print_file) - { - pstring wd; - GetWd(wd); - unbecome_user(); - - if (!become_user(Files[i].cnum,uid) || - !become_service(Files[i].cnum,True)) - break; - - if (sys_rename(Files[i].name,name) == 0) - string_set(&Files[i].name,name); - break; - } - } - desc.errcode=NERR_Success; - - break; - default: /* not implemented */ - return False; - } - - SSVALS(*rparam,0,desc.errcode); - SSVAL(*rparam,2,0); /* converter word */ - - return(True); + return False; } /**************************************************************************** - get info about the server - ****************************************************************************/ -static BOOL api_RNetServerGetInfo(int cnum,int uid, char *param,char *data, - int mdrcnt,int mprcnt, - char **rdata,char **rparam, - int *rdata_len,int *rparam_len) -{ - char *str1 = param+2; - char *str2 = skip_string(str1,1); - char *p = skip_string(str2,1); - int uLevel = SVAL(p,0); - char *p2; - int struct_len; - - DEBUG(4,("NetServerGetInfo level %d\n",uLevel)); - - /* check it's a supported varient */ - if (!prefix_ok(str1,"WrLh")) return False; - switch( uLevel ) { - case 0: - if (strcmp(str2,"B16") != 0) return False; - struct_len = 16; - break; - case 1: - if (strcmp(str2,"B16BBDz") != 0) return False; - struct_len = 26; - break; - case 2: - if (strcmp(str2,"B16BBDzDDDWWzWWWWWWWBB21zWWWWWWWWWWWWWWWWWWWWWWz") - != 0) return False; - struct_len = 134; - break; - case 3: - if (strcmp(str2,"B16BBDzDDDWWzWWWWWWWBB21zWWWWWWWWWWWWWWWWWWWWWWzDWz") - != 0) return False; - struct_len = 144; - break; - case 20: - if (strcmp(str2,"DN") != 0) return False; - struct_len = 6; - break; - case 50: - if (strcmp(str2,"B16BBDzWWzzz") != 0) return False; - struct_len = 42; - break; - default: return False; - } - - *rdata_len = mdrcnt; - *rdata = REALLOC(*rdata,*rdata_len); - - p = *rdata; - p2 = p + struct_len; - if (uLevel != 20) { - StrnCpy(p,local_machine,16); - strupper(p); - } - p += 16; - if (uLevel > 0) - { - struct srv_info_struct *servers=NULL; - int i,count; - pstring comment; - uint32 servertype=SV_TYPE_SERVER_UNIX|SV_TYPE_WORKSTATION| - SV_TYPE_SERVER|SV_TYPE_TIME_SOURCE; - - strcpy(comment,lp_serverstring()); - - if ((count=get_server_info(SV_TYPE_ALL,&servers))>0) { - for (i=0;i<count;i++) - if (strequal(servers[i].name,local_machine)) { - servertype = servers[i].type; - strcpy(comment,servers[i].comment); - } - } - if (servers) free(servers); - - SCVAL(p,0,2); /* version_major */ - SCVAL(p,1,0); /* version_minor */ - SIVAL(p,2,servertype); - if (mdrcnt == struct_len) { - SIVAL(p,6,0); - } else { - SIVAL(p,6,PTR_DIFF(p2,*rdata)); - standard_sub(cnum,comment); - StrnCpy(p2,comment,MAX(mdrcnt - struct_len,0)); - p2 = skip_string(p2,1); - } - } - if (uLevel > 1) - { - return False; /* not yet implemented */ - } - - *rdata_len = PTR_DIFF(p2,*rdata); - - *rparam_len = 6; - *rparam = REALLOC(*rparam,*rparam_len); - SSVAL(*rparam,0,NERR_Success); - SSVAL(*rparam,2,0); /* converter word */ - SSVAL(*rparam,4,*rdata_len); - - return(True); -} - + SetNamedPipeHandleState +****************************************************************************/ -/**************************************************************************** - 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) +static BOOL api_SNPHS(char *outbuf, smb_np_struct *p, char *param, int param_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); + uint16 id; - *rdata_len = mdrcnt + 1024; - *rdata = REALLOC(*rdata,*rdata_len); + if (!param || param_len < 2) + return False; - SSVAL(*rparam,0,NERR_Success); - SSVAL(*rparam,2,0); /* converter word */ + id = SVAL(param,0); + DEBUG(4,("SetNamedPipeHandleState to code %x\n", id)); - 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); + if (set_rpc_pipe_hnd_state(p, id)) { + /* now send the reply */ + send_trans_reply(outbuf, NULL, 0, NULL, 0, False); + return True; + } + return False; } /**************************************************************************** - get info about a user - ****************************************************************************/ + When no reply is generated, indicate unsupported. + ****************************************************************************/ -#define USER_PRIV_GUEST 0 -#define USER_PRIV_USER 1 -#define USER_PRIV_ADMIN 2 - -static BOOL api_RNetUserGetInfo(int cnum,int uid, char *param,char *data, - int mdrcnt,int mprcnt, - char **rdata,char **rparam, - int *rdata_len,int *rparam_len) -{ - char *str1 = param+2; - char *str2 = skip_string(str1,1); - char *UserName = skip_string(str2,1); - char *p = skip_string(UserName,1); - int uLevel = SVAL(p,0); - char *p2; - - *rparam_len = 6; - *rparam = REALLOC(*rparam,*rparam_len); - - /* check it's a supported varient */ - if (strcmp(str1,"zWrLh") != 0) return False; - switch( uLevel ) { - case 0: p2 = "B21"; break; - case 1: p2 = "B21BB16DWzzWz"; break; - case 2: p2 = "B21BB16DWzzWzDzzzzDDDDWb21WWzWW"; break; - case 10: p2 = "B21Bzzz"; break; - case 11: p2 = "B21BzzzWDDzzDDWWzWzDWb21W"; break; - default: return False; - } - if (strcmp(p2,str2) != 0) return False; - - *rdata_len = mdrcnt + 1024; - *rdata = REALLOC(*rdata,*rdata_len); - - SSVAL(*rparam,0,NERR_Success); - SSVAL(*rparam,2,0); /* converter word */ - - p = *rdata; - p2 = p + 86; - - memset(p,0,21); - strcpy(p,UserName); - if (uLevel > 0) { - SCVAL(p,21,0); - *p2 = 0; - if (uLevel >= 10) { - SIVAL(p,22,PTR_DIFF(p2,p)); /* comment */ - strcpy(p2,"<Comment>"); - p2 = skip_string(p2,1); - SIVAL(p,26,PTR_DIFF(p2,p)); /* user_comment */ - strcpy(p2,"<UserComment>"); - p2 = skip_string(p2,1); - SIVAL(p,30,PTR_DIFF(p2,p)); /* full name */ - strcpy(p2,"<FullName>"); - p2 = skip_string(p2,1); - } - if (uLevel == 11) { /* modelled after NTAS 3.51 reply */ - SSVAL(p,34,USER_PRIV_USER); /* user privilege */ - SIVAL(p,36,0); /* auth flags */ - SIVALS(p,40,-1); /* password age */ - SIVAL(p,44,PTR_DIFF(p2,p)); /* home dir */ - strcpy(p2,"\\\\%L\\HOMES"); - standard_sub_basic(p2); - p2 = skip_string(p2,1); - SIVAL(p,48,PTR_DIFF(p2,p)); /* parms */ - strcpy(p2,""); - p2 = skip_string(p2,1); - SIVAL(p,52,0); /* last logon */ - SIVAL(p,56,0); /* last logoff */ - SSVALS(p,60,-1); /* bad pw counts */ - SSVALS(p,62,-1); /* num logons */ - SIVAL(p,64,PTR_DIFF(p2,p)); /* logon server */ - strcpy(p2,"\\\\*"); - p2 = skip_string(p2,1); - SSVAL(p,68,0); /* country code */ - - SIVAL(p,70,PTR_DIFF(p2,p)); /* workstations */ - strcpy(p2,""); - p2 = skip_string(p2,1); - - SIVALS(p,74,-1); /* max storage */ - SSVAL(p,78,168); /* units per week */ - SIVAL(p,80,PTR_DIFF(p2,p)); /* logon hours */ - memset(p2,-1,21); - SCVAL(p2,21,0); /* fix zero termination */ - p2 = skip_string(p2,1); - - SSVAL(p,84,0); /* code page */ - } - if (uLevel == 1 || uLevel == 2) { - memset(p+22,' ',16); /* password */ - SIVALS(p,38,-1); /* password age */ - SSVAL(p,42,USER_PRIV_ADMIN); /* user privilege */ - SIVAL(p,44,PTR_DIFF(p2,*rdata)); /* home dir */ - strcpy(p2,"\\\\%L\\HOMES"); - standard_sub_basic(p2); - p2 = skip_string(p2,1); - SIVAL(p,48,PTR_DIFF(p2,*rdata)); /* comment */ - *p2++ = 0; - SSVAL(p,52,0); /* flags */ - SIVAL(p,54,0); /* script_path */ - if (uLevel == 2) { - SIVAL(p,60,0); /* auth_flags */ - SIVAL(p,64,PTR_DIFF(p2,*rdata)); /* full_name */ - strcpy(p2,"<Full Name>"); - p2 = skip_string(p2,1); - SIVAL(p,68,0); /* urs_comment */ - SIVAL(p,72,PTR_DIFF(p2,*rdata)); /* parms */ - strcpy(p2,""); - p2 = skip_string(p2,1); - SIVAL(p,76,0); /* workstations */ - SIVAL(p,80,0); /* last_logon */ - SIVAL(p,84,0); /* last_logoff */ - SIVALS(p,88,-1); /* acct_expires */ - SIVALS(p,92,-1); /* max_storage */ - SSVAL(p,96,168); /* units_per_week */ - SIVAL(p,98,PTR_DIFF(p2,*rdata)); /* logon_hours */ - memset(p2,-1,21); - p2 += 21; - SSVALS(p,102,-1); /* bad_pw_count */ - SSVALS(p,104,-1); /* num_logons */ - SIVAL(p,106,PTR_DIFF(p2,*rdata)); /* logon_server */ - strcpy(p2,"\\\\%L"); - standard_sub_basic(p2); - p2 = skip_string(p2,1); - SSVAL(p,110,49); /* country_code */ - SSVAL(p,112,860); /* code page */ - } - } - } - - *rdata_len = PTR_DIFF(p2,*rdata); - - SSVAL(*rparam,4,*rdata_len); /* is this right?? */ - - return(True); -} - - -/******************************************************************* - get groups that a user is a member of - ******************************************************************/ -static BOOL api_NetUserGetGroups(int cnum,int uid, char *param,char *data, - int mdrcnt,int mprcnt, - char **rdata,char **rparam, - int *rdata_len,int *rparam_len) +static BOOL api_no_reply(char *outbuf, int max_rdata_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 */ + char rparam[4]; - p = *rdata; + /* unsupported */ + SSVAL(rparam,0,NERR_notsupported); + SSVAL(rparam,2,0); /* converter word */ - /* 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++; + DEBUG(3,("Unsupported API fd command\n")); - *rdata_len = PTR_DIFF(p,*rdata); + /* now send the reply */ + send_trans_reply(outbuf, rparam, 4, NULL, 0, False); - SSVAL(*rparam,4,count); /* is this right?? */ - SSVAL(*rparam,6,count); /* is this right?? */ - - return(True); + return -1; } - -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); -} + Handle remote api calls delivered to a named pipe already opened. + ****************************************************************************/ -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) +static int api_fd_reply(connection_struct *conn,uint16 vuid,char *outbuf, + uint16 *setup,char *data,char *params, + int suwcnt,int tdscnt,int tpscnt,int mdrcnt,int mprcnt) { - 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); + BOOL reply = False; + smb_np_struct *p = NULL; + int pnum; + int subcommand; - *rdata_len = desc.usedlen; + DEBUG(5,("api_fd_reply\n")); - *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)); + /* First find out the name of this file. */ + if (suwcnt != 2) { + DEBUG(0,("Unexpected named pipe transaction.\n")); + return(-1); + } - /* check it's a supported varient */ - if (strcmp(str1,"WrLeh") != 0) return False; - if (uLevel != 0 || strcmp(str2,"B13") != 0) return False; + /* Get the file handle and hence the file name. */ + /* + * NB. The setup array has already been transformed + * via SVAL and so is in gost byte order. + */ + pnum = ((int)setup[1]) & 0xFFFF; + subcommand = ((int)setup[0]) & 0xFFFF; + + if(!(p = get_rpc_pipe(pnum))) { + DEBUG(1,("api_fd_reply: INVALID PIPE HANDLE: %x\n", pnum)); + return api_no_reply(outbuf, mdrcnt); + } - 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"); - } + DEBUG(3,("Got API command 0x%x on pipe \"%s\" (pnum %x)", subcommand, p->name, pnum)); - succnt = (desc.errcode == NERR_Success ? 1 : 0); + /* record maximum data length that can be transmitted in an SMBtrans */ + p->max_trans_reply = mdrcnt; - *rdata_len = desc.usedlen; + DEBUG(10,("api_fd_reply: p:%p max_trans_reply: %d\n", p, p->max_trans_reply)); - *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); + switch (subcommand) { + case 0x26: + /* dce/rpc command */ + reply = write_to_pipe(p, data, tdscnt); + if (reply) + reply = api_rpc_trans_reply(outbuf, p); + break; + case 0x53: + /* Wait Named Pipe Handle state */ + reply = api_WNPHS(outbuf, p, params, tpscnt); + break; + case 0x01: + /* Set Named Pipe Handle state */ + reply = api_SNPHS(outbuf, p, params, tpscnt); + break; + } - DEBUG(4,("WPrintQProcEnum: errorcode %d\n",desc.errcode)); - return(True); -} + if (!reply) + return api_no_reply(outbuf, mdrcnt); -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); + return -1; } /**************************************************************************** - the buffer was too small + handle named pipe commands ****************************************************************************/ -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) +static int named_pipe(connection_struct *conn,uint16 vuid, char *outbuf,char *name, + uint16 *setup,char *data,char *params, + int suwcnt,int tdscnt,int tpscnt, + int msrcnt,int mdrcnt,int mprcnt) { - *rparam_len = MIN(*rparam_len,mprcnt); - *rparam = REALLOC(*rparam,*rparam_len); - - *rdata_len = 0; - - SSVAL(*rparam,0,NERR_BufTooSmall); + DEBUG(3,("named pipe command on <%s> name\n", name)); - DEBUG(3,("Supplied buffer too small in API command\n")); + if (strequal(name,"LANMAN")) + return api_reply(conn,vuid,outbuf,data,params,tdscnt,tpscnt,mdrcnt,mprcnt); - 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; + if (strequal(name,"WKSSVC") || + strequal(name,"SRVSVC") || + strequal(name,"WINREG") || + strequal(name,"SAMR") || + strequal(name,"LSARPC")) + { + DEBUG(4,("named pipe command from Win95 (wow!)\n")); + return api_fd_reply(conn,vuid,outbuf,setup,data,params,suwcnt,tdscnt,tpscnt,mdrcnt,mprcnt); + } - SSVAL(*rparam,0,NERR_notsupported); - SSVAL(*rparam,2,0); /* converter word */ + if (strlen(name) < 1) + return api_fd_reply(conn,vuid,outbuf,setup,data,params,suwcnt,tdscnt,tpscnt,mdrcnt,mprcnt); - DEBUG(3,("Unsupported API command\n")); + if (setup) + DEBUG(3,("unknown named pipe: setup 0x%X setup1=%d\n", (int)setup[0],(int)setup[1])); - return(True); + return 0; } - - -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); + Reply to a SMBtrans. + ****************************************************************************/ + +int reply_trans(connection_struct *conn, char *inbuf,char *outbuf, int size, int bufsize) +{ + fstring name; + int name_offset = 0; + char *data=NULL,*params=NULL; + uint16 *setup=NULL; + int outsize = 0; + uint16 vuid = 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); + START_PROFILE(SMBtrans); + + memset(name, '\0',sizeof(name)); + srvstr_pull(inbuf, name, smb_buf(inbuf), sizeof(name), -1, STR_TERMINATE); + + if (dscnt > tdscnt || pscnt > tpscnt) { + exit_server("invalid trans parameters"); + } - 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 (tdscnt) { + if((data = (char *)malloc(tdscnt)) == NULL) { + DEBUG(0,("reply_trans: data malloc fail for %d bytes !\n", tdscnt)); + END_PROFILE(SMBtrans); + return(ERROR_DOS(ERRDOS,ERRnomem)); + } + memcpy(data,smb_base(inbuf)+dsoff,dscnt); + } - if (strequal(name,"LANMAN")) - return(api_reply(cnum,uid,outbuf,data,params,tdscnt,tpscnt,mdrcnt,mprcnt)); + if (tpscnt) { + if((params = (char *)malloc(tpscnt)) == NULL) { + DEBUG(0,("reply_trans: param malloc fail for %d bytes !\n", tpscnt)); + END_PROFILE(SMBtrans); + return(ERROR_DOS(ERRDOS,ERRnomem)); + } + memcpy(params,smb_base(inbuf)+psoff,pscnt); + } - DEBUG(3,("named pipe command on <%s> 0x%X setup1=%d\n", - name,(int)setup[0],(int)setup[1])); - - return(0); -} + if (suwcnt) { + int i; + if((setup = (uint16 *)malloc(suwcnt*sizeof(uint16))) == NULL) { + DEBUG(0,("reply_trans: setup malloc fail for %d bytes !\n", (int)(suwcnt * sizeof(uint16)))); + END_PROFILE(SMBtrans); + return(ERROR_DOS(ERRDOS,ERRnomem)); + } + for (i=0;i<suwcnt;i++) + setup[i] = SVAL(inbuf,smb_vwv14+i*SIZEOFWORD); + } -/**************************************************************************** - reply to a SMBtrans - ****************************************************************************/ -int reply_trans(char *inbuf,char *outbuf) -{ - fstring name; - - char *data=NULL,*params=NULL; - uint16 *setup=NULL; - - int outsize = 0; - int cnum = SVAL(inbuf,smb_tid); - int uid = SVAL(inbuf,smb_uid); - - int tpscnt = SVAL(inbuf,smb_vwv0); - int tdscnt = SVAL(inbuf,smb_vwv1); - int mprcnt = SVAL(inbuf,smb_vwv2); - int mdrcnt = SVAL(inbuf,smb_vwv3); - int msrcnt = CVAL(inbuf,smb_vwv4); - BOOL close_on_completion = BITSETW(inbuf+smb_vwv5,0); - BOOL one_way = BITSETW(inbuf+smb_vwv5,1); - int pscnt = SVAL(inbuf,smb_vwv9); - int psoff = SVAL(inbuf,smb_vwv10); - int dscnt = SVAL(inbuf,smb_vwv11); - int dsoff = SVAL(inbuf,smb_vwv12); - int suwcnt = CVAL(inbuf,smb_vwv13); - - StrnCpy(name,smb_buf(inbuf),sizeof(name)-1); - - if (tdscnt) - { - data = (char *)malloc(tdscnt); - memcpy(data,smb_base(inbuf)+dsoff,dscnt); - } - if (tpscnt) - { - params = (char *)malloc(tpscnt); - memcpy(params,smb_base(inbuf)+psoff,pscnt); - } - - if (suwcnt) - { - int i; - setup = (uint16 *)malloc(suwcnt*sizeof(setup[0])); - for (i=0;i<suwcnt;i++) - setup[i] = SVAL(inbuf,smb_vwv14+i*SIZEOFWORD); - } - - - if (pscnt < tpscnt || dscnt < tdscnt) - { - /* We need to send an interim response then receive the rest - of the parameter/data bytes */ - outsize = set_message(outbuf,0,0,True); - show_msg(outbuf); - send_smb(Client,outbuf); - } - - /* receive the rest of the trans packet */ - while (pscnt < tpscnt || dscnt < tdscnt) - { - int pcnt,poff,dcnt,doff,pdisp,ddisp; - - receive_smb(Client,inbuf, 0); - show_msg(inbuf); - - /* Ensure this is still a trans packet (sanity check) */ - if(CVAL(inbuf, smb_com) != SMBtrans) - { - DEBUG(2,("Invalid secondary trans2 packet\n")); - if (params) free(params); - if (data) free(data); - if (setup) free(setup); - return(ERROR(ERRSRV,ERRerror)); + if (pscnt < tpscnt || dscnt < tdscnt) { + /* We need to send an interim response then receive the rest + of the parameter/data bytes */ + outsize = set_message(outbuf,0,0,True); + show_msg(outbuf); + if (!send_smb(smbd_server_fd(),outbuf)) + exit_server("reply_trans: send_smb failed."); } - - tpscnt = SVAL(inbuf,smb_vwv0); - tdscnt = SVAL(inbuf,smb_vwv1); - pcnt = SVAL(inbuf,smb_vwv2); - poff = SVAL(inbuf,smb_vwv3); - pdisp = SVAL(inbuf,smb_vwv4); + /* receive the rest of the trans packet */ + while (pscnt < tpscnt || dscnt < tdscnt) { + BOOL ret; + int pcnt,poff,dcnt,doff,pdisp,ddisp; - dcnt = SVAL(inbuf,smb_vwv5); - doff = SVAL(inbuf,smb_vwv6); - ddisp = SVAL(inbuf,smb_vwv7); + ret = receive_next_smb(inbuf,bufsize,SMB_SECONDARY_WAIT); + + if ((ret && (CVAL(inbuf, smb_com) != SMBtranss)) || !ret) { + if(ret) { + DEBUG(0,("reply_trans: Invalid secondary trans packet\n")); + } else { + DEBUG(0,("reply_trans: %s in getting secondary trans response.\n", + (smb_read_error == READ_ERROR) ? "error" : "timeout" )); + } + SAFE_FREE(params); + SAFE_FREE(data); + SAFE_FREE(setup); + END_PROFILE(SMBtrans); + return(ERROR_DOS(ERRSRV,ERRerror)); + } + + show_msg(inbuf); - pscnt += pcnt; - dscnt += dcnt; - - if (pcnt) - memcpy(params+pdisp,smb_base(inbuf)+poff,pcnt); - if (dcnt) - memcpy(data+ddisp,smb_base(inbuf)+doff,dcnt); - } - - - DEBUG(3,("trans <%s> data=%d params=%d setup=%d\n",name,tdscnt,tpscnt,suwcnt)); - - - if (strncmp(name,"\\PIPE\\",strlen("\\PIPE\\")) == 0) - outsize = named_pipe(cnum,uid,outbuf,name+strlen("\\PIPE\\"),setup,data,params, - suwcnt,tdscnt,tpscnt,msrcnt,mdrcnt,mprcnt); - - - if (data) free(data); - if (params) free(params); - if (setup) free(setup); - - if (close_on_completion) - close_cnum(cnum,uid); + tpscnt = SVAL(inbuf,smb_vwv0); + tdscnt = SVAL(inbuf,smb_vwv1); + + pcnt = SVAL(inbuf,smb_vwv2); + poff = SVAL(inbuf,smb_vwv3); + pdisp = SVAL(inbuf,smb_vwv4); + + dcnt = SVAL(inbuf,smb_vwv5); + doff = SVAL(inbuf,smb_vwv6); + ddisp = SVAL(inbuf,smb_vwv7); + + pscnt += pcnt; + dscnt += dcnt; + + if (dscnt > tdscnt || pscnt > tpscnt) { + exit_server("invalid trans parameters"); + } + + if (pcnt) + memcpy(params+pdisp,smb_base(inbuf)+poff,pcnt); + if (dcnt) + memcpy(data+ddisp,smb_base(inbuf)+doff,dcnt); + } + + + DEBUG(3,("trans <%s> data=%d params=%d setup=%d\n", + name,tdscnt,tpscnt,suwcnt)); + + /* + * WinCE wierdness.... + */ + + if (name[0] == '\\' && (StrnCaseCmp(&name[1],local_machine, strlen(local_machine)) == 0) && + (name[strlen(local_machine)+1] == '\\')) + name_offset = strlen(local_machine)+1; + + if (strnequal(&name[name_offset], "\\PIPE", strlen("\\PIPE"))) { + name_offset += strlen("\\PIPE"); + + /* Win9x weirdness. When talking to a unicode server Win9x + only sends \PIPE instead of \PIPE\ */ + + if (name[name_offset] == '\\') + name_offset++; + + DEBUG(5,("calling named_pipe\n")); + outsize = named_pipe(conn,vuid,outbuf, + name+name_offset,setup,data,params, + suwcnt,tdscnt,tpscnt,msrcnt,mdrcnt,mprcnt); + } else { + DEBUG(3,("invalid pipe name\n")); + outsize = 0; + } - if (one_way) - return(-1); - - if (outsize == 0) - return(ERROR(ERRSRV,ERRnosupport)); + + SAFE_FREE(data); + SAFE_FREE(params); + SAFE_FREE(setup); + + if (close_on_completion) + close_cnum(conn,vuid); - return(outsize); + if (one_way) { + END_PROFILE(SMBtrans); + return(-1); + } + + if (outsize == 0) { + END_PROFILE(SMBtrans); + return(ERROR_DOS(ERRSRV,ERRnosupport)); + } + + END_PROFILE(SMBtrans); + return(outsize); } - - diff --git a/source3/smbd/lanman.c b/source3/smbd/lanman.c new file mode 100644 index 0000000000..666bbb5f61 --- /dev/null +++ b/source3/smbd/lanman.c @@ -0,0 +1,3650 @@ +/* + Unix SMB/CIFS implementation. + Inter-process communication and named pipe handling + Copyright (C) Andrew Tridgell 1992-1998 + + SMB Version handling + Copyright (C) John H Terpstra 1995-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + 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" + +#ifdef CHECK_TYPES +#undef CHECK_TYPES +#endif +#define CHECK_TYPES 0 + +extern fstring local_machine; +extern pstring global_myname; +extern fstring global_myworkgroup; + +#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 ACCESS_READ 0x01 +#define ACCESS_WRITE 0x02 +#define ACCESS_CREATE 0x04 + +#define SHPWLEN 8 /* share password length */ + +static BOOL api_Unsupported(connection_struct *conn,uint16 vuid, char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len); +static BOOL api_TooSmall(connection_struct *conn,uint16 vuid, char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len); + + +static int CopyExpanded(connection_struct *conn, + 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); + pstring_sub(buf,"%S",lp_servicename(snum)); + standard_sub_conn(conn,buf); + l = push_ascii(*dst,buf,*n-1, STR_TERMINATE); + (*dst) += l; + (*n) -= l; + return l; +} + +static int CopyAndAdvance(char** dst, char* src, int* n) +{ + int l; + if (!src || !dst || !n || !(*dst)) return(0); + l = push_ascii(*dst,src,*n, STR_TERMINATE); + (*dst) += l; + (*n) -= l; + return l; +} + +static int StrlenExpanded(connection_struct *conn, int snum, char* s) +{ + pstring buf; + if (!s) return(0); + StrnCpy(buf,s,sizeof(buf)/2); + pstring_sub(buf,"%S",lp_servicename(snum)); + standard_sub_conn(conn,buf); + return strlen(buf) + 1; +} + +static char* Expand(connection_struct *conn, int snum, char* s) +{ + static pstring buf; + if (!s) return(NULL); + StrnCpy(buf,s,sizeof(buf)/2); + pstring_sub(buf,"%S",lp_servicename(snum)); + standard_sub_conn(conn,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); +} + +struct pack_desc { + char* format; /* formatstring for structure */ + char* subformat; /* subformat for structure */ + char* base; /* baseaddress of buffer */ + int buflen; /* remaining size for fixed part; on init: length of base */ + int subcount; /* count of substructures */ + char* structbuf; /* pointer into buffer for remaining fixed part */ + int stringlen; /* remaining size for variable part */ + char* stringbuf; /* pointer into buffer for remaining variable part */ + int neededlen; /* total needed size */ + int usedlen; /* total used size (usedlen <= neededlen and usedlen <= buflen) */ + char* curpos; /* current position; pointer into format or subformat */ + int errcode; +}; + +static int get_counter(char** p) +{ + int i, n; + if (!p || !(*p)) return(1); + if (!isdigit((int)**p)) return 1; + for (n = 0;;) { + i = **p; + if (isdigit(i)) + n = 10 * n + (i - '0'); + else + return n; + (*p)++; + } +} + +static int getlen(char* p) +{ + int n = 0; + if (!p) return(0); + while (*p) { + switch( *p++ ) { + case 'W': /* word (2 byte) */ + n += 2; + break; + case 'K': /* status word? (2 byte) */ + n += 2; + break; + case 'N': /* count of substructures (word) at end */ + n += 2; + break; + case 'D': /* double word (4 byte) */ + case 'z': /* offset to zero terminated string (4 byte) */ + case 'l': /* offset to user data (4 byte) */ + n += 4; + break; + case 'b': /* offset to data (with counter) (4 byte) */ + n += 4; + get_counter(&p); + break; + case 'B': /* byte (with optional counter) */ + n += get_counter(&p); + break; + } + } + return n; +} + +static BOOL init_package(struct pack_desc* p, int count, int subcount) +{ + int n = p->buflen; + int i; + + if (!p->format || !p->base) return(False); + + i = count * getlen(p->format); + if (p->subformat) i += subcount * getlen(p->subformat); + p->structbuf = p->base; + p->neededlen = 0; + p->usedlen = 0; + p->subcount = 0; + p->curpos = p->format; + if (i > n) { + p->neededlen = i; + i = n = 0; +#if 0 + /* + * This is the old error code we used. Aparently + * WinNT/2k systems return ERRbuftoosmall (2123) and + * OS/2 needs this. I'm leaving this here so we can revert + * if needed. JRA. + */ + p->errcode = ERRmoredata; +#else + p->errcode = ERRbuftoosmall; +#endif + } + else + p->errcode = NERR_Success; + p->buflen = i; + n -= i; + p->stringbuf = p->base + i; + p->stringlen = n; + return(p->errcode == NERR_Success); +} + +static int package(struct pack_desc* p, ...) +{ + va_list args; + int needed=0, stringneeded; + char* str=NULL; + int is_string=0, stringused; + int32 temp; + + va_start(args,p); + + 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*); + SMB_ASSERT(strncmp(str,p->curpos,strlen(str)) == 0); +#endif + stringneeded = -1; + + if (!p->curpos) { + va_end(args); + 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 'K': /* status 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-1); + } + 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 = ERRmoredata; + } + 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 = ERRmoredata; + } + 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); +} + + +/**************************************************************************** + get a print queue + ****************************************************************************/ +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 */ + push_ascii(drivdata+8,"NULL",-1, STR_TERMINATE); + PACKl(desc,"l",drivdata,sizeof drivdata); /* pDriverData */ +} + +static int check_printq_info(struct pack_desc* desc, + int uLevel, char *id1, 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; + case 51: + desc->format = "K"; + break; + case 52: + desc->format = "WzzzzzzzzN"; + desc->subformat = "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; +} + + +#define RAP_JOB_STATUS_QUEUED 0 +#define RAP_JOB_STATUS_PAUSED 1 +#define RAP_JOB_STATUS_SPOOLING 2 +#define RAP_JOB_STATUS_PRINTING 3 +#define RAP_JOB_STATUS_PRINTED 4 + +#define RAP_QUEUE_STATUS_PAUSED 1 +#define RAP_QUEUE_STATUS_ERROR 2 + +/* turn a print job status into a on the wire status +*/ +static int printj_status(int v) +{ + switch (v) { + case LPQ_QUEUED: + return RAP_JOB_STATUS_QUEUED; + case LPQ_PAUSED: + return RAP_JOB_STATUS_PAUSED; + case LPQ_SPOOLING: + return RAP_JOB_STATUS_SPOOLING; + case LPQ_PRINTING: + return RAP_JOB_STATUS_PRINTING; + } + return 0; +} + +/* turn a print queue status into a on the wire status +*/ +static int printq_status(int v) +{ + switch (v) { + case LPQ_QUEUED: + return 0; + case LPQ_PAUSED: + return RAP_QUEUE_STATUS_PAUSED; + } + return RAP_QUEUE_STATUS_ERROR; +} + +static void fill_printjob_info(connection_struct *conn, int snum, int uLevel, + struct pack_desc* desc, + print_queue_struct* queue, int n) +{ + time_t t = queue->time; + + /* the client expects localtime */ + t -= TimeDiff(t); + + PACKI(desc,"W",queue->job); /* uJobId */ + if (uLevel == 1) { + PACKS(desc,"B21",queue->fs_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",printj_status(queue->status)); /* fsStatus */ + PACKS(desc,"z",""); /* pszStatus */ + PACKI(desc,"D",t); /* ulSubmitted */ + PACKI(desc,"D",queue->size); /* ulSize */ + PACKS(desc,"z",queue->fs_file); /* pszComment */ + } + if (uLevel == 2 || uLevel == 3 || uLevel == 4) { + PACKI(desc,"W",queue->priority); /* uPriority */ + PACKS(desc,"z",queue->fs_user); /* pszUserName */ + PACKI(desc,"W",n+1); /* uPosition */ + PACKI(desc,"W",printj_status(queue->status)); /* fsStatus */ + PACKI(desc,"D",t); /* ulSubmitted */ + PACKI(desc,"D",queue->size); /* ulSize */ + PACKS(desc,"z","Samba"); /* pszComment */ + PACKS(desc,"z",queue->fs_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 */ + } else if (uLevel == 4) { /* OS2 */ + PACKS(desc,"z",""); /* pszSpoolFileName */ + PACKS(desc,"z",""); /* pszPortName */ + PACKS(desc,"z",""); /* pszStatus */ + PACKI(desc,"D",0); /* ulPagesSpooled */ + PACKI(desc,"D",0); /* ulPagesSent */ + PACKI(desc,"D",0); /* ulPagesPrinted */ + PACKI(desc,"D",0); /* ulTimePrinted */ + PACKI(desc,"D",0); /* ulExtendJobStatus */ + PACKI(desc,"D",0); /* ulStartPage */ + PACKI(desc,"D",0); /* ulEndPage */ + } + } +} + +/******************************************************************** + Return a driver name given an snum. + Looks in a tdb first. Returns True if from tdb, False otherwise. + ********************************************************************/ + +static BOOL get_driver_name(int snum, pstring drivername) +{ + NT_PRINTER_INFO_LEVEL *info = NULL; + BOOL in_tdb = False; + + get_a_printer (&info, 2, lp_servicename(snum)); + if (info != NULL) { + pstrcpy( drivername, info->info_2->drivername); + in_tdb = True; + free_a_printer(&info, 2); + } else { + pstrcpy( drivername, lp_printerdriver(snum)); + } + + return in_tdb; +} + +/******************************************************************** + Respond to the DosPrintQInfo command with a level of 52 + This is used to get printer driver information for Win9x clients + ********************************************************************/ +static void fill_printq_info_52(connection_struct *conn, int snum, int uLevel, + struct pack_desc* desc, + int count, print_queue_struct* queue, + print_status_struct* status) +{ + int i; + BOOL ok = False; + pstring tok,driver,datafile,langmon,helpfile,datatype; + char *p; + char **lines = NULL; + pstring gen_line; + BOOL in_tdb = False; + fstring location; + pstring drivername; + + /* + * Check in the tdb *first* before checking the legacy + * files. This allows an NT upload to take precedence over + * the existing fileset. JRA. + * + * we need to lookup the driver name prior to making the call + * to get_a_printer_driver_9x_compatible() and not rely on the + * 'print driver' parameter --jerry + */ + + + if ((get_driver_name(snum,drivername)) && + ((ok = get_a_printer_driver_9x_compatible(gen_line, drivername)) == True)) + { + in_tdb = True; + p = gen_line; + DEBUG(10,("9x compatable driver line for [%s]: [%s]\n", drivername, gen_line)); + } + else + { + /* didn't find driver in tdb */ + + DEBUG(10,("snum: %d\nprinterdriver: [%s]\nlp_driverfile: [%s]\n", + snum, drivername, lp_driverfile(snum))); + + lines = file_lines_load(lp_driverfile(snum),NULL); + if (!lines) + { + DEBUG(3,("Can't open %s - %s\n", lp_driverfile(snum), + strerror(errno))); + desc->errcode=NERR_notsupported; + goto done; + } + + /* lookup the long printer driver name in the file description */ + for (i=0;lines[i] && !ok;i++) + { + p = lines[i]; + if (next_token(&p,tok,":",sizeof(tok)) && + (strlen(drivername) == strlen(tok)) && + (!strncmp(tok,drivername,strlen(drivername)))) + { + ok = True; + } + } + } + + if (ok) + { + /* driver file name */ + if (!next_token(&p,driver,":",sizeof(driver))) + goto err; + + /* data file name */ + if (!next_token(&p,datafile,":",sizeof(datafile))) + goto err; + + /* + * for the next tokens - which may be empty - I have + * to check for empty tokens first because the + * next_token function will skip all empty token + * fields */ + + /* help file */ + if (*p == ':') + { + *helpfile = '\0'; + p++; + } + else if (!next_token(&p,helpfile,":",sizeof(helpfile))) + goto err; + + /* language monitor */ + if (*p == ':') + { + *langmon = '\0'; + p++; + } + else if (!next_token(&p,langmon,":",sizeof(langmon))) + goto err; + + /* default data type */ + if (!next_token(&p,datatype,":",sizeof(datatype))) + goto err; + + PACKI(desc,"W",0x0400); /* don't know */ + PACKS(desc,"z",drivername); /* long printer name */ + PACKS(desc,"z",driver); /* Driverfile Name */ + PACKS(desc,"z",datafile); /* Datafile name */ + PACKS(desc,"z",langmon); /* language monitor */ + if (in_tdb) + { + fstrcpy(location, "\\\\"); + fstrcat(location, global_myname); + fstrcat(location, "\\print$\\WIN40\\0"); + PACKS(desc,"z",location); /* share to retrieve files */ + } + else + { + PACKS(desc,"z",lp_driverlocation(snum)); /* share to retrieve files */ + } + PACKS(desc,"z",datatype); /* default data type */ + PACKS(desc,"z",helpfile); /* helpfile name */ + PACKS(desc,"z",driver); /* driver name */ + + DEBUG(3,("printerdriver:%s:\n",drivername)); + DEBUG(3,("Driver:%s:\n",driver)); + DEBUG(3,("Data File:%s:\n",datafile)); + DEBUG(3,("Language Monitor:%s:\n",langmon)); + if (in_tdb) + DEBUG(3,("lp_driverlocation:%s:\n",location)); + else + DEBUG(3,("lp_driverlocation:%s:\n",lp_driverlocation(snum))); + DEBUG(3,("Data Type:%s:\n",datatype)); + DEBUG(3,("Help File:%s:\n",helpfile)); + PACKI(desc,"N",count); /* number of files to copy */ + + for (i=0;i<count;i++) + { + /* no need to check return value here + * - it was already tested in + * get_printerdrivernumber */ + next_token(&p,tok,",",sizeof(tok)); + PACKS(desc,"z",tok); /* driver files to copy */ + DEBUG(3,("file:%s:\n",tok)); + } + + DEBUG(3,("fill_printq_info on <%s> gave %d entries\n", + SERVICE(snum),count)); + + desc->errcode=NERR_Success; + goto done; + } + + err: + + DEBUG(3,("fill_printq_info: Can't supply driver files\n")); + desc->errcode=NERR_notsupported; + + done: + file_lines_free(lines); +} + + +static void fill_printq_info(connection_struct *conn, int snum, int uLevel, + struct pack_desc* desc, + int count, print_queue_struct* queue, + print_status_struct* status) +{ + switch (uLevel) { + case 1: + case 2: + PACKS(desc,"B13",SERVICE(snum)); + break; + case 3: + case 4: + case 5: + PACKS(desc,"z",Expand(conn,snum,SERVICE(snum))); + break; + case 51: + PACKI(desc,"K",printq_status(status->status)); + break; + } + + 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(conn,snum,lp_comment(snum))); + PACKI(desc,"W",LPSTAT_OK); /* status */ + } else { + PACKS(desc,"z",status->message); + PACKI(desc,"W",printq_status(status->status)); /* status */ + } + PACKI(desc,(uLevel == 1 ? "W" : "N"),count); + } + + if (uLevel == 3 || uLevel == 4) { + pstring drivername; + + 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","WinPrint"); /* pszPrProc */ + PACKS(desc,"z",NULL); /* pszParms */ + PACKS(desc,"z",NULL); /* pszComment - don't ask.... JRA */ + /* "don't ask" that it's done this way to fix corrupted + Win9X/ME printer comments. */ + if (!status) { + PACKI(desc,"W",LPSTAT_OK); /* fsStatus */ + } else { + PACKI(desc,"W",printq_status(status->status)); /* fsStatus */ + } + PACKI(desc,(uLevel == 3 ? "W" : "N"),count); /* cJobs */ + PACKS(desc,"z",SERVICE(snum)); /* pszPrinters */ + get_driver_name(snum,drivername); + PACKS(desc,"z",drivername); /* pszDriverName */ + PackDriverData(desc); /* pDriverData */ + } + + if (uLevel == 2 || uLevel == 4) { + int i; + for (i=0;i<count;i++) + fill_printjob_info(conn,snum,uLevel == 2 ? 1 : 2,desc,&queue[i],i); + } + + if (uLevel==52) { + fill_printq_info_52(conn, snum, uLevel, desc, count, queue, status); + } +} + +/* This function returns the number of files for a given driver */ +static int get_printerdrivernumber(int snum) +{ + int i, result = 0; + BOOL ok = False; + pstring tok; + char *p; + char **lines = NULL; + pstring gen_line; + pstring drivername; + + /* + * Check in the tdb *first* before checking the legacy + * files. This allows an NT upload to take precedence over + * the existing fileset. JRA. + * + * we need to lookup the driver name prior to making the call + * to get_a_printer_driver_9x_compatible() and not rely on the + * 'print driver' parameter --jerry + */ + + if ((get_driver_name(snum,drivername)) && + (ok = get_a_printer_driver_9x_compatible(gen_line, drivername) == True)) + { + p = gen_line; + DEBUG(10,("9x compatable driver line for [%s]: [%s]\n", drivername, gen_line)); + } + else + { + /* didn't find driver in tdb */ + + DEBUG(10,("snum: %d\nprinterdriver: [%s]\nlp_driverfile: [%s]\n", + snum, drivername, lp_driverfile(snum))); + + lines = file_lines_load(lp_driverfile(snum), NULL); + if (!lines) + { + DEBUG(3,("Can't open %s - %s\n", lp_driverfile(snum),strerror(errno))); + goto done; + } + + /* lookup the long printer driver name in the file description */ + for (i=0;lines[i] && !ok;i++) + { + p = lines[i]; + if (next_token(&p,tok,":",sizeof(tok)) && + (strlen(drivername) == strlen(tok)) && + (!strncmp(tok,drivername,strlen(drivername)))) + { + ok = True; + } + } + } + + if( ok ) + { + /* skip 5 fields */ + i = 5; + while (*p && i) { + if (*p++ == ':') i--; + } + if (!*p || i) { + DEBUG(3,("Can't determine number of printer driver files\n")); + goto done; + } + + /* count the number of files */ + while (next_token(&p,tok,",",sizeof(tok))) + i++; + + result = i; + } + + done: + + file_lines_free(lines); + + return result; +} + +static BOOL api_DosPrintQGetInfo(connection_struct *conn, + uint16 vuid, 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; + int count=0; + int snum; + char* str3; + struct pack_desc desc; + print_queue_struct *queue=NULL; + print_status_struct status; + char* tmpdata=NULL; + + memset((char *)&status,'\0',sizeof(status)); + memset((char *)&desc,'\0',sizeof(desc)); + + p = skip_string(p,1); + uLevel = SVAL(p,0); + str3 = p + 4; + + /* remove any trailing username */ + if ((p = strchr_m(QueueName,'%'))) + *p = 0; + + DEBUG(3,("api_DosPrintQGetInfo 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)) { + /* + * Patch from Scott Moomaw <scott@bridgewater.edu> + * to return the 'invalid info level' error if an + * unknown level was requested. + */ + *rdata_len = 0; + *rparam_len = 6; + *rparam = REALLOC(*rparam,*rparam_len); + SSVALS(*rparam,0,ERRunknownlevel); + SSVAL(*rparam,2,0); + SSVAL(*rparam,4,0); + return(True); + } + + 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); + + if (uLevel==52) { + count = get_printerdrivernumber(snum); + DEBUG(3,("api_DosPrintQGetInfo: Driver files count: %d\n",count)); + } else { + count = print_queue_status(snum, &queue,&status); + } + + if (mdrcnt > 0) { + *rdata = REALLOC(*rdata,mdrcnt); + desc.base = *rdata; + desc.buflen = mdrcnt; + } else { + /* + * Don't return data but need to get correct length + * init_package will return wrong size if buflen=0 + */ + desc.buflen = getlen(desc.format); + desc.base = tmpdata = (char *) malloc (desc.buflen); + } + + if (init_package(&desc,1,count)) { + desc.subcount = count; + fill_printq_info(conn,snum,uLevel,&desc,count,queue,&status); + } + + *rdata_len = desc.usedlen; + + /* + * We must set the return code to ERRbuftoosmall + * in order to support lanman style printing with Win NT/2k + * clients --jerry + */ + if (!mdrcnt && lp_disable_spoolss()) + desc.errcode = ERRbuftoosmall; + + *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)); + + SAFE_FREE(queue); + SAFE_FREE(tmpdata); + + return(True); +} + +/**************************************************************************** + View list of all print jobs on all queues. +****************************************************************************/ + +static BOOL api_DosPrintQEnum(connection_struct *conn, uint16 vuid, 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; + + memset((char *)&desc,'\0',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)) { + /* + * Patch from Scott Moomaw <scott@bridgewater.edu> + * to return the 'invalid info level' error if an + * unknown level was requested. + */ + *rdata_len = 0; + *rparam_len = 6; + *rparam = REALLOC(*rparam,*rparam_len); + SSVALS(*rparam,0,ERRunknownlevel); + SSVAL(*rparam,2,0); + SSVAL(*rparam,4,0); + return(True); + } + + queuecnt = 0; + for (i = 0; i < services; i++) + if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i)) + queuecnt++; + if (uLevel > 0) { + if((queue = (print_queue_struct**)malloc(queuecnt*sizeof(print_queue_struct*))) == NULL) { + DEBUG(0,("api_DosPrintQEnum: malloc fail !\n")); + return False; + } + memset(queue,0,queuecnt*sizeof(print_queue_struct*)); + if((status = (print_status_struct*)malloc(queuecnt*sizeof(print_status_struct))) == NULL) { + DEBUG(0,("api_DosPrintQEnum: malloc fail !\n")); + return False; + } + memset(status,0,queuecnt*sizeof(print_status_struct)); + if((subcntarr = (int*)malloc(queuecnt*sizeof(int))) == NULL) { + DEBUG(0,("api_DosPrintQEnum: malloc fail !\n")); + return False; + } + subcnt = 0; + n = 0; + for (i = 0; i < services; i++) + if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i)) { + subcntarr[n] = print_queue_status(i, &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(conn,i,uLevel,&desc,subcntarr[n],queue[n],&status[n]); + n++; + if (desc.errcode == NERR_Success) succnt = n; + } + } + + SAFE_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) SAFE_FREE(queue[i]); + } + + SAFE_FREE(queue); + SAFE_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; +} + +struct srv_info_struct +{ + fstring name; + uint32 type; + fstring comment; + fstring domain; + BOOL server_added; +}; + + +/******************************************************************* + 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, + char *domain) +{ + int count=0; + int alloced=0; + char **lines; + BOOL local_list_only; + int i; + + lines = file_lines_load(lock_path(SERVER_LIST), NULL); + if (!lines) { + DEBUG(4,("Can't open %s - %s\n",lock_path(SERVER_LIST),strerror(errno))); + return(0); + } + + /* request for everything is code for request all servers */ + if (servertype == SV_TYPE_ALL) + servertype &= ~(SV_TYPE_DOMAIN_ENUM|SV_TYPE_LOCAL_LIST_ONLY); + + local_list_only = (servertype & SV_TYPE_LOCAL_LIST_ONLY); + + DEBUG(4,("Servertype search: %8x\n",servertype)); + + for (i=0;lines[i];i++) { + fstring stype; + struct srv_info_struct *s; + char *ptr = lines[i]; + BOOL ok = True; + + if (!*ptr) continue; + + if (count == alloced) { + struct srv_info_struct *ts; + + alloced += 10; + ts = (struct srv_info_struct *) + Realloc(*servers,sizeof(**servers)*alloced); + if (!ts) { + DEBUG(0,("get_server_info: failed to enlarge servers info struct!\n")); + return(0); + } + else *servers = ts; + memset((char *)((*servers)+count),'\0',sizeof(**servers)*(alloced-count)); + } + s = &(*servers)[count]; + + if (!next_token(&ptr,s->name , NULL, sizeof(s->name))) continue; + if (!next_token(&ptr,stype , NULL, sizeof(stype))) continue; + if (!next_token(&ptr,s->comment, NULL, sizeof(s->comment))) continue; + if (!next_token(&ptr,s->domain , NULL, sizeof(s->domain))) { + /* this allows us to cope with an old nmbd */ + pstrcpy(s->domain,global_myworkgroup); + } + + if (sscanf(stype,"%X",&s->type) != 1) { + DEBUG(4,("r:host file ")); + ok = False; + } + + /* Filter the servers/domains we return based on what was asked for. */ + + /* Check to see if we are being asked for a local list only. */ + if(local_list_only && ((s->type & SV_TYPE_LOCAL_LIST_ONLY) == 0)) { + DEBUG(4,("r: local list only")); + ok = False; + } + + /* doesn't match up: don't want it */ + if (!(servertype & s->type)) { + DEBUG(4,("r:serv type ")); + ok = False; + } + + if ((servertype & SV_TYPE_DOMAIN_ENUM) != + (s->type & SV_TYPE_DOMAIN_ENUM)) + { + DEBUG(4,("s: dom mismatch ")); + ok = False; + } + + if (!strequal(domain, s->domain) && !(servertype & SV_TYPE_DOMAIN_ENUM)) + { + ok = False; + } + + /* We should never return a server type with a SV_TYPE_LOCAL_LIST_ONLY set. */ + s->type &= ~SV_TYPE_LOCAL_LIST_ONLY; + + if (ok) + { + DEBUG(4,("**SV** %20s %8x %25s %15s\n", + s->name, s->type, s->comment, s->domain)); + + s->server_added = True; + count++; + } + else + { + DEBUG(4,("%20s %8x %25s %15s\n", + s->name, s->type, s->comment, s->domain)); + } + } + + file_lines_free(lines); + 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: + push_ascii(p,service->name, 15, STR_TERMINATE); + break; + + case 1: + push_ascii(p,service->name,15, STR_TERMINATE); + 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; +} + + +static BOOL srv_comp(struct srv_info_struct *s1,struct srv_info_struct *s2) +{ + return(strcmp(s1->name,s2->name)); +} + +/**************************************************************************** + 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(connection_struct *conn, uint16 vuid, 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 = 0, s_len = 0; + struct srv_info_struct *servers=NULL; + int counted=0,total=0; + int i,missed; + fstring domain; + BOOL domain_request; + BOOL local_request; + + /* If someone sets all the bits they don't really mean to set + DOMAIN_ENUM and LOCAL_LIST_ONLY, they just want all the + known servers. */ + + if (servertype == SV_TYPE_ALL) + servertype &= ~(SV_TYPE_DOMAIN_ENUM|SV_TYPE_LOCAL_LIST_ONLY); + + /* If someone sets SV_TYPE_LOCAL_LIST_ONLY but hasn't set + any other bit (they may just set this bit on it's own) they + want all the locally seen servers. However this bit can be + set on its own so set the requested servers to be + ALL - DOMAIN_ENUM. */ + + if ((servertype & SV_TYPE_LOCAL_LIST_ONLY) && !(servertype & SV_TYPE_DOMAIN_ENUM)) + servertype = SV_TYPE_ALL & ~(SV_TYPE_DOMAIN_ENUM); + + domain_request = ((servertype & SV_TYPE_DOMAIN_ENUM) != 0); + local_request = ((servertype & SV_TYPE_LOCAL_LIST_ONLY) != 0); + + p += 8; + + if (!prefix_ok(str1,"WrLehD")) return False; + if (!check_server_info(uLevel,str2)) return False; + + DEBUG(4, ("server request level: %s %8x ", str2, servertype)); + DEBUG(4, ("domains_req:%s ", BOOLSTR(domain_request))); + DEBUG(4, ("local_only:%s\n", BOOLSTR(local_request))); + + if (strcmp(str1, "WrLehDz") == 0) { + pull_ascii_fstring(domain, p); + } else { + fstrcpy(domain, global_myworkgroup); + } + + if (lp_browse_list()) + total = get_server_info(servertype,&servers,domain); + + data_len = fixed_len = string_len = 0; + missed = 0; + + if (total > 0) + qsort(servers,total,sizeof(servers[0]),QSORT_CAST srv_comp); + + { + char *lastname=NULL; + + for (i=0;i<total;i++) + { + struct srv_info_struct *s = &servers[i]; + if (lastname && strequal(lastname,s->name)) continue; + lastname = s->name; + data_len += fill_srv_info(s,uLevel,0,&f_len,0,&s_len,0); + DEBUG(4,("fill_srv_info %20s %8x %25s %15s\n", + s->name, s->type, s->comment, s->domain)); + + if (data_len <= buf_len) { + counted++; + fixed_len += f_len; + string_len += s_len; + } else { + missed++; + } + } + } + + *rdata_len = fixed_len + string_len; + *rdata = REALLOC(*rdata,*rdata_len); + memset(*rdata,'\0',*rdata_len); + + p2 = (*rdata) + fixed_len; /* auxilliary data (strings) will go here */ + p = *rdata; + f_len = fixed_len; + s_len = string_len; + + { + char *lastname=NULL; + int count2 = counted; + for (i = 0; i < total && count2;i++) + { + struct srv_info_struct *s = &servers[i]; + if (lastname && strequal(lastname,s->name)) continue; + lastname = s->name; + fill_srv_info(s,uLevel,&p,&f_len,&p2,&s_len,*rdata); + DEBUG(4,("fill_srv_info %20s %8x %25s %15s\n", + s->name, s->type, s->comment, s->domain)); + count2--; + } + } + + *rparam_len = 8; + *rparam = REALLOC(*rparam,*rparam_len); + SSVAL(*rparam,0,(missed == 0 ? NERR_Success : ERRmoredata)); + SSVAL(*rparam,2,0); + SSVAL(*rparam,4,counted); + SSVAL(*rparam,6,counted+missed); + + SAFE_FREE(servers); + + DEBUG(3,("NetServerEnum domain = %s uLevel=%d counted=%d total=%d\n", + domain,uLevel,counted,counted+missed)); + + return(True); +} + +/**************************************************************************** + command 0x34 - suspected of being a "Lookup Names" stub api + ****************************************************************************/ +static BOOL api_RNetGroupGetUsers(connection_struct *conn, uint16 vuid, 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); + int counted=0; + int missed=0; + + DEBUG(5,("RNetGroupGetUsers: %s %s %s %d %d\n", + str1, str2, p, uLevel, buf_len)); + + if (!prefix_ok(str1,"zWrLeh")) return False; + + *rdata_len = 0; + + *rparam_len = 8; + *rparam = REALLOC(*rparam,*rparam_len); + + SSVAL(*rparam,0,0x08AC); /* informational warning message */ + SSVAL(*rparam,2,0); + SSVAL(*rparam,4,counted); + SSVAL(*rparam,6,counted+missed); + + return(True); +} + +/**************************************************************************** + get info about a share + ****************************************************************************/ +static BOOL check_share_info(int uLevel, char* id) +{ + switch( uLevel ) { + case 0: + if (strcmp(id,"B13") != 0) return False; + break; + case 1: + if (strcmp(id,"B13BWz") != 0) return False; + break; + case 2: + if (strcmp(id,"B13BWzWWWzB9B") != 0) return False; + break; + case 91: + if (strcmp(id,"B13BWzWWWzB9BB9BWzWWzWW") != 0) return False; + break; + default: return False; + } + return True; +} + +static int fill_share_info(connection_struct *conn, int snum, int uLevel, + char** buf, int* buflen, + char** stringbuf, int* stringspace, char* baseaddr) +{ + int struct_len; + char* p; + char* p2; + int l2; + int len; + + switch( uLevel ) { + case 0: struct_len = 13; break; + case 1: struct_len = 20; break; + case 2: struct_len = 40; break; + case 91: struct_len = 68; break; + default: return -1; + } + + + if (!buf) + { + len = 0; + if (uLevel > 0) len += StrlenExpanded(conn,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; + + push_ascii(p,lp_servicename(snum),13, STR_TERMINATE); + + if (uLevel > 0) + { + int type; + SCVAL(p,13,0); + type = STYPE_DISKTREE; + if (lp_print_ok(snum)) type = STYPE_PRINTQ; + if (strequal("IPC",lp_fstype(snum))) type = STYPE_IPC; + SSVAL(p,14,type); /* device type */ + SIVAL(p,16,PTR_DIFF(p2,baseaddr)); + len += CopyExpanded(conn,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(connection_struct *conn,uint16 vuid, 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(conn,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(connection_struct *conn,uint16 vuid, 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; + BOOL missed = False; + int i; + int data_len, fixed_len, string_len; + int f_len = 0, s_len = 0; + + if (!prefix_ok(str1,"WrLeh")) return False; + if (!check_share_info(uLevel,str2)) return False; + + data_len = fixed_len = string_len = 0; + for (i=0;i<count;i++) + if (lp_browseable(i) && lp_snum_ok(i)) + { + total++; + data_len += fill_share_info(conn,i,uLevel,0,&f_len,0,&s_len,0); + if (data_len <= buf_len) + { + counted++; + fixed_len += f_len; + string_len += s_len; + } + else + missed = True; + } + *rdata_len = fixed_len + string_len; + *rdata = REALLOC(*rdata,*rdata_len); + memset(*rdata,0,*rdata_len); + + p2 = (*rdata) + fixed_len; /* auxiliary data (strings) will go here */ + p = *rdata; + f_len = fixed_len; + s_len = string_len; + for (i = 0; i < count;i++) + if (lp_browseable(i) && lp_snum_ok(i)) + if (fill_share_info(conn,i,uLevel,&p,&f_len,&p2,&s_len,*rdata) < 0) + break; + + *rparam_len = 8; + *rparam = REALLOC(*rparam,*rparam_len); + SSVAL(*rparam,0,missed ? ERRmoredata : NERR_Success); + SSVAL(*rparam,2,0); + SSVAL(*rparam,4,counted); + SSVAL(*rparam,6,total); + + DEBUG(3,("RNetShareEnum gave %d entries of %d (%d %d %d %d)\n", + counted,total,uLevel, + buf_len,*rdata_len,mdrcnt)); + return(True); +} + +/**************************************************************************** + Add a share + ****************************************************************************/ +static BOOL api_RNetShareAdd(connection_struct *conn,uint16 vuid, 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); + fstring sharename; + fstring comment; + pstring pathname; + char *command, *cmdname; + uint offset; + int snum; + int res = ERRunsup; + + /* check it's a supported varient */ + if (!prefix_ok(str1,RAP_WShareAdd_REQ)) return False; + if (!check_share_info(uLevel,str2)) return False; + if (uLevel != 2) return False; + + pull_ascii_fstring(sharename,data); + snum = find_service(sharename); + if (snum >= 0) { /* already exists */ + res = ERRfilexists; + goto error_exit; + } + + /* only support disk share adds */ + if (SVAL(data,14)!=STYPE_DISKTREE) return False; + + offset = IVAL(data, 16); + if (offset >= mdrcnt) { + res = ERRinvalidparam; + goto error_exit; + } + pull_ascii_fstring(comment, offset? (data+offset) : ""); + + offset = IVAL(data, 26); + if (offset >= mdrcnt) { + res = ERRinvalidparam; + goto error_exit; + } + pull_ascii_pstring(pathname, offset? (data+offset) : ""); + + string_replace(sharename, '"', ' '); + string_replace(pathname, '"', ' '); + string_replace(comment, '"', ' '); + + cmdname = lp_add_share_cmd(); + + if (!cmdname || *cmdname == '\0') return False; + + asprintf(&command, "%s \"%s\" \"%s\" \"%s\" \"%s\"", + lp_add_share_cmd(), dyn_CONFIGFILE, sharename, pathname, comment); + + if (command) { + DEBUG(10,("api_RNetShareAdd: Running [%s]\n", command )); + if ((res = smbrun(command, NULL)) != 0) { + DEBUG(1,("api_RNetShareAdd: Running [%s] returned (%d)\n", command, res )); + SAFE_FREE(command); + res = ERRnoaccess; + goto error_exit; + } else { + SAFE_FREE(command); + message_send_all(conn_tdb_ctx(), MSG_SMB_CONF_UPDATED, NULL, 0, False, NULL); + } + } else 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); + *rdata_len = 0; + + return True; + + error_exit: + *rparam_len = 4; + *rparam = REALLOC(*rparam,*rparam_len); + *rdata_len = 0; + SSVAL(*rparam,0,res); + SSVAL(*rparam,2,0); + return True; + +} + +/**************************************************************************** + view list of groups available + ****************************************************************************/ +static BOOL api_RNetGroupEnum(connection_struct *conn,uint16 vuid, char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len) +{ + char *str1 = param+2; + char *str2 = skip_string(str1,1); + char *p = skip_string(str2,1); + int uLevel = SVAL(p,0); + char *p2; + int count=0; + + if (!prefix_ok(str1,"WrLeh")) return False; + + /* check it's a supported variant */ + 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 */ + pstrcpy(p,"Users"); p += 21; count++; + pstrcpy(p,"Domain Users"); p += 21; count++; + pstrcpy(p,"Guests"); p += 21; count++; + pstrcpy(p,"Domain Guests"); p += 21; count++; + + *rdata_len = PTR_DIFF(p,*rdata); + + *rparam_len = 8; + *rparam = REALLOC(*rparam,*rparam_len); + + SSVAL(*rparam,4,count); /* is this right?? */ + SSVAL(*rparam,6,count); /* is this right?? */ + + DEBUG(3,("api_RNetGroupEnum gave %d entries\n", count)); + + return(True); +} + +/**************************************************************************** + view list of groups available + ****************************************************************************/ +static BOOL api_RNetUserEnum(connection_struct *conn,uint16 vuid, char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len) +{ + char *str1 = param+2; + char *str2 = skip_string(str1,1); + char *p = skip_string(str2,1); + int uLevel = SVAL(p,0); + char *p2; + int count=0; + + if (!prefix_ok(str1,"WrLeh")) return False; + + /* check it's a supported variant */ + 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 */ + pstrcpy(p,"Users"); p += 21; count++; + pstrcpy(p,"Domain Users"); p += 21; count++; + pstrcpy(p,"Guests"); p += 21; count++; + pstrcpy(p,"Domain Guests"); p += 21; count++; + + *rdata_len = PTR_DIFF(p,*rdata); + + *rparam_len = 8; + *rparam = REALLOC(*rparam,*rparam_len); + + SSVAL(*rparam,4,count); /* is this right?? */ + SSVAL(*rparam,6,count); /* is this right?? */ + + DEBUG(3,("api_RNetUserEnum gave %d entries\n", count)); + + return(True); +} + + + +/**************************************************************************** + get the time of day info + ****************************************************************************/ +static BOOL api_NetRemoteTOD(connection_struct *conn,uint16 vuid, char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len) +{ + char *p; + *rparam_len = 4; + *rparam = REALLOC(*rparam,*rparam_len); + + *rdata_len = 21; + *rdata = REALLOC(*rdata,*rdata_len); + + SSVAL(*rparam,0,NERR_Success); + SSVAL(*rparam,2,0); /* converter word */ + + p = *rdata; + + { + struct tm *t; + time_t unixdate = time(NULL); + + put_dos_date3(p,0,unixdate); /* this is the time that is looked at + by NT in a "net time" operation, + it seems to ignore the one below */ + + /* the client expects to get localtime, not GMT, in this bit + (I think, this needs testing) */ + t = LocalTime(&unixdate); + + SIVAL(p,4,0); /* msecs ? */ + SCVAL(p,8,t->tm_hour); + SCVAL(p,9,t->tm_min); + SCVAL(p,10,t->tm_sec); + SCVAL(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 */ + SCVAL(p,16,t->tm_mday); + SCVAL(p,17,t->tm_mon + 1); + SSVAL(p,18,1900+t->tm_year); + SCVAL(p,20,t->tm_wday); + } + + + return(True); +} + +/**************************************************************************** + Set the user password. +*****************************************************************************/ + +static BOOL api_SetUserPassword(connection_struct *conn,uint16 vuid, 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; + + pull_ascii_fstring(user,p); + + p = skip_string(p,1); + + memset(pass1,'\0',sizeof(pass1)); + memset(pass2,'\0',sizeof(pass2)); + memcpy(pass1,p,16); + memcpy(pass2,p+16,16); + + *rparam_len = 4; + *rparam = REALLOC(*rparam,*rparam_len); + + *rdata_len = 0; + + SSVAL(*rparam,0,NERR_badpass); + SSVAL(*rparam,2,0); /* converter word */ + + DEBUG(3,("Set password for <%s>\n",user)); + + /* + * Attempt to verify the old password against smbpasswd entries + * Win98 clients send old and new password in plaintext for this call. + */ + + { + auth_serversupplied_info *server_info = NULL; + DATA_BLOB password = data_blob(pass1, strlen(pass1)+1); + if (NT_STATUS_IS_OK(check_plaintext_password(user,password,&server_info))) { + + /* + * If unix password sync was requested, attempt to change + * the /etc/passwd database first. Return failure if this cannot + * be done. + * + * This occurs before the oem change, becouse we don't want to + * update it if chgpasswd failed. + * + * Conditional on lp_unix_password_sync() becouse we don't want + * to touch the unix db unless we have admin permission. + */ + + if(lp_unix_password_sync() && IS_SAM_UNIX_USER(server_info->sam_account) + && !chgpasswd(pdb_get_username(server_info->sam_account), + pass1,pass2,False)) { + SSVAL(*rparam,0,NERR_badpass); + } + + if (change_oem_password(server_info->sam_account,pass2)) + { + SSVAL(*rparam,0,NERR_Success); + } + + free_server_info(&server_info); + } + data_blob_clear_free(&password); + } + + /* + * If the plaintext change failed, attempt + * the old encrypted method. NT will generate this + * after trying the samr method. Note that this + * method is done as a last resort as this + * password change method loses the NT password hash + * and cannot change the UNIX password as no plaintext + * is received. + */ + + if(SVAL(*rparam,0) != NERR_Success) + { + SAM_ACCOUNT *hnd = NULL; + + if (check_lanman_password(user,(unsigned char *)pass1,(unsigned char *)pass2, &hnd) && + change_lanman_password(hnd,(unsigned char *)pass1,(unsigned char *)pass2)) + { + SSVAL(*rparam,0,NERR_Success); + } + pdb_free_sam(&hnd); + } + + + memset((char *)pass1,'\0',sizeof(fstring)); + memset((char *)pass2,'\0',sizeof(fstring)); + + return(True); +} + +/**************************************************************************** + Set the user password (SamOEM version - gets plaintext). +****************************************************************************/ + +static BOOL api_SamOEMChangePassword(connection_struct *conn,uint16 vuid, char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len) +{ + fstring user; + char *p = param + 2; + *rparam_len = 2; + *rparam = REALLOC(*rparam,*rparam_len); + + *rdata_len = 0; + + SSVAL(*rparam,0,NERR_badpass); + + /* + * Check the parameter definition is correct. + */ + if(!strequal(param + 2, "zsT")) { + DEBUG(0,("api_SamOEMChangePassword: Invalid parameter string %s\n", param + 2)); + return False; + } + p = skip_string(p, 1); + + if(!strequal(p, "B516B16")) { + DEBUG(0,("api_SamOEMChangePassword: Invalid data parameter string %s\n", p)); + return False; + } + p = skip_string(p,1); + + p += pull_ascii_fstring(user,p); + + DEBUG(3,("api_SamOEMChangePassword: Change password for <%s>\n",user)); + + /* + * Pass the user through the NT -> unix user mapping + * function. + */ + + (void)map_username(user); + + if (pass_oem_change(user, (uchar*) data, (uchar *)&data[516], NULL, NULL)) + { + SSVAL(*rparam,0,NERR_Success); + } + + return(True); +} + +/**************************************************************************** + delete a print job + Form: <W> <> + ****************************************************************************/ +static BOOL api_RDosPrintJobDel(connection_struct *conn,uint16 vuid, 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, errcode; + extern struct current_user current_user; + WERROR werr = WERR_OK; + + jobid = SVAL(p,0); + + /* 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; + + if (!print_job_exists(jobid)) { + errcode = NERR_JobNotFound; + goto out; + } + + errcode = NERR_notsupported; + + switch (function) { + case 81: /* delete */ + if (print_job_delete(¤t_user, jobid, &werr)) + errcode = NERR_Success; + break; + case 82: /* pause */ + if (print_job_pause(¤t_user, jobid, &werr)) + errcode = NERR_Success; + break; + case 83: /* resume */ + if (print_job_resume(¤t_user, jobid, &werr)) + errcode = NERR_Success; + break; + } + + if (!W_ERROR_IS_OK(werr)) + errcode = W_ERROR_V(werr); + + out: + SSVAL(*rparam,0,errcode); + SSVAL(*rparam,2,0); /* converter word */ + + return(True); +} + +/**************************************************************************** + Purge a print queue - or pause or resume it. + ****************************************************************************/ +static BOOL api_WPrintQueueCtrl(connection_struct *conn,uint16 vuid, 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 *QueueName = skip_string(str2,1); + int errcode = NERR_notsupported; + int snum; + WERROR werr = WERR_OK; + extern struct current_user current_user; + + /* check it's a supported varient */ + if (!(strcsequal(str1,"z") && strcsequal(str2,""))) + return(False); + + *rparam_len = 4; + *rparam = REALLOC(*rparam,*rparam_len); + *rdata_len = 0; + + snum = print_queue_snum(QueueName); + + if (snum == -1) { + errcode = NERR_JobNotFound; + goto out; + } + + switch (function) { + case 74: /* Pause queue */ + if (print_queue_pause(¤t_user, snum, &werr)) errcode = NERR_Success; + break; + case 75: /* Resume queue */ + if (print_queue_resume(¤t_user, snum, &werr)) errcode = NERR_Success; + break; + case 103: /* Purge */ + if (print_queue_purge(¤t_user, snum, &werr)) errcode = NERR_Success; + break; + } + + if (!W_ERROR_IS_OK(werr)) errcode = W_ERROR_V(werr); + + out: + SSVAL(*rparam,0,errcode); + SSVAL(*rparam,2,0); /* converter word */ + + return(True); +} + + +/**************************************************************************** + set the property of a print job (undocumented?) + ? function = 0xb -> set name of print job + ? function = 0x6 -> move print job up/down + Form: <WWsTP> <WWzWWDDzzzzzzzzzzlz> + or <WWsTP> <WB21BB16B10zWWzDDz> +****************************************************************************/ +static int check_printjob_info(struct pack_desc* desc, + int uLevel, char* id) +{ + desc->subformat = NULL; + switch( uLevel ) { + case 0: desc->format = "W"; break; + case 1: desc->format = "WB21BB16B10zWWzDDz"; break; + case 2: desc->format = "WWzWWDDzz"; break; + case 3: desc->format = "WWzWWDDzzzzzzzzzzlz"; break; + case 4: desc->format = "WWzWWDDzzzzzDDDDDDD"; break; + default: return False; + } + if (strcmp(desc->format,id) != 0) return False; + return True; +} + +static BOOL api_PrintJobInfo(connection_struct *conn,uint16 vuid,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; + int uLevel = SVAL(p,2); + int function = SVAL(p,4); + int place, errcode; + + jobid = SVAL(p,0); + *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); + + if (!print_job_exists(jobid)) { + errcode=NERR_JobNotFound; + goto out; + } + + errcode = NERR_notsupported; + + switch (function) { + case 0x6: + /* change job place in the queue, + data gives the new place */ + place = SVAL(data,0); + if (print_job_set_place(jobid, place)) { + errcode=NERR_Success; + } + break; + + case 0xb: + /* change print job name, data gives the name */ + if (print_job_set_name(jobid, data)) { + errcode=NERR_Success; + } + break; + + default: + return False; + } + + out: + SSVALS(*rparam,0,errcode); + SSVAL(*rparam,2,0); /* converter word */ + + return(True); +} + + +/**************************************************************************** + get info about the server + ****************************************************************************/ +static BOOL api_RNetServerGetInfo(connection_struct *conn,uint16 vuid, char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len) +{ + char *str1 = param+2; + char *str2 = skip_string(str1,1); + char *p = skip_string(str2,1); + int uLevel = SVAL(p,0); + char *p2; + int struct_len; + + DEBUG(4,("NetServerGetInfo level %d\n",uLevel)); + + /* check it's a supported varient */ + if (!prefix_ok(str1,"WrLh")) return False; + switch( uLevel ) { + case 0: + if (strcmp(str2,"B16") != 0) return False; + struct_len = 16; + break; + case 1: + if (strcmp(str2,"B16BBDz") != 0) return False; + struct_len = 26; + break; + case 2: + if (strcmp(str2,"B16BBDzDDDWWzWWWWWWWBB21zWWWWWWWWWWWWWWWWWWWWWWz") + != 0) return False; + struct_len = 134; + break; + case 3: + if (strcmp(str2,"B16BBDzDDDWWzWWWWWWWBB21zWWWWWWWWWWWWWWWWWWWWWWzDWz") + != 0) return False; + struct_len = 144; + break; + case 20: + if (strcmp(str2,"DN") != 0) return False; + struct_len = 6; + break; + case 50: + if (strcmp(str2,"B16BBDzWWzzz") != 0) return False; + struct_len = 42; + break; + default: return False; + } + + *rdata_len = mdrcnt; + *rdata = REALLOC(*rdata,*rdata_len); + + p = *rdata; + p2 = p + struct_len; + if (uLevel != 20) { + srvstr_push(NULL, p,local_machine,16, + STR_ASCII|STR_UPPER|STR_TERMINATE); + } + p += 16; + if (uLevel > 0) + { + struct srv_info_struct *servers=NULL; + int i,count; + pstring comment; + uint32 servertype= lp_default_server_announce(); + + pstrcpy(comment,string_truncate(lp_serverstring(), MAX_SERVER_STRING_LENGTH)); + + if ((count=get_server_info(SV_TYPE_ALL,&servers,global_myworkgroup))>0) { + for (i=0;i<count;i++) + if (strequal(servers[i].name,local_machine)) + { + servertype = servers[i].type; + pstrcpy(comment,servers[i].comment); + } + } + SAFE_FREE(servers); + + SCVAL(p,0,lp_major_announce_version()); + SCVAL(p,1,lp_minor_announce_version()); + SIVAL(p,2,servertype); + + if (mdrcnt == struct_len) { + SIVAL(p,6,0); + } else { + SIVAL(p,6,PTR_DIFF(p2,*rdata)); + standard_sub_conn(conn,comment); + StrnCpy(p2,comment,MAX(mdrcnt - struct_len,0)); + p2 = skip_string(p2,1); + } + } + if (uLevel > 1) + { + return False; /* not yet implemented */ + } + + *rdata_len = PTR_DIFF(p2,*rdata); + + *rparam_len = 6; + *rparam = REALLOC(*rparam,*rparam_len); + SSVAL(*rparam,0,NERR_Success); + SSVAL(*rparam,2,0); /* converter word */ + SSVAL(*rparam,4,*rdata_len); + + return(True); +} + + +/**************************************************************************** + get info about the server + ****************************************************************************/ +static BOOL api_NetWkstaGetInfo(connection_struct *conn,uint16 vuid, 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 userdom_struct current_user_info; + 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)); /* host name */ + pstrcpy(p2,local_machine); + strupper(p2); + p2 = skip_string(p2,1); + p += 4; + + SIVAL(p,0,PTR_DIFF(p2,*rdata)); + pstrcpy(p2,current_user_info.smb_name); + p2 = skip_string(p2,1); + p += 4; + + SIVAL(p,0,PTR_DIFF(p2,*rdata)); /* login domain */ + pstrcpy(p2,global_myworkgroup); + strupper(p2); + p2 = skip_string(p2,1); + p += 4; + + SCVAL(p,0,lp_major_announce_version()); /* system version - e.g 4 in 4.1 */ + SCVAL(p,1,lp_minor_announce_version()); /* system version - e.g .1 in 4.1 */ + p += 2; + + SIVAL(p,0,PTR_DIFF(p2,*rdata)); + pstrcpy(p2,global_myworkgroup); /* don't know. login domain?? */ + p2 = skip_string(p2,1); + p += 4; + + SIVAL(p,0,PTR_DIFF(p2,*rdata)); /* don't know */ + pstrcpy(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 + + struct user_info_11 { + char usri11_name[21]; 0-20 + char usri11_pad; 21 + char *usri11_comment; 22-25 + char *usri11_usr_comment; 26-29 + unsigned short usri11_priv; 30-31 + unsigned long usri11_auth_flags; 32-35 + long usri11_password_age; 36-39 + char *usri11_homedir; 40-43 + char *usri11_parms; 44-47 + long usri11_last_logon; 48-51 + long usri11_last_logoff; 52-55 + unsigned short usri11_bad_pw_count; 56-57 + unsigned short usri11_num_logons; 58-59 + char *usri11_logon_server; 60-63 + unsigned short usri11_country_code; 64-65 + char *usri11_workstations; 66-69 + unsigned long usri11_max_storage; 70-73 + unsigned short usri11_units_per_week; 74-75 + unsigned char *usri11_logon_hours; 76-79 + unsigned short usri11_code_page; 80-81 + }; + +where: + + usri11_name specifies the user name for which information is retireved + + usri11_pad aligns the next data structure element to a word boundary + + usri11_comment is a null terminated ASCII comment + + usri11_user_comment is a null terminated ASCII comment about the user + + usri11_priv specifies the level of the privilege assigned to the user. + The possible values are: + +Name Value Description +USER_PRIV_GUEST 0 Guest privilege +USER_PRIV_USER 1 User privilege +USER_PRV_ADMIN 2 Administrator privilege + + usri11_auth_flags specifies the account operator privileges. The + possible values are: + +Name Value Description +AF_OP_PRINT 0 Print operator + + +Leach, Naik [Page 28] + + + +INTERNET-DRAFT CIFS Remote Admin Protocol January 10, 1997 + + +AF_OP_COMM 1 Communications operator +AF_OP_SERVER 2 Server operator +AF_OP_ACCOUNTS 3 Accounts operator + + + usri11_password_age specifies how many seconds have elapsed since the + password was last changed. + + usri11_home_dir points to a null terminated ASCII string that contains + the path name of the user's home directory. + + usri11_parms points to a null terminated ASCII string that is set + aside for use by applications. + + usri11_last_logon specifies the time when the user last logged on. + This value is stored as the number of seconds elapsed since + 00:00:00, January 1, 1970. + + usri11_last_logoff specifies the time when the user last logged off. + This value is stored as the number of seconds elapsed since + 00:00:00, January 1, 1970. A value of 0 means the last logoff + time is unknown. + + usri11_bad_pw_count specifies the number of incorrect passwords + entered since the last successful logon. + + usri11_log1_num_logons specifies the number of times this user has + logged on. A value of -1 means the number of logons is unknown. + + usri11_logon_server points to a null terminated ASCII string that + contains the name of the server to which logon requests are sent. + A null string indicates logon requests should be sent to the + domain controller. + + usri11_country_code specifies the country code for the user's language + of choice. + + usri11_workstations points to a null terminated ASCII string that + contains the names of workstations the user may log on from. + There may be up to 8 workstations, with the names separated by + commas. A null strings indicates there are no restrictions. + + usri11_max_storage specifies the maximum amount of disk space the user + can occupy. A value of 0xffffffff indicates there are no + restrictions. + + usri11_units_per_week specifies the equal number of time units into + which a week is divided. This value must be equal to 168. + + usri11_logon_hours points to a 21 byte (168 bits) string that + specifies the time during which the user can log on. Each bit + represents one unique hour in a week. The first bit (bit 0, word + 0) is Sunday, 0:00 to 0:59, the second bit (bit 1, word 0) is + + + +Leach, Naik [Page 29] + + + +INTERNET-DRAFT CIFS Remote Admin Protocol January 10, 1997 + + + Sunday, 1:00 to 1:59 and so on. A null pointer indicates there + are no restrictions. + + usri11_code_page specifies the code page for the user's language of + choice + +All of the pointers in this data structure need to be treated +specially. The pointer is a 32 bit pointer. The higher 16 bits need +to be ignored. The converter word returned in the parameters section +needs to be subtracted from the lower 16 bits to calculate an offset +into the return buffer where this ASCII string resides. + +There is no auxiliary data in the response. + + ****************************************************************************/ + +#define usri11_name 0 +#define usri11_pad 21 +#define usri11_comment 22 +#define usri11_usr_comment 26 +#define usri11_full_name 30 +#define usri11_priv 34 +#define usri11_auth_flags 36 +#define usri11_password_age 40 +#define usri11_homedir 44 +#define usri11_parms 48 +#define usri11_last_logon 52 +#define usri11_last_logoff 56 +#define usri11_bad_pw_count 60 +#define usri11_num_logons 62 +#define usri11_logon_server 64 +#define usri11_country_code 68 +#define usri11_workstations 70 +#define usri11_max_storage 74 +#define usri11_units_per_week 78 +#define usri11_logon_hours 80 +#define usri11_code_page 84 +#define usri11_end 86 + +#define USER_PRIV_GUEST 0 +#define USER_PRIV_USER 1 +#define USER_PRIV_ADMIN 2 + +#define AF_OP_PRINT 0 +#define AF_OP_COMM 1 +#define AF_OP_SERVER 2 +#define AF_OP_ACCOUNTS 3 + + +static BOOL api_RNetUserGetInfo(connection_struct *conn,uint16 vuid, 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; + + /* get NIS home of a previously validated user - simeon */ + /* With share level security vuid will always be zero. + Don't depend on vuser being non-null !!. JRA */ + user_struct *vuser = get_valid_user_struct(vuid); + if(vuser != NULL) + DEBUG(3,(" Username of UID %d is %s\n", (int)vuser->uid, + vuser->user.unix_name)); + + *rparam_len = 6; + *rparam = REALLOC(*rparam,*rparam_len); + + DEBUG(4,("RNetUserGetInfo level=%d\n", uLevel)); + + /* check it's a supported variant */ + 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 + usri11_end; + + memset(p,0,21); + fstrcpy(p+usri11_name,UserName); /* 21 bytes - user name */ + + if (uLevel > 0) + { + SCVAL(p,usri11_pad,0); /* padding - 1 byte */ + *p2 = 0; + } + if (uLevel >= 10) + { + SIVAL(p,usri11_comment,PTR_DIFF(p2,p)); /* comment */ + pstrcpy(p2,"Comment"); + p2 = skip_string(p2,1); + + SIVAL(p,usri11_usr_comment,PTR_DIFF(p2,p)); /* user_comment */ + pstrcpy(p2,"UserComment"); + p2 = skip_string(p2,1); + + /* EEK! the cifsrap.txt doesn't have this in!!!! */ + SIVAL(p,usri11_full_name,PTR_DIFF(p2,p)); /* full name */ + pstrcpy(p2,((vuser != NULL) ? vuser->user.full_name : UserName)); + p2 = skip_string(p2,1); + } + + if (uLevel == 11) /* modelled after NTAS 3.51 reply */ + { + SSVAL(p,usri11_priv,conn->admin_user?USER_PRIV_ADMIN:USER_PRIV_USER); + SIVAL(p,usri11_auth_flags,AF_OP_PRINT); /* auth flags */ + SIVALS(p,usri11_password_age,-1); /* password age */ + SIVAL(p,usri11_homedir,PTR_DIFF(p2,p)); /* home dir */ + pstrcpy(p2, lp_logon_home()); + standard_sub_conn(conn, p2); + p2 = skip_string(p2,1); + SIVAL(p,usri11_parms,PTR_DIFF(p2,p)); /* parms */ + pstrcpy(p2,""); + p2 = skip_string(p2,1); + SIVAL(p,usri11_last_logon,0); /* last logon */ + SIVAL(p,usri11_last_logoff,0); /* last logoff */ + SSVALS(p,usri11_bad_pw_count,-1); /* bad pw counts */ + SSVALS(p,usri11_num_logons,-1); /* num logons */ + SIVAL(p,usri11_logon_server,PTR_DIFF(p2,p)); /* logon server */ + pstrcpy(p2,"\\\\*"); + p2 = skip_string(p2,1); + SSVAL(p,usri11_country_code,0); /* country code */ + + SIVAL(p,usri11_workstations,PTR_DIFF(p2,p)); /* workstations */ + pstrcpy(p2,""); + p2 = skip_string(p2,1); + + SIVALS(p,usri11_max_storage,-1); /* max storage */ + SSVAL(p,usri11_units_per_week,168); /* units per week */ + SIVAL(p,usri11_logon_hours,PTR_DIFF(p2,p)); /* logon hours */ + + /* a simple way to get logon hours at all times. */ + memset(p2,0xff,21); + SCVAL(p2,21,0); /* fix zero termination */ + p2 = skip_string(p2,1); + + SSVAL(p,usri11_code_page,0); /* code page */ + } + if (uLevel == 1 || uLevel == 2) + { + memset(p+22,' ',16); /* password */ + SIVALS(p,38,-1); /* password age */ + SSVAL(p,42, + conn->admin_user?USER_PRIV_ADMIN:USER_PRIV_USER); + SIVAL(p,44,PTR_DIFF(p2,*rdata)); /* home dir */ + pstrcpy(p2,lp_logon_home()); + standard_sub_conn(conn, p2); + p2 = skip_string(p2,1); + SIVAL(p,48,PTR_DIFF(p2,*rdata)); /* comment */ + *p2++ = 0; + SSVAL(p,52,0); /* flags */ + SIVAL(p,54,PTR_DIFF(p2,*rdata)); /* script_path */ + pstrcpy(p2,lp_logon_script()); + standard_sub_conn( conn, p2 ); + p2 = skip_string(p2,1); + if (uLevel == 2) + { + SIVAL(p,60,0); /* auth_flags */ + SIVAL(p,64,PTR_DIFF(p2,*rdata)); /* full_name */ + pstrcpy(p2,((vuser != NULL) ? vuser->user.full_name : UserName)); + p2 = skip_string(p2,1); + SIVAL(p,68,0); /* urs_comment */ + SIVAL(p,72,PTR_DIFF(p2,*rdata)); /* parms */ + pstrcpy(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 */ + pstrcpy(p2,"\\\\%L"); + standard_sub_conn(conn, 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(connection_struct *conn,uint16 vuid, 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 */ + pstrcpy(p,"Users"); p += 21; count++; + pstrcpy(p,"Domain Users"); p += 21; count++; + pstrcpy(p,"Guests"); p += 21; count++; + pstrcpy(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(connection_struct *conn,uint16 vuid, 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; + + memset((char *)&desc,'\0',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", + conn->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",0); /* 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; + fstrcpy(mypath,"\\\\"); + fstrcat(mypath,local_machine); + strupper(mypath); + PACKS(&desc,"z",mypath); /* computer */ + } + PACKS(&desc,"z",global_myworkgroup);/* domain */ + +/* JHT - By calling lp_logon_script() and standard_sub() we have */ +/* made sure all macros are fully substituted and available */ + { + pstring logon_script; + pstrcpy(logon_script,lp_logon_script()); + standard_sub_conn( conn, logon_script ); + PACKS(&desc,"z", logon_script); /* script path */ + } +/* End of JHT mods */ + + PACKI(&desc,"D",0x00000000); /* 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(connection_struct *conn,uint16 vuid, 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(connection_struct *conn,uint16 vuid, 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; + int count; + int i; + int snum; + int job; + struct pack_desc desc; + print_queue_struct *queue=NULL; + print_status_struct status; + char *tmpdata=NULL; + + uLevel = SVAL(p,2); + + memset((char *)&desc,'\0',sizeof(desc)); + memset((char *)&status,'\0',sizeof(status)); + + DEBUG(3,("WPrintJobGetInfo uLevel=%d uJobId=0x%X\n",uLevel,SVAL(p,0))); + + /* check it's a supported varient */ + if (strcmp(str1,"WWrLh") != 0) return False; + if (!check_printjob_info(&desc,uLevel,str2)) return False; + + job = SVAL(p,0); + snum = print_job_snum(job); + + if (snum < 0 || !VALID_SNUM(snum)) return(False); + + count = print_queue_status(snum,&queue,&status); + for (i = 0; i < count; i++) { + if (queue[i].job == job) break; + } + + if (mdrcnt > 0) { + *rdata = REALLOC(*rdata,mdrcnt); + desc.base = *rdata; + desc.buflen = mdrcnt; + } else { + /* + * Don't return data but need to get correct length + * init_package will return wrong size if buflen=0 + */ + desc.buflen = getlen(desc.format); + desc.base = tmpdata = (char *)malloc ( desc.buflen ); + } + + if (init_package(&desc,1,0)) { + if (i < count) { + fill_printjob_info(conn,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); + + SAFE_FREE(queue); + SAFE_FREE(tmpdata); + + DEBUG(4,("WPrintJobGetInfo: errorcode %d\n",desc.errcode)); + return(True); +} + +static BOOL api_WPrintJobEnumerate(connection_struct *conn,uint16 vuid, 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; + int count; + int i, succnt=0; + int snum; + struct pack_desc desc; + print_queue_struct *queue=NULL; + print_status_struct status; + + memset((char *)&desc,'\0',sizeof(desc)); + memset((char *)&status,'\0',sizeof(status)); + + p = skip_string(p,1); + uLevel = SVAL(p,0); + + 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 = print_queue_status(snum,&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(conn,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); + + SAFE_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(connection_struct *conn, int snum, int uLevel, + struct pack_desc* desc) +{ + char buf[100]; + strncpy(buf,SERVICE(snum),sizeof(buf)-1); + buf[sizeof(buf)-1] = 0; + 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(connection_struct *conn,uint16 vuid, 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; + struct pack_desc desc; + int snum; + char *tmpdata=NULL; + + memset((char *)&desc,'\0',sizeof(desc)); + + p = skip_string(p,1); + uLevel = SVAL(p,0); + + 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; + } else { + /* + * Don't return data but need to get correct length + * init_package will return wrong size if buflen=0 + */ + desc.buflen = getlen(desc.format); + desc.base = tmpdata = (char *)malloc ( desc.buflen ); + } + if (init_package(&desc,1,0)) { + fill_printdest_info(conn,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)); + SAFE_FREE(tmpdata); + return(True); +} + +static BOOL api_WPrintDestEnum(connection_struct *conn,uint16 vuid, 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; + int queuecnt; + int i, n, succnt=0; + struct pack_desc desc; + int services = lp_numservices(); + + memset((char *)&desc,'\0',sizeof(desc)); + + uLevel = SVAL(p,0); + + 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(conn,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(connection_struct *conn,uint16 vuid, 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; + int succnt; + struct pack_desc desc; + + memset((char *)&desc,'\0',sizeof(desc)); + + uLevel = SVAL(p,0); + + 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(connection_struct *conn,uint16 vuid, 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; + int succnt; + struct pack_desc desc; + + memset((char *)&desc,'\0',sizeof(desc)); + + uLevel = SVAL(p,0); + + 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(connection_struct *conn,uint16 vuid, 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; + int succnt; + struct pack_desc desc; + + memset((char *)&desc,'\0',sizeof(desc)); + + uLevel = SVAL(p,0); + + 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); + memset((char *)&desc,'\0',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); +} + +struct session_info { + char machine[31]; + char username[24]; + char clitype[24]; + int opens; + int time; +}; + +struct sessions_info { + int count; + struct session_info *session_list; +}; + +static int gather_sessioninfo(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state) +{ + struct sessions_info *sinfo = state; + struct session_info *curinfo = NULL; + struct sessionid *sessid = (struct sessionid *) dbuf.dptr; + + sinfo->count += 1; + sinfo->session_list = REALLOC(sinfo->session_list, sinfo->count * sizeof(struct session_info)); + + curinfo = &(sinfo->session_list[sinfo->count - 1]); + + safe_strcpy(curinfo->machine, sessid->remote_machine, + sizeof(curinfo->machine)); + safe_strcpy(curinfo->username, uidtoname(sessid->uid), + sizeof(curinfo->username)); + DEBUG(7,("gather_sessioninfo session from %s@%s\n", + curinfo->username, curinfo->machine)); + return 0; +} + +/**************************************************************************** + List open sessions + ****************************************************************************/ +static BOOL api_RNetSessionEnum(connection_struct *conn,uint16 vuid, 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; + struct sessions_info sinfo; + int i; + + memset((char *)&desc,'\0',sizeof(desc)); + + uLevel = SVAL(p,0); + + DEBUG(3,("RNetSessionEnum uLevel=%d\n",uLevel)); + DEBUG(7,("RNetSessionEnum req string=%s\n",str1)); + DEBUG(7,("RNetSessionEnum ret string=%s\n",str2)); + + /* check it's a supported varient */ + if (strcmp(str1,RAP_NetSessionEnum_REQ) != 0) return False; + if (uLevel != 2 || strcmp(str2,RAP_SESSION_INFO_L2) != 0) return False; + + sinfo.count = 0; + sinfo.session_list = NULL; + + if (!session_traverse(gather_sessioninfo, &sinfo)) { + DEBUG(4,("RNetSessionEnum session_traverse failed\n")); + return False; + } + + if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt); + memset((char *)&desc,'\0',sizeof(desc)); + desc.base = *rdata; + desc.buflen = mdrcnt; + desc.format = str2; + if (!init_package(&desc,sinfo.count,0)) { + return False; + } + + for(i=0; i<sinfo.count; i++) { + PACKS(&desc, "z", sinfo.session_list[i].machine); + PACKS(&desc, "z", sinfo.session_list[i].username); + PACKI(&desc, "W", 1); /* num conns */ + PACKI(&desc, "W", 0); /* num opens */ + PACKI(&desc, "W", 1); /* num users */ + PACKI(&desc, "D", 0); /* session time */ + PACKI(&desc, "D", 0); /* idle time */ + PACKI(&desc, "D", 0); /* flags */ + PACKS(&desc, "z", "Unknown Client"); /* client type string */ + } + + *rdata_len = desc.usedlen; + + *rparam_len = 8; + *rparam = REALLOC(*rparam,*rparam_len); + SSVALS(*rparam,0,desc.errcode); + SSVAL(*rparam,2,0); /* converter */ + SSVAL(*rparam,4,sinfo.count); /* count */ + + DEBUG(4,("RNetSessionEnum: errorcode %d\n",desc.errcode)); + return True; +} + + +/**************************************************************************** + The buffer was too small + ****************************************************************************/ + +static BOOL api_TooSmall(connection_struct *conn,uint16 vuid, 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(connection_struct *conn,uint16 vuid, 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)(connection_struct *,uint16,char *,char *, + int,int,char **,char **,int *,int *); + int flags; +} api_commands[] = { + {"RNetShareEnum", RAP_WshareEnum, api_RNetShareEnum,0}, + {"RNetShareGetInfo", RAP_WshareGetInfo, api_RNetShareGetInfo,0}, + {"RNetShareAdd", RAP_WshareAdd, api_RNetShareAdd,0}, + {"RNetSessionEnum", RAP_WsessionEnum, api_RNetSessionEnum,0}, + {"RNetServerGetInfo", RAP_WserverGetInfo, api_RNetServerGetInfo,0}, + {"RNetGroupEnum", RAP_WGroupEnum, api_RNetGroupEnum,0}, + {"RNetGroupGetUsers", RAP_WGroupGetUsers, api_RNetGroupGetUsers,0}, + {"RNetUserEnum", RAP_WUserEnum, api_RNetUserEnum,0}, + {"RNetUserGetInfo", RAP_WUserGetInfo, api_RNetUserGetInfo,0}, + {"NetUserGetGroups", RAP_WUserGetGroups, api_NetUserGetGroups,0}, + {"NetWkstaGetInfo", RAP_WWkstaGetInfo, api_NetWkstaGetInfo,0}, + {"DosPrintQEnum", RAP_WPrintQEnum, api_DosPrintQEnum,0}, + {"DosPrintQGetInfo", RAP_WPrintQGetInfo, api_DosPrintQGetInfo,0}, + {"WPrintQueuePause", RAP_WPrintQPause, api_WPrintQueueCtrl,0}, + {"WPrintQueueResume", RAP_WPrintQContinue, api_WPrintQueueCtrl,0}, + {"WPrintJobEnumerate",RAP_WPrintJobEnum, api_WPrintJobEnumerate,0}, + {"WPrintJobGetInfo", RAP_WPrintJobGetInfo, api_WPrintJobGetInfo,0}, + {"RDosPrintJobDel", RAP_WPrintJobDel, api_RDosPrintJobDel,0}, + {"RDosPrintJobPause", RAP_WPrintJobPause, api_RDosPrintJobDel,0}, + {"RDosPrintJobResume",RAP_WPrintJobContinue, api_RDosPrintJobDel,0}, + {"WPrintDestEnum", RAP_WPrintDestEnum, api_WPrintDestEnum,0}, + {"WPrintDestGetInfo", RAP_WPrintDestGetInfo, api_WPrintDestGetInfo,0}, + {"NetRemoteTOD", RAP_NetRemoteTOD, api_NetRemoteTOD,0}, + {"WPrintQueuePurge", RAP_WPrintQPurge, api_WPrintQueueCtrl,0}, + {"NetServerEnum", RAP_NetServerEnum2, api_RNetServerEnum,0}, + {"WAccessGetUserPerms",RAP_WAccessGetUserPerms,api_WAccessGetUserPerms,0}, + {"SetUserPassword", RAP_WUserPasswordSet2, api_SetUserPassword,0}, + {"WWkstaUserLogon", RAP_WWkstaUserLogon, api_WWkstaUserLogon,0}, + {"PrintJobInfo", RAP_WPrintJobSetInfo, api_PrintJobInfo,0}, + {"WPrintDriverEnum", RAP_WPrintDriverEnum, api_WPrintDriverEnum,0}, + {"WPrintQProcEnum", RAP_WPrintQProcessorEnum,api_WPrintQProcEnum,0}, + {"WPrintPortEnum", RAP_WPrintPortEnum, api_WPrintPortEnum,0}, + {"SamOEMChangePassword",RAP_SamOEMChgPasswordUser2_P,api_SamOEMChangePassword,0}, + {NULL, -1, api_Unsupported,0}}; + + +/**************************************************************************** + Handle remote api calls + ****************************************************************************/ + +int api_reply(connection_struct *conn,uint16 vuid,char *outbuf,char *data,char *params, + int tdscnt,int tpscnt,int mdrcnt,int mprcnt) +{ + int api_command; + char *rdata = NULL; + char *rparam = NULL; + int rdata_len = 0; + int rparam_len = 0; + BOOL reply=False; + int i; + + if (!params) { + DEBUG(0,("ERROR: NULL params in api_reply()\n")); + return 0; + } + + api_command = SVAL(params,0); + + 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) + memset(rdata,'\0',1024); + + rparam = (char *)malloc(1024); + if (rparam) + memset(rparam,'\0',1024); + + if(!rdata || !rparam) { + DEBUG(0,("api_reply: malloc fail !\n")); + return -1; + } + + reply = api_commands[i].fn(conn,vuid,params,data,mdrcnt,mprcnt, + &rdata,&rparam,&rdata_len,&rparam_len); + + + if (rdata_len > mdrcnt || + rparam_len > mprcnt) { + reply = api_TooSmall(conn,vuid,params,data,mdrcnt,mprcnt, + &rdata,&rparam,&rdata_len,&rparam_len); + } + + /* if we get False back then it's actually unsupported */ + if (!reply) + api_Unsupported(conn,vuid,params,data,mdrcnt,mprcnt, + &rdata,&rparam,&rdata_len,&rparam_len); + + send_trans_reply(outbuf, rparam, rparam_len, rdata, rdata_len, False); + + SAFE_FREE(rdata); + SAFE_FREE(rparam); + + return -1; +} diff --git a/source3/smbd/mangle.c b/source3/smbd/mangle.c index 8f1490c528..20b2b419cf 100644 --- a/source3/smbd/mangle.c +++ b/source3/smbd/mangle.c @@ -1,8 +1,7 @@ /* - Unix SMB/Netbios implementation. - Version 1.9. - Name mangling - Copyright (C) Andrew Tridgell 1992-1995 + Unix SMB/CIFS implementation. + Name mangling interface + Copyright (C) Andrew Tridgell 2002 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 @@ -20,591 +19,99 @@ */ #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); -} +static struct mangle_fns *mangle_fns; +/* this allows us to add more mangling backends */ +static struct { + char *name; + struct mangle_fns *(*init_fn)(void); +} mangle_backends[] = { + { "hash", mangle_hash_init }, + { "hash2", mangle_hash2_init }, + { NULL, NULL } +}; /* -keep a stack of name mangling results - just -so file moves and copies have a chance of working + initialise the mangling subsystem */ -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) +static void mangle_init(void) { - int i; - char *p; - - if (!mangled_stack) - return; - - for (i=0;i<mangled_stack_len;i++) - if (strcmp(s,mangled_stack[i]) == 0) - { - array_promote(mangled_stack[0],sizeof(fstring),i); - return; - } + int i; + char *method; - memmove(mangled_stack[1],mangled_stack[0], - sizeof(fstring)*MIN(mangled_stack_len,mangled_stack_size-1)); - strcpy(mangled_stack[0],s); - p = strrchr(mangled_stack[0],'.'); - if (p && (!strhasupper(p+1)) && (strlen(p+1) < 4)) - *p = 0; - mangled_stack_len = MIN(mangled_stack_size,mangled_stack_len+1); -} - -/**************************************************************************** -check for a name on the mangled name stack -****************************************************************************/ -BOOL check_mangled_stack(char *s) -{ - int i; - pstring tmpname; - char extension[5]; - char *p = strrchr(s,'.'); - BOOL check_extension = False; + if (mangle_fns) return; - extension[0] = 0; + method = lp_mangling_method(); - if (!mangled_stack) return(False); - - if (p) - { - check_extension = True; - StrnCpy(extension,p,4); - strlower(extension); /* XXXXXXX */ - } - - for (i=0;i<mangled_stack_len;i++) - { - strcpy(tmpname,mangled_stack[i]); - mangle_name_83(tmpname); - if (strequal(tmpname,s)) - { - strcpy(s,mangled_stack[i]); - break; - } - if (check_extension && !strchr(mangled_stack[i],'.')) - { - strcpy(tmpname,mangled_stack[i]); - strcat(tmpname,extension); - mangle_name_83(tmpname); - if (strequal(tmpname,s)) - { - strcpy(s,mangled_stack[i]); - strcat(s,extension); - break; - } + /* find the first mangling method that manages to initialise and + matches the "mangling method" parameter */ + for (i=0; mangle_backends[i].name && !mangle_fns; i++) { + if (!method || !*method || strcmp(method, mangle_backends[i].name) == 0) { + mangle_fns = mangle_backends[i].init_fn(); + } } - } - - if (i < mangled_stack_len) - { - DEBUG(3,("Found %s on mangled stack as %s\n",s,mangled_stack[i])); - array_promote(mangled_stack[0],sizeof(fstring),i); - return(True); - } - - return(False); -} -static char *map_filename(char *s, /* This is null terminated */ - char *pattern, /* This isn't. */ - int len) /* This is the length of pattern. */ -{ - static pstring matching_bit; /* The bit of the string which matches */ - /* a * in pattern if indeed there is a * */ - char *sp; /* Pointer into s. */ - char *pp; /* Pointer into p. */ - char *match_start; /* Where the matching bit starts. */ - pstring pat; - - StrnCpy(pat, pattern, len); /* Get pattern into a proper string! */ - strcpy(matching_bit,""); /* Match but no star gets this. */ - pp = pat; /* Initialise the pointers. */ - sp = s; - if ((len == 1) && (*pattern == '*')) { - return NULL; /* Impossible, too ambiguous for */ - /* words! */ - } - - while ((*sp) /* Not the end of the string. */ - && (*pp) /* Not the end of the pattern. */ - && (*sp == *pp) /* The two match. */ - && (*pp != '*')) { /* No wildcard. */ - sp++; /* Keep looking. */ - pp++; - } - if (!*sp && !*pp) /* End of pattern. */ - return matching_bit; /* Simple match. Return empty string. */ - if (*pp == '*') { - pp++; /* Always interrested in the chacter */ - /* after the '*' */ - if (!*pp) { /* It is at the end of the pattern. */ - StrnCpy(matching_bit, s, sp-s); - return matching_bit; - } else { - /* The next character in pattern must match a character further */ - /* along s than sp so look for that character. */ - match_start = sp; - while ((*sp) /* Not the end of s. */ - && (*sp != *pp)) /* Not the same */ - sp++; /* Keep looking. */ - if (!*sp) { /* Got to the end without a match. */ - return NULL; - } else { /* Still hope for a match. */ - /* Now sp should point to a matching character. */ - StrnCpy(matching_bit, match_start, sp-match_start); - /* Back to needing a stright match again. */ - while ((*sp) /* Not the end of the string. */ - && (*pp) /* Not the end of the pattern. */ - && (*sp == *pp)) { /* The two match. */ - sp++; /* Keep looking. */ - pp++; - } - if (!*sp && !*pp) /* Both at end so it matched */ - return matching_bit; - else - return NULL; - } - } - } - return NULL; /* No match. */ + if (!mangle_fns) { + DEBUG(0,("Failed to initialise mangling system '%s'\n", method)); + exit_server("mangling init failed"); + } } -/* this is the magic char used for mangling */ -char magic_char = '~'; - - -/**************************************************************************** -determine whther is name could be a mangled name -****************************************************************************/ -BOOL is_mangled(char *s) +/* + reset the cache. This is called when smb.conf has been reloaded +*/ +void mangle_reset_cache(void) { - char *m = strchr(s,magic_char); - if (!m) return(False); - - /* we use two base 36 chars efore the extension */ - if (m[1] == '.' || m[1] == 0 || - m[2] == '.' || m[2] == 0 || - (m[3] != '.' && m[3] != 0)) - return(is_mangled(m+1)); + mangle_init(); - /* it could be */ - return(True); + mangle_fns->reset(); } - - -/**************************************************************************** -return a base 36 character. v must be from 0 to 35. -****************************************************************************/ -static char base36(int v) +/* + see if a filename has come out of our mangling code +*/ +BOOL mangle_is_mangled(const char *s) { - v = v % 36; - if (v < 10) - return('0'+v); - else /* needed to work around a DEC C compiler bug */ - return('A' + (v-10)); + return mangle_fns->is_mangled(s); } - -static void do_fwd_mangled_map(char *s, char *MangledMap) +/* + see if a filename matches the rules of a 8.3 filename +*/ +BOOL mangle_is_8_3(const char *fname, BOOL check_case) { - /* MangledMap is a series of name pairs in () separated by spaces. - * If s matches the first of the pair then the name given is the - * second of the pair. A * means any number of any character and if - * present in the second of the pair as well as the first the - * matching part of the first string takes the place of the * in the - * second. - * - * I wanted this so that we could have RCS files which can be used - * by UNIX and DOS programs. My mapping string is (RCS rcs) which - * converts the UNIX RCS file subdirectory to lowercase thus - * preventing mangling. - */ - char *start=MangledMap; /* Use this to search for mappings. */ - char *end; /* Used to find the end of strings. */ - char *match_string; - pstring new_string; /* Make up the result here. */ - char *np; /* Points into new_string. */ - - DEBUG(5,("Mangled Mapping '%s' map '%s'\n", s, MangledMap)); - while (*start) { - while ((*start) && (*start != '(')) - start++; - start++; /* Skip the ( */ - if (!*start) - continue; /* Always check for the end. */ - end = start; /* Search for the ' ' or a ')' */ - DEBUG(5,("Start of first in pair '%s'\n", start)); - while ((*end) && !((*end == ' ') || (*end == ')'))) - end++; - if (!*end) { - start = end; - continue; /* Always check for the end. */ - } - DEBUG(5,("End of first in pair '%s'\n", end)); - if ((match_string = map_filename(s, start, end-start))) { - DEBUG(5,("Found a match\n")); - /* Found a match. */ - start = end+1; /* Point to start of what it is to become. */ - DEBUG(5,("Start of second in pair '%s'\n", start)); - end = start; - np = new_string; - while ((*end) /* Not the end of string. */ - && (*end != ')') /* Not the end of the pattern. */ - && (*end != '*')) /* Not a wildcard. */ - *np++ = *end++; - if (!*end) { - start = end; - continue; /* Always check for the end. */ - } - if (*end == '*') { - strcpy(np, match_string); - np += strlen(match_string); - end++; /* Skip the '*' */ - while ((*end) /* Not the end of string. */ - && (*end != ')') /* Not the end of the pattern. */ - && (*end != '*')) /* Not a wildcard. */ - *np++ = *end++; - } - if (!*end) { - start = end; - continue; /* Always check for the end. */ - } - *np++ = '\0'; /* NULL terminate it. */ - DEBUG(5,("End of second in pair '%s'\n", end)); - strcpy(s, new_string); /* Substitute with the new name. */ - DEBUG(5,("s is now '%s'\n", s)); - } - start = end; /* Skip a bit which cannot be wanted */ - /* anymore. */ - start++; - } + return mangle_fns->is_8_3(fname, check_case); } -/**************************************************************************** -do the actual mangling to 8.3 format -****************************************************************************/ -void mangle_name_83(char *s) -{ - int csum = str_checksum(s); - char *p; - char extension[4]; - char base[9]; - int baselen = 0; - int extlen = 0; - - extension[0]=0; - base[0]=0; - - p = strrchr(s,'.'); - if (p && (strlen(p+1)<4) ) - { - BOOL all_normal = (strisnormal(p+1)); /* XXXXXXXXX */ - if (all_normal && p[1] != 0) - { - *p = 0; - csum = str_checksum(s); - *p = '.'; - } - } - - - strupper(s); - - DEBUG(5,("Mangling name %s to ",s)); - - if (p) - { - if (p == s) - strcpy(extension,"___"); - else - { - *p++ = 0; - while (*p && extlen < 3) - { - if (isdoschar(*p) && *p != '.') - extension[extlen++] = *p; - p++; - } - extension[extlen] = 0; - } - } - - p = s; - - while (*p && baselen < 5) - { - if (isdoschar(*p) && *p != '.') - base[baselen++] = *p; - p++; - } - base[baselen] = 0; - - csum = csum % (36*36); - - sprintf(s,"%s%c%c%c",base,magic_char,base36(csum/36),base36(csum%36)); - - if (*extension) - { - strcat(s,"."); - strcat(s,extension); - } - DEBUG(5,("%s\n",s)); -} - - - -/******************************************************************* - work out if a name is illegal, even for long names - ******************************************************************/ -static BOOL illegal_name(char *name) +/* + try to reverse map a 8.3 name to the original filename. This doesn't have to + always succeed, as the directory handling code in smbd will scan the directory + looking for a matching name if it doesn't. It should succeed most of the time + or there will be a huge performance penalty +*/ +BOOL mangle_check_cache(char *s) { - static unsigned char illegal[256]; - static BOOL initialised=False; - unsigned char *s; - - if (!initialised) { - char *ill = "*\\/?<>|\":{}"; - initialised = True; - - bzero((char *)illegal,256); - for (s = (unsigned char *)ill; *s; s++) - illegal[*s] = True; - } - -#ifdef KANJI - for (s = (unsigned char *)name; *s;) { - if (is_shift_jis (*s)) { - s += 2; - } else if (illegal[*s]) { - return(True); - } else { - s++; - } - } -#else - for (s = (unsigned char *)name;*s;s++) - if (illegal[*s]) return(True); -#endif - - - return(False); + return mangle_fns->check_cache(s); } - -/**************************************************************************** -convert a filename to DOS format. return True if successful. -****************************************************************************/ -BOOL name_map_mangle(char *OutName,BOOL need83,int snum) +/* + map a long filename to a 8.3 name. + */ +BOOL mangle_map(char *OutName, BOOL need83, BOOL cache83, int snum) { -#ifdef MANGLE_LONG_FILENAMES - if (!need83 && illegal_name(OutName)) need83 = True; -#endif - - /* apply any name mappings */ - { - char *map = lp_mangled_map(snum); - if (map && *map) - do_fwd_mangled_map(OutName,map); - } + /* name mangling can be disabled for speed, in which case + we just truncate the string */ + if (!lp_manglednames(snum)) { + if (need83) { + string_truncate(OutName, 12); + } + return True; + } - /* check if it's already in 8.3 format */ - if (need83 && !is_8_3(OutName)) { - if (!lp_manglednames(snum)) return(False); + /* invoke the inane "mangled map" code */ + mangle_map_filename(OutName, snum); - /* mangle it into 8.3 */ - push_mangled_name(OutName); - mangle_name_83(OutName); - } - - return(True); + return mangle_fns->name_map(OutName, need83, cache83); } - diff --git a/source3/smbd/mangle_hash.c b/source3/smbd/mangle_hash.c new file mode 100644 index 0000000000..f38c2ae2c4 --- /dev/null +++ b/source3/smbd/mangle_hash.c @@ -0,0 +1,775 @@ +/* + Unix SMB/CIFS implementation. + Name mangling + Copyright (C) Andrew Tridgell 1992-2002 + Copyright (C) Simo Sorce 2001 + Copyright (C) Andrew Bartlett 2002 + + 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. +*/ + + +/* -------------------------------------------------------------------------- ** + * Notable problems... + * + * March/April 1998 CRH + * - Many of the functions in this module overwrite string buffers passed to + * them. This causes a variety of problems and is, generally speaking, + * dangerous and scarry. See the kludge notes in name_map() + * below. + * - It seems that something is calling name_map() twice. The + * first call is probably some sort of test. Names which contain + * illegal characters are being doubly mangled. I'm not sure, but + * I'm guessing the problem is in server.c. + * + * -------------------------------------------------------------------------- ** + */ + +/* -------------------------------------------------------------------------- ** + * History... + * + * March/April 1998 CRH + * Updated a bit. Rewrote is_mangled() to be a bit more selective. + * Rewrote the mangled name cache. Added comments here and there. + * &c. + * -------------------------------------------------------------------------- ** + */ + +#include "includes.h" + + +/* -------------------------------------------------------------------------- ** + * External Variables... + */ + +extern int case_default; /* Are conforming 8.3 names all upper or lower? */ +extern BOOL case_mangle; /* If true, all chars in 8.3 should be same case. */ + +/* -------------------------------------------------------------------------- ** + * Other stuff... + * + * magic_char - This is the magic char used for mangling. It's + * global. There is a call to lp_magicchar() in server.c + * that is used to override the initial value. + * + * MANGLE_BASE - This is the number of characters we use for name mangling. + * + * basechars - The set characters used for name mangling. This + * is static (scope is this file only). + * + * mangle() - Macro used to select a character from basechars (i.e., + * mangle(n) will return the nth digit, modulo MANGLE_BASE). + * + * chartest - array 0..255. The index range is the set of all possible + * values of a byte. For each byte value, the content is a + * two nibble pair. See BASECHAR_MASK and ILLEGAL_MASK, + * below. + * + * ct_initialized - False until the chartest array has been initialized via + * a call to init_chartest(). + * + * BASECHAR_MASK - Masks the upper nibble of a one-byte value. + * + * ILLEGAL_MASK - Masks the lower nibble of a one-byte value. + * + * isbasecahr() - Given a character, check the chartest array to see + * if that character is in the basechars set. This is + * faster than using strchr_m(). + * + * isillegal() - Given a character, check the chartest array to see + * if that character is in the illegal characters set. + * This is faster than using strchr_m(). + * + * mangled_cache - Cache header used for storing mangled -> original + * reverse maps. + * + * mc_initialized - False until the mangled_cache structure has been + * initialized via a call to reset_mangled_cache(). + * + * MANGLED_CACHE_MAX_ENTRIES - Default maximum number of entries for the + * cache. A value of 0 indicates "infinite". + * + * MANGLED_CACHE_MAX_MEMORY - Default maximum amount of memory for the + * cache. When the cache was kept as an array of 256 + * byte strings, the default cache size was 50 entries. + * This required a fixed 12.5Kbytes of memory. The + * mangled stack parameter is no longer used (though + * this might change). We're now using a fixed 16Kbyte + * maximum cache size. This will probably be much more + * than 50 entries. + */ + +char magic_char = '~'; + +static char basechars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-!@#$%"; +#define MANGLE_BASE (sizeof(basechars)/sizeof(char)-1) + +static unsigned char chartest[256] = { 0 }; +static BOOL ct_initialized = False; + +#define mangle(V) ((char)(basechars[(V) % MANGLE_BASE])) +#define BASECHAR_MASK 0xf0 +#define ILLEGAL_MASK 0x0f +#define isbasechar(C) ( (chartest[ ((C) & 0xff) ]) & BASECHAR_MASK ) +#define isillegal(C) ( (chartest[ ((C) & 0xff) ]) & ILLEGAL_MASK ) + +static ubi_cacheRoot mangled_cache[1] = { { { 0, 0, 0, 0 }, 0, 0, 0, 0, 0, 0 } }; +static BOOL mc_initialized = False; +#define MANGLED_CACHE_MAX_ENTRIES 0 +#define MANGLED_CACHE_MAX_MEMORY 16384 + + +/* -------------------------------------------------------------------------- ** + * External Variables... + */ + +extern int case_default; /* Are conforming 8.3 names all upper or lower? */ +extern BOOL case_mangle; /* If true, all chars in 8.3 should be same case. */ + +/* -------------------------------------------------------------------- */ + +static NTSTATUS has_valid_chars(const smb_ucs2_t *s) +{ + if (!s || !*s) return NT_STATUS_INVALID_PARAMETER; + + /* CHECK: this should not be necessary if the ms wild chars + are not valid in valid.dat --- simo */ + if (ms_has_wild_w(s)) return NT_STATUS_UNSUCCESSFUL; + + while (*s) { + if(!isvalid83_w(*s)) return NT_STATUS_UNSUCCESSFUL; + s++; + } + + return NT_STATUS_OK; +} + +/* return False if something fail and + * return 2 alloced unicode strings that contain prefix and extension + */ +static NTSTATUS mangle_get_prefix(const smb_ucs2_t *ucs2_string, smb_ucs2_t **prefix, smb_ucs2_t **extension) +{ + size_t ext_len; + smb_ucs2_t *p; + + *extension = 0; + *prefix = strdup_w(ucs2_string); + if (!*prefix) { + return NT_STATUS_NO_MEMORY; + } + if ((p = strrchr_w(*prefix, UCS2_CHAR('.')))) + { + ext_len = strlen_w(p+1); + if ((ext_len > 0) && (ext_len < 4) && (p != *prefix) && + (NT_STATUS_IS_OK(has_valid_chars(p+1)))) /* check extension */ + { + *p = 0; + *extension = strdup_w(p+1); + if (!*extension) { + SAFE_FREE(*prefix); + return NT_STATUS_NO_MEMORY; + } + } + } + return NT_STATUS_OK; +} + +/* ************************************************************************** ** + * Return NT_STATUS_UNSUCCESSFUL if a name is a special msdos reserved name. + * + * Input: fname - String containing the name to be tested. + * + * Output: NT_STATUS_UNSUCCESSFUL, if the name matches one of the list of reserved names. + * + * Notes: This is a static function called by is_8_3(), below. + * + * ************************************************************************** ** + */ +static NTSTATUS is_valid_name(const smb_ucs2_t *fname) +{ + smb_ucs2_t *str, *p; + NTSTATUS ret = NT_STATUS_OK; + + if (!fname || !*fname) return NT_STATUS_INVALID_PARAMETER; + + if (*fname == UCS2_CHAR('.')) return NT_STATUS_UNSUCCESSFUL; + + ret = has_valid_chars(fname); + if (NT_STATUS_IS_ERR(ret)) return ret; + + str = strdup_w(fname); + p = strchr_w(str, UCS2_CHAR('.')); + if (p) *p = 0; + strupper_w(str); + p = &(str[1]); + + switch(str[0]) + { + case UCS2_CHAR('A'): + if(strcmp_wa(p, "UX") == 0) + ret = NT_STATUS_UNSUCCESSFUL; + break; + case UCS2_CHAR('C'): + if((strcmp_wa(p, "LOCK$") == 0) + || (strcmp_wa(p, "ON") == 0) + || (strcmp_wa(p, "OM1") == 0) + || (strcmp_wa(p, "OM2") == 0) + || (strcmp_wa(p, "OM3") == 0) + || (strcmp_wa(p, "OM4") == 0) + ) + ret = NT_STATUS_UNSUCCESSFUL; + break; + case UCS2_CHAR('L'): + if((strcmp_wa(p, "PT1") == 0) + || (strcmp_wa(p, "PT2") == 0) + || (strcmp_wa(p, "PT3") == 0) + ) + ret = NT_STATUS_UNSUCCESSFUL; + break; + case UCS2_CHAR('N'): + if(strcmp_wa(p, "UL") == 0) + ret = NT_STATUS_UNSUCCESSFUL; + break; + case UCS2_CHAR('P'): + if(strcmp_wa(p, "RN") == 0) + ret = NT_STATUS_UNSUCCESSFUL; + break; + default: + break; + } + + SAFE_FREE(str); + return ret; +} + +static NTSTATUS is_8_3_w(const smb_ucs2_t *fname) +{ + smb_ucs2_t *pref = 0, *ext = 0; + size_t plen; + NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + + if (!fname || !*fname) return NT_STATUS_INVALID_PARAMETER; + + if (strlen_w(fname) > 12) return NT_STATUS_UNSUCCESSFUL; + + if (strcmp_wa(fname, ".") == 0 || strcmp_wa(fname, "..") == 0) + return NT_STATUS_OK; + + if (NT_STATUS_IS_ERR(is_valid_name(fname))) goto done; + + if (NT_STATUS_IS_ERR(mangle_get_prefix(fname, &pref, &ext))) goto done; + plen = strlen_w(pref); + + if (strchr_wa(pref, '.')) goto done; + if (plen < 1 || plen > 8) goto done; + if (ext) if (strlen_w(ext) > 3) goto done; + + ret = NT_STATUS_OK; + +done: + SAFE_FREE(pref); + SAFE_FREE(ext); + return ret; +} + +static BOOL is_8_3(const char *fname, BOOL check_case) +{ + const char *f; + smb_ucs2_t *ucs2name; + NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + + if (!fname || !*fname) return False; + if ((f = strrchr(fname, '/')) == NULL) f = fname; + else f++; + + if (strlen(f) > 12) return False; + + ucs2name = acnv_uxu2(f); + if (!ucs2name) + { + DEBUG(0,("is_8_3: internal error acnv_uxu2() failed!\n")); + goto done; + } + + ret = is_8_3_w(ucs2name); + +done: + SAFE_FREE(ucs2name); + + if (!NT_STATUS_IS_OK(ret)) { + return False; + } + + return True; +} + + + +/* -------------------------------------------------------------------------- ** + * Functions... + */ + +/* ************************************************************************** ** + * Initialize the static character test array. + * + * Input: none + * + * Output: none + * + * Notes: This function changes (loads) the contents of the <chartest> + * array. The scope of <chartest> is this file. + * + * ************************************************************************** ** + */ +static void init_chartest( void ) + { + char *illegalchars = "*\\/?<>|\":"; + unsigned char *s; + + memset( (char *)chartest, '\0', 256 ); + + for( s = (unsigned char *)illegalchars; *s; s++ ) + chartest[*s] = ILLEGAL_MASK; + + for( s = (unsigned char *)basechars; *s; s++ ) + chartest[*s] |= BASECHAR_MASK; + + ct_initialized = True; + } /* init_chartest */ + + +/* ************************************************************************** ** + * Return True if the name *could be* a mangled name. + * + * Input: s - A path name - in UNIX pathname format. + * + * Output: True if the name matches the pattern described below in the + * notes, else False. + * + * Notes: The input name is *not* tested for 8.3 compliance. This must be + * done separately. This function returns true if the name contains + * a magic character followed by excactly two characters from the + * basechars list (above), which in turn are followed either by the + * nul (end of string) byte or a dot (extension) or by a '/' (end of + * a directory name). + * + * ************************************************************************** ** + */ +static BOOL is_mangled(const char *s) + { + char *magic; + + if( !ct_initialized ) + init_chartest(); + + magic = strchr_m( s, magic_char ); + while( magic && magic[1] && magic[2] ) /* 3 chars, 1st is magic. */ + { + if( ('.' == magic[3] || '/' == magic[3] || !(magic[3])) /* Ends with '.' or nul or '/' ? */ + && isbasechar( toupper(magic[1]) ) /* is 2nd char basechar? */ + && isbasechar( toupper(magic[2]) ) ) /* is 3rd char basechar? */ + return( True ); /* If all above, then true, */ + magic = strchr_m( magic+1, magic_char ); /* else seek next magic. */ + } + return( False ); + } /* is_mangled */ + + +/* ************************************************************************** ** + * Compare two cache keys and return a value indicating their ordinal + * relationship. + * + * Input: ItemPtr - Pointer to a comparison key. In this case, this will + * be a mangled name string. + * NodePtr - Pointer to a node in the cache. The node structure + * will be followed in memory by a mangled name string. + * + * Output: A signed integer, as follows: + * (x < 0) <==> Key1 less than Key2 + * (x == 0) <==> Key1 equals Key2 + * (x > 0) <==> Key1 greater than Key2 + * + * Notes: This is a ubiqx-style comparison routine. See ubi_BinTree for + * more info. + * + * ************************************************************************** ** + */ +static signed int cache_compare( ubi_btItemPtr ItemPtr, ubi_btNodePtr NodePtr ) + { + char *Key1 = (char *)ItemPtr; + char *Key2 = (char *)(((ubi_cacheEntryPtr)NodePtr) + 1); + + return( StrCaseCmp( Key1, Key2 ) ); + } /* cache_compare */ + +/* ************************************************************************** ** + * Free a cache entry. + * + * Input: WarrenZevon - Pointer to the entry that is to be returned to + * Nirvana. + * Output: none. + * + * Notes: This function gets around the possibility that the standard + * free() function may be implemented as a macro, or other evil + * subversions (oh, so much fun). + * + * ************************************************************************** ** + */ +static void cache_free_entry( ubi_trNodePtr WarrenZevon ) + { + ZERO_STRUCTP(WarrenZevon); + SAFE_FREE( WarrenZevon ); + } /* cache_free_entry */ + +/* ************************************************************************** ** + * Initializes or clears the mangled cache. + * + * Input: none. + * Output: none. + * + * Notes: There is a section below that is commented out. It shows how + * one might use lp_ calls to set the maximum memory and entry size + * of the cache. You might also want to remove the constants used + * in ubi_cacheInit() and replace them with lp_ calls. If so, then + * the calls to ubi_cacheSetMax*() would be moved into the else + * clause. Another option would be to pass in the max_entries and + * max_memory values as parameters. crh 09-Apr-1998. + * + * ************************************************************************** ** + */ +static void mangle_reset( void ) + { + if( !mc_initialized ) + { + (void)ubi_cacheInit( mangled_cache, + cache_compare, + cache_free_entry, + MANGLED_CACHE_MAX_ENTRIES, + MANGLED_CACHE_MAX_MEMORY ); + mc_initialized = True; + } + else + { + (void)ubi_cacheClear( mangled_cache ); + } + + /* + (void)ubi_cacheSetMaxEntries( mangled_cache, lp_mangled_cache_entries() ); + (void)ubi_cacheSetMaxMemory( mangled_cache, lp_mangled_cache_memory() ); + */ + } /* reset_mangled_cache */ + + +/* ************************************************************************** ** + * Add a mangled name into the cache. + * + * Notes: If the mangled cache has not been initialized, then the + * function will simply fail. It could initialize the cache, + * but that's not the way it was done before I changed the + * cache mechanism, so I'm sticking with the old method. + * + * If the extension of the raw name maps directly to the + * extension of the mangled name, then we'll store both names + * *without* extensions. That way, we can provide consistent + * reverse mangling for all names that match. The test here is + * a bit more careful than the one done in earlier versions of + * mangle.c: + * + * - the extension must exist on the raw name, + * - it must be all lower case + * - it must match the mangled extension (to prove that no + * mangling occurred). + * + * crh 07-Apr-1998 + * + * ************************************************************************** ** + */ +static void cache_mangled_name( char *mangled_name, char *raw_name ) + { + ubi_cacheEntryPtr new_entry; + char *s1; + char *s2; + size_t mangled_len; + size_t raw_len; + size_t i; + + /* If the cache isn't initialized, give up. */ + if( !mc_initialized ) + return; + + /* Init the string lengths. */ + mangled_len = strlen( mangled_name ); + raw_len = strlen( raw_name ); + + /* See if the extensions are unmangled. If so, store the entry + * without the extension, thus creating a "group" reverse map. + */ + s1 = strrchr( mangled_name, '.' ); + if( s1 && (s2 = strrchr( raw_name, '.' )) ) + { + i = 1; + while( s1[i] && (tolower( s1[i] ) == s2[i]) ) + i++; + if( !s1[i] && !s2[i] ) + { + mangled_len -= i; + raw_len -= i; + } + } + + /* Allocate a new cache entry. If the allocation fails, just return. */ + i = sizeof( ubi_cacheEntry ) + mangled_len + raw_len + 2; + new_entry = malloc( i ); + if( !new_entry ) + return; + + /* Fill the new cache entry, and add it to the cache. */ + s1 = (char *)(new_entry + 1); + s2 = (char *)&(s1[mangled_len + 1]); + (void)StrnCpy( s1, mangled_name, mangled_len ); + (void)StrnCpy( s2, raw_name, raw_len ); + ubi_cachePut( mangled_cache, i, new_entry, s1 ); + } /* cache_mangled_name */ + +/* ************************************************************************** ** + * Check for a name on the mangled name stack + * + * Input: s - Input *and* output string buffer. + * + * Output: True if the name was found in the cache, else False. + * + * Notes: If a reverse map is found, the function will overwrite the string + * space indicated by the input pointer <s>. This is frightening. + * It should be rewritten to return NULL if the long name was not + * found, and a pointer to the long name if it was found. + * + * ************************************************************************** ** + */ + +static BOOL check_cache( char *s ) +{ + ubi_cacheEntryPtr FoundPtr; + char *ext_start = NULL; + char *found_name; + char *saved_ext = NULL; + + /* If the cache isn't initialized, give up. */ + if( !mc_initialized ) + return( False ); + + FoundPtr = ubi_cacheGet( mangled_cache, (ubi_trItemPtr)s ); + + /* If we didn't find the name *with* the extension, try without. */ + if( !FoundPtr ) + { + ext_start = strrchr( s, '.' ); + if( ext_start ) + { + if((saved_ext = strdup(ext_start)) == NULL) + return False; + + *ext_start = '\0'; + FoundPtr = ubi_cacheGet( mangled_cache, (ubi_trItemPtr)s ); + /* + * At this point s is the name without the + * extension. We re-add the extension if saved_ext + * is not null, before freeing saved_ext. + */ + } + } + + /* Okay, if we haven't found it we're done. */ + if( !FoundPtr ) + { + if(saved_ext) + { + /* Replace the saved_ext as it was truncated. */ + (void)pstrcat( s, saved_ext ); + SAFE_FREE(saved_ext); + } + return( False ); + } + + /* If we *did* find it, we need to copy it into the string buffer. */ + found_name = (char *)(FoundPtr + 1); + found_name += (strlen( found_name ) + 1); + + (void)pstrcpy( s, found_name ); + if( saved_ext ) + { + /* Replace the saved_ext as it was truncated. */ + (void)pstrcat( s, saved_ext ); + SAFE_FREE(saved_ext); + } + + return( True ); +} /* check_mangled_cache */ + + +/***************************************************************************** + * do the actual mangling to 8.3 format + * the buffer must be able to hold 13 characters (including the null) + ***************************************************************************** + */ +static void to_8_3(char *s) + { + int csum; + char *p; + char extension[4]; + char base[9]; + int baselen = 0; + int extlen = 0; + + extension[0] = 0; + base[0] = 0; + + p = strrchr(s,'.'); + if( p && (strlen(p+1) < (size_t)4) ) + { + BOOL all_normal = ( strisnormal(p+1) ); /* XXXXXXXXX */ + + if( all_normal && p[1] != 0 ) + { + *p = 0; + csum = str_checksum( s ); + *p = '.'; + } + else + csum = str_checksum(s); + } + else + csum = str_checksum(s); + + strupper( s ); + + if( p ) + { + if( p == s ) + safe_strcpy( extension, "___", 3 ); + else + { + *p++ = 0; + while( *p && extlen < 3 ) + { + if ( *p != '.') { + extension[extlen++] = p[0]; + } + p++; + } + extension[extlen] = 0; + } + } + + p = s; + + while( *p && baselen < 5 ) + { + if (*p != '.') { + base[baselen++] = p[0]; + } + p++; + } + base[baselen] = 0; + + csum = csum % (MANGLE_BASE*MANGLE_BASE); + + (void)slprintf(s, 12, "%s%c%c%c", + base, magic_char, mangle( csum/MANGLE_BASE ), mangle( csum ) ); + + if( *extension ) + { + (void)pstrcat( s, "." ); + (void)pstrcat( s, extension ); + } + + } /* mangle_name_83 */ + +/***************************************************************************** + * Convert a filename to DOS format. Return True if successful. + * + * Input: OutName - Source *and* destination buffer. + * + * NOTE that OutName must point to a memory space that + * is at least 13 bytes in size! + * + * need83 - If False, name mangling will be skipped unless the + * name contains illegal characters. Mapping will still + * be done, if appropriate. This is probably used to + * signal that a client does not require name mangling, + * thus skipping the name mangling even on shares which + * have name-mangling turned on. + * cache83 - If False, the mangled name cache will not be updated. + * This is usually used to prevent that we overwrite + * a conflicting cache entry prematurely, i.e. before + * we know whether the client is really interested in the + * current name. (See PR#13758). UKD. + * + * Output: Returns False only if the name wanted mangling but the share does + * not have name mangling turned on. + * + * **************************************************************************** + */ +static BOOL name_map(char *OutName, BOOL need83, BOOL cache83) +{ + smb_ucs2_t *OutName_ucs2; + DEBUG(5,("name_map( %s, need83 = %s, cache83 = %s)\n", OutName, + need83 ? "True" : "False", cache83 ? "True" : "False")); + + if (push_ucs2_allocate((void **)&OutName_ucs2, OutName) < 0) { + DEBUG(0, ("push_ucs2_allocate failed!\n")); + return False; + } + + /* check if it's already in 8.3 format */ + if (need83 && !NT_STATUS_IS_OK(is_8_3_w(OutName_ucs2))) { + char *tmp = NULL; + + /* mangle it into 8.3 */ + if (cache83) + tmp = strdup(OutName); + + to_8_3(OutName); + + if(tmp != NULL) { + cache_mangled_name(OutName, tmp); + SAFE_FREE(tmp); + } + } + + DEBUG(5,("name_map() ==> [%s]\n", OutName)); + SAFE_FREE(OutName_ucs2); + return(True); +} /* name_map */ + + +/* + the following provides the abstraction layer to make it easier + to drop in an alternative mangling implementation +*/ +static struct mangle_fns mangle_fns = { + is_mangled, + is_8_3, + mangle_reset, + check_cache, + name_map +}; + +/* return the methods for this mangling implementation */ +struct mangle_fns *mangle_hash_init(void) +{ + mangle_reset(); + + return &mangle_fns; +} diff --git a/source3/smbd/mangle_hash2.c b/source3/smbd/mangle_hash2.c new file mode 100644 index 0000000000..96ca7360b8 --- /dev/null +++ b/source3/smbd/mangle_hash2.c @@ -0,0 +1,587 @@ +/* + Unix SMB/CIFS implementation. + new hash based name mangling implementation + Copyright (C) Andrew Tridgell 2002 + + 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 mangling scheme uses the following format + + Annnn~n.AAA + + where nnnnn is a base 36 hash, and A represents characters from the original string + + The hash is taken of the leading part of the long filename, in uppercase + + for simplicity, we only allow ascii characters in 8.3 names + */ + + +/* + =============================================================================== + NOTE NOTE NOTE!!! + + This file deliberately uses non-multibyte string functions in many places. This + is *not* a mistake. This code is multi-byte safe, but it gets this property + through some very subtle knowledge of the way multi-byte strings are encoded + and the fact that this mangling algorithm only supports ascii characters in + 8.3 names. + + please don't convert this file to use the *_m() functions!! + =============================================================================== +*/ + + +#include "includes.h" + +#if 0 +#define M_DEBUG(level, x) DEBUG(level, x) +#else +#define M_DEBUG(level, x) +#endif + +/* these flags are used to mark characters in as having particular + properties */ +#define FLAG_BASECHAR 1 +#define FLAG_ASCII 2 +#define FLAG_ILLEGAL 4 +#define FLAG_WILDCARD 8 + +/* the "possible" flags are used as a fast way to find possible DOS + reserved filenames */ +#define FLAG_POSSIBLE1 16 +#define FLAG_POSSIBLE2 32 +#define FLAG_POSSIBLE3 64 +#define FLAG_POSSIBLE4 128 + +/* by default have a max of 4096 entries in the cache. */ +#ifndef MANGLE_CACHE_SIZE +#define MANGLE_CACHE_SIZE 4096 +#endif + +/* these tables are used to provide fast tests for characters */ +static unsigned char char_flags[256]; + +#define FLAG_CHECK(c, flag) (char_flags[(unsigned char)(c)] & (flag)) + +/* we will use a very simple direct mapped prefix cache. The big + advantage of this cache structure is speed and low memory usage + + The cache is indexed by the low-order bits of the hash, and confirmed by + hashing the resulting cache entry to match the known hash +*/ +static char **prefix_cache; +static u32 *prefix_cache_hashes; + +/* these are the characters we use in the 8.3 hash. Must be 36 chars long */ +const char *basechars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +static unsigned char base_reverse[256]; +#define base_forward(v) basechars[v] + +/* the list of reserved dos names - all of these are illegal */ +const char *reserved_names[] = { "AUX", "LOCK$", "CON", "COM1", "COM2", "COM3", "COM4", + "LPT1", "LPT2", "LPT3", "NUL", "PRN", NULL }; + +/* + hash a string of the specified length. The string does not need to be + null terminated + + this hash needs to be fast with a low collision rate (what hash doesn't?) +*/ +static u32 mangle_hash(const char *key, unsigned length) +{ + u32 value; + u32 i; + fstring str; + + /* we have to uppercase here to ensure that the mangled name + doesn't depend on the case of the long name. Note that this + is the only place where we need to use a multi-byte string + function */ + strncpy(str, key, length); + str[length] = 0; + strupper_m(str); + + /* the length of a multi-byte string can change after a strupper_m */ + length = strlen(str); + + /* Set the initial value from the key size. */ + for (value = 0x238F13AF * length, i=0; i < length; i++) { + value = (value + (((unsigned char)str[i]) << (i*5 % 24))); + } + + /* note that we force it to a 31 bit hash, to keep within the limits + of the 36^6 mangle space */ + return (1103515243 * value + 12345) & ~0x80000000; +} + +/* + initialise (ie. allocate) the prefix cache + */ +static BOOL cache_init(void) +{ + if (prefix_cache) return True; + + prefix_cache = malloc(sizeof(char *) * MANGLE_CACHE_SIZE); + if (!prefix_cache) return False; + + prefix_cache_hashes = malloc(sizeof(u32) * MANGLE_CACHE_SIZE); + if (!prefix_cache_hashes) return False; + + memset(prefix_cache, 0, sizeof(char *) * MANGLE_CACHE_SIZE); + memset(prefix_cache_hashes, 0, sizeof(char *) * MANGLE_CACHE_SIZE); + return True; +} + +/* + insert an entry into the prefix cache. The string might not be null + terminated */ +static void cache_insert(const char *prefix, int length, u32 hash) +{ + int i = hash % MANGLE_CACHE_SIZE; + + if (prefix_cache[i]) { + free(prefix_cache[i]); + } + + prefix_cache[i] = strndup(prefix, length); + prefix_cache_hashes[i] = hash; +} + +/* + lookup an entry in the prefix cache. Return NULL if not found. +*/ +static const char *cache_lookup(u32 hash) +{ + int i = hash % MANGLE_CACHE_SIZE; + + if (!prefix_cache[i] || hash != prefix_cache_hashes[i]) { + return NULL; + } + + /* yep, it matched */ + return prefix_cache[i]; +} + + +/* + determine if a string is possibly in a mangled format, ignoring + case + + In this algorithm, mangled names use only pure ascii characters (no + multi-byte) so we can avoid doing a UCS2 conversion +*/ +static BOOL is_mangled(const char *name) +{ + int len, i; + + M_DEBUG(0,("is_mangled %s ?\n", name)); + + /* the best distinguishing characteristic is the ~ */ + if (name[6] != '~') return False; + + /* check the length */ + len = strlen(name); + if (len > 12 || len < 8) return False; + + /* check extension */ + if (len > 8) { + if (name[8] != '.') return False; + for (i=9; name[i]; i++) { + if (! FLAG_CHECK(name[i], FLAG_ASCII)) { + return False; + } + } + } + + /* check first character */ + if (! FLAG_CHECK(name[0], FLAG_ASCII)) { + return False; + } + + /* check rest of hash */ + if (! FLAG_CHECK(name[7], FLAG_BASECHAR)) { + return False; + } + for (i=1;i<6;i++) { + if (! FLAG_CHECK(name[i], FLAG_BASECHAR)) { + return False; + } + } + + M_DEBUG(0,("is_mangled %s -> yes\n", name)); + + return True; +} + + +/* + see if a filename is an allowable 8.3 name. + + we are only going to allow ascii characters in 8.3 names, as this + simplifies things greatly (it means that we know the string won't + get larger when converted from UNIX to DOS formats) +*/ +static BOOL is_8_3(const char *name, BOOL check_case) +{ + int len, i; + char *dot_p; + + /* as a special case, the names '.' and '..' are allowable 8.3 names */ + if (name[0] == '.') { + if (!name[1] || (name[1] == '.' && !name[2])) { + return True; + } + } + + /* the simplest test is on the overall length of the + filename. Note that we deliberately use the ascii string + length (not the multi-byte one) as it is faster, and gives us + the result we need in this case. Using strlen_m would not + only be slower, it would be incorrect */ + len = strlen(name); + if (len > 12) return False; + + /* find the '.'. Note that once again we use the non-multibyte + function */ + dot_p = strchr(name, '.'); + + if (!dot_p) { + /* if the name doesn't contain a '.' then its length + must be less than 8 */ + if (len > 8) { + return False; + } + } else { + int prefix_len, suffix_len; + + /* if it does contain a dot then the prefix must be <= + 8 and the suffix <= 3 in length */ + prefix_len = PTR_DIFF(dot_p, name); + suffix_len = len - (prefix_len+1); + + if (prefix_len > 8 || suffix_len > 3) { + return False; + } + + /* a 8.3 name cannot contain more than 1 '.' */ + if (strchr(dot_p+1, '.')) { + return False; + } + } + + /* the length are all OK. Now check to see if the characters themselves are OK */ + for (i=0; name[i]; i++) { + /* note that we allow wildcard petterns! */ + if (!FLAG_CHECK(name[i], FLAG_ASCII|FLAG_WILDCARD) && name[i] != '.') { + return False; + } + } + + /* it is a good 8.3 name */ + return True; +} + + +/* + reset the mangling cache on a smb.conf reload. This only really makes sense for + mangling backends that have parameters in smb.conf, and as this backend doesn't + this is a NULL operation +*/ +static void mangle_reset(void) +{ + /* noop */ +} + + +/* + try to find a 8.3 name in the cache, and if found then + replace the string with the original long name. + + The filename must be able to hold at least sizeof(fstring) +*/ +static BOOL check_cache(char *name) +{ + u32 hash, multiplier; + int i; + const char *prefix; + char extension[4]; + + /* make sure that this is a mangled name from this cache */ + if (!is_mangled(name)) { + M_DEBUG(0,("check_cache: %s -> not mangled\n", name)); + return False; + } + + /* we need to extract the hash from the 8.3 name */ + hash = base_reverse[(unsigned char)name[7]]; + for (multiplier=36, i=5;i>=1;i--) { + u32 v = base_reverse[(unsigned char)name[i]]; + hash += multiplier * v; + multiplier *= 36; + } + + /* now look in the prefix cache for that hash */ + prefix = cache_lookup(hash); + if (!prefix) { + M_DEBUG(0,("check_cache: %s -> %08X -> not found\n", name, hash)); + return False; + } + + /* we found it - construct the full name */ + strncpy(extension, name+9, 3); + + if (extension[0]) { + M_DEBUG(0,("check_cache: %s -> %s.%s\n", name, prefix, extension)); + slprintf(name, sizeof(fstring), "%s.%s", prefix, extension); + } else { + M_DEBUG(0,("check_cache: %s -> %s\n", name, prefix)); + fstrcpy(name, prefix); + } + + return True; +} + + +/* + look for a DOS reserved name +*/ +static BOOL is_reserved_name(const char *name) +{ + if (FLAG_CHECK(name[0], FLAG_POSSIBLE1) && + FLAG_CHECK(name[1], FLAG_POSSIBLE2) && + FLAG_CHECK(name[2], FLAG_POSSIBLE3) && + FLAG_CHECK(name[3], FLAG_POSSIBLE4)) { + /* a likely match, scan the lot */ + int i; + for (i=0; reserved_names[i]; i++) { + int len = strlen(reserved_names[i]); + /* note that we match on COM1 as well as COM1.foo */ + if (strncasecmp(name, reserved_names[i], len) == 0 && + (name[len] == '.' || name[len] == 0)) { + return True; + } + } + } + + return False; +} + +/* + see if a filename is a legal long filename +*/ +static BOOL is_legal_name(const char *name) +{ + while (*name) { + if (FLAG_CHECK(name[0], FLAG_ILLEGAL)) { + return False; + } + name++; + } + + return True; +} + +/* + the main forward mapping function, which converts a long filename to + a 8.3 name + + if need83 is not set then we only do the mangling if the name is illegal + as a long name + + if cache83 is not set then we don't cache the result + + the name parameter must be able to hold 13 bytes +*/ +static BOOL name_map(char *name, BOOL need83, BOOL cache83) +{ + char *dot_p; + char lead_char; + char extension[4]; + int extension_length, i; + int prefix_len; + u32 hash, v; + char new_name[13]; + + /* reserved names are handled specially */ + if (!is_reserved_name(name)) { + /* if the name is already a valid 8.3 name then we don't need to + do anything */ + if (is_8_3(name, False)) { + return True; + } + + /* if the caller doesn't strictly need 8.3 then just check for illegal + filenames */ + if (!need83 && is_legal_name(name)) { + return True; + } + } + + /* find the '.' if any */ + dot_p = strrchr(name, '.'); + + /* the leading character in the mangled name is taken from + the first character of the name, if it is ascii + otherwise '_' is used + */ + lead_char = name[0]; + if (! FLAG_CHECK(lead_char, FLAG_ASCII)) { + lead_char = '_'; + } + lead_char = toupper(lead_char); + + /* the prefix is anything up to the first dot */ + if (dot_p) { + prefix_len = PTR_DIFF(dot_p, name); + } else { + prefix_len = strlen(name); + } + + /* the extension of the mangled name is taken from the first 3 + ascii chars after the dot */ + extension_length = 0; + if (dot_p) { + for (i=1; extension_length < 3 && dot_p[i]; i++) { + char c = dot_p[i]; + if (FLAG_CHECK(c, FLAG_ASCII)) { + extension[extension_length++] = toupper(c); + } + } + } + + /* find the hash for this prefix */ + v = hash = mangle_hash(name, prefix_len); + + /* now form the mangled name. */ + new_name[0] = lead_char; + new_name[7] = base_forward(v % 36); + new_name[6] = '~'; + for (i=5; i>=1; i--) { + v = v / 36; + new_name[i] = base_forward(v % 36); + } + + /* add the extension */ + if (extension_length) { + new_name[8] = '.'; + memcpy(&new_name[9], extension, extension_length); + new_name[9+extension_length] = 0; + } else { + new_name[8] = 0; + } + + if (cache83) { + /* put it in the cache */ + cache_insert(name, prefix_len, hash); + } + + M_DEBUG(0,("name_map: %s -> %08X -> %s (cache=%d)\n", + name, hash, new_name, cache83)); + + /* and overwrite the old name */ + fstrcpy(name, new_name); + + /* all done, we've managed to mangle it */ + return True; +} + + +/* initialise the flags table + + we allow only a very restricted set of characters as 'ascii' in this + mangling backend. This isn't a significant problem as modern clients + use the 'long' filenames anyway, and those don't have these + restrictions. +*/ +static void init_tables(void) +{ + int i; + + memset(char_flags, 0, sizeof(char_flags)); + + for (i=0;i<128;i++) { + if ((i >= '0' && i <= '9') || + (i >= 'a' && i <= 'z') || + (i >= 'A' && i <= 'Z')) { + char_flags[i] |= (FLAG_ASCII | FLAG_BASECHAR); + } + if (strchr("_-$~", i)) { + char_flags[i] |= FLAG_ASCII; + } + + if (strchr("*\\/?<>|\":", i)) { + char_flags[i] |= FLAG_ILLEGAL; + } + + if (strchr("*?\"<>", i)) { + char_flags[i] |= FLAG_WILDCARD; + } + } + + memset(base_reverse, 0, sizeof(base_reverse)); + for (i=0;i<36;i++) { + base_reverse[(unsigned char)base_forward(i)] = i; + } + + /* fill in the reserved names flags. These are used as a very + fast filter for finding possible DOS reserved filenames */ + for (i=0; reserved_names[i]; i++) { + unsigned char c1, c2, c3, c4; + + c1 = (unsigned char)reserved_names[i][0]; + c2 = (unsigned char)reserved_names[i][1]; + c3 = (unsigned char)reserved_names[i][2]; + c4 = (unsigned char)reserved_names[i][3]; + + char_flags[c1] |= FLAG_POSSIBLE1; + char_flags[c2] |= FLAG_POSSIBLE2; + char_flags[c3] |= FLAG_POSSIBLE3; + char_flags[c4] |= FLAG_POSSIBLE4; + char_flags[tolower(c1)] |= FLAG_POSSIBLE1; + char_flags[tolower(c2)] |= FLAG_POSSIBLE2; + char_flags[tolower(c3)] |= FLAG_POSSIBLE3; + char_flags[tolower(c4)] |= FLAG_POSSIBLE4; + + char_flags[(unsigned char)'.'] |= FLAG_POSSIBLE4; + } +} + + +/* + the following provides the abstraction layer to make it easier + to drop in an alternative mangling implementation */ +static struct mangle_fns mangle_fns = { + is_mangled, + is_8_3, + mangle_reset, + check_cache, + name_map +}; + +/* return the methods for this mangling implementation */ +struct mangle_fns *mangle_hash2_init(void) +{ + init_tables(); + mangle_reset(); + + if (!cache_init()) { + return NULL; + } + + return &mangle_fns; +} diff --git a/source3/smbd/mangle_map.c b/source3/smbd/mangle_map.c new file mode 100644 index 0000000000..71d9340718 --- /dev/null +++ b/source3/smbd/mangle_map.c @@ -0,0 +1,203 @@ +/* + Unix SMB/CIFS implementation. + Name mapping code + Copyright (C) Jeremy Allison 1998 + Copyright (C) Andrew Tridgell 2002 + + 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" + + +/* ************************************************************************** ** + * Used only in do_fwd_mangled_map(), below. + * ************************************************************************** ** + */ +static char *map_filename( char *s, /* This is null terminated */ + const char *pattern, /* This isn't. */ + int len ) /* This is the length of pattern. */ + { + static pstring matching_bit; /* The bit of the string which matches */ + /* a * in pattern if indeed there is a * */ + char *sp; /* Pointer into s. */ + char *pp; /* Pointer into p. */ + char *match_start; /* Where the matching bit starts. */ + pstring pat; + + StrnCpy( pat, pattern, len ); /* Get pattern into a proper string! */ + pstrcpy( matching_bit, "" ); /* Match but no star gets this. */ + pp = pat; /* Initialize the pointers. */ + sp = s; + + if( strequal(s, ".") || strequal(s, "..")) + { + return NULL; /* Do not map '.' and '..' */ + } + + if( (len == 1) && (*pattern == '*') ) + { + return NULL; /* Impossible, too ambiguous for */ + } /* words! */ + + while( (*sp) /* Not the end of the string. */ + && (*pp) /* Not the end of the pattern. */ + && (*sp == *pp) /* The two match. */ + && (*pp != '*') ) /* No wildcard. */ + { + sp++; /* Keep looking. */ + pp++; + } + + if( !*sp && !*pp ) /* End of pattern. */ + return( matching_bit ); /* Simple match. Return empty string. */ + + if( *pp == '*' ) + { + pp++; /* Always interrested in the chacter */ + /* after the '*' */ + if( !*pp ) /* It is at the end of the pattern. */ + { + StrnCpy( matching_bit, s, sp-s ); + return( matching_bit ); + } + else + { + /* The next character in pattern must match a character further */ + /* along s than sp so look for that character. */ + match_start = sp; + while( (*sp) /* Not the end of s. */ + && (*sp != *pp) ) /* Not the same */ + sp++; /* Keep looking. */ + if( !*sp ) /* Got to the end without a match. */ + { + return( NULL ); + } /* Still hope for a match. */ + else + { + /* Now sp should point to a matching character. */ + StrnCpy(matching_bit, match_start, sp-match_start); + /* Back to needing a stright match again. */ + while( (*sp) /* Not the end of the string. */ + && (*pp) /* Not the end of the pattern. */ + && (*sp == *pp) ) /* The two match. */ + { + sp++; /* Keep looking. */ + pp++; + } + if( !*sp && !*pp ) /* Both at end so it matched */ + return( matching_bit ); + else + return( NULL ); + } + } + } + return( NULL ); /* No match. */ + } /* map_filename */ + + +/* ************************************************************************** ** + * MangledMap is a series of name pairs in () separated by spaces. + * If s matches the first of the pair then the name given is the + * second of the pair. A * means any number of any character and if + * present in the second of the pair as well as the first the + * matching part of the first string takes the place of the * in the + * second. + * + * I wanted this so that we could have RCS files which can be used + * by UNIX and DOS programs. My mapping string is (RCS rcs) which + * converts the UNIX RCS file subdirectory to lowercase thus + * preventing mangling. + * + * See 'mangled map' in smb.conf(5). + * + * ************************************************************************** ** + */ +static void mangled_map(char *s, const char *MangledMap) +{ + char *start=MangledMap; /* Use this to search for mappings. */ + char *end; /* Used to find the end of strings. */ + char *match_string; + pstring new_string; /* Make up the result here. */ + char *np; /* Points into new_string. */ + + DEBUG( 5, ("Mangled Mapping '%s' map '%s'\n", s, MangledMap) ); + while( *start ) { + while( (*start) && (*start != '(') ) + start++; + if( !*start ) + continue; /* Always check for the end. */ + start++; /* Skip the ( */ + end = start; /* Search for the ' ' or a ')' */ + DEBUG( 5, ("Start of first in pair '%s'\n", start) ); + while( (*end) && !((*end == ' ') || (*end == ')')) ) + end++; + if( !*end ) { + start = end; + continue; /* Always check for the end. */ + } + DEBUG( 5, ("End of first in pair '%s'\n", end) ); + if( (match_string = map_filename( s, start, end-start )) ) { + DEBUG( 5, ("Found a match\n") ); + /* Found a match. */ + start = end + 1; /* Point to start of what it is to become. */ + DEBUG( 5, ("Start of second in pair '%s'\n", start) ); + end = start; + np = new_string; + while( (*end) /* Not the end of string. */ + && (*end != ')') /* Not the end of the pattern. */ + && (*end != '*') ) /* Not a wildcard. */ + *np++ = *end++; + + if( !*end ) { + start = end; + continue; /* Always check for the end. */ + } + if( *end == '*' ) { + pstrcpy( np, match_string ); + np += strlen( match_string ); + end++; /* Skip the '*' */ + while ((*end) /* Not the end of string. */ + && (*end != ')') /* Not the end of the pattern. */ + && (*end != '*'))/* Not a wildcard. */ + *np++ = *end++; + } + if (!*end) { + start = end; + continue; /* Always check for the end. */ + } + *np++ = '\0'; /* NULL terminate it. */ + DEBUG(5,("End of second in pair '%s'\n", end)); + pstrcpy( s, new_string ); /* Substitute with the new name. */ + DEBUG( 5, ("s is now '%s'\n", s) ); + } + start = end; /* Skip a bit which cannot be wanted anymore. */ + start++; + } +} + +/* + front end routine to the mangled map code + personally I think that the whole idea of "mangled map" is completely bogus +*/ +void mangle_map_filename(char *fname, int snum) +{ + char *map; + + map = lp_mangled_map(snum); + if (!map || !*map) return; + + mangled_map(fname, map); +} diff --git a/source3/smbd/message.c b/source3/smbd/message.c index 6a96b4c7a9..971834c012 100644 --- a/source3/smbd/message.c +++ b/source3/smbd/message.c @@ -1,8 +1,7 @@ /* - Unix SMB/Netbios implementation. - Version 1.9. + Unix SMB/CIFS implementation. SMB messaging - Copyright (C) Andrew Tridgell 1992-1995 + Copyright (C) Andrew Tridgell 1992-1998 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -25,26 +24,23 @@ #include "includes.h" -#include "loadparm.h" - -/* look in server.c for some explanation of these variables */ -extern int DEBUGLEVEL; +extern userdom_struct current_user_info; +/* look in server.c for some explanation of these variables */ static char msgbuf[1600]; -static int msgpos=0; -static fstring msgfrom=""; -static fstring msgto=""; +static int msgpos; +static fstring msgfrom; +static fstring msgto; /**************************************************************************** deliver the message ****************************************************************************/ static void msg_deliver(void) { - pstring s; - fstring name; - FILE *f; + pstring name; int i; + int fd; if (! (*lp_msg_command())) { @@ -54,34 +50,43 @@ static void msg_deliver(void) } /* put it in a temporary file */ - sprintf(s,"/tmp/msg.XXXXXX"); - strcpy(name,(char *)mktemp(s)); + slprintf(name,sizeof(name)-1, "%s/msg.XXXXXX",tmpdir()); + fd = smb_mkstemp(name); - f = fopen(name,"w"); - if (!f) - { - DEBUG(1,("can't open message file %s\n",name)); - return; - } + if (fd == -1) { + DEBUG(1,("can't open message file %s\n",name)); + return; + } - for (i=0;i<msgpos;) - { - if (msgbuf[i]=='\r' && i<(msgpos-1) && msgbuf[i+1]=='\n') - i++; - fputc(msgbuf[i++],f); - } + /* + * Incoming message is in DOS codepage format. Convert to UNIX. + */ - fclose(f); + if(msgpos > 0) { + msgbuf[msgpos] = '\0'; /* Ensure null terminated. */ + } + + for (i=0;i<msgpos;) { + if (msgbuf[i]=='\r' && i<(msgpos-1) && msgbuf[i+1]=='\n') { + i++; continue; + } + write(fd,&msgbuf[i++],1); + } + close(fd); /* run the command */ if (*lp_msg_command()) { - strcpy(s,lp_msg_command()); - string_sub(s,"%s",name); - string_sub(s,"%f",msgfrom); - string_sub(s,"%t",msgto); - standard_sub(-1,s); + fstring alpha_msgfrom; + fstring alpha_msgto; + pstring s; + + pstrcpy(s,lp_msg_command()); + pstring_sub(s,"%f",alpha_strcpy(alpha_msgfrom,msgfrom,NULL,sizeof(alpha_msgfrom))); + pstring_sub(s,"%t",alpha_strcpy(alpha_msgto,msgto,NULL,sizeof(alpha_msgto))); + standard_sub_basic(current_user_info.smb_name, s); + pstring_sub(s,"%s",name); smbrun(s,NULL); } @@ -93,37 +98,42 @@ static void msg_deliver(void) /**************************************************************************** reply to a sends ****************************************************************************/ -int reply_sends(char *inbuf,char *outbuf) +int reply_sends(connection_struct *conn, + char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { int len; - char *orig,*dest,*msg; + char *msg; int outsize = 0; + char *p; - msgpos = 0; + START_PROFILE(SMBsends); + msgpos = 0; - if (! (*lp_msg_command())) - return(ERROR(ERRSRV,ERRmsgoff)); + if (! (*lp_msg_command())) { + END_PROFILE(SMBsends); + return(ERROR_DOS(ERRSRV,ERRmsgoff)); + } outsize = set_message(outbuf,0,0,True); - orig = smb_buf(inbuf)+1; - dest = skip_string(orig,1)+1; - msg = skip_string(dest,1)+1; + p = smb_buf(inbuf)+1; + p += srvstr_pull(inbuf, msgfrom, p, sizeof(msgfrom), -1, STR_TERMINATE) + 1; + p += srvstr_pull(inbuf, msgto, p, sizeof(msgto), -1, STR_TERMINATE) + 1; - strcpy(msgfrom,orig); - strcpy(msgto,dest); + msg = p; len = SVAL(msg,0); - len = MIN(len,1600-msgpos); + len = MIN(len,sizeof(msgbuf)-msgpos); + + memset(msgbuf,'\0',sizeof(msgbuf)); memcpy(&msgbuf[msgpos],msg+2,len); msgpos += len; - DEBUG(3,("%s SMBsends (from %s to %s)\n",timestring(),orig,dest)); - msg_deliver(); + END_PROFILE(SMBsends); return(outsize); } @@ -131,26 +141,31 @@ int reply_sends(char *inbuf,char *outbuf) /**************************************************************************** reply to a sendstrt ****************************************************************************/ -int reply_sendstrt(char *inbuf,char *outbuf) +int reply_sendstrt(connection_struct *conn, + char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { - char *orig,*dest; int outsize = 0; + char *p; - if (! (*lp_msg_command())) - return(ERROR(ERRSRV,ERRmsgoff)); + START_PROFILE(SMBsendstrt); + + if (! (*lp_msg_command())) { + END_PROFILE(SMBsendstrt); + return(ERROR_DOS(ERRSRV,ERRmsgoff)); + } outsize = set_message(outbuf,1,0,True); + memset(msgbuf,'\0',sizeof(msgbuf)); msgpos = 0; - orig = smb_buf(inbuf)+1; - dest = skip_string(orig,1)+1; - - strcpy(msgfrom,orig); - strcpy(msgto,dest); + p = smb_buf(inbuf)+1; + p += srvstr_pull(inbuf, msgfrom, p, sizeof(msgfrom), -1, STR_TERMINATE) + 1; + p += srvstr_pull(inbuf, msgto, p, sizeof(msgto), -1, STR_TERMINATE) + 1; - DEBUG(3,("%s SMBsendstrt (from %s to %s)\n",timestring(),orig,dest)); + DEBUG( 3, ( "SMBsendstrt (from %s to %s)\n", msgfrom, msgto ) ); + END_PROFILE(SMBsendstrt); return(outsize); } @@ -158,27 +173,32 @@ int reply_sendstrt(char *inbuf,char *outbuf) /**************************************************************************** reply to a sendtxt ****************************************************************************/ -int reply_sendtxt(char *inbuf,char *outbuf) +int reply_sendtxt(connection_struct *conn, + char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { int len; int outsize = 0; char *msg; + START_PROFILE(SMBsendtxt); - if (! (*lp_msg_command())) - return(ERROR(ERRSRV,ERRmsgoff)); + if (! (*lp_msg_command())) { + END_PROFILE(SMBsendtxt); + return(ERROR_DOS(ERRSRV,ERRmsgoff)); + } outsize = set_message(outbuf,0,0,True); msg = smb_buf(inbuf) + 1; len = SVAL(msg,0); - len = MIN(len,1600-msgpos); + len = MIN(len,sizeof(msgbuf)-msgpos); memcpy(&msgbuf[msgpos],msg+2,len); msgpos += len; - DEBUG(3,("%s SMBsendtxt\n",timestring())); + DEBUG( 3, ( "SMBsendtxt\n" ) ); + END_PROFILE(SMBsendtxt); return(outsize); } @@ -186,19 +206,23 @@ int reply_sendtxt(char *inbuf,char *outbuf) /**************************************************************************** reply to a sendend ****************************************************************************/ -int reply_sendend(char *inbuf,char *outbuf) +int reply_sendend(connection_struct *conn, + char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { int outsize = 0; + START_PROFILE(SMBsendend); - if (! (*lp_msg_command())) - return(ERROR(ERRSRV,ERRmsgoff)); + if (! (*lp_msg_command())) { + END_PROFILE(SMBsendend); + return(ERROR_DOS(ERRSRV,ERRmsgoff)); + } outsize = set_message(outbuf,0,0,True); - DEBUG(3,("%s SMBsendend\n",timestring())); + DEBUG(3,("SMBsendend\n")); msg_deliver(); + END_PROFILE(SMBsendend); return(outsize); } - diff --git a/source3/smbd/negprot.c b/source3/smbd/negprot.c new file mode 100644 index 0000000000..18682e6c9f --- /dev/null +++ b/source3/smbd/negprot.c @@ -0,0 +1,509 @@ +/* + Unix SMB/CIFS implementation. + negprot reply code + Copyright (C) Andrew Tridgell 1992-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + 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" + +extern int Protocol; +extern int max_recv; +extern fstring global_myworkgroup; +extern fstring remote_machine; +BOOL global_encrypted_passwords_negotiated = False; +BOOL global_spnego_negotiated = False; +struct auth_context *negprot_global_auth_context = NULL; + +static void get_challenge(char buff[8]) +{ + NTSTATUS nt_status; + const uint8 *cryptkey; + + /* We might be called more than once, muliple negprots are premitted */ + if (negprot_global_auth_context) { + DEBUG(3, ("get challenge: is this a secondary negprot? negprot_global_auth_context is non-NULL!\n")); + (negprot_global_auth_context->free)(&negprot_global_auth_context); + } + + DEBUG(10, ("get challenge: creating negprot_global_auth_context\n")); + if (!NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(&negprot_global_auth_context))) { + DEBUG(0, ("make_auth_context_subsystem returned %s", nt_errstr(nt_status))); + smb_panic("cannot make_negprot_global_auth_context!\n"); + } + DEBUG(10, ("get challenge: getting challenge\n")); + cryptkey = negprot_global_auth_context->get_ntlm_challenge(negprot_global_auth_context); + memcpy(buff, cryptkey, 8); +} + +/**************************************************************************** +reply for the core protocol +****************************************************************************/ +static int reply_corep(char *inbuf, char *outbuf) +{ + int outsize = set_message(outbuf,1,0,True); + + Protocol = PROTOCOL_CORE; + + return outsize; +} + + +/**************************************************************************** +reply for the coreplus protocol +****************************************************************************/ +static int reply_coreplus(char *inbuf, char *outbuf) +{ + int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0); + int outsize = set_message(outbuf,13,0,True); + SSVAL(outbuf,smb_vwv5,raw); /* tell redirector we support + readbraw and writebraw (possibly) */ + /* Reply, SMBlockread, SMBwritelock supported. */ + SCVAL(outbuf,smb_flg,FLAG_REPLY|FLAG_SUPPORT_LOCKREAD); + SSVAL(outbuf,smb_vwv1,0x1); /* user level security, don't encrypt */ + + Protocol = PROTOCOL_COREPLUS; + + return outsize; +} + + +/**************************************************************************** +reply for the lanman 1.0 protocol +****************************************************************************/ +static int reply_lanman1(char *inbuf, char *outbuf) +{ + int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0); + int secword=0; + time_t t = time(NULL); + + global_encrypted_passwords_negotiated = lp_encrypted_passwords(); + + if (lp_security()>=SEC_USER) secword |= 1; + if (global_encrypted_passwords_negotiated) secword |= 2; + + set_message(outbuf,13,global_encrypted_passwords_negotiated?8:0,True); + SSVAL(outbuf,smb_vwv1,secword); + /* Create a token value and add it to the outgoing packet. */ + if (global_encrypted_passwords_negotiated) { + get_challenge(smb_buf(outbuf)); + } + + Protocol = PROTOCOL_LANMAN1; + + /* Reply, SMBlockread, SMBwritelock supported. */ + SCVAL(outbuf,smb_flg,FLAG_REPLY|FLAG_SUPPORT_LOCKREAD); + SSVAL(outbuf,smb_vwv2,max_recv); + 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,sys_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 +****************************************************************************/ +static int reply_lanman2(char *inbuf, char *outbuf) +{ + int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0); + int secword=0; + time_t t = time(NULL); + + global_encrypted_passwords_negotiated = lp_encrypted_passwords(); + + if (lp_security()>=SEC_USER) secword |= 1; + if (global_encrypted_passwords_negotiated) secword |= 2; + + set_message(outbuf,13,global_encrypted_passwords_negotiated?8:0,True); + SSVAL(outbuf,smb_vwv1,secword); + SIVAL(outbuf,smb_vwv6,sys_getpid()); + + /* Create a token value and add it to the outgoing packet. */ + if (global_encrypted_passwords_negotiated) { + get_challenge(smb_buf(outbuf)); + } + + Protocol = PROTOCOL_LANMAN2; + + /* Reply, SMBlockread, SMBwritelock supported. */ + SCVAL(outbuf,smb_flg,FLAG_REPLY|FLAG_SUPPORT_LOCKREAD); + SSVAL(outbuf,smb_vwv2,max_recv); + 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); +} + + + +/* + generate the spnego negprot reply blob. Return the number of bytes used +*/ +static int negprot_spnego(char *p) +{ + DATA_BLOB blob; + extern pstring global_myname; + uint8 guid[16]; + const char *OIDs_krb5[] = {OID_NTLMSSP, + OID_KERBEROS5, + OID_KERBEROS5_OLD, + NULL}; + const char *OIDs_plain[] = {OID_NTLMSSP, NULL}; + char *principal; + int len; + + global_spnego_negotiated = True; + + memset(guid, 0, 16); + safe_strcpy((char *)guid, global_myname, 16); + strlower((char *)guid); + +#if 0 + /* strangely enough, NT does not sent the single OID NTLMSSP when + not a ADS member, it sends no OIDs at all + + we can't do this until we teach our sesssion setup parser to know + about raw NTLMSSP (clients send no ASN.1 wrapping if we do this) + */ + if (lp_security() != SEC_ADS) { + memcpy(p, guid, 16); + return 16; + } +#endif + if (lp_security() != SEC_ADS) { + blob = spnego_gen_negTokenInit(guid, OIDs_plain, "NONE"); + } else { + ADS_STRUCT *ads; + ads = ads_init(NULL, NULL, NULL, NULL); + /* win2000 uses host$@REALM, which we will probably use eventually, + but for now this works */ + asprintf(&principal, "HOST/%s@%s", guid, ads->realm); + blob = spnego_gen_negTokenInit(guid, OIDs_krb5, principal); + free(principal); + ads_destroy(&ads); + } + memcpy(p, blob.data, blob.length); + len = blob.length; + data_blob_free(&blob); + return len; +} + + + +/**************************************************************************** +reply for the nt protocol +****************************************************************************/ +static int reply_nt1(char *inbuf, char *outbuf) +{ + /* dual names + lock_and_read + nt SMBs + remote API calls */ + int capabilities = CAP_NT_FIND|CAP_LOCK_AND_READ| + CAP_LEVEL_II_OPLOCKS; + + int secword=0; + time_t t = time(NULL); + char *p, *q; + BOOL negotiate_spnego = False; + + global_encrypted_passwords_negotiated = lp_encrypted_passwords(); + + /* do spnego in user level security if the client + supports it and we can do encrypted passwords */ + + if (global_encrypted_passwords_negotiated && + (lp_security() != SEC_SHARE) && + lp_use_spnego() && + (SVAL(inbuf, smb_flg2) & FLAGS2_EXTENDED_SECURITY)) { + negotiate_spnego = True; + capabilities |= CAP_EXTENDED_SECURITY; + } + + capabilities |= CAP_NT_SMBS|CAP_RPC_REMOTE_APIS|CAP_UNIX; + + if (lp_large_readwrite() && (SMB_OFF_T_BITS == 64)) + capabilities |= CAP_LARGE_READX|CAP_LARGE_WRITEX|CAP_W2K_SMBS; + + if (SMB_OFF_T_BITS == 64) + capabilities |= CAP_LARGE_FILES; + + if (lp_readraw() && lp_writeraw()) + capabilities |= CAP_RAW_MODE; + + /* allow for disabling unicode */ + if (lp_unicode()) + capabilities |= CAP_UNICODE; + + if (lp_nt_status_support()) + capabilities |= CAP_STATUS32; + + if (lp_host_msdfs()) + capabilities |= CAP_DFS; + + if (lp_security() >= SEC_USER) + secword |= 1; + if (global_encrypted_passwords_negotiated) + secword |= 2; + + set_message(outbuf,17,0,True); + + SCVAL(outbuf,smb_vwv1,secword); + + Protocol = PROTOCOL_NT1; + + SSVAL(outbuf,smb_vwv1+1,lp_maxmux()); /* maxmpx */ + SSVAL(outbuf,smb_vwv2+1,1); /* num vcs */ + SIVAL(outbuf,smb_vwv3+1,0xffff); /* max buffer. LOTS! */ + SIVAL(outbuf,smb_vwv5+1,0x10000); /* raw size. full 64k */ + SIVAL(outbuf,smb_vwv7+1,sys_getpid()); /* session key */ + SIVAL(outbuf,smb_vwv9+1,capabilities); /* capabilities */ + put_long_date(outbuf+smb_vwv11+1,t); + SSVALS(outbuf,smb_vwv15+1,TimeDiff(t)/60); + + p = q = smb_buf(outbuf); + if (!negotiate_spnego) { + /* Create a token value and add it to the outgoing packet. */ + if (global_encrypted_passwords_negotiated) { + get_challenge(p); + } + SSVALS(outbuf,smb_vwv16+1,8); + p += 8; + p += srvstr_push(outbuf, p, global_myworkgroup, -1, + STR_UNICODE|STR_TERMINATE|STR_NOALIGN); + DEBUG(3,("not using SPNEGO\n")); + } else { + int len = negprot_spnego(p); + + SSVALS(outbuf,smb_vwv16+1,len); + p += len; + DEBUG(3,("using SPNEGO\n")); + } + + SSVAL(outbuf,smb_vwv17, p - q); /* length of challenge+domain strings */ + set_message_end(outbuf, p); + + 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] + +Win2K: +protocol [PC NETWORK PROGRAM 1.0] +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 Win2K OS/2 + PC NETWORK PROGRAM 1.0 1 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 2 3 + Windows for Workgroups 3.1a 5 5 5 3 + LM1.2X002 6 4 4 + LANMAN2.1 7 5 5 + NT LM 0.12 6 8 6 + * + * tim@fsg.com 09/29/95 + * Win2K added by matty 17/7/99 + */ + +#define ARCH_WFWG 0x3 /* This is a fudge because WfWg is like Win95 */ +#define ARCH_WIN95 0x2 +#define ARCH_WINNT 0x4 +#define ARCH_WIN2K 0xC /* Win2K is like NT */ +#define ARCH_OS2 0x14 /* Again OS/2 is like NT */ +#define ARCH_SAMBA 0x20 + +#define ARCH_ALL 0x3F + +/* List of supported protocols, most desired first */ +static struct { + char *proto_name; + char *short_name; + int (*proto_reply_fn)(char *, 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,NULL,0}, +}; + + +/**************************************************************************** + reply to a negprot +****************************************************************************/ +int reply_negprot(connection_struct *conn, + char *inbuf,char *outbuf, int dum_size, + int dum_buffsize) +{ + 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; + START_PROFILE(SMBnegprot); + + 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 | ARCH_WIN2K ); + 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 | ARCH_WIN2K ); + else if (strcsequal(p,"LANMAN2.1")) + arch &= ( ARCH_WINNT | ARCH_WIN2K | ARCH_OS2 ); + else if (strcsequal(p,"LM1.2X002")) + arch &= ( ARCH_WINNT | ARCH_WIN2K | 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: + set_remote_arch(RA_SAMBA); + break; + case ARCH_WFWG: + set_remote_arch(RA_WFWG); + break; + case ARCH_WIN95: + set_remote_arch(RA_WIN95); + break; + case ARCH_WINNT: + if(SVAL(inbuf,smb_flg2)==FLAGS2_WIN2K_SIGNATURE) + set_remote_arch(RA_WIN2K); + else + set_remote_arch(RA_WINNT); + break; + case ARCH_WIN2K: + set_remote_arch(RA_WIN2K); + break; + case ARCH_OS2: + set_remote_arch(RA_OS2); + break; + default: + set_remote_arch(RA_UNKNOWN); + break; + } + + /* possibly reload - change of architecture */ + reload_services(True); + + /* Check for protocols, most desirable first */ + for (protocol = 0; supported_protocols[protocol].proto_name; protocol++) + { + p = smb_buf(inbuf)+1; + Index = 0; + if ((supported_protocols[protocol].protocol_level <= lp_maxprotocol()) && + (supported_protocols[protocol].protocol_level >= lp_minprotocol())) + 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; + fstrcpy(remote_proto,supported_protocols[protocol].short_name); + reload_services(True); + outsize = supported_protocols[protocol].proto_reply_fn(inbuf, 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, ( "negprot index=%d\n", choice ) ); + + END_PROFILE(SMBnegprot); + return(outsize); +} + diff --git a/source3/smbd/noquotas.c b/source3/smbd/noquotas.c new file mode 100644 index 0000000000..85caef57e1 --- /dev/null +++ b/source3/smbd/noquotas.c @@ -0,0 +1,38 @@ +/* + Unix SMB/CIFS implementation. + No support for quotas :-). + Copyright (C) Andrew Tridgell 1992-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + 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" + +/* + * Needed for auto generation of proto.h. + */ + +BOOL disk_quotas(const char *path,SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize) +{ + (*bsize) = 512; /* This value should be ignored */ + + /* And just to be sure we set some values that hopefully */ + /* will be larger that any possible real-world value */ + (*dfree) = (SMB_BIG_UINT)-1; + (*dsize) = (SMB_BIG_UINT)-1; + + /* As we have select not to use quotas, allways fail */ + return False; +} diff --git a/source3/smbd/notify.c b/source3/smbd/notify.c new file mode 100644 index 0000000000..0895ddaf87 --- /dev/null +++ b/source3/smbd/notify.c @@ -0,0 +1,220 @@ +/* + Unix SMB/CIFS implementation. + change notify handling + Copyright (C) Andrew Tridgell 2000 + Copyright (C) Jeremy Allison 1994-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + 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" + +static struct cnotify_fns *cnotify; + +/**************************************************************************** + This is the structure to queue to implement NT change + notify. It consists of smb_size bytes stored from the + transact command (to keep the mid, tid etc around). + Plus the fid to examine and notify private data. +*****************************************************************************/ + +struct change_notify { + struct change_notify *next, *prev; + files_struct *fsp; + connection_struct *conn; + uint32 flags; + char request_buf[smb_size]; + void *change_data; +}; + +static struct change_notify *change_notify_list; + +/**************************************************************************** + Setup the common parts of the return packet and send it. +*****************************************************************************/ +static void change_notify_reply_packet(char *inbuf, NTSTATUS error_code) +{ + char outbuf[smb_size+38]; + + memset(outbuf, '\0', sizeof(outbuf)); + construct_reply_common(inbuf, outbuf); + + ERROR_NT(error_code); + + /* + * Seems NT needs a transact command with an error code + * in it. This is a longer packet than a simple error. + */ + set_message(outbuf,18,0,False); + + if (!send_smb(smbd_server_fd(),outbuf)) + exit_server("change_notify_reply_packet: send_smb failed."); +} + +/**************************************************************************** + Remove an entry from the list and free it, also closing any + directory handle if necessary. +*****************************************************************************/ + +static void change_notify_remove(struct change_notify *cnbp) +{ + cnotify->remove_notify(cnbp->change_data); + DLIST_REMOVE(change_notify_list, cnbp); + ZERO_STRUCTP(cnbp); + SAFE_FREE(cnbp); +} + +/**************************************************************************** + Delete entries by fnum from the change notify pending queue. +*****************************************************************************/ + +void remove_pending_change_notify_requests_by_fid(files_struct *fsp) +{ + struct change_notify *cnbp, *next; + + for (cnbp=change_notify_list; cnbp; cnbp=next) { + next=cnbp->next; + if (cnbp->fsp->fnum == fsp->fnum) { + change_notify_remove(cnbp); + } + } +} + +/**************************************************************************** + Delete entries by mid from the change notify pending queue. Always send reply. +*****************************************************************************/ + +void remove_pending_change_notify_requests_by_mid(int mid) +{ + struct change_notify *cnbp, *next; + + for (cnbp=change_notify_list; cnbp; cnbp=next) { + next=cnbp->next; + if(SVAL(cnbp->request_buf,smb_mid) == mid) { + change_notify_reply_packet(cnbp->request_buf,NT_STATUS_CANCELLED); + change_notify_remove(cnbp); + } + } +} + +/**************************************************************************** + Delete entries by filename and cnum from the change notify pending queue. + Always send reply. +*****************************************************************************/ + +void remove_pending_change_notify_requests_by_filename(files_struct *fsp) +{ + struct change_notify *cnbp, *next; + + for (cnbp=change_notify_list; cnbp; cnbp=next) { + next=cnbp->next; + /* + * We know it refers to the same directory if the connection number and + * the filename are identical. + */ + if((cnbp->fsp->conn == fsp->conn) && strequal(cnbp->fsp->fsp_name,fsp->fsp_name)) { + change_notify_reply_packet(cnbp->request_buf,NT_STATUS_CANCELLED); + change_notify_remove(cnbp); + } + } +} + +/**************************************************************************** + Return true if there are pending change notifies. +****************************************************************************/ + +int change_notify_timeout(void) +{ + return cnotify->select_time; +} + +/**************************************************************************** + Process the change notify queue. Note that this is only called as root. + Returns True if there are still outstanding change notify requests on the + queue. +*****************************************************************************/ + +BOOL process_pending_change_notify_queue(time_t t) +{ + struct change_notify *cnbp, *next; + uint16 vuid; + + for (cnbp=change_notify_list; cnbp; cnbp=next) { + next=cnbp->next; + + vuid = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID : SVAL(cnbp->request_buf,smb_uid); + + if (cnotify->check_notify(cnbp->conn, vuid, cnbp->fsp->fsp_name, cnbp->flags, cnbp->change_data, t)) { + DEBUG(10,("process_pending_change_notify_queue: dir %s changed !\n", cnbp->fsp->fsp_name )); + change_notify_reply_packet(cnbp->request_buf,STATUS_NOTIFY_ENUM_DIR); + change_notify_remove(cnbp); + } + } + + return (change_notify_list != NULL); +} + +/**************************************************************************** + Now queue an entry on the notify change list. + We only need to save smb_size bytes from this incoming packet + as we will always by returning a 'read the directory yourself' + error. +****************************************************************************/ + +BOOL change_notify_set(char *inbuf, files_struct *fsp, connection_struct *conn, uint32 flags) +{ + struct change_notify *cnbp; + + if((cnbp = (struct change_notify *)malloc(sizeof(*cnbp))) == NULL) { + DEBUG(0,("call_nt_transact_notify_change: malloc fail !\n" )); + return -1; + } + + ZERO_STRUCTP(cnbp); + + memcpy(cnbp->request_buf, inbuf, smb_size); + cnbp->fsp = fsp; + cnbp->conn = conn; + cnbp->flags = flags; + cnbp->change_data = cnotify->register_notify(conn, fsp->fsp_name, flags); + + if (!cnbp->change_data) { + SAFE_FREE(cnbp); + return False; + } + + DLIST_ADD(change_notify_list, cnbp); + + return True; +} + +/**************************************************************************** + Initialise the change notify subsystem. +****************************************************************************/ + +BOOL init_change_notify(void) +{ +#if HAVE_KERNEL_CHANGE_NOTIFY + cnotify = kernel_notify_init(); +#endif + if (!cnotify) cnotify = hash_notify_init(); + + if (!cnotify) { + DEBUG(0,("Failed to init change notify system\n")); + return False; + } + + return True; +} diff --git a/source3/smbd/notify_hash.c b/source3/smbd/notify_hash.c new file mode 100644 index 0000000000..d8b35462ac --- /dev/null +++ b/source3/smbd/notify_hash.c @@ -0,0 +1,225 @@ +/* + Unix SMB/CIFS implementation. + change notify handling - hash based implementation + Copyright (C) Jeremy Allison 1994-1998 + Copyright (C) Andrew Tridgell 2000 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +struct change_data { + time_t last_check_time; /* time we last checked this entry */ + time_t modify_time; /* Info from the directory we're monitoring. */ + time_t status_time; /* Info from the directory we're monitoring. */ + time_t total_time; /* Total time of all directory entries - don't care if it wraps. */ + unsigned int num_entries; /* Zero or the number of files in the directory. */ + unsigned int mode_sum; + unsigned char name_hash[16]; +}; + +/**************************************************************************** + Create the hash we will use to determine if the contents changed. +*****************************************************************************/ + +static BOOL notify_hash(connection_struct *conn, char *path, uint32 flags, + struct change_data *data, struct change_data *old_data) +{ + SMB_STRUCT_STAT st; + pstring full_name; + char *p; + char *fname; + size_t remaining_len; + size_t fullname_len; + void *dp; + + ZERO_STRUCTP(data); + + if(vfs_stat(conn,path, &st) == -1) + return False; + + data->modify_time = st.st_mtime; + data->status_time = st.st_ctime; + + if (old_data) { + /* + * Shortcut to avoid directory scan if the time + * has changed - we always must return true then. + */ + if (old_data->modify_time != data->modify_time || + old_data->status_time != data->status_time ) { + return True; + } + } + + /* + * If we are to watch for changes that are only stored + * in inodes of files, not in the directory inode, we must + * scan the directory and produce a unique identifier with + * which we can determine if anything changed. We use the + * modify and change times from all the files in the + * directory, added together (ignoring wrapping if it's + * larger than the max time_t value). + */ + + dp = OpenDir(conn, path, True); + if (dp == NULL) + return False; + + data->num_entries = 0; + + pstrcpy(full_name, path); + pstrcat(full_name, "/"); + + fullname_len = strlen(full_name); + remaining_len = sizeof(full_name) - fullname_len - 1; + p = &full_name[fullname_len]; + + while ((fname = ReadDirName(dp))) { + if(strequal(fname, ".") || strequal(fname, "..")) + continue; + + data->num_entries++; + safe_strcpy(p, fname, remaining_len); + + ZERO_STRUCT(st); + + /* + * Do the stat - but ignore errors. + */ + vfs_stat(conn,full_name, &st); + + /* + * Always sum the times. + */ + + data->total_time += (st.st_mtime + st.st_ctime); + + /* + * If requested hash the names. + */ + + if (flags & (FILE_NOTIFY_CHANGE_DIR_NAME|FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_FILE)) { + int i; + unsigned char tmp_hash[16]; + mdfour(tmp_hash, (unsigned char *)fname, strlen(fname)); + for (i=0;i<16;i++) + data->name_hash[i] ^= tmp_hash[i]; + } + + /* + * If requested sum the mode_t's. + */ + + if (flags & (FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_SECURITY)) + data->mode_sum = st.st_mode; + } + + CloseDir(dp); + + return True; +} + +/**************************************************************************** + Register a change notify request. +*****************************************************************************/ + +static void *hash_register_notify(connection_struct *conn, char *path, uint32 flags) +{ + struct change_data data; + + if (!notify_hash(conn, path, flags, &data, NULL)) + return NULL; + + data.last_check_time = time(NULL); + + return (void *)memdup(&data, sizeof(data)); +} + +/**************************************************************************** + Check if a change notify should be issued. + A time of zero means instantaneous check - don't modify the last check time. +*****************************************************************************/ + +static BOOL hash_check_notify(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *datap, time_t t) +{ + struct change_data *data = (struct change_data *)datap; + struct change_data data2; + + if (t && t < data->last_check_time + lp_change_notify_timeout()) + return False; + + if (!change_to_user(conn,vuid)) + return True; + if (!set_current_service(conn,True)) { + change_to_root_user(); + return True; + } + + if (!notify_hash(conn, path, flags, &data2, data) || + data2.modify_time != data->modify_time || + data2.status_time != data->status_time || + data2.total_time != data->total_time || + data2.num_entries != data->num_entries || + data2.mode_sum != data->mode_sum || + memcmp(data2.name_hash, data->name_hash, sizeof(data2.name_hash))) { + change_to_root_user(); + return True; + } + + if (t) + data->last_check_time = t; + + change_to_root_user(); + + return False; +} + +/**************************************************************************** + Remove a change notify data structure. +*****************************************************************************/ + +static void hash_remove_notify(void *datap) +{ + free(datap); +} + +/**************************************************************************** + Setup hash based change notify. +****************************************************************************/ + +struct cnotify_fns *hash_notify_init(void) +{ + static struct cnotify_fns cnotify; + + cnotify.register_notify = hash_register_notify; + cnotify.check_notify = hash_check_notify; + cnotify.remove_notify = hash_remove_notify; + cnotify.select_time = lp_change_notify_timeout(); + + return &cnotify; +} + +/* + change_notify_reply_packet(cnbp->request_buf,ERRSRV,ERRaccess); + change_notify_reply_packet(cnbp->request_buf,0,NT_STATUS_NOTIFY_ENUM_DIR); + + chain_size = 0; + file_chain_reset(); + + uint16 vuid = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID : + SVAL(cnbp->request_buf,smb_uid); +*/ diff --git a/source3/smbd/notify_kernel.c b/source3/smbd/notify_kernel.c new file mode 100644 index 0000000000..19ea41e195 --- /dev/null +++ b/source3/smbd/notify_kernel.c @@ -0,0 +1,206 @@ +/* + Unix SMB/CIFS implementation. + change notify handling - linux kernel based implementation + Copyright (C) Andrew Tridgell 2000 + + 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" + +#if HAVE_KERNEL_CHANGE_NOTIFY + +static VOLATILE sig_atomic_t fd_pending; +static VOLATILE sig_atomic_t signals_received; +static VOLATILE sig_atomic_t signals_processed; + +#ifndef DN_ACCESS +#define DN_ACCESS 0x00000001 /* File accessed in directory */ +#define DN_MODIFY 0x00000002 /* File modified in directory */ +#define DN_CREATE 0x00000004 /* File created in directory */ +#define DN_DELETE 0x00000008 /* File removed from directory */ +#define DN_RENAME 0x00000010 /* File renamed in directory */ +#define DN_ATTRIB 0x00000020 /* File changed attribute */ +#define DN_MULTISHOT 0x80000000 /* Don't remove notifier */ +#endif + + +#ifndef RT_SIGNAL_NOTIFY +#define RT_SIGNAL_NOTIFY 34 +#endif + +#ifndef F_SETSIG +#define F_SETSIG 10 +#endif + +#ifndef F_NOTIFY +#define F_NOTIFY 1026 +#endif + +/**************************************************************************** + This is the structure to keep the information needed to + determine if a directory has changed. +*****************************************************************************/ +struct change_data { + int directory_handle; +}; + +/**************************************************************************** +the signal handler for change notify +*****************************************************************************/ +static void signal_handler(int sig, siginfo_t *info, void *unused) +{ + BlockSignals(True, sig); + fd_pending = (sig_atomic_t)info->si_fd; + signals_received++; + sys_select_signal(); +} + + + +/**************************************************************************** + Check if a change notify should be issued. + time non-zero means timeout check (used for hash). Ignore this (async method + where time is zero will be used instead). +*****************************************************************************/ +static BOOL kernel_check_notify(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *datap, time_t t) +{ + struct change_data *data = (struct change_data *)datap; + + if (t) + return False; + + if (data->directory_handle != (int)fd_pending) return False; + + DEBUG(3,("kernel change notify on %s fd=%d\n", path, (int)fd_pending)); + + close((int)fd_pending); + fd_pending = (sig_atomic_t)-1; + data->directory_handle = -1; + signals_processed++; + BlockSignals(False, RT_SIGNAL_NOTIFY); + return True; +} + +/**************************************************************************** +remove a change notify data structure +*****************************************************************************/ +static void kernel_remove_notify(void *datap) +{ + struct change_data *data = (struct change_data *)datap; + int fd = data->directory_handle; + if (fd != -1) { + if (fd == (int)fd_pending) { + fd_pending = (sig_atomic_t)-1; + signals_processed++; + BlockSignals(False, RT_SIGNAL_NOTIFY); + } + close(fd); + } + SAFE_FREE(data); + DEBUG(3,("removed kernel change notify fd=%d\n", fd)); +} + + +/**************************************************************************** +register a change notify request +*****************************************************************************/ +static void *kernel_register_notify(connection_struct *conn, char *path, uint32 flags) +{ + struct change_data data; + int fd; + unsigned long kernel_flags; + + fd = conn->vfs_ops.open(conn, path, O_RDONLY, 0); + + if (fd == -1) { + DEBUG(3,("Failed to open directory %s for change notify\n", path)); + return NULL; + } + + if (fcntl(fd, F_SETSIG, RT_SIGNAL_NOTIFY) == -1) { + DEBUG(3,("Failed to set signal handler for change notify\n")); + return NULL; + } + + kernel_flags = DN_CREATE|DN_DELETE|DN_RENAME; /* creation/deletion changes everything! */ + if (flags & FILE_NOTIFY_CHANGE_FILE) kernel_flags |= DN_MODIFY; + if (flags & FILE_NOTIFY_CHANGE_DIR_NAME) kernel_flags |= DN_RENAME|DN_DELETE; + if (flags & FILE_NOTIFY_CHANGE_ATTRIBUTES) kernel_flags |= DN_ATTRIB; + if (flags & FILE_NOTIFY_CHANGE_SIZE) kernel_flags |= DN_MODIFY; + if (flags & FILE_NOTIFY_CHANGE_LAST_WRITE) kernel_flags |= DN_MODIFY; + if (flags & FILE_NOTIFY_CHANGE_LAST_ACCESS) kernel_flags |= DN_ACCESS; + if (flags & FILE_NOTIFY_CHANGE_CREATION) kernel_flags |= DN_CREATE; + if (flags & FILE_NOTIFY_CHANGE_SECURITY) kernel_flags |= DN_ATTRIB; + if (flags & FILE_NOTIFY_CHANGE_EA) kernel_flags |= DN_ATTRIB; + if (flags & FILE_NOTIFY_CHANGE_FILE_NAME) kernel_flags |= DN_RENAME|DN_DELETE; + + if (fcntl(fd, F_NOTIFY, kernel_flags) == -1) { + DEBUG(3,("Failed to set async flag for change notify\n")); + return NULL; + } + + data.directory_handle = fd; + + DEBUG(3,("kernel change notify on %s (ntflags=0x%x flags=0x%x) fd=%d\n", + path, (int)flags, (int)kernel_flags, fd)); + + return (void *)memdup(&data, sizeof(data)); +} + +/**************************************************************************** +see if the kernel supports change notify +****************************************************************************/ +static BOOL kernel_notify_available(void) +{ + int fd, ret; + fd = open("/tmp", O_RDONLY); + if (fd == -1) return False; /* uggh! */ + ret = fcntl(fd, F_NOTIFY, 0); + close(fd); + return ret == 0; +} + + +/**************************************************************************** +setup kernel based change notify +****************************************************************************/ +struct cnotify_fns *kernel_notify_init(void) +{ + static struct cnotify_fns cnotify; + struct sigaction act; + + act.sa_handler = NULL; + act.sa_sigaction = signal_handler; + act.sa_flags = SA_SIGINFO; + if (sigaction(RT_SIGNAL_NOTIFY, &act, NULL) != 0) { + DEBUG(0,("Failed to setup RT_SIGNAL_NOTIFY handler\n")); + return NULL; + } + + if (!kernel_notify_available()) return NULL; + + cnotify.register_notify = kernel_register_notify; + cnotify.check_notify = kernel_check_notify; + cnotify.remove_notify = kernel_remove_notify; + cnotify.select_time = -1; + + return &cnotify; +} + + +#else + void notify_kernel_dummy(void) {} +#endif /* HAVE_KERNEL_CHANGE_NOTIFY */ diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c new file mode 100644 index 0000000000..ed2979b3a4 --- /dev/null +++ b/source3/smbd/nttrans.c @@ -0,0 +1,1818 @@ +/* + Unix SMB/CIFS implementation. + SMB NT transaction handling + Copyright (C) Jeremy Allison 1994-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + 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" + +extern int Protocol; +extern int smb_read_error; +extern int global_oplock_break; +extern BOOL case_sensitive; +extern BOOL case_preserve; +extern BOOL short_case_preserve; + +static char *known_nt_pipes[] = { + "\\LANMAN", + "\\srvsvc", + "\\samr", + "\\wkssvc", + "\\NETLOGON", + "\\ntlsa", + "\\ntsvcs", + "\\lsass", + "\\lsarpc", + "\\winreg", + "\\spoolss", + "\\netdfs", + NULL +}; + +/* Map generic permissions to file object specific permissions */ + +struct generic_mapping file_generic_mapping = { + FILE_GENERIC_READ, + FILE_GENERIC_WRITE, + FILE_GENERIC_EXECUTE, + FILE_GENERIC_ALL +}; + +/**************************************************************************** + 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_nt_replies(char *inbuf, char *outbuf, int bufsize, NTSTATUS nt_error, char *params, + int paramsize, char *pdata, int datasize) +{ + extern int max_send; + 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 = 3; + int data_alignment_offset = 0; + + /* + * Initially set the wcnt area to be 18 - this is true for all + * transNT replies. + */ + + set_message(outbuf,18,0,True); + + if (NT_STATUS_V(nt_error)) { + ERROR_NT(nt_error); + } + + /* + * If there genuinely are no parameters or data to send just send + * the empty packet. + */ + + if(params_to_send == 0 && data_to_send == 0) { + if (!send_smb(smbd_server_fd(),outbuf)) + exit_server("send_nt_replies: send_smb failed."); + return 0; + } + + /* + * When sending params and data ensure that both are nicely aligned. + * Only do this alignment when there is also data to send - else + * can cause NT redirector problems. + */ + + if (((params_to_send % 4) != 0) && (data_to_send != 0)) + data_alignment_offset = 4 - (params_to_send % 4); + + /* + * Space is bufsize minus Netbios over TCP header minus SMB header. + * The alignment_offset is to align the param bytes on a four byte + * boundary (2 bytes for data len, one byte pad). + * NT needs this to work correctly. + */ + + useable_space = bufsize - ((smb_buf(outbuf)+ + alignment_offset+data_alignment_offset) - + outbuf); + + /* + * useable_space can never be more than max_send minus the + * alignment offset. + */ + + useable_space = MIN(useable_space, + max_send - (alignment_offset+data_alignment_offset)); + + + 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 + data_alignment_offset; + + /* + * We can never send more than useable_space. + */ + + total_sent_thistime = MIN(total_sent_thistime, useable_space); + + set_message(outbuf, 18, total_sent_thistime, True); + + /* + * Set total params and data to be sent. + */ + + SIVAL(outbuf,smb_ntr_TotalParameterCount,paramsize); + SIVAL(outbuf,smb_ntr_TotalDataCount,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); + + SIVAL(outbuf,smb_ntr_ParameterCount,params_sent_thistime); + + if(params_sent_thistime == 0) { + SIVAL(outbuf,smb_ntr_ParameterOffset,0); + SIVAL(outbuf,smb_ntr_ParameterDisplacement,0); + } else { + /* + * smb_ntr_ParameterOffset 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. + */ + + SIVAL(outbuf,smb_ntr_ParameterOffset, + ((smb_buf(outbuf)+alignment_offset) - smb_base(outbuf))); + /* + * Absolute displacement of param bytes sent in this packet. + */ + + SIVAL(outbuf,smb_ntr_ParameterDisplacement,pp - params); + } + + /* + * Deal with the data portion. + */ + + SIVAL(outbuf,smb_ntr_DataCount, data_sent_thistime); + + if(data_sent_thistime == 0) { + SIVAL(outbuf,smb_ntr_DataOffset,0); + SIVAL(outbuf,smb_ntr_DataDisplacement, 0); + } else { + /* + * The offset of the data bytes is the offset of the + * parameter bytes plus the number of parameters being sent this time. + */ + + SIVAL(outbuf,smb_ntr_DataOffset,((smb_buf(outbuf)+alignment_offset) - + smb_base(outbuf)) + params_sent_thistime + data_alignment_offset); + SIVAL(outbuf,smb_ntr_DataDisplacement, 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+ + data_alignment_offset,pd,data_sent_thistime); + + DEBUG(9,("nt_rep: params_sent_thistime = %d, data_sent_thistime = %d, useable_space = %d\n", + params_sent_thistime, data_sent_thistime, useable_space)); + DEBUG(9,("nt_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 */ + if (!send_smb(smbd_server_fd(),outbuf)) + exit_server("send_nt_replies: send_smb failed."); + + 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(0,("send_nt_replies failed sanity check pts = %d, dts = %d\n!!!", + params_to_send, data_to_send)); + return -1; + } + } + + return 0; +} + +/**************************************************************************** + Save case statics. +****************************************************************************/ + +static BOOL saved_case_sensitive; +static BOOL saved_case_preserve; +static BOOL saved_short_case_preserve; + +/**************************************************************************** + Save case semantics. +****************************************************************************/ + +static void set_posix_case_semantics(uint32 file_attributes) +{ + if(!(file_attributes & FILE_FLAG_POSIX_SEMANTICS)) + return; + + saved_case_sensitive = case_sensitive; + saved_case_preserve = case_preserve; + saved_short_case_preserve = short_case_preserve; + + /* Set to POSIX. */ + case_sensitive = True; + case_preserve = True; + short_case_preserve = True; +} + +/**************************************************************************** + Restore case semantics. +****************************************************************************/ + +static void restore_case_semantics(uint32 file_attributes) +{ + if(!(file_attributes & FILE_FLAG_POSIX_SEMANTICS)) + return; + + case_sensitive = saved_case_sensitive; + case_preserve = saved_case_preserve; + short_case_preserve = saved_short_case_preserve; +} + +/**************************************************************************** + Utility function to map create disposition. +****************************************************************************/ + +static int map_create_disposition( uint32 create_disposition) +{ + int ret; + + switch( create_disposition ) { + case FILE_CREATE: + /* create if not exist, fail if exist */ + ret = (FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_FAIL); + break; + case FILE_SUPERSEDE: + case FILE_OVERWRITE_IF: + /* create if not exist, trunc if exist */ + ret = (FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_TRUNCATE); + break; + case FILE_OPEN: + /* fail if not exist, open if exists */ + ret = (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN); + break; + case FILE_OPEN_IF: + /* create if not exist, open if exists */ + ret = (FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_OPEN); + break; + case FILE_OVERWRITE: + /* fail if not exist, truncate if exists */ + ret = (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_TRUNCATE); + break; + default: + DEBUG(0,("map_create_disposition: Incorrect value for create_disposition = %d\n", + create_disposition )); + return -1; + } + + DEBUG(10,("map_create_disposition: Mapped create_disposition 0x%lx to 0x%x\n", + (unsigned long)create_disposition, ret )); + + return ret; +} + +/**************************************************************************** + Utility function to map share modes. +****************************************************************************/ + +static int map_share_mode( char *fname, uint32 create_options, + uint32 *desired_access, uint32 share_access, uint32 file_attributes) +{ + int smb_open_mode = -1; + + /* + * Convert GENERIC bits to specific bits. + */ + + se_map_generic(desired_access, &file_generic_mapping); + + switch( *desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA) ) { + case FILE_READ_DATA: + smb_open_mode = DOS_OPEN_RDONLY; + break; + case FILE_WRITE_DATA: + case FILE_APPEND_DATA: + case FILE_WRITE_DATA|FILE_APPEND_DATA: + smb_open_mode = DOS_OPEN_WRONLY; + break; + case FILE_READ_DATA|FILE_WRITE_DATA: + case FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA: + case FILE_READ_DATA|FILE_APPEND_DATA: + smb_open_mode = DOS_OPEN_RDWR; + break; + } + + /* + * NB. For DELETE_ACCESS we should really check the + * directory permissions, as that is what controls + * delete, and for WRITE_DAC_ACCESS we should really + * check the ownership, as that is what controls the + * chmod. Note that this is *NOT* a security hole (this + * note is for you, Andrew) as we are not *allowing* + * the access at this point, the actual unlink or + * chown or chmod call would do this. We are just helping + * clients out by telling them if they have a hope + * of any of this succeeding. POSIX acls may still + * deny the real call. JRA. + */ + + if (smb_open_mode == -1) { + + if(*desired_access & (DELETE_ACCESS|WRITE_DAC_ACCESS|WRITE_OWNER_ACCESS| + FILE_EXECUTE|FILE_READ_ATTRIBUTES| + FILE_READ_EA|FILE_WRITE_EA|SYSTEM_SECURITY_ACCESS| + FILE_WRITE_ATTRIBUTES|READ_CONTROL_ACCESS)) { + smb_open_mode = DOS_OPEN_RDONLY; + } else if(*desired_access == 0) { + + /* + * JRA - NT seems to sometimes send desired_access as zero. play it safe + * and map to a stat open. + */ + + smb_open_mode = DOS_OPEN_RDONLY; + + } else { + DEBUG(0,("map_share_mode: Incorrect value 0x%lx for desired_access to file %s\n", + (unsigned long)*desired_access, fname)); + return -1; + } + } + + /* + * Set the special bit that means allow share delete. + * This is held outside the normal share mode bits at 1<<15. + * JRA. + */ + + if(share_access & FILE_SHARE_DELETE) { + smb_open_mode |= ALLOW_SHARE_DELETE; + DEBUG(10,("map_share_mode: FILE_SHARE_DELETE requested. open_mode = 0x%x\n", smb_open_mode)); + } + + /* + * We need to store the intent to open for Delete. This + * is what determines if a delete on close flag can be set. + * This is the wrong way (and place) to store this, but for 2.2 this + * is the only practical way. JRA. + */ + + if(*desired_access & DELETE_ACCESS) { + DEBUG(10,("map_share_mode: DELETE_ACCESS requested. open_mode = 0x%x\n", smb_open_mode)); + } + + if (create_options & FILE_DELETE_ON_CLOSE) { + /* Implicit delete access is *NOT* requested... */ + smb_open_mode |= DELETE_ON_CLOSE_FLAG; + DEBUG(10,("map_share_mode: FILE_DELETE_ON_CLOSE requested. open_mode = 0x%x\n", smb_open_mode)); + } + + /* Add in the requested share mode. */ + switch( share_access & (FILE_SHARE_READ|FILE_SHARE_WRITE)) { + case FILE_SHARE_READ: + smb_open_mode |= SET_DENY_MODE(DENY_WRITE); + break; + case FILE_SHARE_WRITE: + smb_open_mode |= SET_DENY_MODE(DENY_READ); + break; + case (FILE_SHARE_READ|FILE_SHARE_WRITE): + smb_open_mode |= SET_DENY_MODE(DENY_NONE); + break; + case FILE_SHARE_NONE: + smb_open_mode |= SET_DENY_MODE(DENY_ALL); + break; + } + + /* + * Handle an O_SYNC request. + */ + + if(file_attributes & FILE_FLAG_WRITE_THROUGH) + smb_open_mode |= FILE_SYNC_OPENMODE; + + DEBUG(10,("map_share_mode: Mapped desired access 0x%lx, share access 0x%lx, file attributes 0x%lx \ +to open_mode 0x%x\n", (unsigned long)*desired_access, (unsigned long)share_access, + (unsigned long)file_attributes, smb_open_mode )); + + return smb_open_mode; +} + +/**************************************************************************** + Reply to an NT create and X call on a pipe. +****************************************************************************/ +static int nt_open_pipe(char *fname, connection_struct *conn, + char *inbuf, char *outbuf, int *ppnum) +{ + smb_np_struct *p = NULL; + + uint16 vuid = SVAL(inbuf, smb_uid); + int i; + + DEBUG(4,("nt_open_pipe: Opening pipe %s.\n", fname)); + + /* See if it is one we want to handle. */ + + if (lp_disable_spoolss() && strequal(fname, "\\spoolss")) + return(ERROR_DOS(ERRSRV,ERRaccess)); + + for( i = 0; known_nt_pipes[i]; i++ ) + if( strequal(fname,known_nt_pipes[i])) + break; + + if ( known_nt_pipes[i] == NULL ) + return(ERROR_BOTH(NT_STATUS_OBJECT_NAME_NOT_FOUND,ERRDOS,ERRbadpipe)); + + /* Strip \\ off the name. */ + fname++; + + DEBUG(3,("nt_open_pipe: Known pipe %s opening.\n", fname)); + + p = open_rpc_pipe_p(fname, conn, vuid); + if (!p) + return(ERROR_DOS(ERRSRV,ERRnofids)); + + *ppnum = p->pnum; + + return 0; +} + +/**************************************************************************** + Reply to an NT create and X call for pipes. +****************************************************************************/ + +static int do_ntcreate_pipe_open(connection_struct *conn, + char *inbuf,char *outbuf,int length,int bufsize) +{ + pstring fname; + int ret; + int pnum = -1; + char *p = NULL; + + srvstr_pull(inbuf, fname, smb_buf(inbuf), sizeof(fname), -1, STR_TERMINATE); + + if ((ret = nt_open_pipe(fname, conn, inbuf, outbuf, &pnum)) != 0) + return ret; + + /* + * Deal with pipe return. + */ + + set_message(outbuf,34,0,True); + + p = outbuf + smb_vwv2; + p++; + SSVAL(p,0,pnum); + p += 2; + SIVAL(p,0,FILE_WAS_OPENED); + p += 4; + p += 32; + SIVAL(p,0,FILE_ATTRIBUTE_NORMAL); /* File Attributes. */ + p += 20; + /* File type. */ + SSVAL(p,0,FILE_TYPE_MESSAGE_MODE_PIPE); + /* Device state. */ + SSVAL(p,2, 0x5FF); /* ? */ + + DEBUG(5,("do_ntcreate_pipe_open: open pipe = %s\n", fname)); + + return chain_reply(inbuf,outbuf,length,bufsize); +} + +/**************************************************************************** + Reply to an NT create and X call. +****************************************************************************/ + +int reply_ntcreate_and_X(connection_struct *conn, + char *inbuf,char *outbuf,int length,int bufsize) +{ + int result; + pstring fname; + uint32 flags = IVAL(inbuf,smb_ntcreate_Flags); + uint32 desired_access = IVAL(inbuf,smb_ntcreate_DesiredAccess); + uint32 file_attributes = IVAL(inbuf,smb_ntcreate_FileAttributes); + uint32 share_access = IVAL(inbuf,smb_ntcreate_ShareAccess); + uint32 create_disposition = IVAL(inbuf,smb_ntcreate_CreateDisposition); + uint32 create_options = IVAL(inbuf,smb_ntcreate_CreateOptions); + uint16 root_dir_fid = (uint16)IVAL(inbuf,smb_ntcreate_RootDirectoryFid); + int smb_ofun; + int smb_open_mode; + int smb_attr = (file_attributes & SAMBA_ATTRIBUTES_MASK); + /* Breakout the oplock request bits so we can set the + reply bits separately. */ + int oplock_request = 0; + mode_t unixmode; + int fmode=0,rmode=0; + SMB_OFF_T file_len = 0; + SMB_STRUCT_STAT sbuf; + int smb_action = 0; + BOOL bad_path = False; + files_struct *fsp=NULL; + char *p = NULL; + time_t c_time; + START_PROFILE(SMBntcreateX); + + /* If it's an IPC, use the pipe handler. */ + + if (IS_IPC(conn)) { + if (lp_nt_pipe_support()) { + END_PROFILE(SMBntcreateX); + return do_ntcreate_pipe_open(conn,inbuf,outbuf,length,bufsize); + } else { + END_PROFILE(SMBntcreateX); + return(ERROR_DOS(ERRDOS,ERRbadaccess)); + } + } + + + /* + * We need to construct the open_and_X ofun value from the + * NT values, as that's what our code is structured to accept. + */ + + if((smb_ofun = map_create_disposition( create_disposition )) == -1) { + END_PROFILE(SMBntcreateX); + return(ERROR_DOS(ERRDOS,ERRbadaccess)); + } + + /* + * Get the file name. + */ + + if(root_dir_fid != 0) { + /* + * This filename is relative to a directory fid. + */ + files_struct *dir_fsp = file_fsp(inbuf,smb_ntcreate_RootDirectoryFid); + size_t dir_name_len; + + if(!dir_fsp) { + END_PROFILE(SMBntcreateX); + return(ERROR_DOS(ERRDOS,ERRbadfid)); + } + + if(!dir_fsp->is_directory) { + /* + * Check to see if this is a mac fork of some kind. + */ + + srvstr_pull(inbuf, fname, smb_buf(inbuf), sizeof(fname), -1, STR_TERMINATE); + + if( strchr_m(fname, ':')) { + END_PROFILE(SMBntcreateX); + return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND); + } + END_PROFILE(SMBntcreateX); + return(ERROR_DOS(ERRDOS,ERRbadfid)); + } + + /* + * Copy in the base directory name. + */ + + pstrcpy( fname, dir_fsp->fsp_name ); + dir_name_len = strlen(fname); + + /* + * Ensure it ends in a '\'. + */ + + if(fname[dir_name_len-1] != '\\' && fname[dir_name_len-1] != '/') { + pstrcat(fname, "\\"); + dir_name_len++; + } + + srvstr_pull(inbuf, &fname[dir_name_len], smb_buf(inbuf), sizeof(fname)-dir_name_len, + -1, STR_TERMINATE); + } else { + srvstr_pull(inbuf, fname, smb_buf(inbuf), sizeof(fname), -1, STR_TERMINATE); + } + + /* + * Now contruct the smb_open_mode value from the filename, + * desired access and the share access. + */ + RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); + + if((smb_open_mode = map_share_mode(fname, create_options, &desired_access, + share_access, + file_attributes)) == -1) { + END_PROFILE(SMBntcreateX); + return ERROR_DOS(ERRDOS,ERRbadaccess); + } + + oplock_request = (flags & REQUEST_OPLOCK) ? EXCLUSIVE_OPLOCK : 0; + if (oplock_request) { + oplock_request |= (flags & REQUEST_BATCH_OPLOCK) ? BATCH_OPLOCK : 0; + } + + /* + * Ordinary file or directory. + */ + + /* + * Check if POSIX semantics are wanted. + */ + + set_posix_case_semantics(file_attributes); + + unix_convert(fname,conn,0,&bad_path,&sbuf); + + unixmode = unix_mode(conn,smb_attr | aARCH, fname); + + /* + * If it's a request for a directory open, deal with it separately. + */ + + if(create_options & FILE_DIRECTORY_FILE) { + oplock_request = 0; + + fsp = open_directory(conn, fname, &sbuf, desired_access, smb_open_mode, smb_ofun, unixmode, &smb_action); + + restore_case_semantics(file_attributes); + + if(!fsp) { + set_bad_path_error(errno, bad_path); + END_PROFILE(SMBntcreateX); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + } else { + /* + * Ordinary file case. + */ + + /* NB. We have a potential bug here. If we + * cause an oplock break to ourselves, then we + * could end up processing filename related + * SMB requests whilst we await the oplock + * break response. As we may have changed the + * filename case semantics to be POSIX-like, + * this could mean a filename request could + * fail when it should succeed. This is a rare + * condition, but eventually we must arrange + * to restore the correct case semantics + * before issuing an oplock break request to + * our client. JRA. */ + + fsp = open_file_shared1(conn,fname,&sbuf, + desired_access, + smb_open_mode, + smb_ofun,unixmode, oplock_request, + &rmode,&smb_action); + + if (!fsp) { + /* We cheat here. There are two cases we + * care about. One is a directory rename, + * where the NT client will attempt to + * open the source directory for + * DELETE access. Note that when the + * NT client does this it does *not* + * set the directory bit in the + * request packet. This is translated + * into a read/write open + * request. POSIX states that any open + * for write request on a directory + * will generate an EISDIR error, so + * we can catch this here and open a + * pseudo handle that is flagged as a + * directory. The second is an open + * for a permissions read only, which + * we handle in the open_file_stat case. JRA. + */ + + if(errno == EISDIR) { + + /* + * Fail the open if it was explicitly a non-directory file. + */ + + if (create_options & FILE_NON_DIRECTORY_FILE) { + restore_case_semantics(file_attributes); + SSVAL(outbuf, smb_flg2, + SVAL(outbuf,smb_flg2) | FLAGS2_32_BIT_ERROR_CODES); + END_PROFILE(SMBntcreateX); + return ERROR_NT(NT_STATUS_FILE_IS_A_DIRECTORY); + } + + oplock_request = 0; + fsp = open_directory(conn, fname, &sbuf, desired_access, smb_open_mode, smb_ofun, unixmode, &smb_action); + + if(!fsp) { + restore_case_semantics(file_attributes); + set_bad_path_error(errno, bad_path); + END_PROFILE(SMBntcreateX); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + } else { + + restore_case_semantics(file_attributes); + set_bad_path_error(errno, bad_path); + END_PROFILE(SMBntcreateX); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + } + } + + restore_case_semantics(file_attributes); + + file_len = sbuf.st_size; + fmode = dos_mode(conn,fname,&sbuf); + if(fmode == 0) + fmode = FILE_ATTRIBUTE_NORMAL; + if (!fsp->is_directory && (fmode & aDIR)) { + close_file(fsp,False); + END_PROFILE(SMBntcreateX); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } + + /* + * If the caller set the extended oplock request bit + * and we granted one (by whatever means) - set the + * correct bit for extended oplock reply. + */ + + if (oplock_request && lp_fake_oplocks(SNUM(conn))) + smb_action |= EXTENDED_OPLOCK_GRANTED; + + if(oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) + smb_action |= EXTENDED_OPLOCK_GRANTED; + +#if 0 + /* W2K sends back 42 words here ! If we do the same it breaks offline sync. Go figure... ? JRA. */ + set_message(outbuf,42,0,True); +#else + set_message(outbuf,34,0,True); +#endif + + p = outbuf + smb_vwv2; + + /* + * Currently as we don't support level II oplocks we just report + * exclusive & batch here. + */ + + if (smb_action & EXTENDED_OPLOCK_GRANTED) { + if (flags & REQUEST_BATCH_OPLOCK) { + SCVAL(p,0, BATCH_OPLOCK_RETURN); + } else { + SCVAL(p,0, EXCLUSIVE_OPLOCK_RETURN); + } + } else if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type)) { + SCVAL(p,0, LEVEL_II_OPLOCK_RETURN); + } else { + SCVAL(p,0,NO_OPLOCK_RETURN); + } + + p++; + SSVAL(p,0,fsp->fnum); + p += 2; + SIVAL(p,0,smb_action); + p += 4; + + /* Create time. */ + c_time = get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn))); + + if (lp_dos_filetime_resolution(SNUM(conn))) { + c_time &= ~1; + sbuf.st_atime &= ~1; + sbuf.st_mtime &= ~1; + sbuf.st_mtime &= ~1; + } + + put_long_date(p,c_time); + p += 8; + put_long_date(p,sbuf.st_atime); /* access time */ + p += 8; + put_long_date(p,sbuf.st_mtime); /* write time */ + p += 8; + put_long_date(p,sbuf.st_mtime); /* change time */ + p += 8; + SIVAL(p,0,fmode); /* File Attributes. */ + p += 4; + SOFF_T(p, 0, SMB_ROUNDUP_ALLOCATION(file_len)); + p += 8; + SOFF_T(p,0,file_len); + p += 12; + SCVAL(p,0,fsp->is_directory ? 1 : 0); + + DEBUG(5,("reply_ntcreate_and_X: fnum = %d, open name = %s\n", fsp->fnum, fsp->fsp_name)); + + result = chain_reply(inbuf,outbuf,length,bufsize); + END_PROFILE(SMBntcreateX); + return result; +} + +/**************************************************************************** + Reply to a NT_TRANSACT_CREATE call to open a pipe. +****************************************************************************/ + +static int do_nt_transact_create_pipe( connection_struct *conn, + char *inbuf, char *outbuf, int length, + int bufsize, char **ppsetup, char **ppparams, + char **ppdata) +{ + pstring fname; + int total_parameter_count = (int)IVAL(inbuf, smb_nt_TotalParameterCount); + char *params = *ppparams; + int ret; + int pnum = -1; + char *p = NULL; + + /* + * Ensure minimum number of parameters sent. + */ + + if(total_parameter_count < 54) { + DEBUG(0,("do_nt_transact_create_pipe - insufficient parameters (%u)\n", (unsigned int)total_parameter_count)); + return ERROR_DOS(ERRDOS,ERRbadaccess); + } + + srvstr_pull(inbuf, fname, params+53, sizeof(fname), -1, STR_TERMINATE); + + if ((ret = nt_open_pipe(fname, conn, inbuf, outbuf, &pnum)) != 0) + return ret; + + /* Realloc the size of parameters and data we will return */ + params = Realloc(*ppparams, 69); + if(params == NULL) + return ERROR_DOS(ERRDOS,ERRnomem); + + *ppparams = params; + + memset((char *)params,'\0',69); + + p = params; + SCVAL(p,0,NO_OPLOCK_RETURN); + + p += 2; + SSVAL(p,0,pnum); + p += 2; + SIVAL(p,0,FILE_WAS_OPENED); + p += 8; + + p += 32; + SIVAL(p,0,FILE_ATTRIBUTE_NORMAL); /* File Attributes. */ + p += 20; + /* File type. */ + SSVAL(p,0,FILE_TYPE_MESSAGE_MODE_PIPE); + /* Device state. */ + SSVAL(p,2, 0x5FF); /* ? */ + + DEBUG(5,("do_nt_transact_create_pipe: open name = %s\n", fname)); + + /* Send the required number of replies */ + send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, params, 69, *ppdata, 0); + + return -1; +} + +/**************************************************************************** + Internal fn to set security descriptors. +****************************************************************************/ + +static BOOL set_sd(files_struct *fsp, char *data, uint32 sd_len, uint32 security_info_sent, int *pdef_class,uint32 *pdef_code) +{ + prs_struct pd; + SEC_DESC *psd = NULL; + TALLOC_CTX *mem_ctx; + BOOL ret; + + if (sd_len == 0) { + *pdef_class = ERRDOS; + *pdef_code = ERRbadaccess; + return False; + } + + /* + * Init the parse struct we will unmarshall from. + */ + + if ((mem_ctx = talloc_init()) == NULL) { + DEBUG(0,("set_sd: talloc_init failed.\n")); + *pdef_class = ERRDOS; + *pdef_code = ERRnomem; + return False; + } + + prs_init(&pd, 0, mem_ctx, UNMARSHALL); + + /* + * Setup the prs_struct to point at the memory we just + * allocated. + */ + + prs_give_memory( &pd, data, sd_len, False); + + /* + * Finally, unmarshall from the data buffer. + */ + + if(!sec_io_desc( "sd data", &psd, &pd, 1)) { + DEBUG(0,("set_sd: Error in unmarshalling security descriptor.\n")); + /* + * Return access denied for want of a better error message.. + */ + talloc_destroy(mem_ctx); + *pdef_class = ERRDOS; + *pdef_code = ERRnomem; + return False; + } + + if (psd->off_owner_sid==0) + security_info_sent &= ~OWNER_SECURITY_INFORMATION; + if (psd->off_grp_sid==0) + security_info_sent &= ~GROUP_SECURITY_INFORMATION; + if (psd->off_sacl==0) + security_info_sent &= ~SACL_SECURITY_INFORMATION; + if (psd->off_dacl==0) + security_info_sent &= ~DACL_SECURITY_INFORMATION; + + ret = fsp->conn->vfs_ops.fset_nt_acl( fsp, fsp->fd, security_info_sent, psd); + + if (!ret) { + talloc_destroy(mem_ctx); + *pdef_class = ERRDOS; + *pdef_code = ERRnoaccess; + return False; + } + + talloc_destroy(mem_ctx); + + *pdef_class = 0; + *pdef_code = 0; + return True; +} + +/**************************************************************************** + Reply to a NT_TRANSACT_CREATE call (needs to process SD's). +****************************************************************************/ + +static int call_nt_transact_create(connection_struct *conn, + char *inbuf, char *outbuf, int length, + int bufsize, char **ppsetup, char **ppparams, + char **ppdata) +{ + pstring fname; + char *params = *ppparams; + char *data = *ppdata; + int total_parameter_count = (int)IVAL(inbuf, smb_nt_TotalParameterCount); + /* Breakout the oplock request bits so we can set the + reply bits separately. */ + int oplock_request = 0; + mode_t unixmode; + int fmode=0,rmode=0; + SMB_OFF_T file_len = 0; + SMB_STRUCT_STAT sbuf; + int smb_action = 0; + BOOL bad_path = False; + files_struct *fsp = NULL; + char *p = NULL; + uint32 flags; + uint32 desired_access; + uint32 file_attributes; + uint32 share_access; + uint32 create_disposition; + uint32 create_options; + uint32 sd_len; + uint16 root_dir_fid; + int smb_ofun; + int smb_open_mode; + int smb_attr; + int error_class; + uint32 error_code; + time_t c_time; + + DEBUG(5,("call_nt_transact_create\n")); + + /* + * If it's an IPC, use the pipe handler. + */ + + if (IS_IPC(conn)) { + if (lp_nt_pipe_support()) + return do_nt_transact_create_pipe(conn, inbuf, outbuf, length, + bufsize, ppsetup, ppparams, ppdata); + else + return ERROR_DOS(ERRDOS,ERRbadaccess); + } + + /* + * Ensure minimum number of parameters sent. + */ + + if(total_parameter_count < 54) { + DEBUG(0,("call_nt_transact_create - insufficient parameters (%u)\n", (unsigned int)total_parameter_count)); + return ERROR_DOS(ERRDOS,ERRbadaccess); + } + + flags = IVAL(params,0); + desired_access = IVAL(params,8); + file_attributes = IVAL(params,20); + share_access = IVAL(params,24); + create_disposition = IVAL(params,28); + create_options = IVAL(params,32); + sd_len = IVAL(params,36); + root_dir_fid = (uint16)IVAL(params,4); + smb_attr = (file_attributes & SAMBA_ATTRIBUTES_MASK); + + /* + * We need to construct the open_and_X ofun value from the + * NT values, as that's what our code is structured to accept. + */ + + if((smb_ofun = map_create_disposition( create_disposition )) == -1) + return ERROR_DOS(ERRDOS,ERRbadmem); + + /* + * Get the file name. + */ + + if(root_dir_fid != 0) { + /* + * This filename is relative to a directory fid. + */ + + files_struct *dir_fsp = file_fsp(params,4); + size_t dir_name_len; + + if(!dir_fsp) + return ERROR_DOS(ERRDOS,ERRbadfid); + + if(!dir_fsp->is_directory) { + /* + * Check to see if this is a mac fork of some kind. + */ + + srvstr_pull(inbuf, fname, params+53, sizeof(fname), -1, STR_TERMINATE); + + if( strchr_m(fname, ':')) { + return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND); + } + + return ERROR_DOS(ERRDOS,ERRbadfid); + } + + /* + * Copy in the base directory name. + */ + + pstrcpy( fname, dir_fsp->fsp_name ); + dir_name_len = strlen(fname); + + /* + * Ensure it ends in a '\'. + */ + + if((fname[dir_name_len-1] != '\\') && (fname[dir_name_len-1] != '/')) { + pstrcat(fname, "\\"); + dir_name_len++; + } + + srvstr_pull(inbuf, &fname[dir_name_len], params+53, sizeof(fname)-dir_name_len, + -1, STR_TERMINATE); + } else { + srvstr_pull(inbuf, fname, params+53, sizeof(fname), -1, STR_TERMINATE); + } + + /* + * Now contruct the smb_open_mode value from the desired access + * and the share access. + */ + + if((smb_open_mode = map_share_mode( fname, create_options, &desired_access, + share_access, file_attributes)) == -1) + return ERROR_DOS(ERRDOS,ERRbadaccess); + + oplock_request = (flags & REQUEST_OPLOCK) ? EXCLUSIVE_OPLOCK : 0; + oplock_request |= (flags & REQUEST_BATCH_OPLOCK) ? BATCH_OPLOCK : 0; + + /* + * Check if POSIX semantics are wanted. + */ + + set_posix_case_semantics(file_attributes); + + RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); + + unix_convert(fname,conn,0,&bad_path,&sbuf); + + unixmode = unix_mode(conn,smb_attr | aARCH, fname); + + /* + * If it's a request for a directory open, deal with it separately. + */ + + if(create_options & FILE_DIRECTORY_FILE) { + + oplock_request = 0; + + /* + * We will get a create directory here if the Win32 + * app specified a security descriptor in the + * CreateDirectory() call. + */ + + fsp = open_directory(conn, fname, &sbuf, desired_access, smb_open_mode, smb_ofun, unixmode, &smb_action); + + if(!fsp) { + restore_case_semantics(file_attributes); + set_bad_path_error(errno, bad_path); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + + } else { + + /* + * Ordinary file case. + */ + + fsp = open_file_shared1(conn,fname,&sbuf,desired_access, + smb_open_mode,smb_ofun,unixmode, + oplock_request,&rmode,&smb_action); + + if (!fsp) { + + if(errno == EISDIR) { + + /* + * Fail the open if it was explicitly a non-directory file. + */ + + if (create_options & FILE_NON_DIRECTORY_FILE) { + restore_case_semantics(file_attributes); + SSVAL(outbuf, smb_flg2, + SVAL(outbuf,smb_flg2) | FLAGS2_32_BIT_ERROR_CODES); + return ERROR_NT(NT_STATUS_FILE_IS_A_DIRECTORY); + } + + oplock_request = 0; + fsp = open_directory(conn, fname, &sbuf, desired_access, smb_open_mode, smb_ofun, unixmode, &smb_action); + + if(!fsp) { + restore_case_semantics(file_attributes); + set_bad_path_error(errno, bad_path); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + } else { + + restore_case_semantics(file_attributes); + set_bad_path_error(errno, bad_path); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + } + + file_len = sbuf.st_size; + fmode = dos_mode(conn,fname,&sbuf); + if(fmode == 0) + fmode = FILE_ATTRIBUTE_NORMAL; + + if (fmode & aDIR) { + close_file(fsp,False); + restore_case_semantics(file_attributes); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } + + /* + * If the caller set the extended oplock request bit + * and we granted one (by whatever means) - set the + * correct bit for extended oplock reply. + */ + + if (oplock_request && lp_fake_oplocks(SNUM(conn))) + smb_action |= EXTENDED_OPLOCK_GRANTED; + + if(oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) + smb_action |= EXTENDED_OPLOCK_GRANTED; + } + + /* + * Now try and apply the desired SD. + */ + + if (!set_sd( fsp, data, sd_len, ALL_SECURITY_INFORMATION, &error_class, &error_code)) { + close_file(fsp,False); + restore_case_semantics(file_attributes); + return ERROR_DOS(error_class, error_code); + } + + restore_case_semantics(file_attributes); + + /* Realloc the size of parameters and data we will return */ + params = Realloc(*ppparams, 69); + if(params == NULL) + return ERROR_DOS(ERRDOS,ERRnomem); + + *ppparams = params; + + memset((char *)params,'\0',69); + + p = params; + if (smb_action & EXTENDED_OPLOCK_GRANTED) + SCVAL(p,0, BATCH_OPLOCK_RETURN); + else if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type)) + SCVAL(p,0, LEVEL_II_OPLOCK_RETURN); + else + SCVAL(p,0,NO_OPLOCK_RETURN); + + p += 2; + SSVAL(p,0,fsp->fnum); + p += 2; + SIVAL(p,0,smb_action); + p += 8; + + /* Create time. */ + c_time = get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn))); + + if (lp_dos_filetime_resolution(SNUM(conn))) { + c_time &= ~1; + sbuf.st_atime &= ~1; + sbuf.st_mtime &= ~1; + sbuf.st_mtime &= ~1; + } + + put_long_date(p,c_time); + p += 8; + put_long_date(p,sbuf.st_atime); /* access time */ + p += 8; + put_long_date(p,sbuf.st_mtime); /* write time */ + p += 8; + put_long_date(p,sbuf.st_mtime); /* change time */ + p += 8; + SIVAL(p,0,fmode); /* File Attributes. */ + p += 4; + SOFF_T(p, 0, SMB_ROUNDUP_ALLOCATION(file_len)); + p += 8; + SOFF_T(p,0,file_len); + + DEBUG(5,("call_nt_transact_create: open name = %s\n", fname)); + + /* Send the required number of replies */ + send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, params, 69, *ppdata, 0); + + return -1; +} + +/**************************************************************************** + Reply to a NT CANCEL request. +****************************************************************************/ +int reply_ntcancel(connection_struct *conn, + char *inbuf,char *outbuf,int length,int bufsize) +{ + /* + * Go through and cancel any pending change notifies. + */ + + int mid = SVAL(inbuf,smb_mid); + START_PROFILE(SMBntcancel); + remove_pending_change_notify_requests_by_mid(mid); + remove_pending_lock_requests_by_mid(mid); + + DEBUG(3,("reply_ntcancel: cancel called on mid = %d.\n", mid)); + + END_PROFILE(SMBntcancel); + return(-1); +} + +/**************************************************************************** + Reply to an unsolicited SMBNTtranss - just ignore it! +****************************************************************************/ +int reply_nttranss(connection_struct *conn, + char *inbuf,char *outbuf,int length,int bufsize) +{ + START_PROFILE(SMBnttranss); + DEBUG(4,("Ignoring nttranss of length %d\n",length)); + END_PROFILE(SMBnttranss); + return(-1); +} + +/**************************************************************************** + Reply to a notify change - queue the request and + don't allow a directory to be opened. +****************************************************************************/ +static int call_nt_transact_notify_change(connection_struct *conn, + char *inbuf, char *outbuf, int length, + int bufsize, + char **ppsetup, + char **ppparams, char **ppdata) +{ + char *setup = *ppsetup; + files_struct *fsp; + uint32 flags; + + fsp = file_fsp(setup,4); + flags = IVAL(setup, 0); + + DEBUG(3,("call_nt_transact_notify_change\n")); + + if(!fsp) + return ERROR_DOS(ERRDOS,ERRbadfid); + + if((!fsp->is_directory) || (conn != fsp->conn)) + return ERROR_DOS(ERRDOS,ERRbadfid); + + if (!change_notify_set(inbuf, fsp, conn, flags)) + return(UNIXERROR(ERRDOS,ERRbadfid)); + + DEBUG(3,("call_nt_transact_notify_change: notify change called on directory \ +name = %s\n", fsp->fsp_name )); + + return -1; +} + +/**************************************************************************** + Reply to an NT transact rename command. +****************************************************************************/ + +static int call_nt_transact_rename(connection_struct *conn, + char *inbuf, char *outbuf, int length, + int bufsize, + char **ppsetup, char **ppparams, char **ppdata) +{ + char *params = *ppparams; + pstring new_name; + files_struct *fsp = file_fsp(params, 0); + BOOL replace_if_exists = (SVAL(params,2) & RENAME_REPLACE_IF_EXISTS) ? True : False; + NTSTATUS status; + + CHECK_FSP(fsp, conn); + srvstr_pull(inbuf, new_name, params+4, sizeof(new_name), -1, STR_TERMINATE); + + status = rename_internals(conn, fsp->fsp_name, + new_name, replace_if_exists); + if (!NT_STATUS_IS_OK(status)) + return ERROR_NT(status); + + /* + * Rename was successful. + */ + send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, NULL, 0, NULL, 0); + + DEBUG(3,("nt transact rename from = %s, to = %s succeeded.\n", + fsp->fsp_name, new_name)); + + /* + * Win2k needs a changenotify request response before it will + * update after a rename.. + */ + + process_pending_change_notify_queue((time_t)0); + + return -1; +} + +/****************************************************************************** + Fake up a completely empty SD. +*******************************************************************************/ + +static size_t get_null_nt_acl(TALLOC_CTX *mem_ctx, SEC_DESC **ppsd) +{ + extern DOM_SID global_sid_World; + size_t sd_size; + + *ppsd = make_standard_sec_desc( mem_ctx, &global_sid_World, &global_sid_World, NULL, &sd_size); + if(!*ppsd) { + DEBUG(0,("get_null_nt_acl: Unable to malloc space for security descriptor.\n")); + sd_size = 0; + } + + return sd_size; +} + +/**************************************************************************** + Reply to query a security descriptor - currently this is not implemented (it + is planned to be though). Right now it just returns the same thing NT would + when queried on a FAT filesystem. JRA. +****************************************************************************/ + +static int call_nt_transact_query_security_desc(connection_struct *conn, + char *inbuf, char *outbuf, + int length, int bufsize, + char **ppsetup, char **ppparams, char **ppdata) +{ + uint32 max_data_count = IVAL(inbuf,smb_nt_MaxDataCount); + char *params = *ppparams; + char *data = *ppdata; + prs_struct pd; + SEC_DESC *psd = NULL; + size_t sd_size; + TALLOC_CTX *mem_ctx; + + files_struct *fsp = file_fsp(params,0); + + if(!fsp) + return ERROR_DOS(ERRDOS,ERRbadfid); + + DEBUG(3,("call_nt_transact_query_security_desc: file = %s\n", fsp->fsp_name )); + + params = Realloc(*ppparams, 4); + if(params == NULL) + return ERROR_DOS(ERRDOS,ERRnomem); + + *ppparams = params; + + if ((mem_ctx = talloc_init()) == NULL) { + DEBUG(0,("call_nt_transact_query_security_desc: talloc_init failed.\n")); + return ERROR_DOS(ERRDOS,ERRnomem); + } + + /* + * Get the permissions to return. + */ + + if (!lp_nt_acl_support(SNUM(conn))) + sd_size = get_null_nt_acl(mem_ctx, &psd); + else + sd_size = conn->vfs_ops.fget_nt_acl(fsp, fsp->fd, &psd); + + if (sd_size == 0) { + talloc_destroy(mem_ctx); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + + DEBUG(3,("call_nt_transact_query_security_desc: sd_size = %d.\n",(int)sd_size)); + + SIVAL(params,0,(uint32)sd_size); + + if(max_data_count < sd_size) { + + send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_BUFFER_TOO_SMALL, + params, 4, *ppdata, 0); + talloc_destroy(mem_ctx); + return -1; + } + + /* + * Allocate the data we will point this at. + */ + + data = Realloc(*ppdata, sd_size); + if(data == NULL) { + talloc_destroy(mem_ctx); + return ERROR_DOS(ERRDOS,ERRnomem); + } + + *ppdata = data; + + memset(data, '\0', sd_size); + + /* + * Init the parse struct we will marshall into. + */ + + prs_init(&pd, 0, mem_ctx, MARSHALL); + + /* + * Setup the prs_struct to point at the memory we just + * allocated. + */ + + prs_give_memory( &pd, data, (uint32)sd_size, False); + + /* + * Finally, linearize into the outgoing buffer. + */ + + if(!sec_io_desc( "sd data", &psd, &pd, 1)) { + DEBUG(0,("call_nt_transact_query_security_desc: Error in marshalling \ +security descriptor.\n")); + /* + * Return access denied for want of a better error message.. + */ + talloc_destroy(mem_ctx); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + + /* + * Now we can delete the security descriptor. + */ + + talloc_destroy(mem_ctx); + + send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, params, 4, data, (int)sd_size); + return -1; +} + +/**************************************************************************** + Reply to set a security descriptor. Map to UNIX perms. +****************************************************************************/ + +static int call_nt_transact_set_security_desc(connection_struct *conn, + char *inbuf, char *outbuf, int length, + int bufsize, char **ppsetup, + char **ppparams, char **ppdata) +{ + uint32 total_parameter_count = IVAL(inbuf, smb_nts_TotalParameterCount); + char *params= *ppparams; + char *data = *ppdata; + uint32 total_data_count = (uint32)IVAL(inbuf, smb_nts_TotalDataCount); + files_struct *fsp = NULL; + uint32 security_info_sent = 0; + int error_class; + uint32 error_code; + + if(total_parameter_count < 8) + return ERROR_DOS(ERRDOS,ERRbadfunc); + + if((fsp = file_fsp(params,0)) == NULL) + return ERROR_DOS(ERRDOS,ERRbadfid); + + if(!lp_nt_acl_support(SNUM(conn))) + goto done; + + security_info_sent = IVAL(params,4); + + DEBUG(3,("call_nt_transact_set_security_desc: file = %s, sent 0x%x\n", fsp->fsp_name, + (unsigned int)security_info_sent )); + + if (!set_sd( fsp, data, total_data_count, security_info_sent, &error_class, &error_code)) + return ERROR_DOS(error_class, error_code); + + done: + + send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, NULL, 0, NULL, 0); + return -1; +} + +/**************************************************************************** + Reply to IOCTL - not implemented - no plans. +****************************************************************************/ +static int call_nt_transact_ioctl(connection_struct *conn, + char *inbuf, char *outbuf, int length, + int bufsize, + char **ppsetup, char **ppparams, char **ppdata) +{ + static BOOL logged_message = False; + + if(!logged_message) { + DEBUG(0,("call_nt_transact_ioctl: Currently not implemented.\n")); + logged_message = True; /* Only print this once... */ + } + return ERROR_DOS(ERRSRV,ERRnosupport); +} + +/**************************************************************************** + Reply to a SMBNTtrans. +****************************************************************************/ +int reply_nttrans(connection_struct *conn, + char *inbuf,char *outbuf,int length,int bufsize) +{ + int outsize = 0; +#if 0 /* Not used. */ + uint16 max_setup_count = CVAL(inbuf, smb_nt_MaxSetupCount); + uint32 max_parameter_count = IVAL(inbuf, smb_nt_MaxParameterCount); + uint32 max_data_count = IVAL(inbuf,smb_nt_MaxDataCount); +#endif /* Not used. */ + uint32 total_parameter_count = IVAL(inbuf, smb_nt_TotalParameterCount); + uint32 total_data_count = IVAL(inbuf, smb_nt_TotalDataCount); + uint32 parameter_count = IVAL(inbuf,smb_nt_ParameterCount); + uint32 parameter_offset = IVAL(inbuf,smb_nt_ParameterOffset); + uint32 data_count = IVAL(inbuf,smb_nt_DataCount); + uint32 data_offset = IVAL(inbuf,smb_nt_DataOffset); + uint16 setup_count = 2*CVAL(inbuf,smb_nt_SetupCount); /* setup count is in *words* */ + uint16 function_code = SVAL( inbuf, smb_nt_Function); + char *params = NULL, *data = NULL, *setup = NULL; + uint32 num_params_sofar, num_data_sofar; + START_PROFILE(SMBnttrans); + + if(global_oplock_break && (function_code == NT_TRANSACT_CREATE)) { + /* + * Queue this open message as we are the process of an oplock break. + */ + + DEBUG(2,("reply_nttrans: queueing message NT_TRANSACT_CREATE \ +due to being in oplock break state.\n" )); + + push_oplock_pending_smb_message( inbuf, length); + END_PROFILE(SMBnttrans); + return -1; + } + + if (IS_IPC(conn) && (function_code != NT_TRANSACT_CREATE)) { + END_PROFILE(SMBnttrans); + return ERROR_DOS(ERRSRV,ERRaccess); + } + + outsize = set_message(outbuf,0,0,True); + + /* + * All nttrans messages we handle have smb_wct == 19 + setup_count. + * Ensure this is so as a sanity check. + */ + + if(CVAL(inbuf, smb_wct) != 19 + (setup_count/2)) { + DEBUG(2,("Invalid smb_wct %d in nttrans call (should be %d)\n", + CVAL(inbuf, smb_wct), 19 + (setup_count/2))); + END_PROFILE(SMBnttrans); + return ERROR_DOS(ERRSRV,ERRerror); + } + + /* Allocate the space for the setup, the maximum needed parameters and data */ + + if(setup_count > 0) + setup = (char *)malloc(setup_count); + if (total_parameter_count > 0) + params = (char *)malloc(total_parameter_count); + if (total_data_count > 0) + data = (char *)malloc(total_data_count); + + if ((total_parameter_count && !params) || (total_data_count && !data) || + (setup_count && !setup)) { + SAFE_FREE(setup); + SAFE_FREE(params); + SAFE_FREE(data); + DEBUG(0,("reply_nttrans : Out of memory\n")); + END_PROFILE(SMBnttrans); + return ERROR_DOS(ERRDOS,ERRnomem); + } + + /* Copy the param and data bytes sent with this request into + the params buffer */ + num_params_sofar = parameter_count; + num_data_sofar = data_count; + + if (parameter_count > total_parameter_count || data_count > total_data_count) + exit_server("reply_nttrans: invalid sizes in packet."); + + if(setup) { + memcpy( setup, &inbuf[smb_nt_SetupStart], setup_count); + DEBUG(10,("reply_nttrans: setup_count = %d\n", setup_count)); + dump_data(10, setup, setup_count); + } + if(params) { + memcpy( params, smb_base(inbuf) + parameter_offset, parameter_count); + DEBUG(10,("reply_nttrans: parameter_count = %d\n", parameter_count)); + dump_data(10, params, parameter_count); + } + if(data) { + memcpy( data, smb_base(inbuf) + data_offset, data_count); + DEBUG(10,("reply_nttrans: data_count = %d\n",data_count)); + dump_data(10, data, data_count); + } + + if(num_data_sofar < total_data_count || num_params_sofar < total_parameter_count) { + /* We need to send an interim response then receive the rest + of the parameter/data bytes */ + outsize = set_message(outbuf,0,0,True); + if (!send_smb(smbd_server_fd(),outbuf)) + exit_server("reply_nttrans: send_smb failed."); + + while( num_data_sofar < total_data_count || num_params_sofar < total_parameter_count) { + BOOL ret; + + ret = receive_next_smb(inbuf,bufsize,SMB_SECONDARY_WAIT); + + if((ret && (CVAL(inbuf, smb_com) != SMBnttranss)) || !ret) { + outsize = set_message(outbuf,0,0,True); + if(ret) { + DEBUG(0,("reply_nttrans: Invalid secondary nttrans packet\n")); + } else { + DEBUG(0,("reply_nttrans: %s in getting secondary nttrans response.\n", + (smb_read_error == READ_ERROR) ? "error" : "timeout" )); + } + SAFE_FREE(params); + SAFE_FREE(data); + SAFE_FREE(setup); + END_PROFILE(SMBnttrans); + return ERROR_DOS(ERRSRV,ERRerror); + } + + /* Revise total_params and total_data in case they have changed downwards */ + total_parameter_count = IVAL(inbuf, smb_nts_TotalParameterCount); + total_data_count = IVAL(inbuf, smb_nts_TotalDataCount); + num_params_sofar += (parameter_count = IVAL(inbuf,smb_nts_ParameterCount)); + num_data_sofar += ( data_count = IVAL(inbuf, smb_nts_DataCount)); + if (num_params_sofar > total_parameter_count || num_data_sofar > total_data_count) + exit_server("reply_nttrans2: data overflow in secondary nttrans packet"); + + memcpy( ¶ms[ IVAL(inbuf, smb_nts_ParameterDisplacement)], + smb_base(inbuf) + IVAL(inbuf, smb_nts_ParameterOffset), parameter_count); + memcpy( &data[IVAL(inbuf, smb_nts_DataDisplacement)], + smb_base(inbuf)+ IVAL(inbuf, smb_nts_DataOffset), data_count); + } + } + + if (Protocol >= PROTOCOL_NT1) + SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2) | FLAGS2_IS_LONG_NAME); + + /* Now we must call the relevant NT_TRANS function */ + switch(function_code) { + case NT_TRANSACT_CREATE: + START_PROFILE_NESTED(NT_transact_create); + outsize = call_nt_transact_create(conn, inbuf, outbuf, length, bufsize, + &setup, ¶ms, &data); + END_PROFILE_NESTED(NT_transact_create); + break; + case NT_TRANSACT_IOCTL: + START_PROFILE_NESTED(NT_transact_ioctl); + outsize = call_nt_transact_ioctl(conn, + inbuf, outbuf, length, bufsize, + &setup, ¶ms, &data); + END_PROFILE_NESTED(NT_transact_ioctl); + break; + case NT_TRANSACT_SET_SECURITY_DESC: + START_PROFILE_NESTED(NT_transact_set_security_desc); + outsize = call_nt_transact_set_security_desc(conn, inbuf, outbuf, + length, bufsize, + &setup, ¶ms, &data); + END_PROFILE_NESTED(NT_transact_set_security_desc); + break; + case NT_TRANSACT_NOTIFY_CHANGE: + START_PROFILE_NESTED(NT_transact_notify_change); + outsize = call_nt_transact_notify_change(conn, inbuf, outbuf, + length, bufsize, + &setup, ¶ms, &data); + END_PROFILE_NESTED(NT_transact_notify_change); + break; + case NT_TRANSACT_RENAME: + START_PROFILE_NESTED(NT_transact_rename); + outsize = call_nt_transact_rename(conn, inbuf, outbuf, length, + bufsize, + &setup, ¶ms, &data); + END_PROFILE_NESTED(NT_transact_rename); + break; + + case NT_TRANSACT_QUERY_SECURITY_DESC: + START_PROFILE_NESTED(NT_transact_query_security_desc); + outsize = call_nt_transact_query_security_desc(conn, inbuf, outbuf, + length, bufsize, + &setup, ¶ms, &data); + END_PROFILE_NESTED(NT_transact_query_security_desc); + break; + default: + /* Error in request */ + DEBUG(0,("reply_nttrans: Unknown request %d in nttrans call\n", function_code)); + SAFE_FREE(setup); + SAFE_FREE(params); + SAFE_FREE(data); + END_PROFILE(SMBnttrans); + return ERROR_DOS(ERRSRV,ERRerror); + } + + /* As we do not know how many data packets will need to be + returned here the various call_nt_transact_xxxx calls + must send their own. Thus a call_nt_transact_xxxx routine only + returns a value other than -1 when it wants to send + an error packet. + */ + + SAFE_FREE(setup); + SAFE_FREE(params); + SAFE_FREE(data); + END_PROFILE(SMBnttrans); + return outsize; /* If a correct response was needed the call_nt_transact_xxxx + calls have already sent it. If outsize != -1 then it is + returning an error packet. */ +} diff --git a/source3/smbd/open.c b/source3/smbd/open.c new file mode 100644 index 0000000000..29a854a397 --- /dev/null +++ b/source3/smbd/open.c @@ -0,0 +1,1328 @@ +/* + Unix SMB/CIFS implementation. + file opening and share modes + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Jeremy Allison 2001 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +extern userdom_struct current_user_info; +extern uint16 global_oplock_port; +extern BOOL global_client_failed_oplock_break; + +/**************************************************************************** + fd support routines - attempt to do a dos_open. +****************************************************************************/ + +static int fd_open(struct connection_struct *conn, char *fname, + int flags, mode_t mode) +{ + int fd; +#ifdef O_NOFOLLOW + if (!lp_symlinks(SNUM(conn))) + flags |= O_NOFOLLOW; +#endif + + fd = conn->vfs_ops.open(conn,fname,flags,mode); + + /* Fix for files ending in '.' */ + if((fd == -1) && (errno == ENOENT) && + (strchr_m(fname,'.')==NULL)) { + pstrcat(fname,"."); + fd = conn->vfs_ops.open(conn,fname,flags,mode); + } + + DEBUG(10,("fd_open: name %s, flags = 0%o mode = 0%o, fd = %d. %s\n", fname, + flags, (int)mode, fd, (fd == -1) ? strerror(errno) : "" )); + + return fd; +} + +/**************************************************************************** + Close the file associated with a fsp. +****************************************************************************/ + +int fd_close(struct connection_struct *conn, files_struct *fsp) +{ + if (fsp->fd == -1) + return 0; /* what we used to call a stat open. */ + return fd_close_posix(conn, fsp); +} + + +/**************************************************************************** + Check a filename for the pipe string. +****************************************************************************/ + +static void check_for_pipe(char *fname) +{ + /* special case of pipe opens */ + char s[10]; + StrnCpy(s,fname,sizeof(s)-1); + 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. +****************************************************************************/ + +static BOOL open_file(files_struct *fsp,connection_struct *conn, + char *fname1,SMB_STRUCT_STAT *psbuf,int flags,mode_t mode, uint32 desired_access) +{ + extern struct current_user current_user; + pstring fname; + int accmode = (flags & O_ACCMODE); + int local_flags = flags; + + fsp->fd = -1; + fsp->oplock_type = NO_OPLOCK; + errno = EPERM; + + pstrcpy(fname,fname1); + + /* Check permissions */ + + /* + * This code was changed after seeing a client open request + * containing the open mode of (DENY_WRITE/read-only) with + * the 'create if not exist' bit set. The previous code + * would fail to open the file read only on a read-only share + * as it was checking the flags parameter directly against O_RDONLY, + * this was failing as the flags parameter was set to O_RDONLY|O_CREAT. + * JRA. + */ + + if (!CAN_WRITE(conn)) { + /* It's a read-only share - fail if we wanted to write. */ + if(accmode != O_RDONLY) { + DEBUG(3,("Permission denied opening %s\n",fname)); + check_for_pipe(fname); + return False; + } else if(flags & O_CREAT) { + /* We don't want to write - but we must make sure that O_CREAT + doesn't create the file if we have write access into the + directory. + */ + flags &= ~O_CREAT; + } + } + + /* + * This little piece of insanity is inspired by the + * fact that an NT client can open a file for O_RDONLY, + * but set the create disposition to FILE_EXISTS_TRUNCATE. + * If the client *can* write to the file, then it expects to + * truncate the file, even though it is opening for readonly. + * Quicken uses this stupid trick in backup file creation... + * Thanks *greatly* to "David W. Chapman Jr." <dwcjr@inethouston.net> + * for helping track this one down. It didn't bite us in 2.0.x + * as we always opened files read-write in that release. JRA. + */ + + if ((accmode == O_RDONLY) && ((flags & O_TRUNC) == O_TRUNC)) + local_flags = (flags & ~O_ACCMODE)|O_RDWR; + + /* + * We can't actually truncate here as the file may be locked. + * open_file_shared will take care of the truncate later. JRA. + */ + + local_flags &= ~O_TRUNC; + + if ((desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) || + (local_flags & O_CREAT)) { + + /* actually do the open */ + fsp->fd = fd_open(conn, fname, local_flags, mode); + + if (fsp->fd == -1) { + DEBUG(3,("Error opening file %s (%s) (local_flags=%d) (flags=%d)\n", + fname,strerror(errno),local_flags,flags)); + check_for_pipe(fname); + return False; + } + } else + fsp->fd = -1; /* What we used to call a stat open. */ + + if (!VALID_STAT(*psbuf)) { + int ret; + + if (fsp->fd == -1) + ret = vfs_stat(conn, fname, psbuf); + else + ret = vfs_fstat(fsp,fsp->fd,psbuf); + + if (ret == -1) { + DEBUG(0,("Error doing fstat on open file %s (%s)\n", fname,strerror(errno) )); + fd_close(conn, fsp); + return False; + } + } + + /* + * POSIX allows read-only opens of directories. We don't + * want to do this (we use a different code path for this) + * so catch a directory open and return an EISDIR. JRA. + */ + + if(S_ISDIR(psbuf->st_mode)) { + fd_close(conn, fsp); + errno = EISDIR; + return False; + } + + fsp->mode = psbuf->st_mode; + fsp->inode = psbuf->st_ino; + fsp->dev = psbuf->st_dev; + fsp->vuid = current_user.vuid; + fsp->size = psbuf->st_size; + fsp->pos = -1; + fsp->can_lock = True; + fsp->can_read = ((flags & O_WRONLY)==0); + fsp->can_write = ((flags & (O_WRONLY|O_RDWR))!=0); + fsp->share_mode = 0; + fsp->desired_access = desired_access; + fsp->print_file = False; + fsp->modified = False; + fsp->oplock_type = NO_OPLOCK; + fsp->sent_oplock_break = NO_BREAK_SENT; + fsp->is_directory = False; + fsp->directory_delete_on_close = False; + fsp->conn = conn; + string_set(&fsp->fsp_name,fname); + fsp->wcp = NULL; /* Write cache pointer. */ + + DEBUG(2,("%s opened file %s read=%s write=%s (numopen=%d)\n", + *current_user_info.smb_name ? current_user_info.smb_name : conn->user,fsp->fsp_name, + BOOLSTR(fsp->can_read), BOOLSTR(fsp->can_write), + conn->num_files_open + 1)); + + return True; +} + +/**************************************************************************** + C. Hoch 11/22/95 + Helper for open_file_shared. + Truncate a file after checking locking; close file if locked. + **************************************************************************/ + +static int truncate_unless_locked(struct connection_struct *conn, files_struct *fsp) +{ + SMB_BIG_UINT mask = (SMB_BIG_UINT)-1; + + if (is_locked(fsp,fsp->conn,mask,0,WRITE_LOCK,True)){ + errno = EACCES; + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRlock; + return -1; + } else { + return conn->vfs_ops.ftruncate(fsp,fsp->fd,0); + } +} + +/******************************************************************* +return True if the filename is one of the special executable types +********************************************************************/ +static BOOL is_executable(const char *fname) +{ + if ((fname = strrchr_m(fname,'.'))) { + if (strequal(fname,".com") || + strequal(fname,".dll") || + strequal(fname,".exe") || + strequal(fname,".sym")) { + return True; + } + } + return False; +} + +enum {AFAIL,AREAD,AWRITE,AALL}; + +/******************************************************************* +reproduce the share mode access table +this is horrendoously complex, and really can't be justified on any +rational grounds except that this is _exactly_ what NT does. See +the DENY1 and DENY2 tests in smbtorture for a comprehensive set of +test routines. +********************************************************************/ +static int access_table(int new_deny,int old_deny,int old_mode, + BOOL same_pid, BOOL isexe) +{ + if (new_deny == DENY_ALL || old_deny == DENY_ALL) return(AFAIL); + + if (same_pid) { + if (isexe && old_mode == DOS_OPEN_RDONLY && + old_deny == DENY_DOS && new_deny == DENY_READ) { + return AFAIL; + } + if (!isexe && old_mode == DOS_OPEN_RDONLY && + old_deny == DENY_DOS && new_deny == DENY_DOS) { + return AREAD; + } + if (new_deny == DENY_FCB && old_deny == DENY_DOS) { + if (isexe) return AFAIL; + if (old_mode == DOS_OPEN_RDONLY) return AFAIL; + return AALL; + } + if (old_mode == DOS_OPEN_RDONLY && old_deny == DENY_DOS) { + if (new_deny == DENY_FCB || new_deny == DENY_READ) { + if (isexe) return AREAD; + return AFAIL; + } + } + if (old_deny == DENY_FCB) { + if (new_deny == DENY_DOS || new_deny == DENY_FCB) return AALL; + return AFAIL; + } + } + + if (old_deny == DENY_DOS || new_deny == DENY_DOS || + old_deny == DENY_FCB || new_deny == DENY_FCB) { + if (isexe) { + if (old_deny == DENY_FCB || new_deny == DENY_FCB) { + return AFAIL; + } + if (old_deny == DENY_DOS) { + if (new_deny == DENY_READ && + (old_mode == DOS_OPEN_RDONLY || + old_mode == DOS_OPEN_RDWR)) { + return AFAIL; + } + if (new_deny == DENY_WRITE && + (old_mode == DOS_OPEN_WRONLY || + old_mode == DOS_OPEN_RDWR)) { + return AFAIL; + } + return AALL; + } + if (old_deny == DENY_NONE) return AALL; + if (old_deny == DENY_READ) return AWRITE; + if (old_deny == DENY_WRITE) return AREAD; + } + /* it isn't a exe, dll, sym or com file */ + if (old_deny == new_deny && same_pid) + return(AALL); + + if (old_deny == DENY_READ || new_deny == DENY_READ) return AFAIL; + if (old_mode == DOS_OPEN_RDONLY) return(AREAD); + + return(AFAIL); + } + + switch (new_deny) + { + case DENY_WRITE: + if (old_deny==DENY_WRITE && old_mode==DOS_OPEN_RDONLY) return(AREAD); + if (old_deny==DENY_READ && old_mode==DOS_OPEN_RDONLY) return(AWRITE); + if (old_deny==DENY_NONE && old_mode==DOS_OPEN_RDONLY) return(AALL); + return(AFAIL); + case DENY_READ: + if (old_deny==DENY_WRITE && old_mode==DOS_OPEN_WRONLY) return(AREAD); + if (old_deny==DENY_READ && old_mode==DOS_OPEN_WRONLY) return(AWRITE); + if (old_deny==DENY_NONE && old_mode==DOS_OPEN_WRONLY) 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 we can open a file with a share mode +****************************************************************************/ + +static BOOL check_share_mode(connection_struct *conn, share_mode_entry *share, int share_mode, uint32 desired_access, + const char *fname, BOOL fcbopen, int *flags) +{ + int deny_mode = GET_DENY_MODE(share_mode); + int old_open_mode = GET_OPEN_MODE(share->share_mode); + int old_deny_mode = GET_DENY_MODE(share->share_mode); + + /* + * share modes = false means don't bother to check for + * DENY mode conflict. This is a *really* bad idea :-). JRA. + */ + + if(!lp_share_modes(SNUM(conn))) + return True; + + /* + * Don't allow any opens once the delete on close flag has been + * set. + */ + + if (GET_DELETE_ON_CLOSE_FLAG(share->share_mode)) { + DEBUG(5,("check_share_mode: Failing open on file %s as delete on close flag is set.\n", + fname )); + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRnoaccess; + return False; + } + + /* this is a nasty hack, but necessary until we rewrite our open + handling to use a NTCreateX call as the basic call. + NT may open a file with neither read nor write access, and in + this case it expects the open not to conflict with any + existing deny modes. This happens (for example) during a + "xcopy /o" where the second file descriptor is used for + ACL sets + (tridge) + */ + + /* + * This is a bit wierd - the test for desired access not having the + * critical bits seems seems odd. Firstly, if both opens have no + * critical bits then always ignore. Then check the "allow delete" + * then check for either. This probably isn't quite right yet but + * gets us much closer. JRA. + */ + + /* + * If desired_access doesn't contain READ_DATA,WRITE_DATA,APPEND_DATA or EXECUTE + * and the existing desired_acces then share modes don't conflict. + */ + + if ( !(desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) && + !(share->desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) ) { + + /* + * Wrinkle discovered by smbtorture.... + * If both are non-io open and requester is asking for delete and current open has delete access + * but neither open has allowed file share delete then deny.... this is very strange and + * seems to be the only case in which non-io opens conflict. JRA. + */ + + if ((desired_access & DELETE_ACCESS) && (share->desired_access & DELETE_ACCESS) && + (!GET_ALLOW_SHARE_DELETE(share->share_mode) || !GET_ALLOW_SHARE_DELETE(share_mode))) { + DEBUG(5,("check_share_mode: Failing open on file %s as delete access requests conflict.\n", + fname )); + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadshare; + + return False; + } + + DEBUG(5,("check_share_mode: Allowing open on file %s as both desired access (0x%x) \ +and existing desired access (0x%x) are non-data opens\n", + fname, (unsigned int)desired_access, (unsigned int)share->desired_access )); + return True; + } + + /* + * If delete access was requested and the existing share mode doesn't have + * ALLOW_SHARE_DELETE then deny. + */ + + if ((desired_access & DELETE_ACCESS) && !GET_ALLOW_SHARE_DELETE(share->share_mode)) { + DEBUG(5,("check_share_mode: Failing open on file %s as delete access requested and allow share delete not set.\n", + fname )); + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadshare; + + return False; + } + + /* + * The inverse of the above. + * If delete access was granted and the new share mode doesn't have + * ALLOW_SHARE_DELETE then deny. + */ + + if ((share->desired_access & DELETE_ACCESS) && !GET_ALLOW_SHARE_DELETE(share_mode)) { + DEBUG(5,("check_share_mode: Failing open on file %s as delete access granted and allow share delete not requested.\n", + fname )); + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadshare; + + return False; + } + + /* + * If desired_access doesn't contain READ_DATA,WRITE_DATA,APPEND_DATA or EXECUTE + * then share modes don't conflict. Likewise with existing desired access. + */ + + if ( !(desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) || + !(share->desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) ) { + DEBUG(5,("check_share_mode: Allowing open on file %s as desired access (0x%x) doesn't conflict with\ +existing desired access (0x%x).\n", fname, (unsigned int)desired_access, (unsigned int)share->desired_access )); + return True; + } + + { + int access_allowed = access_table(deny_mode,old_deny_mode,old_open_mode, + (share->pid == sys_getpid()),is_executable(fname)); + + if ((access_allowed == AFAIL) || + (!fcbopen && (access_allowed == AREAD && *flags == O_RDWR)) || + (access_allowed == AREAD && *flags != O_RDONLY) || + (access_allowed == AWRITE && *flags != O_WRONLY)) { + + DEBUG(2,("Share violation on file (%d,%d,%d,%d,%s,fcbopen = %d, flags = %d) = %d\n", + deny_mode,old_deny_mode,old_open_mode, + (int)share->pid,fname, fcbopen, *flags, access_allowed)); + + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadshare; + + return False; + } + + if (access_allowed == AREAD) + *flags = O_RDONLY; + + if (access_allowed == AWRITE) + *flags = O_WRONLY; + + } + + return True; +} + +/**************************************************************************** + Deal with open deny mode and oplock break processing. + Invarient: Share mode must be locked on entry and exit. + Returns -1 on error, or number of share modes on success (may be zero). +****************************************************************************/ + +static int open_mode_check(connection_struct *conn, const char *fname, SMB_DEV_T dev, + SMB_INO_T inode, + uint32 desired_access, + int share_mode, int *p_flags, int *p_oplock_request, + BOOL *p_all_current_opens_are_level_II) +{ + int i; + int num_share_modes; + int oplock_contention_count = 0; + share_mode_entry *old_shares = 0; + BOOL fcbopen = False; + BOOL broke_oplock; + + if(GET_OPEN_MODE(share_mode) == DOS_OPEN_FCB) + fcbopen = True; + + num_share_modes = get_share_modes(conn, dev, inode, &old_shares); + + if(num_share_modes == 0) + return 0; + + /* + * Check if the share modes will give us access. + */ + + do { + share_mode_entry broken_entry; + + broke_oplock = False; + *p_all_current_opens_are_level_II = True; + + for(i = 0; i < num_share_modes; i++) { + share_mode_entry *share_entry = &old_shares[i]; + + /* + * By observation of NetBench, oplocks are broken *before* share + * modes are checked. This allows a file to be closed by the client + * if the share mode would deny access and the client has an oplock. + * Check if someone has an oplock on this file. If so we must break + * it before continuing. + */ + + if((*p_oplock_request && EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type)) || + (!*p_oplock_request && (share_entry->op_type != NO_OPLOCK))) { + + BOOL opb_ret; + + DEBUG(5,("open_mode_check: oplock_request = %d, breaking oplock (%x) on file %s, \ +dev = %x, inode = %.0f\n", *p_oplock_request, share_entry->op_type, fname, (unsigned int)dev, (double)inode)); + + /* Oplock break - unlock to request it. */ + unlock_share_entry(conn, dev, inode); + + opb_ret = request_oplock_break(share_entry); + + /* Now relock. */ + lock_share_entry(conn, dev, inode); + + if(opb_ret == False) { + DEBUG(0,("open_mode_check: FAILED when breaking oplock (%x) on file %s, \ +dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (double)inode)); + SAFE_FREE(old_shares); + errno = EACCES; + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadshare; + return -1; + } + + broke_oplock = True; + broken_entry = *share_entry; + break; + + } else if (!LEVEL_II_OPLOCK_TYPE(share_entry->op_type)) { + *p_all_current_opens_are_level_II = False; + } + + /* someone else has a share lock on it, check to see if we can too */ + if (!check_share_mode(conn, share_entry, share_mode, desired_access, + fname, fcbopen, p_flags)) { + SAFE_FREE(old_shares); + errno = EACCES; + return -1; + } + + } /* end for */ + + if(broke_oplock) { + SAFE_FREE(old_shares); + num_share_modes = get_share_modes(conn, dev, inode, &old_shares); + oplock_contention_count++; + + /* Paranoia check that this is no longer an exlusive entry. */ + for(i = 0; i < num_share_modes; i++) { + share_mode_entry *share_entry = &old_shares[i]; + + if (share_modes_identical(&broken_entry, share_entry) && + EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type) ) { + + /* + * This should not happen. The target left this oplock + * as exlusive.... The process *must* be dead.... + */ + + DEBUG(0,("open_mode_check: exlusive oplock left by process %d after break ! For file %s, \ +dev = %x, inode = %.0f. Deleting it to continue...\n", (int)broken_entry.pid, fname, (unsigned int)dev, (double)inode)); + + if (process_exists(broken_entry.pid)) { + DEBUG(0,("open_mode_check: Existent process %d left active oplock.\n", + broken_entry.pid )); + } + + if (del_share_entry(dev, inode, &broken_entry, NULL) == -1) { + errno = EACCES; + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadshare; + return -1; + } + + /* + * We must reload the share modes after deleting the + * other process's entry. + */ + + SAFE_FREE(old_shares); + num_share_modes = get_share_modes(conn, dev, inode, &old_shares); + break; + } + } /* end for paranoia... */ + } /* end if broke_oplock */ + + } while(broke_oplock); + + if(old_shares != 0) + SAFE_FREE(old_shares); + + /* + * Refuse to grant an oplock in case the contention limit is + * reached when going through the lock list multiple times. + */ + + if(oplock_contention_count >= lp_oplock_contention_limit(SNUM(conn))) { + *p_oplock_request = 0; + DEBUG(4,("open_mode_check: oplock contention = %d. Not granting oplock.\n", + oplock_contention_count )); + } + + return num_share_modes; +} + +/**************************************************************************** +set a kernel flock on a file for NFS interoperability +this requires a patch to Linux +****************************************************************************/ +static void kernel_flock(files_struct *fsp, int deny_mode) +{ +#if HAVE_KERNEL_SHARE_MODES + int kernel_mode = 0; + if (deny_mode == DENY_READ) kernel_mode = LOCK_MAND|LOCK_WRITE; + else if (deny_mode == DENY_WRITE) kernel_mode = LOCK_MAND|LOCK_READ; + else if (deny_mode == DENY_ALL) kernel_mode = LOCK_MAND; + if (kernel_mode) flock(fsp->fd, kernel_mode); +#endif + ;; +} + + +/**************************************************************************** + Open a file with a share mode. On output from this open we are guarenteeing + that +****************************************************************************/ +files_struct *open_file_shared(connection_struct *conn,char *fname, SMB_STRUCT_STAT *psbuf, + int share_mode,int ofun, mode_t mode,int oplock_request, + int *Access,int *action) +{ + return open_file_shared1(conn, fname, psbuf, 0, share_mode, ofun, mode, + oplock_request, Access, action); +} + +/**************************************************************************** + Open a file with a share mode. On output from this open we are guarenteeing + that +****************************************************************************/ +files_struct *open_file_shared1(connection_struct *conn,char *fname, SMB_STRUCT_STAT *psbuf, + uint32 desired_access, + int share_mode,int ofun, mode_t mode,int oplock_request, + int *Access,int *action) +{ + int flags=0; + int flags2=0; + int deny_mode = GET_DENY_MODE(share_mode); + BOOL allow_share_delete = GET_ALLOW_SHARE_DELETE(share_mode); + BOOL delete_on_close = GET_DELETE_ON_CLOSE_FLAG(share_mode); + BOOL file_existed = VALID_STAT(*psbuf); + BOOL fcbopen = False; + BOOL def_acl = False; + SMB_DEV_T dev = 0; + SMB_INO_T inode = 0; + int num_share_modes = 0; + BOOL all_current_opens_are_level_II = False; + BOOL fsp_open = False; + files_struct *fsp = NULL; + int open_mode=0; + uint16 port = 0; + + if (conn->printer) { + /* printers are handled completely differently. Most of the passed parameters are + ignored */ + if (Access) + *Access = DOS_OPEN_WRONLY; + if (action) + *action = FILE_WAS_CREATED; + return print_fsp_open(conn, fname); + } + + fsp = file_new(conn); + if(!fsp) + return NULL; + + DEBUG(10,("open_file_shared: fname = %s, share_mode = %x, ofun = %x, mode = %o, oplock request = %d\n", + fname, share_mode, ofun, (int)mode, oplock_request )); + + if (!check_name(fname,conn)) { + file_free(fsp); + return NULL; + } + + /* ignore any oplock requests if oplocks are disabled */ + if (!lp_oplocks(SNUM(conn)) || global_client_failed_oplock_break) { + oplock_request = 0; + } + + /* this is for OS/2 EAs - try and say we don't support them */ + if (strstr(fname,".+,;=[].")) { + unix_ERR_class = ERRDOS; + /* OS/2 Workplace shell fix may be main code stream in a later release. */ +#if 1 /* OS2_WPS_FIX - Recent versions of OS/2 need this. */ + unix_ERR_code = ERRcannotopen; +#else /* OS2_WPS_FIX */ + unix_ERR_code = ERROR_EAS_NOT_SUPPORTED; +#endif /* OS2_WPS_FIX */ + + DEBUG(5,("open_file_shared: OS/2 EA's are not supported.\n")); + file_free(fsp); + return NULL; + } + + if ((GET_FILE_OPEN_DISPOSITION(ofun) == FILE_EXISTS_FAIL) && file_existed) { + DEBUG(5,("open_file_shared: create new requested for file %s and file already exists.\n", + fname )); + file_free(fsp); + errno = EEXIST; + return NULL; + } + + if (CAN_WRITE(conn) && (GET_FILE_CREATE_DISPOSITION(ofun) == FILE_CREATE_IF_NOT_EXIST)) + flags2 |= O_CREAT; + + if (CAN_WRITE(conn) && (GET_FILE_OPEN_DISPOSITION(ofun) == FILE_EXISTS_TRUNCATE)) + flags2 |= O_TRUNC; + + if (GET_FILE_OPEN_DISPOSITION(ofun) == FILE_EXISTS_FAIL) + flags2 |= O_EXCL; + + /* note that we ignore the append flag as + append does not mean the same thing under dos and unix */ + + switch (GET_OPEN_MODE(share_mode)) { + case DOS_OPEN_WRONLY: + flags = O_WRONLY; + if (desired_access == 0) + desired_access = FILE_WRITE_DATA; + break; + case DOS_OPEN_FCB: + fcbopen = True; + flags = O_RDWR; + if (desired_access == 0) + desired_access = FILE_READ_DATA|FILE_WRITE_DATA; + break; + case DOS_OPEN_RDWR: + flags = O_RDWR; + if (desired_access == 0) + desired_access = FILE_READ_DATA|FILE_WRITE_DATA; + break; + default: + flags = O_RDONLY; + if (desired_access == 0) + desired_access = FILE_READ_DATA; + break; + } + +#if defined(O_SYNC) + if (GET_FILE_SYNC_OPENMODE(share_mode)) { + flags2 |= O_SYNC; + } +#endif /* O_SYNC */ + + if (flags != O_RDONLY && file_existed && + (!CAN_WRITE(conn) || IS_DOS_READONLY(dos_mode(conn,fname,psbuf)))) { + if (!fcbopen) { + DEBUG(5,("open_file_shared: read/write access requested for file %s on read only %s\n", + fname, !CAN_WRITE(conn) ? "share" : "file" )); + file_free(fsp); + errno = EACCES; + return NULL; + } + 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)); + file_free(fsp); + errno = EINVAL; + return NULL; + } + + if (file_existed) { + + dev = psbuf->st_dev; + inode = psbuf->st_ino; + + lock_share_entry(conn, dev, inode); + + num_share_modes = open_mode_check(conn, fname, dev, inode, + desired_access, + share_mode, + &flags, &oplock_request, &all_current_opens_are_level_II); + if(num_share_modes == -1) { + + /* + * This next line is a subtlety we need for MS-Access. If a file open will + * fail due to share permissions and also for security (access) + * reasons, we need to return the access failed error, not the + * share error. This means we must attempt to open the file anyway + * in order to get the UNIX access error - even if we're going to + * fail the open for share reasons. This is bad, as we're burning + * another fd if there are existing locks but there's nothing else + * we can do. We also ensure we're not going to create or tuncate + * the file as we only want an access decision at this stage. JRA. + */ + fsp_open = open_file(fsp,conn,fname,psbuf, + flags|(flags2&~(O_TRUNC|O_CREAT)),mode,desired_access); + + DEBUG(4,("open_file_shared : share_mode deny - calling open_file with \ +flags=0x%X flags2=0x%X mode=0%o returned %d\n", + flags,(flags2&~(O_TRUNC|O_CREAT)),(int)mode,(int)fsp_open )); + + unlock_share_entry(conn, dev, inode); + if (fsp_open) + fd_close(conn, fsp); + file_free(fsp); + return NULL; + } + + /* + * We exit this block with the share entry *locked*..... + */ + } + + /* + * Ensure we pay attention to default ACLs on directories if required. + */ + + if ((flags2 & O_CREAT) && lp_inherit_acls(SNUM(conn)) && + (def_acl = directory_has_default_acl(conn, parent_dirname(fname)))) + mode = 0777; + + DEBUG(4,("calling open_file with flags=0x%X flags2=0x%X mode=0%o\n", + flags,flags2,(int)mode)); + + /* + * open_file strips any O_TRUNC flags itself. + */ + + fsp_open = open_file(fsp,conn,fname,psbuf,flags|flags2,mode,desired_access); + + if (!fsp_open && (flags == O_RDWR) && (errno != ENOENT) && fcbopen) { + if((fsp_open = open_file(fsp,conn,fname,psbuf,O_RDONLY,mode,desired_access)) == True) + flags = O_RDONLY; + } + + if (!fsp_open) { + if(file_existed) + unlock_share_entry(conn, dev, inode); + file_free(fsp); + return NULL; + } + + /* + * Deal with the race condition where two smbd's detect the file doesn't + * exist and do the create at the same time. One of them will win and + * set a share mode, the other (ie. this one) should check if the + * requested share mode for this create is allowed. + */ + + if (!file_existed) { + + lock_share_entry_fsp(fsp); + + num_share_modes = open_mode_check(conn, fname, dev, inode, + desired_access, + share_mode, + &flags, &oplock_request, &all_current_opens_are_level_II); + + if(num_share_modes == -1) { + unlock_share_entry_fsp(fsp); + fd_close(conn,fsp); + file_free(fsp); + return NULL; + } + + /* + * If there are any share modes set then the file *did* + * exist. Ensure we return the correct value for action. + */ + + if (num_share_modes > 0) + file_existed = True; + + /* + * We exit this block with the share entry *locked*..... + */ + } + + /* note that we ignore failure for the following. It is + basically a hack for NFS, and NFS will never set one of + these only read them. Nobody but Samba can ever set a deny + mode and we have already checked our more authoritative + locking database for permission to set this deny mode. If + the kernel refuses the operations then the kernel is wrong */ + kernel_flock(fsp, deny_mode); + + /* + * At this point onwards, we can guarentee that the share entry + * is locked, whether we created the file or not, and that the + * deny mode is compatible with all current opens. + */ + + /* + * If requested, truncate the file. + */ + + if (flags2&O_TRUNC) { + /* + * We are modifing the file after open - update the stat struct.. + */ + if ((truncate_unless_locked(conn,fsp) == -1) || (vfs_fstat(fsp,fsp->fd,psbuf)==-1)) { + unlock_share_entry_fsp(fsp); + fd_close(conn,fsp); + file_free(fsp); + return NULL; + } + } + + switch (flags) { + case O_RDONLY: + open_mode = DOS_OPEN_RDONLY; + break; + case O_RDWR: + open_mode = DOS_OPEN_RDWR; + break; + case O_WRONLY: + open_mode = DOS_OPEN_WRONLY; + break; + } + + fsp->share_mode = SET_DENY_MODE(deny_mode) | + SET_OPEN_MODE(open_mode) | + SET_ALLOW_SHARE_DELETE(allow_share_delete); + + DEBUG(10,("open_file_shared : share_mode = %x\n", fsp->share_mode )); + + if (Access) + (*Access) = open_mode; + + if (action) { + if (file_existed && !(flags2 & O_TRUNC)) + *action = FILE_WAS_OPENED; + if (!file_existed) + *action = FILE_WAS_CREATED; + if (file_existed && (flags2 & O_TRUNC)) + *action = FILE_WAS_OVERWRITTEN; + } + + /* + * Setup the oplock info in both the shared memory and + * file structs. + */ + + if(oplock_request && (num_share_modes == 0) && + !IS_VETO_OPLOCK_PATH(conn,fname) && set_file_oplock(fsp, oplock_request) ) { + port = global_oplock_port; + } else if (oplock_request && all_current_opens_are_level_II) { + port = global_oplock_port; + oplock_request = LEVEL_II_OPLOCK; + set_file_oplock(fsp, oplock_request); + } else { + port = 0; + oplock_request = 0; + } + + set_share_mode(fsp, port, oplock_request); + + if (delete_on_close) { + NTSTATUS result = set_delete_on_close_internal(fsp, delete_on_close); + + if (NT_STATUS_V(result) != NT_STATUS_V(NT_STATUS_OK)) { + /* Remember to delete the mode we just added. */ + del_share_mode(fsp, NULL); + unlock_share_entry_fsp(fsp); + fd_close(conn,fsp); + file_free(fsp); + return NULL; + } + } + + /* + * Take care of inherited ACLs on created files - if default ACL not + * selected. + */ + + if (!file_existed && !def_acl && (conn->vfs_ops.fchmod_acl != NULL)) { + int saved_errno = errno; /* We might get ENOSYS in the next call.. */ + if (conn->vfs_ops.fchmod_acl(fsp, fsp->fd, mode) == -1 && errno == ENOSYS) + errno = saved_errno; /* Ignore ENOSYS */ + } + + unlock_share_entry_fsp(fsp); + + conn->num_files_open++; + + return fsp; +} + +/**************************************************************************** + Open a file for for write to ensure that we can fchmod it. +****************************************************************************/ + +files_struct *open_file_fchmod(connection_struct *conn, char *fname, SMB_STRUCT_STAT *psbuf) +{ + files_struct *fsp = NULL; + BOOL fsp_open; + + if (!VALID_STAT(*psbuf)) + return NULL; + + fsp = file_new(conn); + if(!fsp) + return NULL; + + fsp_open = open_file(fsp,conn,fname,psbuf,O_WRONLY,0,0); + + /* + * This is not a user visible file open. + * Don't set a share mode and don't increment + * the conn->num_files_open. + */ + + if (!fsp_open) { + file_free(fsp); + return NULL; + } + + return fsp; +} + +/**************************************************************************** + Close the fchmod file fd - ensure no locks are lost. +****************************************************************************/ + +int close_file_fchmod(files_struct *fsp) +{ + int ret = fd_close(fsp->conn, fsp); + file_free(fsp); + return ret; +} + +/**************************************************************************** + Open a directory from an NT SMB call. +****************************************************************************/ + +files_struct *open_directory(connection_struct *conn, char *fname, SMB_STRUCT_STAT *psbuf, + uint32 desired_access, int share_mode, int smb_ofun, mode_t unixmode, int *action) +{ + extern struct current_user current_user; + BOOL got_stat = False; + files_struct *fsp = file_new(conn); + BOOL delete_on_close = GET_DELETE_ON_CLOSE_FLAG(share_mode); + + if(!fsp) + return NULL; + + fsp->conn = conn; /* The vfs_fXXX() macros need this. */ + + if (VALID_STAT(*psbuf)) + got_stat = True; + + if (got_stat && (GET_FILE_OPEN_DISPOSITION(smb_ofun) == FILE_EXISTS_FAIL)) { + file_free(fsp); + errno = EEXIST; /* Setup so correct error is returned to client. */ + return NULL; + } + + if (GET_FILE_CREATE_DISPOSITION(smb_ofun) == FILE_CREATE_IF_NOT_EXIST) { + + if (got_stat) { + + if(!S_ISDIR(psbuf->st_mode)) { + DEBUG(0,("open_directory: %s is not a directory !\n", fname )); + file_free(fsp); + errno = EACCES; + return NULL; + } + *action = FILE_WAS_OPENED; + + } else { + + /* + * Try and create the directory. + */ + + if(!CAN_WRITE(conn)) { + DEBUG(2,("open_directory: failing create on read-only share\n")); + file_free(fsp); + errno = EACCES; + return NULL; + } + + if(vfs_mkdir(conn,fname, unix_mode(conn,aDIR, fname)) < 0) { + DEBUG(2,("open_directory: unable to create %s. Error was %s\n", + fname, strerror(errno) )); + file_free(fsp); + return NULL; + } + + if(vfs_stat(conn,fname, psbuf) != 0) { + file_free(fsp); + return NULL; + } + + *action = FILE_WAS_CREATED; + + } + } else { + + /* + * Don't create - just check that it *was* a directory. + */ + + if(!got_stat) { + DEBUG(0,("open_directory: unable to stat name = %s. Error was %s\n", + fname, strerror(errno) )); + file_free(fsp); + return NULL; + } + + if(!S_ISDIR(psbuf->st_mode)) { + DEBUG(0,("open_directory: %s is not a directory !\n", fname )); + file_free(fsp); + return NULL; + } + + *action = FILE_WAS_OPENED; + } + + DEBUG(5,("open_directory: opening directory %s\n", fname)); + + /* + * Setup the files_struct for it. + */ + + fsp->mode = psbuf->st_mode; + fsp->inode = psbuf->st_ino; + fsp->dev = psbuf->st_dev; + fsp->size = psbuf->st_size; + fsp->vuid = current_user.vuid; + fsp->pos = -1; + fsp->can_lock = True; + fsp->can_read = False; + fsp->can_write = False; + fsp->share_mode = share_mode; + fsp->desired_access = desired_access; + fsp->print_file = False; + fsp->modified = False; + fsp->oplock_type = NO_OPLOCK; + fsp->sent_oplock_break = NO_BREAK_SENT; + fsp->is_directory = True; + fsp->directory_delete_on_close = False; + fsp->conn = conn; + string_set(&fsp->fsp_name,fname); + + if (delete_on_close) { + NTSTATUS result = set_delete_on_close_internal(fsp, delete_on_close); + + if (NT_STATUS_V(result) != NT_STATUS_V(NT_STATUS_OK)) { + file_free(fsp); + return NULL; + } + } + conn->num_files_open++; + + return fsp; +} +#if 0 + +Old code - I have replaced with correct desired_access checking. JRA. + +/******************************************************************* + 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(connection_struct *conn,char *fname, BOOL rename_op) +{ + int i; + int ret = False; + share_mode_entry *old_shares = 0; + int num_share_modes; + SMB_STRUCT_STAT sbuf; + pid_t pid = sys_getpid(); + SMB_DEV_T dev; + SMB_INO_T inode; + + if (vfs_stat(conn,fname,&sbuf) == -1) + return(True); + + dev = sbuf.st_dev; + inode = sbuf.st_ino; + + lock_share_entry(conn, dev, inode); + num_share_modes = get_share_modes(conn, dev, inode, &old_shares); + + /* + * Check if the share modes will give us access. + */ + + if(num_share_modes != 0) { + BOOL broke_oplock; + + do { + + broke_oplock = False; + for(i = 0; i < num_share_modes; i++) { + share_mode_entry *share_entry = &old_shares[i]; + + /* + * Break oplocks before checking share modes. See comment in + * open_file_shared for details. + * Check if someone has an oplock on this file. If so we must + * break it before continuing. + */ + if(BATCH_OPLOCK_TYPE(share_entry->op_type)) { + + DEBUG(5,("check_file_sharing: breaking oplock (%x) on file %s, \ +dev = %x, inode = %.0f\n", share_entry->op_type, fname, (unsigned int)dev, (double)inode)); + + /* Oplock break.... */ + unlock_share_entry(conn, dev, inode); + + if(request_oplock_break(share_entry) == False) { + DEBUG(0,("check_file_sharing: FAILED when breaking oplock (%x) on file %s, \ +dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (double)inode)); + + SAFE_FREE(old_shares); + return False; + } + lock_share_entry(conn, dev, inode); + broke_oplock = True; + break; + } + + /* + * If this is a delete request and ALLOW_SHARE_DELETE is set then allow + * this to proceed. This takes precedence over share modes. + */ + + if(!rename_op && GET_ALLOW_SHARE_DELETE(share_entry->share_mode)) + continue; + + /* + * Someone else has a share lock on it, check to see + * if we can too. + */ + if ((GET_DENY_MODE(share_entry->share_mode) != DENY_DOS) || + (share_entry->pid != pid)) + goto free_and_exit; + + } /* end for */ + + if(broke_oplock) { + SAFE_FREE(old_shares); + num_share_modes = get_share_modes(conn, dev, inode, &old_shares); + } + } while(broke_oplock); + } + + /* + * XXXX exactly what share mode combinations should be allowed for + * deleting/renaming? + */ + + /* + * If we got here then either there were no share modes or + * all share modes were DENY_DOS and the pid == getpid() or + * delete access was requested and all share modes had the + * ALLOW_SHARE_DELETE bit set (takes precedence over other + * share modes). + */ + + ret = True; + +free_and_exit: + + unlock_share_entry(conn, dev, inode); + SAFE_FREE(old_shares); + return(ret); +} +#endif diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c new file mode 100644 index 0000000000..23606f1d14 --- /dev/null +++ b/source3/smbd/oplock.c @@ -0,0 +1,1219 @@ +/* + Unix SMB/CIFS implementation. + oplock processing + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Jeremy Allison 1998 - 2001 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/* Oplock ipc UDP socket. */ +static int oplock_sock = -1; +uint16 global_oplock_port = 0; + +/* Current number of oplocks we have outstanding. */ +static int32 exclusive_oplocks_open = 0; +static int32 level_II_oplocks_open = 0; +BOOL global_client_failed_oplock_break = False; +BOOL global_oplock_break = False; + +extern int smb_read_error; + +static struct kernel_oplocks *koplocks; + +static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, unsigned long file_id, BOOL local); + +/**************************************************************************** + Get the number of current exclusive oplocks. +****************************************************************************/ + +int32 get_number_of_exclusive_open_oplocks(void) +{ + return exclusive_oplocks_open; +} + +/**************************************************************************** + Return True if an oplock message is pending. +****************************************************************************/ + +BOOL oplock_message_waiting(fd_set *fds) +{ + if (koplocks && koplocks->msg_waiting(fds)) + return True; + + if (FD_ISSET(oplock_sock, fds)) + return True; + + return False; +} + +/**************************************************************************** + Read an oplock break message from either the oplock UDP fd or the + kernel (if kernel oplocks are supported). + + If timeout is zero then *fds contains the file descriptors that + are ready to be read and acted upon. If timeout is non-zero then + *fds contains the file descriptors to be selected on for read. + The timeout is in milliseconds + +****************************************************************************/ + +BOOL receive_local_message(fd_set *fds, char *buffer, int buffer_len, int timeout) +{ + struct sockaddr_in from; + int fromlen = sizeof(from); + int32 msg_len = 0; + + smb_read_error = 0; + + if(timeout != 0) { + struct timeval to; + int selrtn; + int maxfd = oplock_sock; + + if (koplocks && koplocks->notification_fd != -1) { + FD_SET(koplocks->notification_fd, fds); + maxfd = MAX(maxfd, koplocks->notification_fd); + } + + to.tv_sec = timeout / 1000; + to.tv_usec = (timeout % 1000) * 1000; + + selrtn = sys_select(maxfd+1,fds,NULL,NULL,&to); + + if (selrtn == -1 && errno == EINTR) { + /* could be a kernel oplock interrupt */ + if (koplocks && koplocks->msg_waiting(fds)) { + return koplocks->receive_message(fds, buffer, buffer_len); + } + } + + /* Check if error */ + if(selrtn == -1) { + /* something is wrong. Maybe the socket is dead? */ + smb_read_error = READ_ERROR; + return False; + } + + /* Did we timeout ? */ + if (selrtn == 0) { + smb_read_error = READ_TIMEOUT; + return False; + } + } + + if (koplocks && koplocks->msg_waiting(fds)) { + return koplocks->receive_message(fds, buffer, buffer_len); + } + + if (!FD_ISSET(oplock_sock, fds)) + return False; + + /* + * From here down we deal with the smbd <--> smbd + * oplock break protocol only. + */ + + /* + * Read a loopback udp message. + */ + msg_len = recvfrom(oplock_sock, &buffer[OPBRK_CMD_HEADER_LEN], + buffer_len - OPBRK_CMD_HEADER_LEN, 0, (struct sockaddr *)&from, &fromlen); + + if(msg_len < 0) { + DEBUG(0,("receive_local_message. Error in recvfrom. (%s).\n",strerror(errno))); + return False; + } + + /* Validate message length. */ + if(msg_len > (buffer_len - OPBRK_CMD_HEADER_LEN)) { + DEBUG(0,("receive_local_message: invalid msg_len (%d) max can be %d\n", msg_len, + buffer_len - OPBRK_CMD_HEADER_LEN)); + return False; + } + + /* Validate message from address (must be localhost). */ + if(from.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) { + DEBUG(0,("receive_local_message: invalid 'from' address \ +(was %lx should be 127.0.0.1)\n", (long)from.sin_addr.s_addr)); + return False; + } + + /* Setup the message header */ + SIVAL(buffer,OPBRK_CMD_LEN_OFFSET,msg_len); + SSVAL(buffer,OPBRK_CMD_PORT_OFFSET,ntohs(from.sin_port)); + + return True; +} + +/**************************************************************************** + Attempt to set an oplock on a file. Always succeeds if kernel oplocks are + disabled (just sets flags). Returns True if oplock set. +****************************************************************************/ + +BOOL set_file_oplock(files_struct *fsp, int oplock_type) +{ + if (koplocks && !koplocks->set_oplock(fsp, oplock_type)) + return False; + + fsp->oplock_type = oplock_type; + fsp->sent_oplock_break = NO_BREAK_SENT; + if (oplock_type == LEVEL_II_OPLOCK) + level_II_oplocks_open++; + else + exclusive_oplocks_open++; + + DEBUG(5,("set_file_oplock: granted oplock on file %s, dev = %x, inode = %.0f, file_id = %lu, \ +tv_sec = %x, tv_usec = %x\n", + fsp->fsp_name, (unsigned int)fsp->dev, (double)fsp->inode, fsp->file_id, + (int)fsp->open_time.tv_sec, (int)fsp->open_time.tv_usec )); + + return True; +} + +/**************************************************************************** + Attempt to release an oplock on a file. Decrements oplock count. +****************************************************************************/ + +void release_file_oplock(files_struct *fsp) +{ + if (koplocks) + koplocks->release_oplock(fsp); + + if (fsp->oplock_type == LEVEL_II_OPLOCK) + level_II_oplocks_open--; + else + exclusive_oplocks_open--; + + fsp->oplock_type = NO_OPLOCK; + fsp->sent_oplock_break = NO_BREAK_SENT; + + flush_write_cache(fsp, OPLOCK_RELEASE_FLUSH); +} + +/**************************************************************************** + Attempt to downgrade an oplock on a file. Doesn't decrement oplock count. +****************************************************************************/ + +static void downgrade_file_oplock(files_struct *fsp) +{ + if (koplocks) + koplocks->release_oplock(fsp); + fsp->oplock_type = LEVEL_II_OPLOCK; + exclusive_oplocks_open--; + level_II_oplocks_open++; + fsp->sent_oplock_break = NO_BREAK_SENT; +} + +/**************************************************************************** + Remove a file oplock. Copes with level II and exclusive. + Locks then unlocks the share mode lock. Client can decide to go directly + to none even if a "break-to-level II" was sent. +****************************************************************************/ + +BOOL remove_oplock(files_struct *fsp, BOOL break_to_none) +{ + SMB_DEV_T dev = fsp->dev; + SMB_INO_T inode = fsp->inode; + BOOL ret = True; + + /* Remove the oplock flag from the sharemode. */ + if (lock_share_entry_fsp(fsp) == False) { + DEBUG(0,("remove_oplock: failed to lock share entry for file %s\n", + fsp->fsp_name )); + ret = False; + } + + if (fsp->sent_oplock_break == EXCLUSIVE_BREAK_SENT || break_to_none) { + /* + * Deal with a reply when a break-to-none was sent. + */ + + if(remove_share_oplock(fsp)==False) { + DEBUG(0,("remove_oplock: failed to remove share oplock for file %s fnum %d, \ +dev = %x, inode = %.0f\n", fsp->fsp_name, fsp->fnum, (unsigned int)dev, (double)inode)); + ret = False; + } + + release_file_oplock(fsp); + } else { + /* + * Deal with a reply when a break-to-level II was sent. + */ + if(downgrade_share_oplock(fsp)==False) { + DEBUG(0,("remove_oplock: failed to downgrade share oplock for file %s fnum %d, \ +dev = %x, inode = %.0f\n", fsp->fsp_name, fsp->fnum, (unsigned int)dev, (double)inode)); + ret = False; + } + + downgrade_file_oplock(fsp); + } + + unlock_share_entry_fsp(fsp); + return ret; +} + +/**************************************************************************** + Setup the listening set of file descriptors for an oplock break + message either from the UDP socket or from the kernel. Returns the maximum + fd used. +****************************************************************************/ + +int setup_oplock_select_set( fd_set *fds) +{ + int maxfd = oplock_sock; + + if(oplock_sock == -1) + return 0; + + FD_SET(oplock_sock,fds); + + if (koplocks && koplocks->notification_fd != -1) { + FD_SET(koplocks->notification_fd, fds); + maxfd = MAX(maxfd, koplocks->notification_fd); + } + + return maxfd; +} + +/**************************************************************************** + Process an oplock break message - whether it came from the UDP socket + or from the kernel. +****************************************************************************/ + +BOOL process_local_message(char *buffer, int buf_size) +{ + int32 msg_len; + uint16 from_port; + char *msg_start; + pid_t remotepid; + SMB_DEV_T dev; + SMB_INO_T inode; + unsigned long file_id; + uint16 break_cmd_type; + + msg_len = IVAL(buffer,OPBRK_CMD_LEN_OFFSET); + from_port = SVAL(buffer,OPBRK_CMD_PORT_OFFSET); + + msg_start = &buffer[OPBRK_CMD_HEADER_LEN]; + + DEBUG(5,("process_local_message: Got a message of length %d from port (%d)\n", + msg_len, from_port)); + + /* + * Pull the info out of the requesting packet. + */ + + break_cmd_type = SVAL(msg_start,OPBRK_MESSAGE_CMD_OFFSET); + + switch(break_cmd_type) { + case KERNEL_OPLOCK_BREAK_CMD: + if (!koplocks) { + DEBUG(0,("unexpected kernel oplock break!\n")); + break; + } + if (!koplocks->parse_message(msg_start, msg_len, &inode, &dev, &file_id)) { + DEBUG(0,("kernel oplock break parse failure!\n")); + } + break; + + case OPLOCK_BREAK_CMD: + case LEVEL_II_OPLOCK_BREAK_CMD: + + /* Ensure that the msg length is correct. */ + if(msg_len != OPLOCK_BREAK_MSG_LEN) { + DEBUG(0,("process_local_message: incorrect length for OPLOCK_BREAK_CMD (was %d, should be %d).\n", + (int)msg_len, (int)OPLOCK_BREAK_MSG_LEN)); + return False; + } + + memcpy((char *)&remotepid, msg_start+OPLOCK_BREAK_PID_OFFSET,sizeof(remotepid)); + memcpy((char *)&inode, msg_start+OPLOCK_BREAK_INODE_OFFSET,sizeof(inode)); + memcpy((char *)&dev, msg_start+OPLOCK_BREAK_DEV_OFFSET,sizeof(dev)); + memcpy((char *)&file_id, msg_start+OPLOCK_BREAK_FILEID_OFFSET,sizeof(file_id)); + + DEBUG(5,("process_local_message: (%s) oplock break request from \ +pid %d, port %d, dev = %x, inode = %.0f, file_id = %lu\n", + (break_cmd_type == OPLOCK_BREAK_CMD) ? "exclusive" : "level II", + (int)remotepid, from_port, (unsigned int)dev, (double)inode, file_id)); + break; + + /* + * Keep this as a debug case - eventually we can remove it. + */ + case 0x8001: + DEBUG(0,("process_local_message: Received unsolicited break \ +reply - dumping info.\n")); + + if(msg_len != OPLOCK_BREAK_MSG_LEN) { + DEBUG(0,("process_local_message: ubr: incorrect length for reply \ +(was %d, should be %d).\n", (int)msg_len, (int)OPLOCK_BREAK_MSG_LEN)); + return False; + } + + memcpy((char *)&inode, msg_start+OPLOCK_BREAK_INODE_OFFSET,sizeof(inode)); + memcpy((char *)&remotepid, msg_start+OPLOCK_BREAK_PID_OFFSET,sizeof(remotepid)); + memcpy((char *)&dev, msg_start+OPLOCK_BREAK_DEV_OFFSET,sizeof(dev)); + memcpy((char *)&file_id, msg_start+OPLOCK_BREAK_FILEID_OFFSET,sizeof(file_id)); + + DEBUG(0,("process_local_message: unsolicited oplock break reply from \ +pid %d, port %d, dev = %x, inode = %.0f, file_id = %lu\n", + (int)remotepid, from_port, (unsigned int)dev, (double)inode, file_id)); + + return False; + + default: + DEBUG(0,("process_local_message: unknown UDP message command code (%x) - ignoring.\n", + (unsigned int)SVAL(msg_start,0))); + return False; + } + + /* + * Now actually process the break request. + */ + + if((exclusive_oplocks_open + level_II_oplocks_open) != 0) { + if (oplock_break(dev, inode, file_id, False) == False) { + DEBUG(0,("process_local_message: oplock break failed.\n")); + return False; + } + } else { + /* + * If we have no record of any currently open oplocks, + * it's not an error, as a close command may have + * just been issued on the file that was oplocked. + * Just log a message and return success in this case. + */ + DEBUG(3,("process_local_message: oplock break requested with no outstanding \ +oplocks. Returning success.\n")); + } + + /* + * Do the appropriate reply - none in the kernel or level II case. + */ + + if(SVAL(msg_start,OPBRK_MESSAGE_CMD_OFFSET) == OPLOCK_BREAK_CMD) { + struct sockaddr_in toaddr; + + /* Send the message back after OR'ing in the 'REPLY' bit. */ + SSVAL(msg_start,OPBRK_MESSAGE_CMD_OFFSET,OPLOCK_BREAK_CMD | CMD_REPLY); + + memset((char *)&toaddr,'\0',sizeof(toaddr)); + toaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + toaddr.sin_port = htons(from_port); + toaddr.sin_family = AF_INET; + + if(sendto( oplock_sock, msg_start, OPLOCK_BREAK_MSG_LEN, 0, + (struct sockaddr *)&toaddr, sizeof(toaddr)) < 0) { + DEBUG(0,("process_local_message: sendto process %d failed. Errno was %s\n", + (int)remotepid, strerror(errno))); + return False; + } + + DEBUG(5,("process_local_message: oplock break reply sent to \ +pid %d, port %d, for file dev = %x, inode = %.0f, file_id = %lu\n", + (int)remotepid, from_port, (unsigned int)dev, (double)inode, file_id)); + } + + return True; +} + +/**************************************************************************** + Set up an oplock break message. +****************************************************************************/ + +static void prepare_break_message(char *outbuf, files_struct *fsp, BOOL level2) +{ + memset(outbuf,'\0',smb_size); + set_message(outbuf,8,0,True); + + SCVAL(outbuf,smb_com,SMBlockingX); + SSVAL(outbuf,smb_tid,fsp->conn->cnum); + SSVAL(outbuf,smb_pid,0xFFFF); + SSVAL(outbuf,smb_uid,0); + SSVAL(outbuf,smb_mid,0xFFFF); + SCVAL(outbuf,smb_vwv0,0xFF); + SSVAL(outbuf,smb_vwv2,fsp->fnum); + SCVAL(outbuf,smb_vwv3,LOCKING_ANDX_OPLOCK_RELEASE); + SCVAL(outbuf,smb_vwv3+1,level2 ? OPLOCKLEVEL_II : OPLOCKLEVEL_NONE); +} + +/**************************************************************************** + Function to do the waiting before sending a local break. +****************************************************************************/ + +static void wait_before_sending_break(BOOL local_request) +{ + extern struct timeval smb_last_time; + + if(local_request) { + struct timeval cur_tv; + long wait_left = (long)lp_oplock_break_wait_time(); + + if (wait_left == 0) + return; + + GetTimeOfDay(&cur_tv); + + wait_left -= ((cur_tv.tv_sec - smb_last_time.tv_sec)*1000) + + ((cur_tv.tv_usec - smb_last_time.tv_usec)/1000); + + if(wait_left > 0) { + wait_left = MIN(wait_left, 1000); + sys_usleep(wait_left * 1000); + } + } +} + +/**************************************************************************** + Ensure that we have a valid oplock. +****************************************************************************/ + +static files_struct *initial_break_processing(SMB_DEV_T dev, SMB_INO_T inode, unsigned long file_id) +{ + files_struct *fsp = NULL; + + if( DEBUGLVL( 3 ) ) { + dbgtext( "initial_break_processing: called for dev = %x, inode = %.0f file_id = %lu\n", + (unsigned int)dev, (double)inode, file_id); + dbgtext( "Current oplocks_open (exclusive = %d, levelII = %d)\n", + exclusive_oplocks_open, level_II_oplocks_open ); + } + + /* + * We need to search the file open table for the + * entry containing this dev and inode, and ensure + * we have an oplock on it. + */ + + fsp = file_find_dif(dev, inode, file_id); + + if(fsp == NULL) { + /* The file could have been closed in the meantime - return success. */ + if( DEBUGLVL( 3 ) ) { + dbgtext( "initial_break_processing: cannot find open file with " ); + dbgtext( "dev = %x, inode = %.0f file_id = %lu", (unsigned int)dev, + (double)inode, file_id); + dbgtext( "allowing break to succeed.\n" ); + } + return NULL; + } + + /* Ensure we have an oplock on the file */ + + /* + * There is a potential race condition in that an oplock could + * have been broken due to another udp request, and yet there are + * still oplock break messages being sent in the udp message + * queue for this file. So return true if we don't have an oplock, + * as we may have just freed it. + */ + + if(fsp->oplock_type == NO_OPLOCK) { + if( DEBUGLVL( 3 ) ) { + dbgtext( "initial_break_processing: file %s ", fsp->fsp_name ); + dbgtext( "(dev = %x, inode = %.0f, file_id = %lu) has no oplock.\n", + (unsigned int)dev, (double)inode, fsp->file_id ); + dbgtext( "Allowing break to succeed regardless.\n" ); + } + return NULL; + } + + return fsp; +} + +/**************************************************************************** + Process a level II oplock break directly. +****************************************************************************/ + +BOOL oplock_break_level2(files_struct *fsp, BOOL local_request, int token) +{ + extern uint32 global_client_caps; + char outbuf[128]; + BOOL got_lock = False; + SMB_DEV_T dev = fsp->dev; + SMB_INO_T inode = fsp->inode; + + /* + * We can have a level II oplock even if the client is not + * level II oplock aware. In this case just remove the + * flags and don't send the break-to-none message to + * the client. + */ + + if (global_client_caps & CAP_LEVEL_II_OPLOCKS) { + /* + * If we are sending an oplock break due to an SMB sent + * by our own client we ensure that we wait at leat + * lp_oplock_break_wait_time() milliseconds before sending + * the packet. Sending the packet sooner can break Win9x + * and has reported to cause problems on NT. JRA. + */ + + wait_before_sending_break(local_request); + + /* Prepare the SMBlockingX message. */ + + prepare_break_message( outbuf, fsp, False); + if (!send_smb(smbd_server_fd(), outbuf)) + exit_server("oplock_break_level2: send_smb failed."); + } + + /* + * Now we must update the shared memory structure to tell + * everyone else we no longer have a level II oplock on + * this open file. If local_request is true then token is + * the existing lock on the shared memory area. + */ + + if(!local_request && lock_share_entry_fsp(fsp) == False) { + DEBUG(0,("oplock_break_level2: unable to lock share entry for file %s\n", fsp->fsp_name )); + } else { + got_lock = True; + } + + if(remove_share_oplock(fsp)==False) { + DEBUG(0,("oplock_break_level2: unable to remove level II oplock for file %s\n", fsp->fsp_name )); + } + + if (!local_request && got_lock) + unlock_share_entry_fsp(fsp); + + fsp->oplock_type = NO_OPLOCK; + level_II_oplocks_open--; + + if(level_II_oplocks_open < 0) { + DEBUG(0,("oplock_break_level2: level_II_oplocks_open < 0 (%d). PANIC ERROR\n", + level_II_oplocks_open)); + abort(); + } + + if( DEBUGLVL( 3 ) ) { + dbgtext( "oplock_break_level2: returning success for " ); + dbgtext( "dev = %x, inode = %.0f, file_id = %lu\n", (unsigned int)dev, (double)inode, fsp->file_id ); + dbgtext( "Current level II oplocks_open = %d\n", level_II_oplocks_open ); + } + + return True; +} + +/**************************************************************************** + Process an oplock break directly. +****************************************************************************/ + +static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, unsigned long file_id, BOOL local_request) +{ + extern uint32 global_client_caps; + extern struct current_user current_user; + char *inbuf = NULL; + char *outbuf = NULL; + files_struct *fsp = NULL; + time_t start_time; + BOOL shutdown_server = False; + BOOL oplock_timeout = False; + connection_struct *saved_user_conn; + connection_struct *saved_fsp_conn; + int saved_vuid; + pstring saved_dir; + int timeout = (OPLOCK_BREAK_TIMEOUT * 1000); + pstring file_name; + BOOL using_levelII; + + if((fsp = initial_break_processing(dev, inode, file_id)) == NULL) + return True; + + /* + * Deal with a level II oplock going break to none separately. + */ + + if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type)) + return oplock_break_level2(fsp, local_request, -1); + + /* Mark the oplock break as sent - we don't want to send twice! */ + if (fsp->sent_oplock_break) { + if( DEBUGLVL( 0 ) ) { + dbgtext( "oplock_break: ERROR: oplock_break already sent for " ); + dbgtext( "file %s ", fsp->fsp_name); + dbgtext( "(dev = %x, inode = %.0f, file_id = %lu)\n", (unsigned int)dev, (double)inode, fsp->file_id ); + } + + /* + * We have to fail the open here as we cannot send another oplock break on + * this file whilst we are awaiting a response from the client - neither + * can we allow another open to succeed while we are waiting for the client. + */ + return False; + } + + if(global_oplock_break) { + DEBUG(0,("ABORT : ABORT : recursion in oplock_break !!!!!\n")); + abort(); + } + + /* + * Now comes the horrid part. We must send an oplock break to the client, + * and then process incoming messages until we get a close or oplock release. + * At this point we know we need a new inbuf/outbuf buffer pair. + * We cannot use these staticaly as we may recurse into here due to + * messages crossing on the wire. + */ + + if((inbuf = (char *)malloc(BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN))==NULL) { + DEBUG(0,("oplock_break: malloc fail for input buffer.\n")); + return False; + } + + if((outbuf = (char *)malloc(BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN))==NULL) { + DEBUG(0,("oplock_break: malloc fail for output buffer.\n")); + SAFE_FREE(inbuf); + return False; + } + + /* + * If we are sending an oplock break due to an SMB sent + * by our own client we ensure that we wait at leat + * lp_oplock_break_wait_time() milliseconds before sending + * the packet. Sending the packet sooner can break Win9x + * and has reported to cause problems on NT. JRA. + */ + + wait_before_sending_break(local_request); + + /* Prepare the SMBlockingX message. */ + + if ((global_client_caps & CAP_LEVEL_II_OPLOCKS) && + !koplocks && /* NOTE: we force levelII off for kernel oplocks - this will change when it is supported */ + lp_level2_oplocks(SNUM(fsp->conn))) { + using_levelII = True; + } else { + using_levelII = False; + } + + prepare_break_message( outbuf, fsp, using_levelII); + /* Remember if we just sent a break to level II on this file. */ + fsp->sent_oplock_break = using_levelII? LEVEL_II_BREAK_SENT:EXCLUSIVE_BREAK_SENT; + + if (!send_smb(smbd_server_fd(), outbuf)) + exit_server("oplock_break: send_smb failed."); + + /* We need this in case a readraw crosses on the wire. */ + global_oplock_break = True; + + /* Process incoming messages. */ + + /* + * JRA - If we don't get a break from the client in OPLOCK_BREAK_TIMEOUT + * seconds we should just die.... + */ + + start_time = time(NULL); + + /* + * Save the information we need to re-become the + * user, then unbecome the user whilst we're doing this. + */ + saved_user_conn = current_user.conn; + saved_vuid = current_user.vuid; + saved_fsp_conn = fsp->conn; + change_to_root_user(); + vfs_GetWd(saved_fsp_conn,saved_dir); + /* Save the chain fnum. */ + file_chain_save(); + + /* + * From Charles Hoch <hoch@exemplary.com>. If the break processing + * code closes the file (as it often does), then the fsp pointer here + * points to free()'d memory. We *must* revalidate fsp each time + * around the loop. + */ + + pstrcpy(file_name, fsp->fsp_name); + + while((fsp = initial_break_processing(dev, inode, file_id)) && + OPEN_FSP(fsp) && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { + if(receive_smb(smbd_server_fd(),inbuf, timeout) == False) { + /* + * Die if we got an error. + */ + + if (smb_read_error == READ_EOF) { + DEBUG( 0, ( "oplock_break: end of file from client\n" ) ); + shutdown_server = True; + } else if (smb_read_error == READ_ERROR) { + DEBUG( 0, ("oplock_break: receive_smb error (%s)\n", strerror(errno)) ); + shutdown_server = True; + } else if (smb_read_error == READ_TIMEOUT) { + DEBUG( 0, ( "oplock_break: receive_smb timed out after %d seconds.\n", OPLOCK_BREAK_TIMEOUT ) ); + oplock_timeout = True; + } + + DEBUGADD( 0, ( "oplock_break failed for file %s ", file_name ) ); + DEBUGADD( 0, ( "(dev = %x, inode = %.0f, file_id = %lu).\n", + (unsigned int)dev, (double)inode, file_id)); + + break; + } + + /* + * There are certain SMB requests that we shouldn't allow + * to recurse. opens, renames and deletes are the obvious + * ones. This is handled in the switch_message() function. + * If global_oplock_break is set they will push the packet onto + * the pending smb queue and return -1 (no reply). + * JRA. + */ + + process_smb(inbuf, outbuf); + + /* + * Die if we go over the time limit. + */ + + if((time(NULL) - start_time) > OPLOCK_BREAK_TIMEOUT) { + if( DEBUGLVL( 0 ) ) { + dbgtext( "oplock_break: no break received from client " ); + dbgtext( "within %d seconds.\n", OPLOCK_BREAK_TIMEOUT ); + dbgtext( "oplock_break failed for file %s ", fsp->fsp_name ); + dbgtext( "(dev = %x, inode = %.0f, file_id = %lu).\n", + (unsigned int)dev, (double)inode, file_id ); + } + oplock_timeout = True; + break; + } + } + + /* + * Go back to being the user who requested the oplock + * break. + */ + if((saved_user_conn != NULL) && (saved_vuid != UID_FIELD_INVALID) && !change_to_user(saved_user_conn, saved_vuid)) { + DEBUG( 0, ( "oplock_break: unable to re-become user!" ) ); + DEBUGADD( 0, ( "Shutting down server\n" ) ); + close(oplock_sock); + exit_server("unable to re-become user"); + } + + /* Including the directory. */ + vfs_ChDir(saved_fsp_conn,saved_dir); + + /* Restore the chain fnum. */ + file_chain_restore(); + + /* Free the buffers we've been using to recurse. */ + SAFE_FREE(inbuf); + SAFE_FREE(outbuf); + + /* We need this in case a readraw crossed on the wire. */ + if(global_oplock_break) + global_oplock_break = False; + + /* + * If the client timed out then clear the oplock (or go to level II) + * and continue. This seems to be what NT does and is better than dropping + * the connection. + */ + + if(oplock_timeout && (fsp = initial_break_processing(dev, inode, file_id)) && + OPEN_FSP(fsp) && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { + DEBUG(0,("oplock_break: client failure in oplock break in file %s\n", fsp->fsp_name)); + remove_oplock(fsp,True); +#if FASCIST_OPLOCK_BACKOFF + global_client_failed_oplock_break = True; /* Never grant this client an oplock again. */ +#endif + } + + /* + * If the client had an error we must die. + */ + + if(shutdown_server) { + DEBUG( 0, ( "oplock_break: client failure in break - " ) ); + DEBUGADD( 0, ( "shutting down this smbd.\n" ) ); + close(oplock_sock); + exit_server("oplock break failure"); + } + + /* Santity check - remove this later. JRA */ + if(exclusive_oplocks_open < 0) { + DEBUG(0,("oplock_break: exclusive_oplocks_open < 0 (%d). PANIC ERROR\n", exclusive_oplocks_open)); + abort(); + } + + if( DEBUGLVL( 3 ) ) { + dbgtext( "oplock_break: returning success for " ); + dbgtext( "dev = %x, inode = %.0f, file_id = %lu\n", (unsigned int)dev, (double)inode, file_id ); + dbgtext( "Current exclusive_oplocks_open = %d\n", exclusive_oplocks_open ); + } + + return True; +} + +/**************************************************************************** +Send an oplock break message to another smbd process. If the oplock is held +by the local smbd then call the oplock break function directly. +****************************************************************************/ + +BOOL request_oplock_break(share_mode_entry *share_entry) +{ + char op_break_msg[OPLOCK_BREAK_MSG_LEN]; + struct sockaddr_in addr_out; + pid_t pid = sys_getpid(); + time_t start_time; + int time_left; + SMB_DEV_T dev = share_entry->dev; + SMB_INO_T inode = share_entry->inode; + unsigned long file_id = share_entry->share_file_id; + + if(pid == share_entry->pid) { + /* We are breaking our own oplock, make sure it's us. */ + if(share_entry->op_port != global_oplock_port) { + DEBUG(0,("request_oplock_break: corrupt share mode entry - pid = %d, port = %d \ +should be %d\n", (int)pid, share_entry->op_port, global_oplock_port)); + return False; + } + + DEBUG(5,("request_oplock_break: breaking our own oplock\n")); + +#if 1 /* JRA PARANOIA TEST.... */ + { + files_struct *fsp = file_find_dif(dev, inode, file_id); + if (!fsp) { + DEBUG(0,("request_oplock_break: PANIC : breaking our own oplock requested for \ +dev = %x, inode = %.0f, file_id = %lu and no fsp found !\n", + (unsigned int)dev, (double)inode, file_id )); + smb_panic("request_oplock_break: no fsp found for our own oplock\n"); + } + } +#endif /* END JRA PARANOIA TEST... */ + + /* Call oplock break direct. */ + return oplock_break(dev, inode, file_id, True); + } + + /* We need to send a OPLOCK_BREAK_CMD message to the port in the share mode entry. */ + + if (LEVEL_II_OPLOCK_TYPE(share_entry->op_type)) { + SSVAL(op_break_msg,OPBRK_MESSAGE_CMD_OFFSET,LEVEL_II_OPLOCK_BREAK_CMD); + } else { + SSVAL(op_break_msg,OPBRK_MESSAGE_CMD_OFFSET,OPLOCK_BREAK_CMD); + } + + memcpy(op_break_msg+OPLOCK_BREAK_PID_OFFSET,(char *)&pid,sizeof(pid)); + memcpy(op_break_msg+OPLOCK_BREAK_DEV_OFFSET,(char *)&dev,sizeof(dev)); + memcpy(op_break_msg+OPLOCK_BREAK_INODE_OFFSET,(char *)&inode,sizeof(inode)); + memcpy(op_break_msg+OPLOCK_BREAK_FILEID_OFFSET,(char *)&file_id,sizeof(file_id)); + + /* Set the address and port. */ + memset((char *)&addr_out,'\0',sizeof(addr_out)); + addr_out.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr_out.sin_port = htons( share_entry->op_port ); + addr_out.sin_family = AF_INET; + + if( DEBUGLVL( 3 ) ) { + dbgtext( "request_oplock_break: sending a oplock break message to " ); + dbgtext( "pid %d on port %d ", (int)share_entry->pid, share_entry->op_port ); + dbgtext( "for dev = %x, inode = %.0f, file_id = %lu\n", + (unsigned int)dev, (double)inode, file_id ); + } + + if(sendto(oplock_sock,op_break_msg,OPLOCK_BREAK_MSG_LEN,0, + (struct sockaddr *)&addr_out,sizeof(addr_out)) < 0) { + if( DEBUGLVL( 0 ) ) { + dbgtext( "request_oplock_break: failed when sending a oplock " ); + dbgtext( "break message to pid %d ", (int)share_entry->pid ); + dbgtext( "on port %d ", share_entry->op_port ); + dbgtext( "for dev = %x, inode = %.0f, file_id = %lu\n", + (unsigned int)dev, (double)inode, file_id ); + dbgtext( "Error was %s\n", strerror(errno) ); + } + return False; + } + + /* + * If we just sent a message to a level II oplock share entry then + * we are done and may return. + */ + + if (LEVEL_II_OPLOCK_TYPE(share_entry->op_type)) { + DEBUG(3,("request_oplock_break: sent break message to level II entry.\n")); + return True; + } + + /* + * Now we must await the oplock broken message coming back + * from the target smbd process. Timeout if it fails to + * return in (OPLOCK_BREAK_TIMEOUT + OPLOCK_BREAK_TIMEOUT_FUDGEFACTOR) seconds. + * While we get messages that aren't ours, loop. + */ + + start_time = time(NULL); + time_left = OPLOCK_BREAK_TIMEOUT+OPLOCK_BREAK_TIMEOUT_FUDGEFACTOR; + + while(time_left >= 0) { + char op_break_reply[OPBRK_CMD_HEADER_LEN+OPLOCK_BREAK_MSG_LEN]; + uint16 reply_from_port; + char *reply_msg_start; + fd_set fds; + + FD_ZERO(&fds); + FD_SET(oplock_sock,&fds); + + if (koplocks && koplocks->notification_fd != -1) { + FD_SET(koplocks->notification_fd, &fds); + } + + if(receive_local_message(&fds, op_break_reply, sizeof(op_break_reply), + time_left ? time_left * 1000 : 1) == False) { + if(smb_read_error == READ_TIMEOUT) { + if( DEBUGLVL( 0 ) ) { + dbgtext( "request_oplock_break: no response received to oplock " ); + dbgtext( "break request to pid %d ", (int)share_entry->pid ); + dbgtext( "on port %d ", share_entry->op_port ); + dbgtext( "for dev = %x, inode = %.0f, file_id = %lu\n", + (unsigned int)dev, (double)inode, file_id ); + } + + /* + * This is a hack to make handling of failing clients more robust. + * If a oplock break response message is not received in the timeout + * period we may assume that the smbd servicing that client holding + * the oplock has died and the client changes were lost anyway, so + * we should continue to try and open the file. + */ + break; + } else { + if( DEBUGLVL( 0 ) ) { + dbgtext( "request_oplock_break: error in response received " ); + dbgtext( "to oplock break request to pid %d ", (int)share_entry->pid ); + dbgtext( "on port %d ", share_entry->op_port ); + dbgtext( "for dev = %x, inode = %.0f, file_id = %lu\n", + (unsigned int)dev, (double)inode, file_id ); + dbgtext( "Error was (%s).\n", strerror(errno) ); + } + } + return False; + } + + reply_from_port = SVAL(op_break_reply,OPBRK_CMD_PORT_OFFSET); + reply_msg_start = &op_break_reply[OPBRK_CMD_HEADER_LEN]; + + /* + * Test to see if this is the reply we are awaiting. + */ + if((SVAL(reply_msg_start,OPBRK_MESSAGE_CMD_OFFSET) & CMD_REPLY) && + ((SVAL(reply_msg_start,OPBRK_MESSAGE_CMD_OFFSET) & ~CMD_REPLY) == OPLOCK_BREAK_CMD) && + (reply_from_port == share_entry->op_port) && + (memcmp(&reply_msg_start[OPLOCK_BREAK_PID_OFFSET], &op_break_msg[OPLOCK_BREAK_PID_OFFSET], + OPLOCK_BREAK_MSG_LEN - OPLOCK_BREAK_PID_OFFSET) == 0)) { + + /* + * This is the reply we've been waiting for. + */ + break; + } else { + /* + * This is another message - a break request. + * Note that both kernel oplock break requests + * and UDP inter-smbd oplock break requests will + * be processed here. + * + * Process it to prevent potential deadlock. + * Note that the code in switch_message() prevents + * us from recursing into here as any SMB requests + * we might process that would cause another oplock + * break request to be made will be queued. + * JRA. + */ + + process_local_message(op_break_reply, sizeof(op_break_reply)); + } + + time_left -= (time(NULL) - start_time); + } + + DEBUG(3,("request_oplock_break: broke oplock.\n")); + + return True; +} + +/**************************************************************************** + Attempt to break an oplock on a file (if oplocked). + Returns True if the file was closed as a result of + the oplock break, False otherwise. + Used as a last ditch attempt to free a space in the + file table when we have run out. +****************************************************************************/ + +BOOL attempt_close_oplocked_file(files_struct *fsp) +{ + DEBUG(5,("attempt_close_oplocked_file: checking file %s.\n", fsp->fsp_name)); + + if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && !fsp->sent_oplock_break && (fsp->fd != -1)) { + /* Try and break the oplock. */ + if (oplock_break(fsp->dev, fsp->inode, fsp->file_id, True)) { + if(file_find_fsp(fsp) == NULL) /* Did the oplock break close the file ? */ + return True; + } + } + + return False; +} + +/**************************************************************************** + This function is called on any file modification or lock request. If a file + is level 2 oplocked then it must tell all other level 2 holders to break to none. +****************************************************************************/ + +void release_level_2_oplocks_on_change(files_struct *fsp) +{ + share_mode_entry *share_list = NULL; + pid_t pid = sys_getpid(); + int token = -1; + int num_share_modes = 0; + int i; + + /* + * If this file is level II oplocked then we need + * to grab the shared memory lock and inform all + * other files with a level II lock that they need + * to flush their read caches. We keep the lock over + * the shared memory area whilst doing this. + */ + + if (!LEVEL_II_OPLOCK_TYPE(fsp->oplock_type)) + return; + + if (lock_share_entry_fsp(fsp) == False) { + DEBUG(0,("release_level_2_oplocks_on_change: failed to lock share mode entry for file %s.\n", fsp->fsp_name )); + } + + num_share_modes = get_share_modes(fsp->conn, fsp->dev, fsp->inode, &share_list); + + DEBUG(10,("release_level_2_oplocks_on_change: num_share_modes = %d\n", + num_share_modes )); + + for(i = 0; i < num_share_modes; i++) { + share_mode_entry *share_entry = &share_list[i]; + + /* + * As there could have been multiple writes waiting at the lock_share_entry + * gate we may not be the first to enter. Hence the state of the op_types + * in the share mode entries may be partly NO_OPLOCK and partly LEVEL_II + * oplock. It will do no harm to re-send break messages to those smbd's + * that are still waiting their turn to remove their LEVEL_II state, and + * also no harm to ignore existing NO_OPLOCK states. JRA. + */ + + DEBUG(10,("release_level_2_oplocks_on_change: share_entry[%i]->op_type == %d\n", + i, share_entry->op_type )); + + if (share_entry->op_type == NO_OPLOCK) + continue; + + /* Paranoia .... */ + if (EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type)) { + DEBUG(0,("release_level_2_oplocks_on_change: PANIC. share mode entry %d is an exlusive oplock !\n", i )); + unlock_share_entry(fsp->conn, fsp->dev, fsp->inode); + abort(); + } + + /* + * Check if this is a file we have open (including the + * file we've been called to do write_file on. If so + * then break it directly without releasing the lock. + */ + + if (pid == share_entry->pid) { + files_struct *new_fsp = file_find_dif(share_entry->dev, share_entry->inode, share_entry->share_file_id); + + /* Paranoia check... */ + if(new_fsp == NULL) { + DEBUG(0,("release_level_2_oplocks_on_change: PANIC. share mode entry %d is not a local file !\n", i )); + unlock_share_entry(fsp->conn, fsp->dev, fsp->inode); + abort(); + } + + DEBUG(10,("release_level_2_oplocks_on_change: breaking our own oplock.\n")); + + oplock_break_level2(new_fsp, True, token); + + } else { + + /* + * This is a remote file and so we send an asynchronous + * message. + */ + + DEBUG(10,("release_level_2_oplocks_on_change: breaking remote oplock.\n")); + request_oplock_break(share_entry); + } + } + + SAFE_FREE(share_list); + unlock_share_entry_fsp(fsp); + + /* Paranoia check... */ + if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type)) { + DEBUG(0,("release_level_2_oplocks_on_change: PANIC. File %s still has a level II oplock.\n", fsp->fsp_name)); + smb_panic("release_level_2_oplocks_on_change"); + } +} + +/**************************************************************************** +setup oplocks for this process +****************************************************************************/ + +BOOL init_oplocks(void) +{ + struct sockaddr_in sock_name; + socklen_t len = sizeof(sock_name); + + DEBUG(3,("open_oplock_ipc: opening loopback UDP socket.\n")); + + /* Open a lookback UDP socket on a random port. */ + oplock_sock = open_socket_in(SOCK_DGRAM, 0, 0, htonl(INADDR_LOOPBACK),False); + if (oplock_sock == -1) { + DEBUG(0,("open_oplock_ipc: Failed to get local UDP socket for \ +address %lx. Error was %s\n", (long)htonl(INADDR_LOOPBACK), strerror(errno))); + global_oplock_port = 0; + return(False); + } + + /* Find out the transient UDP port we have been allocated. */ + if(getsockname(oplock_sock, (struct sockaddr *)&sock_name, &len)<0) { + DEBUG(0,("open_oplock_ipc: Failed to get local UDP port. Error was %s\n", + strerror(errno))); + close(oplock_sock); + oplock_sock = -1; + global_oplock_port = 0; + return False; + } + global_oplock_port = ntohs(sock_name.sin_port); + + if (lp_kernel_oplocks()) { +#if HAVE_KERNEL_OPLOCKS_IRIX + koplocks = irix_init_kernel_oplocks(); +#elif HAVE_KERNEL_OPLOCKS_LINUX + koplocks = linux_init_kernel_oplocks(); +#endif + } + + DEBUG(3,("open_oplock ipc: pid = %d, global_oplock_port = %u\n", + (int)sys_getpid(), global_oplock_port)); + + return True; +} diff --git a/source3/smbd/oplock_irix.c b/source3/smbd/oplock_irix.c new file mode 100644 index 0000000000..14f6de27c4 --- /dev/null +++ b/source3/smbd/oplock_irix.c @@ -0,0 +1,285 @@ +/* + Unix SMB/CIFS implementation. + IRIX kernel oplock processing + Copyright (C) Andrew Tridgell 1992-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + 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" + +#if HAVE_KERNEL_OPLOCKS_IRIX + +static int oplock_pipe_write = -1; +static int oplock_pipe_read = -1; + +/**************************************************************************** + Test to see if IRIX kernel oplocks work. +****************************************************************************/ + +static BOOL irix_oplocks_available(void) +{ + int fd; + int pfd[2]; + pstring tmpname; + + oplock_set_capability(True, False); + + slprintf(tmpname,sizeof(tmpname)-1, "%s/koplock.%d", lp_lockdir(), (int)sys_getpid()); + + if(pipe(pfd) != 0) { + DEBUG(0,("check_kernel_oplocks: Unable to create pipe. Error was %s\n", + strerror(errno) )); + return False; + } + + if((fd = sys_open(tmpname, O_RDWR|O_CREAT|O_EXCL|O_TRUNC, 0600)) < 0) { + DEBUG(0,("check_kernel_oplocks: Unable to open temp test file %s. Error was %s\n", + tmpname, strerror(errno) )); + unlink( tmpname ); + close(pfd[0]); + close(pfd[1]); + return False; + } + + unlink(tmpname); + + if(fcntl(fd, F_OPLKREG, pfd[1]) == -1) { + DEBUG(0,("check_kernel_oplocks: Kernel oplocks are not available on this machine. \ +Disabling kernel oplock support.\n" )); + close(pfd[0]); + close(pfd[1]); + close(fd); + return False; + } + + if(fcntl(fd, F_OPLKACK, OP_REVOKE) < 0 ) { + DEBUG(0,("check_kernel_oplocks: Error when removing kernel oplock. Error was %s. \ +Disabling kernel oplock support.\n", strerror(errno) )); + close(pfd[0]); + close(pfd[1]); + close(fd); + return False; + } + + close(pfd[0]); + close(pfd[1]); + close(fd); + + return True; +} + +/**************************************************************************** + * Deal with the IRIX kernel <--> smbd + * oplock break protocol. +****************************************************************************/ + +static BOOL irix_oplock_receive_message(fd_set *fds, char *buffer, int buffer_len) +{ + extern int smb_read_error; + oplock_stat_t os; + char dummy; + files_struct *fsp; + + /* + * Read one byte of zero to clear the + * kernel break notify message. + */ + + if(read(oplock_pipe_read, &dummy, 1) != 1) { + DEBUG(0,("receive_local_message: read of kernel notification failed. \ +Error was %s.\n", strerror(errno) )); + smb_read_error = READ_ERROR; + return False; + } + + /* + * Do a query to get the + * device and inode of the file that has the break + * request outstanding. + */ + + if(fcntl(oplock_pipe_read, F_OPLKSTAT, &os) < 0) { + DEBUG(0,("receive_local_message: fcntl of kernel notification failed. \ +Error was %s.\n", strerror(errno) )); + if(errno == EAGAIN) { + /* + * Duplicate kernel break message - ignore. + */ + memset(buffer, '\0', KERNEL_OPLOCK_BREAK_MSG_LEN); + return True; + } + smb_read_error = READ_ERROR; + return False; + } + + /* + * We only have device and inode info here - we have to guess that this + * is the first fsp open with this dev,ino pair. + */ + + if ((fsp = file_find_di_first((SMB_DEV_T)os.os_dev, (SMB_INO_T)os.os_ino)) == NULL) { + DEBUG(0,("receive_local_message: unable to find open file with dev = %x, inode = %.0f\n", + (unsigned int)os.os_dev, (double)os.os_ino )); + return False; + } + + DEBUG(5,("receive_local_message: kernel oplock break request received for \ +dev = %x, inode = %.0f\n, file_id = %ul", (unsigned int)fsp->dev, (double)fsp->inode, fsp->file_id )); + + /* + * Create a kernel oplock break message. + */ + + /* Setup the message header */ + SIVAL(buffer,OPBRK_CMD_LEN_OFFSET,KERNEL_OPLOCK_BREAK_MSG_LEN); + SSVAL(buffer,OPBRK_CMD_PORT_OFFSET,0); + + buffer += OPBRK_CMD_HEADER_LEN; + + SSVAL(buffer,OPBRK_MESSAGE_CMD_OFFSET,KERNEL_OPLOCK_BREAK_CMD); + + memcpy(buffer + KERNEL_OPLOCK_BREAK_DEV_OFFSET, (char *)&fsp->dev, sizeof(fsp->dev)); + memcpy(buffer + KERNEL_OPLOCK_BREAK_INODE_OFFSET, (char *)&fsp->inode, sizeof(fsp->inode)); + memcpy(buffer + KERNEL_OPLOCK_BREAK_FILEID_OFFSET, (char *)&fsp->file_id, sizeof(fsp->file_id)); + + return True; +} + +/**************************************************************************** + Attempt to set an kernel oplock on a file. +****************************************************************************/ + +static BOOL irix_set_kernel_oplock(files_struct *fsp, int oplock_type) +{ + if (fcntl(fsp->fd, F_OPLKREG, oplock_pipe_write) == -1) { + if(errno != EAGAIN) { + DEBUG(0,("set_file_oplock: Unable to get kernel oplock on file %s, dev = %x, \ +inode = %.0f, file_id = %ul. Error was %s\n", + fsp->fsp_name, (unsigned int)fsp->dev, (double)fsp->inode, fsp->file_id, + strerror(errno) )); + } else { + DEBUG(5,("set_file_oplock: Refused oplock on file %s, fd = %d, dev = %x, \ +inode = %.0f, file_id = %ul. Another process had the file open.\n", + fsp->fsp_name, fsp->fd, (unsigned int)fsp->dev, (double)fsp->inode, fsp->file_id )); + } + return False; + } + + DEBUG(10,("set_file_oplock: got kernel oplock on file %s, dev = %x, inode = %.0f, file_id = %ul\n", + fsp->fsp_name, (unsigned int)fsp->dev, (double)fsp->inode, fsp->file_id)); + + return True; +} + +/**************************************************************************** + Release a kernel oplock on a file. +****************************************************************************/ + +static void irix_release_kernel_oplock(files_struct *fsp) +{ + if (DEBUGLVL(10)) { + /* + * Check and print out the current kernel + * oplock state of this file. + */ + int state = fcntl(fsp->fd, F_OPLKACK, -1); + dbgtext("release_kernel_oplock: file %s, dev = %x, inode = %.0f file_id = %ul, has kernel \ +oplock state of %x.\n", fsp->fsp_name, (unsigned int)fsp->dev, + (double)fsp->inode, fsp->file_id, state ); + } + + /* + * Remove the kernel oplock on this file. + */ + if(fcntl(fsp->fd, F_OPLKACK, OP_REVOKE) < 0) { + if( DEBUGLVL( 0 )) { + dbgtext("release_kernel_oplock: Error when removing kernel oplock on file " ); + dbgtext("%s, dev = %x, inode = %.0f, file_id = %ul. Error was %s\n", + fsp->fsp_name, (unsigned int)fsp->dev, + (double)fsp->inode, fsp->file_id, strerror(errno) ); + } + } +} + +/**************************************************************************** + Parse a kernel oplock message. +****************************************************************************/ + +static BOOL irix_kernel_oplock_parse(char *msg_start, int msg_len, + SMB_INO_T *inode, SMB_DEV_T *dev, unsigned long *file_id) +{ + /* Ensure that the msg length is correct. */ + if(msg_len != KERNEL_OPLOCK_BREAK_MSG_LEN) { + DEBUG(0,("incorrect length for KERNEL_OPLOCK_BREAK_CMD (was %d, should be %d).\n", + msg_len, KERNEL_OPLOCK_BREAK_MSG_LEN)); + return False; + } + + memcpy((char *)inode, msg_start+KERNEL_OPLOCK_BREAK_INODE_OFFSET, sizeof(*inode)); + memcpy((char *)dev, msg_start+KERNEL_OPLOCK_BREAK_DEV_OFFSET, sizeof(*dev)); + memcpy((char *)file_id, msg_start+KERNEL_OPLOCK_BREAK_FILEID_OFFSET, sizeof(*file_id)); + + DEBUG(5,("kernel oplock break request for file dev = %x, inode = %.0f, file_id = %ul\n", + (unsigned int)*dev, (double)*inode, *file_id)); + + return True; +} + +/**************************************************************************** + Set *maxfd to include oplock read pipe. +****************************************************************************/ + +static BOOL irix_oplock_msg_waiting(fd_set *fds) +{ + if (oplock_pipe_read == -1) + return False; + + return FD_ISSET(oplock_pipe_read,fds); +} + +/**************************************************************************** + Setup kernel oplocks. +****************************************************************************/ + +struct kernel_oplocks *irix_init_kernel_oplocks(void) +{ + int pfd[2]; + static struct kernel_oplocks koplocks; + + if (!irix_oplocks_available()) + return NULL; + + if(pipe(pfd) != 0) { + DEBUG(0,("setup_kernel_oplock_pipe: Unable to create pipe. Error was %s\n", + strerror(errno) )); + return False; + } + + oplock_pipe_read = pfd[0]; + oplock_pipe_write = pfd[1]; + + koplocks.receive_message = irix_oplock_receive_message; + koplocks.set_oplock = irix_set_kernel_oplock; + koplocks.release_oplock = irix_release_kernel_oplock; + koplocks.parse_message = irix_kernel_oplock_parse; + koplocks.msg_waiting = irix_oplock_msg_waiting; + koplocks.notification_fd = oplock_pipe_read; + + return &koplocks; +} +#else + void oplock_irix_dummy(void) {} +#endif /* HAVE_KERNEL_OPLOCKS_IRIX */ diff --git a/source3/smbd/oplock_linux.c b/source3/smbd/oplock_linux.c new file mode 100644 index 0000000000..d946578380 --- /dev/null +++ b/source3/smbd/oplock_linux.c @@ -0,0 +1,300 @@ +/* + Unix SMB/CIFS implementation. + kernel oplock processing for Linux + Copyright (C) Andrew Tridgell 2000 + + 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" + +#if HAVE_KERNEL_OPLOCKS_LINUX + +static VOLATILE sig_atomic_t signals_received; +static VOLATILE sig_atomic_t signals_processed; +static VOLATILE sig_atomic_t fd_pending; /* the fd of the current pending signal */ + +#ifndef F_SETLEASE +#define F_SETLEASE 1024 +#endif + +#ifndef F_GETLEASE +#define F_GETLEASE 1025 +#endif + +#ifndef CAP_LEASE +#define CAP_LEASE 28 +#endif + +#ifndef RT_SIGNAL_LEASE +#define RT_SIGNAL_LEASE 33 +#endif + +#ifndef F_SETSIG +#define F_SETSIG 10 +#endif + +/**************************************************************************** + Handle a LEASE signal, incrementing the signals_received and blocking the signal. +****************************************************************************/ + +static void signal_handler(int sig, siginfo_t *info, void *unused) +{ + BlockSignals(True, sig); + fd_pending = (sig_atomic_t)info->si_fd; + signals_received++; + sys_select_signal(); +} + +/**************************************************************************** + Try to gain a linux capability. +****************************************************************************/ + +static void set_capability(unsigned capability) +{ +#ifndef _LINUX_CAPABILITY_VERSION +#define _LINUX_CAPABILITY_VERSION 0x19980330 +#endif + /* these can be removed when they are in glibc headers */ + struct { + uint32 version; + int pid; + } header; + struct { + uint32 effective; + uint32 permitted; + uint32 inheritable; + } data; + + header.version = _LINUX_CAPABILITY_VERSION; + header.pid = 0; + + if (capget(&header, &data) == -1) { + DEBUG(3,("Unable to get kernel capabilities (%s)\n", strerror(errno))); + return; + } + + data.effective |= (1<<capability); + + if (capset(&header, &data) == -1) { + DEBUG(3,("Unable to set %d capability (%s)\n", + capability, strerror(errno))); + } +} + +/**************************************************************************** + Call SETLEASE. If we get EACCES then we try setting up the right capability and + try again +****************************************************************************/ + +static int linux_setlease(int fd, int leasetype) +{ + int ret; + + if (fcntl(fd, F_SETSIG, RT_SIGNAL_LEASE) == -1) { + DEBUG(3,("Failed to set signal handler for kernel lease\n")); + return -1; + } + + ret = fcntl(fd, F_SETLEASE, leasetype); + if (ret == -1 && errno == EACCES) { + set_capability(CAP_LEASE); + ret = fcntl(fd, F_SETLEASE, leasetype); + } + + return ret; +} + +/**************************************************************************** + * Deal with the Linux kernel <--> smbd + * oplock break protocol. +****************************************************************************/ + +static BOOL linux_oplock_receive_message(fd_set *fds, char *buffer, int buffer_len) +{ + BOOL ret = True; + struct files_struct *fsp; + + if (signals_received == signals_processed) + return False; + + if ((fsp = file_find_fd(fd_pending)) == NULL) { + DEBUG(0,("Invalid file descriptor %d in kernel oplock break!\n", (int)fd_pending)); + ret = False; + goto out; + } + + DEBUG(3,("receive_local_message: kernel oplock break request received for \ +dev = %x, inode = %.0f\n", (unsigned int)fsp->dev, (double)fsp->inode )); + + /* + * Create a kernel oplock break message. + */ + + /* Setup the message header */ + SIVAL(buffer,OPBRK_CMD_LEN_OFFSET,KERNEL_OPLOCK_BREAK_MSG_LEN); + SSVAL(buffer,OPBRK_CMD_PORT_OFFSET,0); + + buffer += OPBRK_CMD_HEADER_LEN; + + SSVAL(buffer,OPBRK_MESSAGE_CMD_OFFSET,KERNEL_OPLOCK_BREAK_CMD); + + memcpy(buffer + KERNEL_OPLOCK_BREAK_DEV_OFFSET, (char *)&fsp->dev, sizeof(fsp->dev)); + memcpy(buffer + KERNEL_OPLOCK_BREAK_INODE_OFFSET, (char *)&fsp->inode, sizeof(fsp->inode)); + memcpy(buffer + KERNEL_OPLOCK_BREAK_FILEID_OFFSET, (char *)&fsp->file_id, sizeof(fsp->file_id)); + + out: + /* now we can receive more signals */ + fd_pending = (sig_atomic_t)-1; + signals_processed++; + BlockSignals(False, RT_SIGNAL_LEASE); + + return ret; +} + +/**************************************************************************** + Attempt to set an kernel oplock on a file. +****************************************************************************/ + +static BOOL linux_set_kernel_oplock(files_struct *fsp, int oplock_type) +{ + if (linux_setlease(fsp->fd, F_WRLCK) == -1) { + DEBUG(3,("set_file_oplock: Refused oplock on file %s, fd = %d, dev = %x, \ +inode = %.0f. (%s)\n", + fsp->fsp_name, fsp->fd, + (unsigned int)fsp->dev, (double)fsp->inode, strerror(errno))); + return False; + } + + DEBUG(3,("set_file_oplock: got kernel oplock on file %s, dev = %x, inode = %.0f, file_id = %lu\n", + fsp->fsp_name, (unsigned int)fsp->dev, (double)fsp->inode, fsp->file_id)); + + return True; +} + +/**************************************************************************** + Release a kernel oplock on a file. +****************************************************************************/ + +static void linux_release_kernel_oplock(files_struct *fsp) +{ + if (DEBUGLVL(10)) { + /* + * Check and print out the current kernel + * oplock state of this file. + */ + int state = fcntl(fsp->fd, F_GETLEASE, 0); + dbgtext("release_kernel_oplock: file %s, dev = %x, inode = %.0f file_id = %lu has kernel \ +oplock state of %x.\n", fsp->fsp_name, (unsigned int)fsp->dev, + (double)fsp->inode, fsp->file_id, state ); + } + + /* + * Remove the kernel oplock on this file. + */ + if (linux_setlease(fsp->fd, F_UNLCK) == -1) { + if (DEBUGLVL(0)) { + dbgtext("release_kernel_oplock: Error when removing kernel oplock on file " ); + dbgtext("%s, dev = %x, inode = %.0f, file_id = %lu. Error was %s\n", + fsp->fsp_name, (unsigned int)fsp->dev, + (double)fsp->inode, fsp->file_id, strerror(errno) ); + } + } +} + +/**************************************************************************** + Parse a kernel oplock message. +****************************************************************************/ + +static BOOL linux_kernel_oplock_parse(char *msg_start, int msg_len, SMB_INO_T *inode, + SMB_DEV_T *dev, unsigned long *file_id) +{ + /* Ensure that the msg length is correct. */ + if (msg_len != KERNEL_OPLOCK_BREAK_MSG_LEN) { + DEBUG(0,("incorrect length for KERNEL_OPLOCK_BREAK_CMD (was %d, should be %d).\n", + msg_len, KERNEL_OPLOCK_BREAK_MSG_LEN)); + return False; + } + + memcpy((char *)inode, msg_start+KERNEL_OPLOCK_BREAK_INODE_OFFSET, sizeof(*inode)); + memcpy((char *)dev, msg_start+KERNEL_OPLOCK_BREAK_DEV_OFFSET, sizeof(*dev)); + memcpy((char *)file_id, msg_start+KERNEL_OPLOCK_BREAK_FILEID_OFFSET, sizeof(*file_id)); + + DEBUG(3,("kernel oplock break request for file dev = %x, inode = %.0f, file_id = %lu\n", + (unsigned int)*dev, (double)*inode, *file_id)); + + return True; +} + +/**************************************************************************** + See if a oplock message is waiting. +****************************************************************************/ + +static BOOL linux_oplock_msg_waiting(fd_set *fds) +{ + return signals_processed != signals_received; +} + +/**************************************************************************** + See if the kernel supports oplocks. +****************************************************************************/ + +static BOOL linux_oplocks_available(void) +{ + int fd, ret; + fd = open("/dev/null", O_RDONLY); + if (fd == -1) + return False; /* uggh! */ + ret = fcntl(fd, F_GETLEASE, 0); + close(fd); + return ret == F_UNLCK; +} + +/**************************************************************************** + Setup kernel oplocks. +****************************************************************************/ + +struct kernel_oplocks *linux_init_kernel_oplocks(void) +{ + static struct kernel_oplocks koplocks; + struct sigaction act; + + if (!linux_oplocks_available()) { + DEBUG(3,("Linux kernel oplocks not available\n")); + return NULL; + } + + act.sa_handler = NULL; + act.sa_sigaction = signal_handler; + act.sa_flags = SA_SIGINFO; + if (sigaction(RT_SIGNAL_LEASE, &act, NULL) != 0) { + DEBUG(0,("Failed to setup RT_SIGNAL_LEASE handler\n")); + return NULL; + } + + koplocks.receive_message = linux_oplock_receive_message; + koplocks.set_oplock = linux_set_kernel_oplock; + koplocks.release_oplock = linux_release_kernel_oplock; + koplocks.parse_message = linux_kernel_oplock_parse; + koplocks.msg_waiting = linux_oplock_msg_waiting; + koplocks.notification_fd = -1; + + DEBUG(3,("Linux kernel oplocks enabled\n")); + + return &koplocks; +} +#else + void oplock_linux_dummy(void) {} +#endif /* HAVE_KERNEL_OPLOCKS_LINUX */ diff --git a/source3/smbd/password.c b/source3/smbd/password.c index 87c1fef94c..629157f22d 100644 --- a/source3/smbd/password.c +++ b/source3/smbd/password.c @@ -1,8 +1,7 @@ /* - Unix SMB/Netbios implementation. - Version 1.9. + Unix SMB/CIFS implementation. Password and authentication handling - Copyright (C) Andrew Tridgell 1992-1995 + Copyright (C) Andrew Tridgell 1992-1998 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,1397 +19,595 @@ */ #include "includes.h" -#include "loadparm.h" - -extern int DEBUGLEVEL; -extern int Protocol; /* users from session setup */ static pstring session_users=""; -/* these are kept here to keep the string_combinations function simple */ -static char this_user[100]=""; -static char this_salt[100]=""; -static char this_crypted[100]=""; - -#ifdef SMB_PASSWD -/* Data to do lanman1/2 password challenge. */ -static unsigned char saved_challenge[8]; -static BOOL challenge_sent=False; - -/******************************************************************* -Get the next challenge value - no repeats. -********************************************************************/ -void generate_next_challenge(char *challenge) -{ - extern void E1(char *,char *,char *); - static int counter = 0; - struct timeval tval; - int v1,v2; - GetTimeOfDay(&tval); - v1 = (counter++) + getpid() + tval.tv_sec; - v2 = (counter++) * getpid() + tval.tv_usec; - SIVAL(challenge,0,v1); - SIVAL(challenge,4,v2); - E1(challenge,"SAMBA",saved_challenge); - memcpy(challenge,saved_challenge,8); - challenge_sent = True; -} - -/******************************************************************* -set the last challenge sent, usually from a password server -********************************************************************/ -BOOL set_challenge(char *challenge) -{ - memcpy(saved_challenge,challenge,8); - challenge_sent = True; - return(True); -} - -/******************************************************************* -get the last challenge sent -********************************************************************/ -BOOL last_challenge(char *challenge) -{ - if (!challenge_sent) return(False); - memcpy(challenge,saved_challenge,8); - return(True); -} -#endif - /* this holds info on user ids that are already validated for this VC */ -static user_struct *validated_users = NULL; -static int num_validated_users = 0; - -/**************************************************************************** -check if a uid has been validated, and return an index if it has. -1 if not -****************************************************************************/ -int valid_uid(int uid) -{ - int i; - if (uid == -1) return(-1); - - for (i=0;i<num_validated_users;i++) - if (validated_users[i].uid == uid) - { - DEBUG(3,("valid uid %d mapped to vuid %d (user=%s)\n", - uid,i,validated_users[i].name)); - return(i); - } - return(-1); -} +static user_struct *validated_users; +static int next_vuid = VUID_OFFSET; +static int num_validated_vuids; /**************************************************************************** check if a uid has been validated, and return an pointer to the user_struct -if it has. NULL if not -****************************************************************************/ -user_struct *get_valid_user_struct(int uid) -{ - int vuid = valid_uid(uid); - if(vuid == -1 || validated_users[vuid].guest) - return NULL; - return &validated_users[vuid]; -} - -/**************************************************************************** -invalidate a uid +if it has. NULL if not. vuid is biased by an offset. This allows us to +tell random client vuid's (normally zero) from valid vuids. ****************************************************************************/ -void invalidate_uid(int uid) +user_struct *get_valid_user_struct(uint16 vuid) { - int i; - for (i=0;i<num_validated_users;i++) - if (validated_users[i].uid == uid) - { - user_struct *vuser = &validated_users[i]; - vuser->uid = -1; - vuser->gid = -1; - vuser->user_ngroups = 0; - if(vuser->user_groups && - (vuser->user_groups != (gid_t *)vuser->user_igroups)) - free(vuser->user_groups); - vuser->user_groups = NULL; - if(vuser->user_igroups) - free(vuser->user_igroups); - vuser->user_igroups = NULL; - } -} - + user_struct *usp; + int count=0; + + if (vuid == UID_FIELD_INVALID) + return NULL; + + for (usp=validated_users;usp;usp=usp->next,count++) { + if (vuid == usp->vuid) { + if (count > 10) { + DLIST_PROMOTE(validated_users, usp); + } + return usp; + } + } -/**************************************************************************** -return a validated username -****************************************************************************/ -char *validated_username(int vuid) -{ - return(validated_users[vuid].name); + return NULL; } /**************************************************************************** -register a uid/name pair as being valid and that a valid password -has been given. +invalidate a uid ****************************************************************************/ -void register_uid(int uid,int gid, char *name,BOOL guest) +void invalidate_vuid(uint16 vuid) { - user_struct *vuser; - - if (valid_uid(uid) >= 0) - return; - validated_users = (user_struct *)Realloc(validated_users, - sizeof(user_struct)* - (num_validated_users+1)); + user_struct *vuser = get_valid_user_struct(vuid); - if (!validated_users) - { - DEBUG(0,("Failed to realloc users struct!\n")); - return; - } + if (vuser == NULL) + return; - vuser = &validated_users[num_validated_users]; - vuser->uid = uid; - vuser->gid = gid; - vuser->guest = guest; - strcpy(vuser->name,name); + SAFE_FREE(vuser->homedir); - vuser->user_ngroups = 0; - vuser->user_groups = NULL; - vuser->user_igroups = NULL; + session_yield(vuser); - /* 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); + DLIST_REMOVE(validated_users, vuser); - DEBUG(3,("uid %d registered to name %s\n",uid,name)); - - num_validated_users++; + SAFE_FREE(vuser->groups); + delete_nt_token(&vuser->nt_user_token); + SAFE_FREE(vuser); + num_validated_vuids--; } - /**************************************************************************** -add a name to the session users list +invalidate all vuid entries for this process ****************************************************************************/ -void add_session_user(char *user) +void invalidate_all_vuids(void) { - fstring suser; - StrnCpy(suser,user,sizeof(suser)-1); - - if (!Get_Pwnam(suser,True)) return; + user_struct *usp, *next=NULL; - 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); + for (usp=validated_users;usp;usp=next) { + next = usp->next; + + invalidate_vuid(usp->vuid); } - } } - -#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 +return a validated username ****************************************************************************/ -static char *osf1_bigcrypt(char *password,char *salt1) +char *validated_username(uint16 vuid) { - static char result[AUTH_MAX_PASSWD_LENGTH] = ""; - char *p1; - char *p2=password; - char salt[3]; - int i; - int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS; - if (strlen(password)%AUTH_CLEARTEXT_SEG_CHARS) - parts++; - - StrnCpy(salt,salt1,2); - StrnCpy(result,salt1,2); - - for (i=0; i<parts;i++) - { - p1 = crypt(p2,salt); - strcat(result,p1+2); - StrnCpy(salt,&result[2+i*AUTH_CIPHERTEXT_SEG_CHARS],2); - p2 += AUTH_CLEARTEXT_SEG_CHARS; - } - - return(result); + user_struct *vuser = get_valid_user_struct(vuid); + if (vuser == NULL) + return 0; + return(vuser->user.unix_name); } -#endif - /**************************************************************************** -update the enhanced security database. Only relevant for OSF1 at the moment. +return a validated domain ****************************************************************************/ -static void update_protected_database( char *user, BOOL result) -{ -#ifdef OSF1_ENH_SEC - struct pr_passwd *mypasswd; - time_t starttime; - long tz; - - mypasswd = getprpwnam (user); - starttime = time (NULL); - tz = mktime ( localtime ( &starttime ) ); - - if (result) - { - mypasswd->ufld.fd_slogin = tz; - mypasswd->ufld.fd_nlogins = 0; - - putprpwnam(user,mypasswd); - - DEBUG(3,("Update protected database for Account %s after succesful connection\n",user)); - } - else - { - mypasswd->ufld.fd_ulogin = tz; - mypasswd->ufld.fd_nlogins = mypasswd->ufld.fd_nlogins + 1; - if ( mypasswd->ufld.fd_max_tries != 0 && mypasswd->ufld.fd_nlogins > mypasswd->ufld.fd_max_tries ) - { - mypasswd->uflg.fg_lock = 0; - DEBUG(3,("Account is disabled -- see Account Administrator.\n")); - } - putprpwnam ( user , mypasswd ); - DEBUG(3,("Update protected database for Account %s after refusing connection\n",user)); - } -#else - DEBUG(6,("Updated database with %s %s\n",user,BOOLSTR(result))); -#endif -} - - -#ifdef AFS_AUTH -/******************************************************************* -check on AFS authentication -********************************************************************/ -static BOOL afs_auth(char *this_user,char *password) -{ - long password_expires = 0; - char *reason; - - /* For versions of AFS prior to 3.3, this routine has few arguments, */ - /* but since I can't find the old documentation... :-) */ - setpag(); - if (ka_UserAuthenticateGeneral(KA_USERAUTH_VERSION+KA_USERAUTH_DOSETPAG, - this_user, - (char *) 0, /* instance */ - (char *) 0, /* cell */ - password, - 0, /* lifetime, default */ - &password_expires, /*days 'til it expires */ - 0, /* spare 2 */ - &reason) == 0) - return(True); - return(False); -} -#endif - - -#ifdef DFS_AUTH - -sec_login_handle_t my_dce_sec_context; -int dcelogin_atmost_once = 0; - -/******************************************************************* -check on a DCE/DFS authentication -********************************************************************/ -static BOOL dfs_auth(char *this_user,char *password) +char *validated_domain(uint16 vuid) { - 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); + user_struct *vuser = get_valid_user_struct(vuid); + if (vuser == NULL) + return 0; + return(vuser->user.domain); } -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 + Create the SID list for this user. ****************************************************************************/ -static int linux_bigcrypt(char *password,char *salt1, char *crypted) + +NT_USER_TOKEN *create_nt_token(uid_t uid, gid_t gid, int ngroups, gid_t *groups, BOOL is_guest, NT_USER_TOKEN *sup_tok) { -#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 + extern DOM_SID global_sid_World; + extern DOM_SID global_sid_Network; + extern DOM_SID global_sid_Builtin_Guests; + extern DOM_SID global_sid_Authenticated_Users; + NT_USER_TOKEN *token; + DOM_SID *psids; + int i, psid_ndx = 0; + size_t num_sids = 0; + fstring sid_str; + + if ((token = (NT_USER_TOKEN *)malloc( sizeof(NT_USER_TOKEN) ) ) == NULL) + return NULL; + + ZERO_STRUCTP(token); + + /* We always have uid/gid plus World and Network and Authenticated Users or Guest SIDs. */ + num_sids = 5 + ngroups; + + if (sup_tok && sup_tok->num_sids) + num_sids += sup_tok->num_sids; + + if ((token->user_sids = (DOM_SID *)malloc( num_sids*sizeof(DOM_SID))) == NULL) { + SAFE_FREE(token); + return NULL; + } + psids = token->user_sids; -/**************************************************************************** -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; + /* + * Note - user SID *MUST* be first in token ! + * se_access_check depends on this. + */ -#ifdef PASSWORD_LENGTH - len = MIN(len,PASSWORD_LENGTH); -#endif + uid_to_sid( &psids[PRIMARY_USER_SID_INDEX], uid); + psid_ndx++; - 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); -} + /* + * Primary group SID is second in token. Convention. + */ -/**************************************************************************** -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); -} + gid_to_sid( &psids[PRIMARY_GROUP_SID_INDEX], gid); + psid_ndx++; + /* Now add the group SIDs. */ + for (i = 0; i < ngroups; i++) { + if (groups[i] != gid) { + gid_to_sid( &psids[psid_ndx++], groups[i]); + } + } -/**************************************************************************** -core of password checking routine -****************************************************************************/ -BOOL password_check(char *password) -{ -#ifdef AFS_AUTH - if (afs_auth(this_user,password)) return(True); -#endif + if (sup_tok) { + /* Now add the additional SIDs from the supplimentary token. */ + for (i = 0; i < sup_tok->num_sids; i++) + sid_copy( &psids[psid_ndx++], &sup_tok->user_sids[i] ); + } -#ifdef DFS_AUTH - if (dfs_auth(this_user,password)) return(True); -#endif + /* + * Finally add the "standard" SIDs. + * The only difference between guest and "anonymous" (which we + * don't really support) is the addition of Authenticated_Users. + */ -#ifdef PWDAUTH - if (pwdauth(this_user,password) == 0) - return(True); -#endif + sid_copy( &psids[psid_ndx++], &global_sid_World); + sid_copy( &psids[psid_ndx++], &global_sid_Network); -#ifdef OSF1_ENH_SEC - return(strcmp(osf1_bigcrypt(password,this_salt),this_crypted) == 0); -#endif + if (is_guest) + sid_copy( &psids[psid_ndx++], &global_sid_Builtin_Guests); + else + sid_copy( &psids[psid_ndx++], &global_sid_Authenticated_Users); -#ifdef ULTRIX_AUTH - return (strcmp((char *)crypt16(password, this_salt ),this_crypted) == 0); -#endif + token->num_sids = psid_ndx; -#ifdef LINUX_BIGCRYPT - return(linux_bigcrypt(password,this_salt,this_crypted)); -#endif + /* Dump list of sids in token */ -#ifdef NO_CRYPT - DEBUG(1,("Warning - no crypt available\n")); - return(False); -#else - return(strcmp((char *)crypt(password,this_salt),this_crypted) == 0); -#endif -} + for (i = 0; i < token->num_sids; i++) { + DEBUG(5, ("user token sid %s\n", + sid_to_string(sid_str, &token->user_sids[i]))); + } -#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); + return token; } -#endif /**************************************************************************** -check if a username/password is OK +register a uid/name pair as being valid and that a valid password +has been given. vuid is biased by an offset. This allows us to +tell random client vuid's (normally zero) from valid vuids. ****************************************************************************/ -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); +int register_vuid(auth_serversupplied_info *server_info, char *smb_name) +{ + user_struct *vuser = NULL; + uid_t uid; + gid_t gid; - if (pwd && !user) - { - pass = (struct passwd *) pwd; - user = pass->pw_name; - } - else - pass = Get_Pwnam(user,True); + /* Ensure no vuid gets registered in share level security. */ + if(lp_security() == SEC_SHARE) + return UID_FIELD_INVALID; -#ifdef SMB_PASSWD + /* Limit allowed vuids to 16bits - VUID_OFFSET. */ + if (num_validated_vuids >= 0xFFFF-VUID_OFFSET) + return UID_FIELD_INVALID; - DEBUG(4,("SMB Password - pwlen = %d, challenge_done = %d\n", pwlen, challenge_done)); + if((vuser = (user_struct *)malloc( sizeof(user_struct) )) == NULL) { + DEBUG(0,("Failed to malloc users struct!\n")); + return UID_FIELD_INVALID; + } - if((pwlen == 24) && challenge_done) - { - DEBUG(4,("Checking SMB password for user %s (l=24)\n",user)); + ZERO_STRUCTP(vuser); - if (!pass) - { - DEBUG(3,("Couldn't find user %s\n",user)); - return(False); + if (!IS_SAM_UNIX_USER(server_info->sam_account)) { + DEBUG(0,("Attempted session setup with invalid user. No uid/gid in SAM_ACCOUNT (flags:%x)\n", pdb_get_init_flag(server_info->sam_account))); + free(vuser); + return UID_FIELD_INVALID; } - smb_pass = get_smbpwnam(user); - if(!smb_pass) - { - DEBUG(3,("Couldn't find user %s in smb_passwd file.\n", user)); - return(False); - } + uid = pdb_get_uid(server_info->sam_account); + gid = pdb_get_gid(server_info->sam_account); - /* 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); + /* Allocate a free vuid. Yes this is a linear search... :-) */ + while( get_valid_user_struct(next_vuid) != NULL ) { + next_vuid++; + /* Check for vuid wrap. */ + if (next_vuid == UID_FIELD_INVALID) + next_vuid = VUID_OFFSET; } - 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); - } - } + DEBUG(10,("register_vuid: allocated vuid = %u\n", (unsigned int)next_vuid )); - /* Try against the lanman password */ + vuser->vuid = next_vuid; + vuser->uid = uid; + vuser->gid = gid; + vuser->guest = server_info->guest; + fstrcpy(vuser->user.unix_name, pdb_get_username(server_info->sam_account)); + fstrcpy(vuser->user.smb_name, smb_name); + fstrcpy(vuser->user.domain, pdb_get_domain(server_info->sam_account)); + fstrcpy(vuser->user.full_name, pdb_get_fullname(server_info->sam_account)); - if(smb_password_check(password, smb_pass->smb_passwd, challenge)) { - update_protected_database(user,True); - return(True); + /* Keep the homedir handy */ + const char *homedir = pdb_get_homedir(server_info->sam_account); + if (homedir) { + vuser->homedir = smb_xstrdup(homedir); + } } - 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; + memcpy(vuser->session_key, server_info->session_key, sizeof(vuser->session_key)); - /* 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 */ + DEBUG(10,("register_vuid: (%u,%u) %s %s %s guest=%d\n", + (unsigned int)vuser->uid, + (unsigned int)vuser->gid, + vuser->user.unix_name, vuser->user.smb_name, vuser->user.domain, vuser->guest )); - spass = getspnam(pass->pw_name); - if (spass && spass->sp_pwdp) - pass->pw_passwd = spass->sp_pwdp; - } -#endif + DEBUG(3, ("User name: %s\tReal name: %s\n",vuser->user.unix_name,vuser->user.full_name)); -#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 + vuser->n_groups = 0; + vuser->groups = NULL; -#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 + /* Find all the groups this uid is in and store them. + Used by change_to_user() */ + initialise_groups(vuser->user.unix_name, vuser->uid, vuser->gid); + get_current_groups( &vuser->n_groups, &vuser->groups); -#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 + if (server_info->ptok) + add_supplementary_nt_login_groups(&vuser->n_groups, &vuser->groups, &server_info->ptok); -#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); + /* Create an NT_USER_TOKEN struct for this user. */ + vuser->nt_user_token = create_nt_token(vuser->uid, vuser->gid, vuser->n_groups, vuser->groups, vuser->guest, server_info->ptok); - /* restore it */ - strcpy(password,pass2); + DEBUG(3,("uid %d registered to name %s\n",(int)vuser->uid,vuser->user.unix_name)); - return(False); - } + next_vuid++; + num_validated_vuids++; - /* last chance - all combinations of up to level chars upper! */ - strlower(password); + DLIST_ADD(validated_users, vuser); - if (string_combinations(password,password_check,level)) - { - update_protected_database(user,True); - return(True); - } + if (!session_claim(vuser)) { + DEBUG(1,("Failed to claim session for vuid=%d\n", vuser->vuid)); + invalidate_vuid(vuser->vuid); + return -1; + } - update_protected_database(user,False); - - /* restore it */ - strcpy(password,pass2); - - return(False); + /* Register a home dir service for this user */ + if ((!vuser->guest) && vuser->homedir && *(vuser->homedir) + && (lp_servicenumber(vuser->user.unix_name) < 0)) { + add_home_service(vuser->user.unix_name, vuser->homedir); + } + + return vuser->vuid; } - /**************************************************************************** -check if a username is valid +add a name to the session users list ****************************************************************************/ -BOOL user_ok(char *user,int snum) +void add_session_user(char *user) { - 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); -} - - + fstring suser; + StrnCpy(suser,user,sizeof(suser)-1); + if (!Get_Pwnam_Modify(suser)) return; -/**************************************************************************** -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 + 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 { - 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 (); + pstrcat(session_users," "); + pstrcat(session_users,suser); } -#endif /* GROUP_CHECK_PWENT */ - } - } -#endif - return(NULL); + } } - /**************************************************************************** -check for authority to login to a service with a given username/password +check if a username is valid ****************************************************************************/ -BOOL authorise_login(int snum,char *user,char *password, int pwlen, - BOOL *guest,BOOL *force,int vuid) +BOOL user_ok(char *user,int snum) { - 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 + char **valid, **invalid; + BOOL ret; - if the service is guest_only then steps 1 to 5 are skipped - */ + valid = invalid = NULL; + ret = True; - 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; + if (lp_invalid_users(snum)) { + lp_list_copy(&invalid, lp_invalid_users(snum)); + if (invalid && lp_list_substitute(invalid, "%S", lp_servicename(snum))) { + ret = !user_in_list(user, invalid); + } } - } - + if (invalid) lp_list_free (&invalid); - /* 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); + if (ret && lp_valid_users(snum)) { + lp_list_copy(&valid, lp_valid_users(snum)); + if (valid && lp_list_substitute(valid, "%S", lp_servicename(snum))) { + ret = user_in_list(user,valid); + } } + if (valid) lp_list_free (&valid); - /* 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")); + if (ret && lp_onlyuser(snum)) { + char **user_list = lp_list_make (lp_username(snum)); + if (user_list && lp_list_substitute(user_list, "%S", lp_servicename(snum))) { + ret = user_in_list(user, user_list); + } + if (user_list) lp_list_free (&user_list); } - 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); + return(ret); } - /**************************************************************************** -read the a hosts.equiv or .rhosts file and check if it -allows this user from this machine +validate a group username entry. Return the username or NULL ****************************************************************************/ -static BOOL check_user_equiv(char *user, char *remote, char *equiv_file) +static char *validate_group(char *group, DATA_BLOB password,int snum) { - 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] == '+') +#ifdef HAVE_NETGROUP { - 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++; + char *host, *user, *domain; + setnetgrent(group); + while (getnetgrent(&host, &user, &domain)) { + if (user) { + if (user_ok(user, snum) && + password_ok(user,password)) { + endnetgrent(); + return(user); + } + } + } + endnetgrent(); } - 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) +#ifdef HAVE_GETGRENT { - sprintf(rhostsfile, "%s/.rhosts", home); - if (check_user_equiv(user,Client_info.name,rhostsfile)) - return(True); - } - } + struct group *gptr; + setgrent(); + while ((gptr = (struct group *)getgrent())) { + if (strequal(gptr->gr_name,group)) + break; + } - return(False); + /* + * As user_ok can recurse doing a getgrent(), we must + * copy the member list into a pstring on the stack before + * use. Bug pointed out by leon@eatworms.swmed.edu. + */ + + if (gptr) { + pstring member_list; + char *member; + size_t copied_len = 0; + int i; + + *member_list = '\0'; + member = member_list; + + for(i = 0; gptr->gr_mem && gptr->gr_mem[i]; i++) { + size_t member_len = strlen(gptr->gr_mem[i]) + 1; + if( copied_len + member_len < sizeof(pstring)) { + + DEBUG(10,("validate_group: = gr_mem = %s\n", gptr->gr_mem[i])); + + safe_strcpy(member, gptr->gr_mem[i], sizeof(pstring) - copied_len - 1); + copied_len += member_len; + member += copied_len; + } else { + *member = '\0'; + } + } + + endgrent(); + + member = member_list; + while (*member) { + static fstring name; + fstrcpy(name,member); + if (user_ok(name,snum) && + password_ok(name,password)) { + endgrent(); + return(&name[0]); + } + + DEBUG(10,("validate_group = member = %s\n", member)); + + member += strlen(member) + 1; + } + } else { + endgrent(); + return NULL; + } + } +#endif + return(NULL); } - -static int password_client = -1; -static fstring pserver; - /**************************************************************************** -attempted support for server level security + Check for authority to login to a service with a given username/password. + Note this is *NOT* used when logging on using sessionsetup_and_X. ****************************************************************************/ -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); - } +BOOL authorise_login(int snum,char *user, DATA_BLOB password, + BOOL *guest,BOOL *force,uint16 vuid) +{ + BOOL ok = False; + user_struct *vuser = get_valid_user_struct(vuid); +#if DEBUG_PASSWORD + DEBUG(100,("authorise_login: checking authorisation on user=%s pass=%s vuid=%d\n", + user,password.data, vuid)); +#endif - /* send a session request (RFC 8002) */ + *guest = False; + + if (GUEST_ONLY(snum)) + *force = True; - /* put in the destination name */ - len = 4; - p = outbuf+len; - name_mangle(desthost,p,' '); - len += name_len(p); + if (!GUEST_ONLY(snum) && (lp_security() > SEC_SHARE)) { - /* and my name */ - p = outbuf+len; - name_mangle(remote_machine,p,' '); - len += name_len(p); + /* + * We should just use the given vuid from a sessionsetup_and_X. + */ - _smb_setlen(outbuf,len); - CVAL(outbuf,0) = 0x81; + if (!vuser) { + DEBUG(1,("authorise_login: refusing user '%s' with no session setup\n", user)); + return False; + } - send_smb(password_client,outbuf); - receive_smb(password_client,inbuf,5000); + if ((!vuser->guest && user_ok(vuser->user.unix_name,snum)) || + (vuser->guest && GUEST_OK(snum))) { + fstrcpy(user,vuser->user.unix_name); + *guest = vuser->guest; + DEBUG(3,("authorise_login: ACCEPTED: validated based on vuid as %sguest \ +(user=%s)\n", vuser->guest ? "" : "non-", user)); + return True; + } + } - 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); + /* there are several possibilities: + 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) && GUEST_OK(snum))) { + /* check for a previously registered guest username */ + if (!ok && (vuser != 0) && vuser->guest) { + if (user_ok(vuser->user.unix_name,snum) && + password_ok(vuser->user.unix_name, password)) { + fstrcpy(user, vuser->user.unix_name); + *guest = False; + DEBUG(3,("authorise_login: ACCEPTED: given password with registered user %s\n", user)); + ok = True; + } + } - send_smb(password_client,outbuf); - ret = receive_smb(password_client,inbuf,5000); + /* 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; + fstrcpy(user2,auser); + if (!user_ok(user2,snum)) + continue; + + if (password_ok(user2,password)) { + ok = True; + fstrcpy(user,user2); + DEBUG(3,("authorise_login: ACCEPTED: session list username (%s) \ +and given password ok\n", user)); + } + } + + SAFE_FREE(user_list); + } - if (!ret || CVAL(inbuf,smb_rcls) != 0) { - DEBUG(1,("password server %s rejected the password\n",pserver)); - return(False); - } + /* check for a previously validated username/password pair */ + if (!ok && (lp_security() > SEC_SHARE) && (vuser != 0) && !vuser->guest && + user_ok(vuser->user.unix_name,snum)) { + fstrcpy(user,vuser->user.unix_name); + *guest = False; + DEBUG(3,("authorise_login: ACCEPTED: validated uid (%s) as non-guest\n", + user)); + ok = True; + } - /* 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); - } + /* 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)); - DEBUG(3,("password server %s accepted the password\n",pserver)); + pstring_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,snum); + if (auser) { + ok = True; + fstrcpy(user,auser); + DEBUG(3,("authorise_login: ACCEPTED: group username \ +and given password ok (%s)\n", user)); + } + } else { + fstring user2; + fstrcpy(user2,auser); + if (user_ok(user2,snum) && password_ok(user2,password)) { + ok = True; + fstrcpy(user,user2); + DEBUG(3,("authorise_login: ACCEPTED: user list username \ +and given password ok (%s)\n", user)); + } + } + } + } + } /* not guest only */ + + /* check for a normal guest connection */ + if (!ok && GUEST_OK(snum)) { + fstring guestname; + StrnCpy(guestname,lp_guestaccount(),sizeof(guestname)-1); + if (Get_Pwnam(guestname)) { + fstrcpy(user,guestname); + ok = True; + DEBUG(3,("authorise_login: ACCEPTED: guest account and guest ok (%s)\n", + user)); + } else { + DEBUG(0,("authorise_login: Invalid guest account %s??\n",guestname)); + } + *guest = True; + } -#ifndef KEEP_PASSWORD_SERVER_OPEN - close(password_client); password_client= -1; -#endif + if (ok && !user_ok(user,snum)) { + DEBUG(0,("authorise_login: rejected invalid user %s\n",user)); + ok = False; + } - return(True); + return(ok); } - - diff --git a/source3/smbd/pipes.c b/source3/smbd/pipes.c new file mode 100644 index 0000000000..6c1e6efa73 --- /dev/null +++ b/source3/smbd/pipes.c @@ -0,0 +1,264 @@ +/* + Unix SMB/CIFS implementation. + Pipe SMB reply routines + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Luke Kenneth Casson Leighton 1996-1998 + Copyright (C) Paul Ashton 1997-1998. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + 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 reply_ calls on named pipes that the server + makes to handle specific protocols +*/ + + +#include "includes.h" + +#define PIPE "\\PIPE\\" +#define PIPELEN strlen(PIPE) + +extern struct pipe_id_info pipe_names[]; + +/**************************************************************************** + reply to an open and X on a named pipe + + This code is basically stolen from reply_open_and_X with some + wrinkles to handle pipes. +****************************************************************************/ +int reply_open_pipe_and_X(connection_struct *conn, + char *inbuf,char *outbuf,int length,int bufsize) +{ + pstring fname; + pstring pipe_name; + uint16 vuid = SVAL(inbuf, smb_uid); + smb_np_struct *p; + int smb_ofun = SVAL(inbuf,smb_vwv8); + int size=0,fmode=0,mtime=0,rmode=0; + int i; + + /* XXXX we need to handle passed times, sattr and flags */ + srvstr_pull(inbuf, pipe_name, smb_buf(inbuf), sizeof(pipe_name), -1, STR_TERMINATE); + + /* If the name doesn't start \PIPE\ then this is directed */ + /* at a mailslot or something we really, really don't understand, */ + /* not just something we really don't understand. */ + if ( strncmp(pipe_name,PIPE,PIPELEN) != 0 ) + return(ERROR_DOS(ERRSRV,ERRaccess)); + + DEBUG(4,("Opening pipe %s.\n", pipe_name)); + + /* See if it is one we want to handle. */ + for( i = 0; pipe_names[i].client_pipe ; i++ ) + if( strequal(pipe_name,pipe_names[i].client_pipe) ) + break; + + if (pipe_names[i].client_pipe == NULL) + return(ERROR_BOTH(NT_STATUS_OBJECT_NAME_NOT_FOUND,ERRDOS,ERRbadpipe)); + + /* Strip \PIPE\ off the name. */ + pstrcpy(fname, pipe_name + PIPELEN); + + +#if 0 + /* + * Hack for NT printers... JRA. + */ + if(should_fail_next_srvsvc_open(fname)) + return(ERROR(ERRSRV,ERRaccess)); +#endif + + /* Known pipes arrive with DIR attribs. Remove it so a regular file */ + /* can be opened and add it in after the open. */ + DEBUG(3,("Known pipe %s opening.\n",fname)); + smb_ofun |= FILE_CREATE_IF_NOT_EXIST; + + p = open_rpc_pipe_p(fname, conn, vuid); + if (!p) return(ERROR_DOS(ERRSRV,ERRnofids)); + + /* Prepare the reply */ + set_message(outbuf,15,0,True); + + /* Mark the opened file as an existing named pipe in message mode. */ + SSVAL(outbuf,smb_vwv9,2); + SSVAL(outbuf,smb_vwv10,0xc700); + + if (rmode == 2) { + DEBUG(4,("Resetting open result to open from create.\n")); + rmode = 1; + } + + SSVAL(outbuf,smb_vwv2, p->pnum); + 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,0x0001); + + return chain_reply(inbuf,outbuf,length,bufsize); +} + +/**************************************************************************** + reply to a write on a pipe +****************************************************************************/ +int reply_pipe_write(char *inbuf,char *outbuf,int length,int dum_bufsize) +{ + smb_np_struct *p = get_rpc_pipe_p(inbuf,smb_vwv0); + size_t numtowrite = SVAL(inbuf,smb_vwv1); + int nwritten; + int outsize; + char *data; + + if (!p) + return(ERROR_DOS(ERRDOS,ERRbadfid)); + + data = smb_buf(inbuf) + 3; + + if (numtowrite == 0) + nwritten = 0; + else + nwritten = write_to_pipe(p, data, numtowrite); + + if ((nwritten == 0 && numtowrite != 0) || (nwritten < 0)) + return (UNIXERROR(ERRDOS,ERRnoaccess)); + + outsize = set_message(outbuf,1,0,True); + + SSVAL(outbuf,smb_vwv0,nwritten); + + DEBUG(3,("write-IPC pnum=%04x nwritten=%d\n", + p->pnum, nwritten)); + + return(outsize); +} + +/**************************************************************************** + Reply to a write and X. + + This code is basically stolen from reply_write_and_X with some + wrinkles to handle pipes. +****************************************************************************/ + +int reply_pipe_write_and_X(char *inbuf,char *outbuf,int length,int bufsize) +{ + smb_np_struct *p = get_rpc_pipe_p(inbuf,smb_vwv2); + size_t numtowrite = SVAL(inbuf,smb_vwv10); + int nwritten = -1; + int smb_doff = SVAL(inbuf, smb_vwv11); + BOOL pipe_start_message_raw = ((SVAL(inbuf, smb_vwv7) & (PIPE_START_MESSAGE|PIPE_RAW_MODE)) == + (PIPE_START_MESSAGE|PIPE_RAW_MODE)); + char *data; + + if (!p) + return(ERROR_DOS(ERRDOS,ERRbadfid)); + + data = smb_base(inbuf) + smb_doff; + + if (numtowrite == 0) + nwritten = 0; + else { + if(pipe_start_message_raw) { + /* + * For the start of a message in named pipe byte mode, + * the first two bytes are a length-of-pdu field. Ignore + * them (we don't trust the client. JRA. + */ + if(numtowrite < 2) { + DEBUG(0,("reply_pipe_write_and_X: start of message set and not enough data sent.(%u)\n", + (unsigned int)numtowrite )); + return (UNIXERROR(ERRDOS,ERRnoaccess)); + } + + data += 2; + numtowrite -= 2; + } + nwritten = write_to_pipe(p, data, numtowrite); + } + + if ((nwritten == 0 && numtowrite != 0) || (nwritten < 0)) + return (UNIXERROR(ERRDOS,ERRnoaccess)); + + set_message(outbuf,6,0,True); + + nwritten = (pipe_start_message_raw ? nwritten + 2 : nwritten); + SSVAL(outbuf,smb_vwv2,nwritten); + + DEBUG(3,("writeX-IPC pnum=%04x nwritten=%d\n", + p->pnum, nwritten)); + + return chain_reply(inbuf,outbuf,length,bufsize); +} + +/**************************************************************************** + reply to a read and X + + This code is basically stolen from reply_read_and_X with some + wrinkles to handle pipes. +****************************************************************************/ +int reply_pipe_read_and_X(char *inbuf,char *outbuf,int length,int bufsize) +{ + smb_np_struct *p = get_rpc_pipe_p(inbuf,smb_vwv2); + int smb_maxcnt = SVAL(inbuf,smb_vwv5); + int smb_mincnt = SVAL(inbuf,smb_vwv6); + int nread = -1; + char *data; + BOOL unused; + + /* we don't use the offset given to use for pipe reads. This + is deliberate, instead we always return the next lump of + data on the pipe */ +#if 0 + uint32 smb_offs = IVAL(inbuf,smb_vwv3); +#endif + + if (!p) + return(ERROR_DOS(ERRDOS,ERRbadfid)); + + set_message(outbuf,12,0,True); + data = smb_buf(outbuf); + + nread = read_from_pipe(p, data, smb_maxcnt, &unused); + + if (nread < 0) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + SSVAL(outbuf,smb_vwv5,nread); + SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf)); + SSVAL(smb_buf(outbuf),-2,nread); + + DEBUG(3,("readX-IPC pnum=%04x min=%d max=%d nread=%d\n", + p->pnum, smb_mincnt, smb_maxcnt, nread)); + + return chain_reply(inbuf,outbuf,length,bufsize); +} + +/**************************************************************************** + reply to a close +****************************************************************************/ +int reply_pipe_close(connection_struct *conn, char *inbuf,char *outbuf) +{ + smb_np_struct *p = get_rpc_pipe_p(inbuf,smb_vwv0); + int outsize = set_message(outbuf,0,0,True); + + if (!p) + return(ERROR_DOS(ERRDOS,ERRbadfid)); + + DEBUG(5,("reply_pipe_close: pnum:%x\n", p->pnum)); + + if (!close_rpc_pipe_hnd(p)) + return ERROR_DOS(ERRDOS,ERRbadfid); + + return(outsize); +} diff --git a/source3/smbd/posix_acls.c b/source3/smbd/posix_acls.c new file mode 100644 index 0000000000..9c8835214f --- /dev/null +++ b/source3/smbd/posix_acls.c @@ -0,0 +1,2313 @@ +/* + Unix SMB/CIFS implementation. + SMB NT Security Descriptor / Unix permission conversion. + Copyright (C) Jeremy Allison 1994-2000 + + 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" + +/**************************************************************************** + Data structures representing the internal ACE format. +****************************************************************************/ + +enum ace_owner {UID_ACE, GID_ACE, WORLD_ACE}; +enum ace_attribute {ALLOW_ACE, DENY_ACE}; /* Used for incoming NT ACLS. */ + +typedef union posix_id { + uid_t uid; + gid_t gid; + int world; +} posix_id; + +typedef struct canon_ace { + struct canon_ace *next, *prev; + SMB_ACL_TAG_T type; + mode_t perms; /* Only use S_I(R|W|X)USR mode bits here. */ + DOM_SID trustee; + enum ace_owner owner_type; + enum ace_attribute attr; + posix_id unix_ug; +} canon_ace; + +#define ALL_ACE_PERMS (S_IRUSR|S_IWUSR|S_IXUSR) + +/**************************************************************************** + Functions to manipulate the internal ACE format. +****************************************************************************/ + +/**************************************************************************** + Count a linked list of canonical ACE entries. +****************************************************************************/ + +static size_t count_canon_ace_list( canon_ace *list_head ) +{ + size_t count = 0; + canon_ace *ace; + + for (ace = list_head; ace; ace = ace->next) + count++; + + return count; +} + +/**************************************************************************** + Free a linked list of canonical ACE entries. +****************************************************************************/ + +static void free_canon_ace_list( canon_ace *list_head ) +{ + while (list_head) { + canon_ace *old_head = list_head; + DLIST_REMOVE(list_head, list_head); + SAFE_FREE(old_head); + } +} + +/**************************************************************************** + Function to duplicate a canon_ace entry. +****************************************************************************/ + +static canon_ace *dup_canon_ace( canon_ace *src_ace) +{ + canon_ace *dst_ace = (canon_ace *)malloc(sizeof(canon_ace)); + + if (dst_ace == NULL) + return NULL; + + *dst_ace = *src_ace; + dst_ace->prev = dst_ace->next = NULL; + return dst_ace; +} + +/**************************************************************************** + Print out a canon ace. +****************************************************************************/ + +static void print_canon_ace(canon_ace *pace, int num) +{ + fstring str; + + dbgtext( "canon_ace index %d. Type = %s ", num, pace->attr == ALLOW_ACE ? "allow" : "deny" ); + dbgtext( "SID = %s ", sid_to_string( str, &pace->trustee)); + if (pace->owner_type == UID_ACE) { + char *u_name = uidtoname(pace->unix_ug.uid); + dbgtext( "uid %u (%s) ", (unsigned int)pace->unix_ug.uid, u_name); + } else if (pace->owner_type == GID_ACE) { + char *g_name = gidtoname(pace->unix_ug.gid); + dbgtext( "gid %u (%s) ", (unsigned int)pace->unix_ug.gid, g_name); + } else + dbgtext( "other "); + switch (pace->type) { + case SMB_ACL_USER: + dbgtext( "SMB_ACL_USER "); + break; + case SMB_ACL_USER_OBJ: + dbgtext( "SMB_ACL_USER_OBJ "); + break; + case SMB_ACL_GROUP: + dbgtext( "SMB_ACL_GROUP "); + break; + case SMB_ACL_GROUP_OBJ: + dbgtext( "SMB_ACL_GROUP_OBJ "); + break; + case SMB_ACL_OTHER: + dbgtext( "SMB_ACL_OTHER "); + break; + } + dbgtext( "perms "); + dbgtext( "%c", pace->perms & S_IRUSR ? 'r' : '-'); + dbgtext( "%c", pace->perms & S_IWUSR ? 'w' : '-'); + dbgtext( "%c\n", pace->perms & S_IXUSR ? 'x' : '-'); +} + +/**************************************************************************** + Print out a canon ace list. +****************************************************************************/ + +static void print_canon_ace_list(const char *name, canon_ace *ace_list) +{ + int count = 0; + + if( DEBUGLVL( 10 )) { + dbgtext( "print_canon_ace_list: %s\n", name ); + for (;ace_list; ace_list = ace_list->next, count++) + print_canon_ace(ace_list, count ); + } +} + +/**************************************************************************** + Map POSIX ACL perms to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits). +****************************************************************************/ + +static mode_t convert_permset_to_mode_t(connection_struct *conn, SMB_ACL_PERMSET_T permset) +{ + mode_t ret = 0; + + ret |= (conn->vfs_ops.sys_acl_get_perm(conn, permset, SMB_ACL_READ) ? S_IRUSR : 0); + ret |= (conn->vfs_ops.sys_acl_get_perm(conn, permset, SMB_ACL_WRITE) ? S_IWUSR : 0); + ret |= (conn->vfs_ops.sys_acl_get_perm(conn, permset, SMB_ACL_EXECUTE) ? S_IXUSR : 0); + + return ret; +} + +/**************************************************************************** + Map generic UNIX permissions to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits). +****************************************************************************/ + +static mode_t unix_perms_to_acl_perms(mode_t mode, int r_mask, int w_mask, int x_mask) +{ + mode_t ret = 0; + + if (mode & r_mask) + ret |= S_IRUSR; + if (mode & w_mask) + ret |= S_IWUSR; + if (mode & x_mask) + ret |= S_IXUSR; + + return ret; +} + +/**************************************************************************** + Map canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits) to + an SMB_ACL_PERMSET_T. +****************************************************************************/ + +static int map_acl_perms_to_permset(connection_struct *conn, mode_t mode, SMB_ACL_PERMSET_T *p_permset) +{ + if (conn->vfs_ops.sys_acl_clear_perms(conn, *p_permset) == -1) + return -1; + if (mode & S_IRUSR) { + if (conn->vfs_ops.sys_acl_add_perm(conn, *p_permset, SMB_ACL_READ) == -1) + return -1; + } + if (mode & S_IWUSR) { + if (conn->vfs_ops.sys_acl_add_perm(conn, *p_permset, SMB_ACL_WRITE) == -1) + return -1; + } + if (mode & S_IXUSR) { + if (conn->vfs_ops.sys_acl_add_perm(conn, *p_permset, SMB_ACL_EXECUTE) == -1) + return -1; + } + return 0; +} +/**************************************************************************** + Function to create owner and group SIDs from a SMB_STRUCT_STAT. +****************************************************************************/ + +static void create_file_sids(SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID *pgroup_sid) +{ + uid_to_sid( powner_sid, psbuf->st_uid ); + gid_to_sid( pgroup_sid, psbuf->st_gid ); +} + +/**************************************************************************** + Merge aces with a common sid - if both are allow or deny, OR the permissions together and + delete the second one. If the first is deny, mask the permissions off and delete the allow + if the permissions become zero, delete the deny if the permissions are non zero. +****************************************************************************/ + +static void merge_aces( canon_ace **pp_list_head ) +{ + canon_ace *list_head = *pp_list_head; + canon_ace *curr_ace_outer; + canon_ace *curr_ace_outer_next; + + /* + * First, merge allow entries with identical SIDs, and deny entries + * with identical SIDs. + */ + + for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) { + canon_ace *curr_ace; + canon_ace *curr_ace_next; + + curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */ + + for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) { + + curr_ace_next = curr_ace->next; /* Save the link in case of delete. */ + + if (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) && + (curr_ace->attr == curr_ace_outer->attr)) { + + if( DEBUGLVL( 10 )) { + dbgtext("merge_aces: Merging ACE's\n"); + print_canon_ace( curr_ace_outer, 0); + print_canon_ace( curr_ace, 0); + } + + /* Merge two allow or two deny ACE's. */ + + curr_ace_outer->perms |= curr_ace->perms; + DLIST_REMOVE(list_head, curr_ace); + SAFE_FREE(curr_ace); + curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */ + } + } + } + + /* + * Now go through and mask off allow permissions with deny permissions. + * We can delete either the allow or deny here as we know that each SID + * appears only once in the list. + */ + + for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) { + canon_ace *curr_ace; + canon_ace *curr_ace_next; + + curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */ + + for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) { + + curr_ace_next = curr_ace->next; /* Save the link in case of delete. */ + + /* + * Subtract ACE's with different entries. Due to the ordering constraints + * we've put on the ACL, we know the deny must be the first one. + */ + + if (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) && + (curr_ace_outer->attr == DENY_ACE) && (curr_ace->attr == ALLOW_ACE)) { + + if( DEBUGLVL( 10 )) { + dbgtext("merge_aces: Masking ACE's\n"); + print_canon_ace( curr_ace_outer, 0); + print_canon_ace( curr_ace, 0); + } + + curr_ace->perms &= ~curr_ace_outer->perms; + + if (curr_ace->perms == 0) { + + /* + * The deny overrides the allow. Remove the allow. + */ + + DLIST_REMOVE(list_head, curr_ace); + SAFE_FREE(curr_ace); + curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */ + + } else { + + /* + * Even after removing permissions, there + * are still allow permissions - delete the deny. + * It is safe to delete the deny here, + * as we are guarenteed by the deny first + * ordering that all the deny entries for + * this SID have already been merged into one + * before we can get to an allow ace. + */ + + DLIST_REMOVE(list_head, curr_ace_outer); + SAFE_FREE(curr_ace_outer); + break; + } + } + + } /* end for curr_ace */ + } /* end for curr_ace_outer */ + + /* We may have modified the list. */ + + *pp_list_head = list_head; +} + +/**************************************************************************** + Map canon_ace perms to permission bits NT. + The attr element is not used here - we only process deny entries on set, + not get. Deny entries are implicit on get with ace->perms = 0. +****************************************************************************/ + +static SEC_ACCESS map_canon_ace_perms(int *pacl_type, DOM_SID *powner_sid, canon_ace *ace) +{ + SEC_ACCESS sa; + uint32 nt_mask = 0; + + *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED; + + if ((ace->perms & ALL_ACE_PERMS) == ALL_ACE_PERMS) { + nt_mask = UNIX_ACCESS_RWX; + } else if ((ace->perms & ALL_ACE_PERMS) == (mode_t)0) { + nt_mask = UNIX_ACCESS_NONE; + } else { + nt_mask |= ((ace->perms & S_IRUSR) ? UNIX_ACCESS_R : 0 ); + nt_mask |= ((ace->perms & S_IWUSR) ? UNIX_ACCESS_W : 0 ); + nt_mask |= ((ace->perms & S_IXUSR) ? UNIX_ACCESS_X : 0 ); + } + + DEBUG(10,("map_canon_ace_perms: Mapped (UNIX) %x to (NT) %x\n", + (unsigned int)ace->perms, (unsigned int)nt_mask )); + + init_sec_access(&sa,nt_mask); + return sa; +} + +/**************************************************************************** + Map NT perms to a UNIX mode_t. +****************************************************************************/ + +#define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES) +#define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES) +#define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE) + +static mode_t map_nt_perms( SEC_ACCESS sec_access, int type) +{ + mode_t mode = 0; + + switch(type) { + case S_IRUSR: + if(sec_access.mask & GENERIC_ALL_ACCESS) + mode = S_IRUSR|S_IWUSR|S_IXUSR; + else { + mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0; + mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0; + mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0; + } + break; + case S_IRGRP: + if(sec_access.mask & GENERIC_ALL_ACCESS) + mode = S_IRGRP|S_IWGRP|S_IXGRP; + else { + mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0; + mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0; + mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0; + } + break; + case S_IROTH: + if(sec_access.mask & GENERIC_ALL_ACCESS) + mode = S_IROTH|S_IWOTH|S_IXOTH; + else { + mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0; + mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0; + mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0; + } + break; + } + + return mode; +} + +/**************************************************************************** + Unpack a SEC_DESC into a UNIX owner and group. +****************************************************************************/ + +static BOOL unpack_nt_owners(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, SEC_DESC *psd) +{ + DOM_SID owner_sid; + DOM_SID grp_sid; + enum SID_NAME_USE sid_type; + + *puser = (uid_t)-1; + *pgrp = (gid_t)-1; + + if(security_info_sent == 0) { + DEBUG(0,("unpack_nt_owners: no security info sent !\n")); + return True; + } + + /* + * Validate the owner and group SID's. + */ + + memset(&owner_sid, '\0', sizeof(owner_sid)); + memset(&grp_sid, '\0', sizeof(grp_sid)); + + DEBUG(5,("unpack_nt_owners: validating owner_sids.\n")); + + /* + * Don't immediately fail if the owner sid cannot be validated. + * This may be a group chown only set. + */ + + if (security_info_sent & OWNER_SECURITY_INFORMATION) { + sid_copy(&owner_sid, psd->owner_sid); + if (!sid_to_uid( &owner_sid, puser, &sid_type)) { + DEBUG(3,("unpack_nt_owners: unable to validate owner sid for %s\n", + sid_string_static(&owner_sid))); + return False; + } + } + + /* + * Don't immediately fail if the group sid cannot be validated. + * This may be an owner chown only set. + */ + + if (security_info_sent & GROUP_SECURITY_INFORMATION) { + sid_copy(&grp_sid, psd->grp_sid); + if (!sid_to_gid( &grp_sid, pgrp, &sid_type)) { + DEBUG(3,("unpack_nt_owners: unable to validate group sid.\n")); + return False; + } + } + + DEBUG(5,("unpack_nt_owners: owner_sids validated.\n")); + + return True; +} + +/**************************************************************************** + Ensure the enforced permissions for this share apply. +****************************************************************************/ + +static mode_t apply_default_perms(files_struct *fsp, mode_t perms, mode_t type) +{ + int snum = SNUM(fsp->conn); + mode_t and_bits = (mode_t)0; + mode_t or_bits = (mode_t)0; + + /* Get the initial bits to apply. */ + + if (fsp->is_directory) { + and_bits = lp_dir_security_mask(snum); + or_bits = lp_force_dir_security_mode(snum); + } else { + and_bits = lp_security_mask(snum); + or_bits = lp_force_security_mode(snum); + } + + /* Now bounce them into the S_USR space. */ + switch(type) { + case S_IRUSR: + and_bits = unix_perms_to_acl_perms(and_bits, S_IRUSR, S_IWUSR, S_IXUSR); + or_bits = unix_perms_to_acl_perms(or_bits, S_IRUSR, S_IWUSR, S_IXUSR); + break; + case S_IRGRP: + and_bits = unix_perms_to_acl_perms(and_bits, S_IRGRP, S_IWGRP, S_IXGRP); + or_bits = unix_perms_to_acl_perms(or_bits, S_IRGRP, S_IWGRP, S_IXGRP); + break; + case S_IROTH: + and_bits = unix_perms_to_acl_perms(and_bits, S_IROTH, S_IWOTH, S_IXOTH); + or_bits = unix_perms_to_acl_perms(or_bits, S_IROTH, S_IWOTH, S_IXOTH); + break; + } + + return ((perms & and_bits)|or_bits); +} + +/**************************************************************************** + A well formed POSIX file or default ACL has at least 3 entries, a + SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ. + In addition, the owner must always have at least read access. + When using this call on get_acl, the pst struct is valid and contains + the mode of the file. When using this call on set_acl, the pst struct has + been modified to have a mode containing the default for this file or directory + type. +****************************************************************************/ + +static BOOL ensure_canon_entry_valid(canon_ace **pp_ace, + files_struct *fsp, + DOM_SID *pfile_owner_sid, + DOM_SID *pfile_grp_sid, + SMB_STRUCT_STAT *pst, + BOOL setting_acl) +{ + extern DOM_SID global_sid_World; + canon_ace *pace; + BOOL got_user = False; + BOOL got_grp = False; + BOOL got_other = False; + + for (pace = *pp_ace; pace; pace = pace->next) { + if (pace->type == SMB_ACL_USER_OBJ) { + + if (setting_acl) { + /* Ensure owner has read access. */ + pace->perms |= S_IRUSR; + if (fsp->is_directory) + pace->perms |= (S_IWUSR|S_IXUSR); + + /* + * Ensure create mask/force create mode is respected on set. + */ + + pace->perms = apply_default_perms(fsp, pace->perms, S_IRUSR); + } + + got_user = True; + } else if (pace->type == SMB_ACL_GROUP_OBJ) { + + /* + * Ensure create mask/force create mode is respected on set. + */ + + if (setting_acl) + pace->perms = apply_default_perms(fsp, pace->perms, S_IRGRP); + got_grp = True; + } else if (pace->type == SMB_ACL_OTHER) { + + /* + * Ensure create mask/force create mode is respected on set. + */ + + if (setting_acl) + pace->perms = apply_default_perms(fsp, pace->perms, S_IROTH); + got_other = True; + } + } + + if (!got_user) { + if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) { + DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n")); + return False; + } + + ZERO_STRUCTP(pace); + pace->type = SMB_ACL_USER_OBJ; + pace->owner_type = UID_ACE; + pace->unix_ug.uid = pst->st_uid; + pace->trustee = *pfile_owner_sid; + pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRUSR, S_IWUSR, S_IXUSR); + pace->attr = ALLOW_ACE; + + DLIST_ADD(*pp_ace, pace); + } + + if (!got_grp) { + if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) { + DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n")); + return False; + } + + ZERO_STRUCTP(pace); + pace->type = SMB_ACL_GROUP_OBJ; + pace->owner_type = GID_ACE; + pace->unix_ug.uid = pst->st_gid; + pace->trustee = *pfile_grp_sid; + pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRGRP, S_IWGRP, S_IXGRP); + pace->attr = ALLOW_ACE; + + DLIST_ADD(*pp_ace, pace); + } + + if (!got_other) { + if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) { + DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n")); + return False; + } + + ZERO_STRUCTP(pace); + pace->type = SMB_ACL_OTHER; + pace->owner_type = WORLD_ACE; + pace->unix_ug.world = -1; + pace->trustee = global_sid_World; + pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IROTH, S_IWOTH, S_IXOTH); + pace->attr = ALLOW_ACE; + + DLIST_ADD(*pp_ace, pace); + } + + return True; +} + +/**************************************************************************** + Unpack a SEC_DESC into two canonical ace lists. +****************************************************************************/ + +static BOOL create_canon_ace_lists(files_struct *fsp, + DOM_SID *pfile_owner_sid, + DOM_SID *pfile_grp_sid, + canon_ace **ppfile_ace, canon_ace **ppdir_ace, + SEC_ACL *dacl) +{ + extern DOM_SID global_sid_World; + extern struct generic_mapping file_generic_mapping; + BOOL all_aces_are_inherit_only = (fsp->is_directory ? True : False); + canon_ace *file_ace = NULL; + canon_ace *dir_ace = NULL; + canon_ace *tmp_ace = NULL; + canon_ace *current_ace = NULL; + BOOL got_dir_allow = False; + BOOL got_file_allow = False; + int i, j; + + *ppfile_ace = NULL; + *ppdir_ace = NULL; + + /* + * Convert the incoming ACL into a more regular form. + */ + + for(i = 0; i < dacl->num_aces; i++) { + SEC_ACE *psa = &dacl->ace[i]; + + if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) && (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) { + DEBUG(3,("create_canon_ace_lists: unable to set anything but an ALLOW or DENY ACE.\n")); + return False; + } + + /* + * The security mask may be UNIX_ACCESS_NONE which should map into + * no permissions (we overload the WRITE_OWNER bit for this) or it + * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this + * to be so. Any other bits override the UNIX_ACCESS_NONE bit. + */ + + /* + * Convert GENERIC bits to specific bits. + */ + + se_map_generic(&psa->info.mask, &file_generic_mapping); + + psa->info.mask &= (UNIX_ACCESS_NONE|FILE_ALL_ACCESS); + + if(psa->info.mask != UNIX_ACCESS_NONE) + psa->info.mask &= ~UNIX_ACCESS_NONE; + } + + /* + * Deal with the fact that NT 4.x re-writes the canonical format + * that we return for default ACLs. If a directory ACE is identical + * to a inherited directory ACE then NT changes the bits so that the + * first ACE is set to OI|IO and the second ACE for this SID is set + * to CI. We need to repair this. JRA. + */ + + for(i = 0; i < dacl->num_aces; i++) { + SEC_ACE *psa1 = &dacl->ace[i]; + + for (j = i + 1; j < dacl->num_aces; j++) { + SEC_ACE *psa2 = &dacl->ace[j]; + + if (psa1->info.mask != psa2->info.mask) + continue; + + if (!sid_equal(&psa1->trustee, &psa2->trustee)) + continue; + + /* + * Ok - permission bits and SIDs are equal. + * Check if flags were re-written. + */ + + if (psa1->flags & SEC_ACE_FLAG_INHERIT_ONLY) { + + psa1->flags |= (psa2->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT)); + psa2->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT); + + } else if (psa2->flags & SEC_ACE_FLAG_INHERIT_ONLY) { + + psa2->flags |= (psa1->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT)); + psa1->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT); + + } + } + } + + for(i = 0; i < dacl->num_aces; i++) { + enum SID_NAME_USE sid_type; + SEC_ACE *psa = &dacl->ace[i]; + + /* + * Ignore non-mappable SIDs (NT Authority, BUILTIN etc). + */ + + if (non_mappable_sid(&psa->trustee)) { + fstring str; + DEBUG(10,("create_canon_ace_lists: ignoring non-mappable SID %s\n", + sid_to_string(str, &psa->trustee) )); + continue; + } + + /* + * Create a cannon_ace entry representing this NT DACL ACE. + */ + + if ((current_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) { + free_canon_ace_list(file_ace); + free_canon_ace_list(dir_ace); + DEBUG(0,("create_canon_ace_lists: malloc fail.\n")); + return False; + } + + ZERO_STRUCTP(current_ace); + + sid_copy(¤t_ace->trustee, &psa->trustee); + + /* + * Try and work out if the SID is a user or group + * as we need to flag these differently for POSIX. + */ + + if( sid_equal(¤t_ace->trustee, &global_sid_World)) { + current_ace->owner_type = WORLD_ACE; + current_ace->unix_ug.world = -1; + } else if (sid_to_uid( ¤t_ace->trustee, ¤t_ace->unix_ug.uid, &sid_type)) { + current_ace->owner_type = UID_ACE; + } else if (sid_to_gid( ¤t_ace->trustee, ¤t_ace->unix_ug.gid, &sid_type)) { + current_ace->owner_type = GID_ACE; + } else { + fstring str; + + free_canon_ace_list(file_ace); + free_canon_ace_list(dir_ace); + DEBUG(0,("create_canon_ace_lists: unable to map SID %s to uid or gid.\n", + sid_to_string(str, ¤t_ace->trustee) )); + SAFE_FREE(current_ace); + return False; + } + + /* + * Map the given NT permissions into a UNIX mode_t containing only + * S_I(R|W|X)USR bits. + */ + + current_ace->perms |= map_nt_perms( psa->info, S_IRUSR); + current_ace->attr = (psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) ? ALLOW_ACE : DENY_ACE; + + /* + * Now note what kind of a POSIX ACL this should map to. + */ + + if(sid_equal(¤t_ace->trustee, pfile_owner_sid)) { + + current_ace->type = SMB_ACL_USER_OBJ; + + } else if( sid_equal(¤t_ace->trustee, pfile_grp_sid)) { + + current_ace->type = SMB_ACL_GROUP_OBJ; + + } else if( sid_equal(¤t_ace->trustee, &global_sid_World)) { + + current_ace->type = SMB_ACL_OTHER; + + } else { + /* + * Could be a SMB_ACL_USER or SMB_ACL_GROUP. Check by + * looking at owner_type. + */ + + current_ace->type = (current_ace->owner_type == UID_ACE) ? SMB_ACL_USER : SMB_ACL_GROUP; + } + + /* + * Now add the created ace to either the file list, the directory + * list, or both. We *MUST* preserve the order here (hence we use + * DLIST_ADD_END) as NT ACLs are order dependent. + */ + + if (fsp->is_directory) { + + /* + * We can only add to the default POSIX ACE list if the ACE is + * designed to be inherited by both files and directories. + */ + + if ((psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) == + (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) { + + DLIST_ADD_END(dir_ace, current_ace, tmp_ace); + + /* + * Note if this was an allow ace. We can't process + * any further deny ace's after this. + */ + + if (current_ace->attr == ALLOW_ACE) + got_dir_allow = True; + + if ((current_ace->attr == DENY_ACE) && got_dir_allow) { + DEBUG(0,("create_canon_ace_lists: malformed ACL in inheritable ACL ! \ +Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name )); + free_canon_ace_list(file_ace); + free_canon_ace_list(dir_ace); + SAFE_FREE(current_ace); + return False; + } + + if( DEBUGLVL( 10 )) { + dbgtext("create_canon_ace_lists: adding dir ACL:\n"); + print_canon_ace( current_ace, 0); + } + + /* + * If this is not an inherit only ACE we need to add a duplicate + * to the file acl. + */ + + if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) { + canon_ace *dup_ace = dup_canon_ace(current_ace); + + if (!dup_ace) { + DEBUG(0,("create_canon_ace_lists: malloc fail !\n")); + free_canon_ace_list(file_ace); + free_canon_ace_list(dir_ace); + return False; + } + + current_ace = dup_ace; + } else { + current_ace = NULL; + } + } + } + + /* + * Only add to the file ACL if not inherit only. + */ + + if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) { + DLIST_ADD_END(file_ace, current_ace, tmp_ace); + + /* + * Note if this was an allow ace. We can't process + * any further deny ace's after this. + */ + + if (current_ace->attr == ALLOW_ACE) + got_file_allow = True; + + if ((current_ace->attr == DENY_ACE) && got_file_allow) { + DEBUG(0,("create_canon_ace_lists: malformed ACL in file ACL ! \ +Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name )); + free_canon_ace_list(file_ace); + free_canon_ace_list(dir_ace); + SAFE_FREE(current_ace); + return False; + } + + if( DEBUGLVL( 10 )) { + dbgtext("create_canon_ace_lists: adding file ACL:\n"); + print_canon_ace( current_ace, 0); + } + all_aces_are_inherit_only = False; + current_ace = NULL; + } + + /* + * Free if ACE was not added. + */ + + SAFE_FREE(current_ace); + } + + if (fsp->is_directory && all_aces_are_inherit_only) { + /* + * Windows 2000 is doing one of these weird 'inherit acl' + * traverses to conserve NTFS ACL resources. Just pretend + * there was no DACL sent. JRA. + */ + + DEBUG(10,("create_canon_ace_lists: Win2k inherit acl traverse. Ignoring DACL.\n")); + free_canon_ace_list(file_ace); + free_canon_ace_list(dir_ace); + file_ace = NULL; + dir_ace = NULL; + } + + *ppfile_ace = file_ace; + *ppdir_ace = dir_ace; + + return True; +} + +/**************************************************************************** + Check if a given uid/SID is in a group gid/SID. This is probably very + expensive and will need optimisation. A *lot* of optimisation :-). JRA. +****************************************************************************/ + +static BOOL uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace ) +{ + extern DOM_SID global_sid_World; + fstring u_name; + fstring g_name; + + /* "Everyone" always matches every uid. */ + + if (sid_equal(&group_ace->trustee, &global_sid_World)) + return True; + + fstrcpy(u_name, uidtoname(uid_ace->unix_ug.uid)); + fstrcpy(g_name, gidtoname(group_ace->unix_ug.gid)); + + /* + * Due to the winbind interfaces we need to do this via names, + * not uids/gids. + */ + + return user_in_group_list(u_name, g_name ); +} + +/**************************************************************************** + ASCII art time again... JRA :-). + + We have 3 cases to process when moving from an NT ACL to a POSIX ACL. Firstly, + we insist the ACL is in canonical form (ie. all DENY entries preceede ALLOW + entries). Secondly, the merge code has ensured that all duplicate SID entries for + allow or deny have been merged, so the same SID can only appear once in the deny + list or once in the allow list. + + We then process as follows : + + --------------------------------------------------------------------------- + First pass - look for a Everyone DENY entry. + + If it is deny all (rwx) trunate the list at this point. + Else, walk the list from this point and use the deny permissions of this + entry as a mask on all following allow entries. Finally, delete + the Everyone DENY entry (we have applied it to everything possible). + + In addition, in this pass we remove any DENY entries that have + no permissions (ie. they are a DENY nothing). + --------------------------------------------------------------------------- + Second pass - only deal with deny user entries. + + DENY user1 (perms XXX) + + new_perms = 0 + for all following allow group entries where user1 is in group + new_perms |= group_perms; + + user1 entry perms = new_perms & ~ XXX; + + Convert the deny entry to an allow entry with the new perms and + push to the end of the list. Note if the user was in no groups + this maps to a specific allow nothing entry for this user. + + The common case from the NT ACL choser (userX deny all) is + optimised so we don't do the group lookup - we just map to + an allow nothing entry. + + What we're doing here is inferring the allow permissions the + person setting the ACE on user1 wanted by looking at the allow + permissions on the groups the user is currently in. This will + be a snapshot, depending on group membership but is the best + we can do and has the advantage of failing closed rather than + open. + --------------------------------------------------------------------------- + Third pass - only deal with deny group entries. + + DENY group1 (perms XXX) + + for all following allow user entries where user is in group1 + user entry perms = user entry perms & ~ XXX; + + If there is a group Everyone allow entry with permissions YYY, + convert the group1 entry to an allow entry and modify its + permissions to be : + + new_perms = YYY & ~ XXX + + and push to the end of the list. + + If there is no group Everyone allow entry then convert the + group1 entry to a allow nothing entry and push to the end of the list. + + Note that the common case from the NT ACL choser (groupX deny all) + cannot be optimised here as we need to modify user entries who are + in the group to change them to a deny all also. + + What we're doing here is modifying the allow permissions of + user entries (which are more specific in POSIX ACLs) to mask + out the explicit deny set on the group they are in. This will + be a snapshot depending on current group membership but is the + best we can do and has the advantage of failing closed rather + than open. + --------------------------------------------------------------------------- + + Note we *MUST* do the deny user pass first as this will convert deny user + entries into allow user entries which can then be processed by the deny + group pass. + + The above algorithm took a *lot* of thinking about - hence this + explaination :-). JRA. +****************************************************************************/ + +/**************************************************************************** + Process a canon_ace list entries. This is very complex code. We need + to go through and remove the "deny" permissions from any allow entry that matches + the id of this entry. We have already refused any NT ACL that wasn't in correct + order (DENY followed by ALLOW). If any allow entry ends up with zero permissions, + we just remove it (to fail safe). We have already removed any duplicate ace + entries. Treat an "Everyone" DENY_ACE as a special case - use it to mask all + allow entries. +****************************************************************************/ + +static void process_deny_list( canon_ace **pp_ace_list ) +{ + extern DOM_SID global_sid_World; + canon_ace *ace_list = *pp_ace_list; + canon_ace *curr_ace = NULL; + canon_ace *curr_ace_next = NULL; + + /* Pass 1 above - look for an Everyone, deny entry. */ + + for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) { + canon_ace *allow_ace_p; + + curr_ace_next = curr_ace->next; /* So we can't lose the link. */ + + if (curr_ace->attr != DENY_ACE) + continue; + + if (curr_ace->perms == (mode_t)0) { + + /* Deny nothing entry - delete. */ + + DLIST_REMOVE(ace_list, curr_ace); + continue; + } + + if (!sid_equal(&curr_ace->trustee, &global_sid_World)) + continue; + + /* JRATEST - assert. */ + SMB_ASSERT(curr_ace->owner_type == WORLD_ACE); + + if (curr_ace->perms == ALL_ACE_PERMS) { + + /* + * Optimisation. This is a DENY_ALL to Everyone. Truncate the + * list at this point including this entry. + */ + + canon_ace *prev_entry = curr_ace->prev; + + free_canon_ace_list( curr_ace ); + if (prev_entry) + prev_entry->next = NULL; + else { + /* We deleted the entire list. */ + ace_list = NULL; + } + break; + } + + for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) { + + /* + * Only mask off allow entries. + */ + + if (allow_ace_p->attr != ALLOW_ACE) + continue; + + allow_ace_p->perms &= ~curr_ace->perms; + } + + /* + * Now it's been applied, remove it. + */ + + DLIST_REMOVE(ace_list, curr_ace); + } + + /* Pass 2 above - deal with deny user entries. */ + + for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) { + mode_t new_perms = (mode_t)0; + canon_ace *allow_ace_p; + canon_ace *tmp_ace; + + curr_ace_next = curr_ace->next; /* So we can't lose the link. */ + + if (curr_ace->attr != DENY_ACE) + continue; + + if (curr_ace->owner_type != UID_ACE) + continue; + + if (curr_ace->perms == ALL_ACE_PERMS) { + + /* + * Optimisation - this is a deny everything to this user. + * Convert to an allow nothing and push to the end of the list. + */ + + curr_ace->attr = ALLOW_ACE; + curr_ace->perms = (mode_t)0; + DLIST_DEMOTE(ace_list, curr_ace, tmp_ace); + continue; + } + + for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) { + + if (allow_ace_p->attr != ALLOW_ACE) + continue; + + /* We process GID_ACE and WORLD_ACE entries only. */ + + if (allow_ace_p->owner_type == UID_ACE) + continue; + + if (uid_entry_in_group( curr_ace, allow_ace_p)) + new_perms |= allow_ace_p->perms; + } + + /* + * Convert to a allow entry, modify the perms and push to the end + * of the list. + */ + + curr_ace->attr = ALLOW_ACE; + curr_ace->perms = (new_perms & ~curr_ace->perms); + DLIST_DEMOTE(ace_list, curr_ace, tmp_ace); + } + + /* Pass 3 above - deal with deny group entries. */ + + for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) { + canon_ace *tmp_ace; + canon_ace *allow_ace_p; + canon_ace *allow_everyone_p = NULL; + + curr_ace_next = curr_ace->next; /* So we can't lose the link. */ + + if (curr_ace->attr != DENY_ACE) + continue; + + if (curr_ace->owner_type != GID_ACE) + continue; + + for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) { + + if (allow_ace_p->attr != ALLOW_ACE) + continue; + + /* Store a pointer to the Everyone allow, if it exists. */ + if (allow_ace_p->owner_type == WORLD_ACE) + allow_everyone_p = allow_ace_p; + + /* We process UID_ACE entries only. */ + + if (allow_ace_p->owner_type != UID_ACE) + continue; + + /* Mask off the deny group perms. */ + + if (uid_entry_in_group( allow_ace_p, curr_ace)) + allow_ace_p->perms &= ~curr_ace->perms; + } + + /* + * Convert the deny to an allow with the correct perms and + * push to the end of the list. + */ + + curr_ace->attr = ALLOW_ACE; + if (allow_everyone_p) + curr_ace->perms = allow_everyone_p->perms & ~curr_ace->perms; + else + curr_ace->perms = (mode_t)0; + DLIST_DEMOTE(ace_list, curr_ace, tmp_ace); + + } + + *pp_ace_list = ace_list; +} + +/**************************************************************************** + Create a default mode that will be used if a security descriptor entry has + no user/group/world entries. +****************************************************************************/ + +static mode_t create_default_mode(files_struct *fsp, BOOL interitable_mode) +{ + int snum = SNUM(fsp->conn); + mode_t and_bits = (mode_t)0; + mode_t or_bits = (mode_t)0; + mode_t mode = interitable_mode ? unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name) : S_IRUSR; + + if (fsp->is_directory) + mode |= (S_IWUSR|S_IXUSR); + + /* + * Now AND with the create mode/directory mode bits then OR with the + * force create mode/force directory mode bits. + */ + + if (fsp->is_directory) { + and_bits = lp_dir_security_mask(snum); + or_bits = lp_force_dir_security_mode(snum); + } else { + and_bits = lp_security_mask(snum); + or_bits = lp_force_security_mode(snum); + } + + return ((mode & and_bits)|or_bits); +} + +/**************************************************************************** + Unpack a SEC_DESC into two canonical ace lists. We don't depend on this + succeeding. +****************************************************************************/ + +static BOOL unpack_canon_ace(files_struct *fsp, + SMB_STRUCT_STAT *pst, + DOM_SID *pfile_owner_sid, + DOM_SID *pfile_grp_sid, + canon_ace **ppfile_ace, canon_ace **ppdir_ace, + uint32 security_info_sent, SEC_DESC *psd) +{ + canon_ace *file_ace = NULL; + canon_ace *dir_ace = NULL; + + *ppfile_ace = NULL; + *ppdir_ace = NULL; + + if(security_info_sent == 0) { + DEBUG(0,("unpack_canon_ace: no security info sent !\n")); + return False; + } + + /* + * If no DACL then this is a chown only security descriptor. + */ + + if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !psd->dacl) + return True; + + /* + * Now go through the DACL and create the canon_ace lists. + */ + + if (!create_canon_ace_lists( fsp, pfile_owner_sid, pfile_grp_sid, + &file_ace, &dir_ace, psd->dacl)) + return False; + + if ((file_ace == NULL) && (dir_ace == NULL)) { + /* W2K traverse DACL set - ignore. */ + return True; + } + + /* + * Go through the canon_ace list and merge entries + * belonging to identical users of identical allow or deny type. + * We can do this as all deny entries come first, followed by + * all allow entries (we have mandated this before accepting this acl). + */ + + print_canon_ace_list( "file ace - before merge", file_ace); + merge_aces( &file_ace ); + + print_canon_ace_list( "dir ace - before merge", dir_ace); + merge_aces( &dir_ace ); + + /* + * NT ACLs are order dependent. Go through the acl lists and + * process DENY entries by masking the allow entries. + */ + + print_canon_ace_list( "file ace - before deny", file_ace); + process_deny_list( &file_ace); + + print_canon_ace_list( "dir ace - before deny", dir_ace); + process_deny_list( &dir_ace); + + /* + * A well formed POSIX file or default ACL has at least 3 entries, a + * SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ + * and optionally a mask entry. Ensure this is the case. + */ + + print_canon_ace_list( "file ace - before valid", file_ace); + + /* + * A default 3 element mode entry for a file should be r-- --- ---. + * A default 3 element mode entry for a directory should be rwx --- ---. + */ + + pst->st_mode = create_default_mode(fsp, False); + + if (!ensure_canon_entry_valid(&file_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) { + free_canon_ace_list(file_ace); + free_canon_ace_list(dir_ace); + return False; + } + + print_canon_ace_list( "dir ace - before valid", dir_ace); + + /* + * A default inheritable 3 element mode entry for a directory should be the + * mode Samba will use to create a file within. Ensure user rwx bits are set if + * it's a directory. + */ + + pst->st_mode = create_default_mode(fsp, True); + + if (!ensure_canon_entry_valid(&dir_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) { + free_canon_ace_list(file_ace); + free_canon_ace_list(dir_ace); + return False; + } + + print_canon_ace_list( "file ace - return", file_ace); + print_canon_ace_list( "dir ace - return", dir_ace); + + *ppfile_ace = file_ace; + *ppdir_ace = dir_ace; + return True; + +} + +/****************************************************************************** + When returning permissions, try and fit NT display + semantics if possible. Note the the canon_entries here must have been malloced. + The list format should be - first entry = owner, followed by group and other user + entries, last entry = other. + + Note that this doesn't exactly match the NT semantics for an ACL. As POSIX entries + are not ordered, and match on the most specific entry rather than walking a list, + then a simple POSIX permission of rw-r--r-- should really map to 6 entries, + + Entry 0: owner : deny all except read and write. + Entry 1: group : deny all except read. + Entry 2: Everyone : deny all except read. + Entry 3: owner : allow read and write. + Entry 4: group : allow read. + Entry 5: Everyone : allow read. + + But NT cannot display this in their ACL editor ! +********************************************************************************/ + +static void arrange_posix_perms( char *filename, canon_ace **pp_list_head) +{ + canon_ace *list_head = *pp_list_head; + canon_ace *owner_ace = NULL; + canon_ace *other_ace = NULL; + canon_ace *ace = NULL; + + for (ace = list_head; ace; ace = ace->next) { + if (ace->type == SMB_ACL_USER_OBJ) + owner_ace = ace; + else if (ace->type == SMB_ACL_OTHER) { + /* Last ace - this is "other" */ + other_ace = ace; + } + } + + if (!owner_ace || !other_ace) { + DEBUG(0,("arrange_posix_perms: Invalid POSIX permissions for file %s, missing owner or other.\n", + filename )); + return; + } + + /* + * The POSIX algorithm applies to owner first, and other last, + * so ensure they are arranged in this order. + */ + + if (owner_ace) { + DLIST_PROMOTE(list_head, owner_ace); + } + + if (other_ace) { + DLIST_DEMOTE(list_head, other_ace, ace); + } + + /* We have probably changed the head of the list. */ + + *pp_list_head = list_head; +} + +/**************************************************************************** + Create a linked list of canonical ACE entries. +****************************************************************************/ + +static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf, + DOM_SID *powner, DOM_SID *pgroup) +{ + extern DOM_SID global_sid_World; + connection_struct *conn = fsp->conn; + mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR); + canon_ace *list_head = NULL; + canon_ace *ace = NULL; + canon_ace *next_ace = NULL; + int entry_id = SMB_ACL_FIRST_ENTRY; + SMB_ACL_ENTRY_T entry; + size_t ace_count; + + while ( posix_acl && (conn->vfs_ops.sys_acl_get_entry(conn, posix_acl, entry_id, &entry) == 1)) { + SMB_ACL_TAG_T tagtype; + SMB_ACL_PERMSET_T permset; + DOM_SID sid; + posix_id unix_ug; + enum ace_owner owner_type; + + /* get_next... */ + if (entry_id == SMB_ACL_FIRST_ENTRY) + entry_id = SMB_ACL_NEXT_ENTRY; + + /* Is this a MASK entry ? */ + if (conn->vfs_ops.sys_acl_get_tag_type(conn, entry, &tagtype) == -1) + continue; + + if (conn->vfs_ops.sys_acl_get_permset(conn, entry, &permset) == -1) + continue; + + /* Decide which SID to use based on the ACL type. */ + switch(tagtype) { + case SMB_ACL_USER_OBJ: + /* Get the SID from the owner. */ + uid_to_sid( &sid, psbuf->st_uid ); + unix_ug.uid = psbuf->st_uid; + owner_type = UID_ACE; + break; + case SMB_ACL_USER: + { + uid_t *puid = (uid_t *)conn->vfs_ops.sys_acl_get_qualifier(conn, entry); + if (puid == NULL) { + DEBUG(0,("canonicalise_acl: Failed to get uid.\n")); + continue; + } + uid_to_sid( &sid, *puid); + unix_ug.uid = *puid; + owner_type = UID_ACE; + conn->vfs_ops.sys_acl_free_qualifier(conn, (void *)puid,tagtype); + break; + } + case SMB_ACL_GROUP_OBJ: + /* Get the SID from the owning group. */ + gid_to_sid( &sid, psbuf->st_gid ); + unix_ug.gid = psbuf->st_gid; + owner_type = GID_ACE; + break; + case SMB_ACL_GROUP: + { + gid_t *pgid = (gid_t *)conn->vfs_ops.sys_acl_get_qualifier(conn, entry); + if (pgid == NULL) { + DEBUG(0,("canonicalise_acl: Failed to get gid.\n")); + continue; + } + gid_to_sid( &sid, *pgid); + unix_ug.gid = *pgid; + owner_type = GID_ACE; + conn->vfs_ops.sys_acl_free_qualifier(conn, (void *)pgid,tagtype); + break; + } + case SMB_ACL_MASK: + acl_mask = convert_permset_to_mode_t(conn, permset); + continue; /* Don't count the mask as an entry. */ + case SMB_ACL_OTHER: + /* Use the Everyone SID */ + sid = global_sid_World; + unix_ug.world = -1; + owner_type = WORLD_ACE; + break; + default: + DEBUG(0,("canonicalise_acl: Unknown tagtype %u\n", (unsigned int)tagtype)); + continue; + } + + /* + * Add this entry to the list. + */ + + if ((ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) + goto fail; + + ZERO_STRUCTP(ace); + ace->type = tagtype; + ace->perms = convert_permset_to_mode_t(conn, permset); + ace->attr = ALLOW_ACE; + ace->trustee = sid; + ace->unix_ug = unix_ug; + ace->owner_type = owner_type; + + DLIST_ADD(list_head, ace); + } + + /* + * This next call will ensure we have at least a user/group/world set. + */ + + if (!ensure_canon_entry_valid(&list_head, fsp, powner, pgroup, psbuf, False)) + goto fail; + + arrange_posix_perms(fsp->fsp_name,&list_head ); + + /* + * Now go through the list, masking the permissions with the + * acl_mask. Ensure all DENY Entries are at the start of the list. + */ + + DEBUG(10,("canonicalise_acl: ace entries before arrange :\n")); + + for ( ace_count = 0, ace = list_head; ace; ace = next_ace, ace_count++) { + next_ace = ace->next; + + /* Masks are only applied to entries other than USER_OBJ and OTHER. */ + if (ace->type != SMB_ACL_OTHER && ace->type != SMB_ACL_USER_OBJ) + ace->perms &= acl_mask; + + if (ace->perms == 0) { + DLIST_PROMOTE(list_head, ace); + } + + if( DEBUGLVL( 10 ) ) { + print_canon_ace(ace, ace_count); + } + } + + print_canon_ace_list( "canonicalise_acl: ace entries after arrange", list_head ); + + return list_head; + + fail: + + free_canon_ace_list(list_head); + return NULL; +} + +/**************************************************************************** + Attempt to apply an ACL to a file or directory. +****************************************************************************/ + +static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL default_ace, BOOL *pacl_set_support) +{ + connection_struct *conn = fsp->conn; + BOOL ret = False; + SMB_ACL_T the_acl = conn->vfs_ops.sys_acl_init(conn, (int)count_canon_ace_list(the_ace) + 1); + canon_ace *p_ace; + int i; + SMB_ACL_ENTRY_T mask_entry; + SMB_ACL_PERMSET_T mask_permset; + SMB_ACL_TYPE_T the_acl_type = (default_ace ? SMB_ACL_TYPE_DEFAULT : SMB_ACL_TYPE_ACCESS); + + if (the_acl == NULL) { + + if (errno != ENOSYS) { + /* + * Only print this error message if we have some kind of ACL + * support that's not working. Otherwise we would always get this. + */ + DEBUG(0,("set_canon_ace_list: Unable to init %s ACL. (%s)\n", + default_ace ? "default" : "file", strerror(errno) )); + } + *pacl_set_support = False; + return False; + } + + for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) { + SMB_ACL_ENTRY_T the_entry; + SMB_ACL_PERMSET_T the_permset; + + /* + * Get the entry for this ACE. + */ + + if (conn->vfs_ops.sys_acl_create_entry(conn, &the_acl, &the_entry) == -1) { + DEBUG(0,("set_canon_ace_list: Failed to create entry %d. (%s)\n", + i, strerror(errno) )); + goto done; + } + + /* + * Ok - we now know the ACL calls should be working, don't + * allow fallback to chmod. + */ + + *pacl_set_support = True; + + /* + * Initialise the entry from the canon_ace. + */ + + /* + * First tell the entry what type of ACE this is. + */ + + if (conn->vfs_ops.sys_acl_set_tag_type(conn, the_entry, p_ace->type) == -1) { + DEBUG(0,("set_canon_ace_list: Failed to set tag type on entry %d. (%s)\n", + i, strerror(errno) )); + goto done; + } + + /* + * Only set the qualifier (user or group id) if the entry is a user + * or group id ACE. + */ + + if ((p_ace->type == SMB_ACL_USER) || (p_ace->type == SMB_ACL_GROUP)) { + if (conn->vfs_ops.sys_acl_set_qualifier(conn, the_entry,(void *)&p_ace->unix_ug.uid) == -1) { + DEBUG(0,("set_canon_ace_list: Failed to set qualifier on entry %d. (%s)\n", + i, strerror(errno) )); + goto done; + } + } + + /* + * Convert the mode_t perms in the canon_ace to a POSIX permset. + */ + + if (conn->vfs_ops.sys_acl_get_permset(conn, the_entry, &the_permset) == -1) { + DEBUG(0,("set_canon_ace_list: Failed to get permset on entry %d. (%s)\n", + i, strerror(errno) )); + goto done; + } + + if (map_acl_perms_to_permset(conn, p_ace->perms, &the_permset) == -1) { + DEBUG(0,("set_canon_ace_list: Failed to create permset for mode (%u) on entry %d. (%s)\n", + (unsigned int)p_ace->perms, i, strerror(errno) )); + goto done; + } + + /* + * ..and apply them to the entry. + */ + + if (conn->vfs_ops.sys_acl_set_permset(conn, the_entry, the_permset) == -1) { + DEBUG(0,("set_canon_ace_list: Failed to add permset on entry %d. (%s)\n", + i, strerror(errno) )); + goto done; + } + + if( DEBUGLVL( 10 )) + print_canon_ace( p_ace, i); + } + + /* + * Add in a mask of rwx. + */ + + if (conn->vfs_ops.sys_acl_create_entry( conn, &the_acl, &mask_entry) == -1) { + DEBUG(0,("set_canon_ace_list: Failed to create mask entry. (%s)\n", strerror(errno) )); + goto done; + } + + if (conn->vfs_ops.sys_acl_set_tag_type(conn, mask_entry, SMB_ACL_MASK) == -1) { + DEBUG(0,("set_canon_ace_list: Failed to set tag type on mask entry. (%s)\n",strerror(errno) )); + goto done; + } + + if (conn->vfs_ops.sys_acl_get_permset(conn, mask_entry, &mask_permset) == -1) { + DEBUG(0,("set_canon_ace_list: Failed to get mask permset. (%s)\n", strerror(errno) )); + goto done; + } + + if (map_acl_perms_to_permset(conn, S_IRUSR|S_IWUSR|S_IXUSR, &mask_permset) == -1) { + DEBUG(0,("set_canon_ace_list: Failed to create mask permset. (%s)\n", strerror(errno) )); + goto done; + } + + if (conn->vfs_ops.sys_acl_set_permset(conn, mask_entry, mask_permset) == -1) { + DEBUG(0,("set_canon_ace_list: Failed to add mask permset. (%s)\n", strerror(errno) )); + goto done; + } + + /* + * Check if the ACL is valid. + */ + + if (conn->vfs_ops.sys_acl_valid(conn, the_acl) == -1) { + DEBUG(0,("set_canon_ace_list: ACL type (%s) is invalid for set (%s).\n", + the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file", + strerror(errno) )); + goto done; + } + + /* + * Finally apply it to the file or directory. + */ + + if(default_ace || fsp->is_directory || fsp->fd == -1) { + if (conn->vfs_ops.sys_acl_set_file(conn, fsp->fsp_name, the_acl_type, the_acl) == -1) { + /* + * Some systems allow all the above calls and only fail with no ACL support + * when attempting to apply the acl. HPUX with HFS is an example of this. JRA. + */ + if (errno == ENOSYS) + *pacl_set_support = False; + DEBUG(2,("set_canon_ace_list: sys_acl_set_file type %s failed for file %s (%s).\n", + the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file", + fsp->fsp_name, strerror(errno) )); + goto done; + } + } else { + if (conn->vfs_ops.sys_acl_set_fd(fsp, fsp->fd, the_acl) == -1) { + /* + * Some systems allow all the above calls and only fail with no ACL support + * when attempting to apply the acl. HPUX with HFS is an example of this. JRA. + */ + if (errno == ENOSYS) + *pacl_set_support = False; + DEBUG(2,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n", + fsp->fsp_name, strerror(errno) )); + goto done; + } + } + + ret = True; + + done: + + if (the_acl != NULL) + conn->vfs_ops.sys_acl_free_acl(conn, the_acl); + + return ret; +} + +/**************************************************************************** + Convert a canon_ace to a generic 3 element permission - if possible. +****************************************************************************/ + +#define MAP_PERM(p,mask,result) (((p) & (mask)) ? (result) : 0 ) + +static BOOL convert_canon_ace_to_posix_perms( files_struct *fsp, canon_ace *file_ace_list, mode_t *posix_perms) +{ + int snum = SNUM(fsp->conn); + size_t ace_count = count_canon_ace_list(file_ace_list); + canon_ace *ace_p; + canon_ace *owner_ace = NULL; + canon_ace *group_ace = NULL; + canon_ace *other_ace = NULL; + mode_t and_bits; + mode_t or_bits; + + if (ace_count != 3) { + DEBUG(3,("convert_canon_ace_to_posix_perms: Too many ACE entries for file %s to convert to \ +posix perms.\n", fsp->fsp_name )); + return False; + } + + for (ace_p = file_ace_list; ace_p; ace_p = ace_p->next) { + if (ace_p->owner_type == UID_ACE) + owner_ace = ace_p; + else if (ace_p->owner_type == GID_ACE) + group_ace = ace_p; + else if (ace_p->owner_type == WORLD_ACE) + other_ace = ace_p; + } + + if (!owner_ace || !group_ace || !other_ace) { + DEBUG(3,("convert_canon_ace_to_posix_perms: Can't get standard entries for file %s.\n", + fsp->fsp_name )); + return False; + } + + *posix_perms = (mode_t)0; + + *posix_perms |= owner_ace->perms; + *posix_perms |= MAP_PERM(group_ace->perms, S_IRUSR, S_IRGRP); + *posix_perms |= MAP_PERM(group_ace->perms, S_IWUSR, S_IWGRP); + *posix_perms |= MAP_PERM(group_ace->perms, S_IXUSR, S_IXGRP); + *posix_perms |= MAP_PERM(other_ace->perms, S_IRUSR, S_IROTH); + *posix_perms |= MAP_PERM(other_ace->perms, S_IWUSR, S_IWOTH); + *posix_perms |= MAP_PERM(other_ace->perms, S_IXUSR, S_IXOTH); + + /* The owner must have at least read access. */ + + *posix_perms |= S_IRUSR; + if (fsp->is_directory) + *posix_perms |= (S_IWUSR|S_IXUSR); + + /* If requested apply the masks. */ + + /* Get the initial bits to apply. */ + + if (fsp->is_directory) { + and_bits = lp_dir_security_mask(snum); + or_bits = lp_force_dir_security_mode(snum); + } else { + and_bits = lp_security_mask(snum); + or_bits = lp_force_security_mode(snum); + } + + *posix_perms = (((*posix_perms) & and_bits)|or_bits); + + DEBUG(10,("convert_canon_ace_to_posix_perms: converted u=%o,g=%o,w=%o to perm=0%o for file %s.\n", + (int)owner_ace->perms, (int)group_ace->perms, (int)other_ace->perms, (int)*posix_perms, + fsp->fsp_name )); + + return True; +} + +static int nt_ace_comp( SEC_ACE *a1, SEC_ACE *a2) +{ + if (a1->type == a2->type) + return 0; + + if (a1->type == SEC_ACE_TYPE_ACCESS_DENIED && a2->type == SEC_ACE_TYPE_ACCESS_ALLOWED) + return -1; + return 1; +} + +/**************************************************************************** + Reply to query a security descriptor from an fsp. If it succeeds it allocates + the space for the return elements and returns the size needed to return the + security descriptor. This should be the only external function needed for + the UNIX style get ACL. +****************************************************************************/ + +size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc) +{ + connection_struct *conn = fsp->conn; + SMB_STRUCT_STAT sbuf; + SEC_ACE *nt_ace_list = NULL; + DOM_SID owner_sid; + DOM_SID group_sid; + size_t sd_size = 0; + SEC_ACL *psa = NULL; + size_t num_acls = 0; + size_t num_dir_acls = 0; + size_t num_aces = 0; + SMB_ACL_T posix_acl = NULL; + SMB_ACL_T dir_acl = NULL; + canon_ace *file_ace = NULL; + canon_ace *dir_ace = NULL; + + *ppdesc = NULL; + + DEBUG(10,("get_nt_acl: called for file %s\n", fsp->fsp_name )); + + if(fsp->is_directory || fsp->fd == -1) { + + /* Get the stat struct for the owner info. */ + if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0) { + return 0; + } + /* + * Get the ACL from the path. + */ + + posix_acl = conn->vfs_ops.sys_acl_get_file(conn, fsp->fsp_name, SMB_ACL_TYPE_ACCESS); + + /* + * If it's a directory get the default POSIX ACL. + */ + + if(fsp->is_directory) + dir_acl = conn->vfs_ops.sys_acl_get_file(conn, fsp->fsp_name, SMB_ACL_TYPE_DEFAULT); + + } else { + + /* Get the stat struct for the owner info. */ + if(vfs_fstat(fsp,fsp->fd,&sbuf) != 0) { + return 0; + } + /* + * Get the ACL from the fd. + */ + posix_acl = conn->vfs_ops.sys_acl_get_fd(fsp, fsp->fd); + } + + DEBUG(5,("get_nt_acl : file ACL %s, directory ACL %s\n", + posix_acl ? "present" : "absent", + dir_acl ? "present" : "absent" )); + + /* + * Get the owner, group and world SIDs. + */ + + create_file_sids(&sbuf, &owner_sid, &group_sid); + + /* Create the canon_ace lists. */ + file_ace = canonicalise_acl( fsp, posix_acl, &sbuf, &owner_sid, &group_sid); + num_acls = count_canon_ace_list(file_ace); + + /* We must have *some* ACLS. */ + + if (num_acls == 0) { + DEBUG(0,("get_nt_acl : No ACLs on file (%s) !\n", fsp->fsp_name )); + return 0; + } + + if (fsp->is_directory) { + /* + * If we have to fake a default ACL then this is the mode to use. + */ + sbuf.st_mode = unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name); + + dir_ace = canonicalise_acl(fsp, dir_acl, &sbuf, &owner_sid, &group_sid); + num_dir_acls = count_canon_ace_list(dir_ace); + } + + /* Allocate the ace list. */ + if ((nt_ace_list = (SEC_ACE *)malloc((num_acls + num_dir_acls)* sizeof(SEC_ACE))) == NULL) { + DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n")); + goto done; + } + + memset(nt_ace_list, '\0', (num_acls + num_dir_acls) * sizeof(SEC_ACE) ); + + /* + * Create the NT ACE list from the canonical ace lists. + */ + + { + canon_ace *ace; + int nt_acl_type; + int i; + + ace = file_ace; + + for (i = 0; i < num_acls; i++, ace = ace->next) { + SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace ); + init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc, 0); + } + + ace = dir_ace; + + for (i = 0; i < num_dir_acls; i++, ace = ace->next) { + SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace ); + init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc, + SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY); + } + + /* + * Sort to force deny entries to the front. + */ + + if (num_acls + num_dir_acls) + qsort( nt_ace_list, num_acls + num_dir_acls, sizeof(nt_ace_list[0]), QSORT_CAST nt_ace_comp); + } + + if (num_acls) { + if((psa = make_sec_acl( main_loop_talloc_get(), ACL_REVISION, num_aces, nt_ace_list)) == NULL) { + DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n")); + goto done; + } + } + + *ppdesc = make_standard_sec_desc( main_loop_talloc_get(), &owner_sid, &group_sid, psa, &sd_size); + + if(!*ppdesc) { + DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n")); + sd_size = 0; + } + + done: + + if (posix_acl) + conn->vfs_ops.sys_acl_free_acl(conn, posix_acl); + if (dir_acl) + conn->vfs_ops.sys_acl_free_acl(conn, dir_acl); + free_canon_ace_list(file_ace); + free_canon_ace_list(dir_ace); + SAFE_FREE(nt_ace_list); + + return sd_size; +} + +/**************************************************************************** + Reply to set a security descriptor on an fsp. security_info_sent is the + description of the following NT ACL. + This should be the only external function needed for the UNIX style set ACL. +****************************************************************************/ + +BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd) +{ + connection_struct *conn = fsp->conn; + uid_t user = (uid_t)-1; + gid_t grp = (gid_t)-1; + SMB_STRUCT_STAT sbuf; + DOM_SID file_owner_sid; + DOM_SID file_grp_sid; + canon_ace *file_ace_list = NULL; + canon_ace *dir_ace_list = NULL; + BOOL acl_perms = False; + mode_t orig_mode = (mode_t)0; + uid_t orig_uid; + gid_t orig_gid; + + DEBUG(10,("set_nt_acl: called for file %s\n", fsp->fsp_name )); + + /* + * Get the current state of the file. + */ + + if(fsp->is_directory || fsp->fd == -1) { + if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0) + return False; + } else { + if(vfs_fstat(fsp,fsp->fd,&sbuf) != 0) + return False; + } + + /* Save the original elements we check against. */ + orig_mode = sbuf.st_mode; + orig_uid = sbuf.st_uid; + orig_gid = sbuf.st_gid; + + /* + * Unpack the user/group/world id's. + */ + + if (!unpack_nt_owners( &sbuf, &user, &grp, security_info_sent, psd)) + return False; + + /* + * Do we need to chown ? + */ + + if((user != (uid_t)-1 || grp != (uid_t)-1) && (orig_uid != user || orig_gid != grp)) { + + DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n", + fsp->fsp_name, (unsigned int)user, (unsigned int)grp )); + + if(vfs_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) { + DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n", + fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) )); + return False; + } + + /* + * Recheck the current state of the file, which may have changed. + * (suid/sgid bits, for instance) + */ + + if(fsp->is_directory) { + if(vfs_stat(fsp->conn, fsp->fsp_name, &sbuf) != 0) { + return False; + } + } else { + + int ret; + + if(fsp->fd == -1) + ret = vfs_stat(fsp->conn, fsp->fsp_name, &sbuf); + else + ret = vfs_fstat(fsp,fsp->fd,&sbuf); + + if(ret != 0) + return False; + } + + /* Save the original elements we check against. */ + orig_mode = sbuf.st_mode; + orig_uid = sbuf.st_uid; + orig_gid = sbuf.st_gid; + } + + create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid); + + acl_perms = unpack_canon_ace( fsp, &sbuf, &file_owner_sid, &file_grp_sid, + &file_ace_list, &dir_ace_list, security_info_sent, psd); + + if ((file_ace_list == NULL) && (dir_ace_list == NULL)) { + /* W2K traverse DACL set - ignore. */ + return True; + } + + if (!acl_perms) { + DEBUG(3,("set_nt_acl: cannot set permissions\n")); + free_canon_ace_list(file_ace_list); + free_canon_ace_list(dir_ace_list); + return False; + } + + /* + * Only change security if we got a DACL. + */ + + if((security_info_sent & DACL_SECURITY_INFORMATION) && (psd->dacl != NULL)) { + + BOOL acl_set_support = False; + BOOL ret = False; + + /* + * Try using the POSIX ACL set first. Fall back to chmod if + * we have no ACL support on this filesystem. + */ + + if (acl_perms && file_ace_list) { + ret = set_canon_ace_list(fsp, file_ace_list, False, &acl_set_support); + if (acl_set_support && ret == False) { + DEBUG(3,("set_nt_acl: failed to set file acl on file %s (%s).\n", fsp->fsp_name, strerror(errno) )); + free_canon_ace_list(file_ace_list); + free_canon_ace_list(dir_ace_list); + return False; + } + } + + if (acl_perms && acl_set_support && fsp->is_directory) { + if (dir_ace_list) { + if (!set_canon_ace_list(fsp, dir_ace_list, True, &acl_set_support)) { + DEBUG(3,("set_nt_acl: failed to set default acl on directory %s (%s).\n", fsp->fsp_name, strerror(errno) )); + free_canon_ace_list(file_ace_list); + free_canon_ace_list(dir_ace_list); + return False; + } + } else { + + /* + * No default ACL - delete one if it exists. + */ + + if (conn->vfs_ops.sys_acl_delete_def_file(conn, fsp->fsp_name) == -1) { + DEBUG(3,("set_nt_acl: sys_acl_delete_def_file failed (%s)\n", strerror(errno))); + free_canon_ace_list(file_ace_list); + return False; + } + } + } + + /* + * If we cannot set using POSIX ACLs we fall back to checking if we need to chmod. + */ + + if(!acl_set_support && acl_perms) { + mode_t posix_perms; + + if (!convert_canon_ace_to_posix_perms( fsp, file_ace_list, &posix_perms)) { + free_canon_ace_list(file_ace_list); + free_canon_ace_list(dir_ace_list); + DEBUG(3,("set_nt_acl: failed to convert file acl to posix permissions for file %s.\n", + fsp->fsp_name )); + return False; + } + + if (orig_mode != posix_perms) { + + DEBUG(3,("set_nt_acl: chmod %s. perms = 0%o.\n", + fsp->fsp_name, (unsigned int)posix_perms )); + + if(conn->vfs_ops.chmod(conn,fsp->fsp_name, posix_perms) == -1) { + DEBUG(3,("set_nt_acl: chmod %s, 0%o failed. Error = %s.\n", + fsp->fsp_name, (unsigned int)posix_perms, strerror(errno) )); + free_canon_ace_list(file_ace_list); + free_canon_ace_list(dir_ace_list); + return False; + } + } + } + } + + free_canon_ace_list(file_ace_list); + free_canon_ace_list(dir_ace_list); + + return True; +} + +/**************************************************************************** + Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL + and set the mask to rwx. Needed to preserve complex ACLs set by NT. +****************************************************************************/ + +static int chmod_acl_internals( connection_struct *conn, SMB_ACL_T posix_acl, mode_t mode) +{ + int entry_id = SMB_ACL_FIRST_ENTRY; + SMB_ACL_ENTRY_T entry; + int num_entries = 0; + + while ( conn->vfs_ops.sys_acl_get_entry(conn, posix_acl, entry_id, &entry) == 1) { + SMB_ACL_TAG_T tagtype; + SMB_ACL_PERMSET_T permset; + mode_t perms; + + /* get_next... */ + if (entry_id == SMB_ACL_FIRST_ENTRY) + entry_id = SMB_ACL_NEXT_ENTRY; + + if (conn->vfs_ops.sys_acl_get_tag_type(conn, entry, &tagtype) == -1) + return -1; + + if (conn->vfs_ops.sys_acl_get_permset(conn, entry, &permset) == -1) + return -1; + + num_entries++; + + switch(tagtype) { + case SMB_ACL_USER_OBJ: + perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR); + break; + case SMB_ACL_GROUP_OBJ: + perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP); + break; + case SMB_ACL_MASK: + perms = S_IRUSR|S_IWUSR|S_IXUSR; + break; + case SMB_ACL_OTHER: + perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH); + break; + default: + continue; + } + + if (map_acl_perms_to_permset(conn, perms, &permset) == -1) + return -1; + + if (conn->vfs_ops.sys_acl_set_permset(conn, entry, permset) == -1) + return -1; + } + + /* + * If this is a simple 3 element ACL or no elements then it's a standard + * UNIX permission set. Just use chmod... + */ + + if ((num_entries == 3) || (num_entries == 0)) + return -1; + + return 0; +} + +/**************************************************************************** + Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL + and set the mask to rwx. Needed to preserve complex ACLs set by NT. + Note that name is in UNIX character set. +****************************************************************************/ + +int chmod_acl(connection_struct *conn, const char *name, mode_t mode) +{ + SMB_ACL_T posix_acl = NULL; + int ret = -1; + + if ((posix_acl = conn->vfs_ops.sys_acl_get_file(conn, name, SMB_ACL_TYPE_ACCESS)) == NULL) + return -1; + + if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1) + goto done; + + ret = conn->vfs_ops.sys_acl_set_file(conn, name, SMB_ACL_TYPE_ACCESS, posix_acl); + + done: + + conn->vfs_ops.sys_acl_free_acl(conn, posix_acl); + return ret; +} + +/**************************************************************************** + Do an fchmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL + and set the mask to rwx. Needed to preserve complex ACLs set by NT. +****************************************************************************/ + +int fchmod_acl(files_struct *fsp, int fd, mode_t mode) +{ + connection_struct *conn = fsp->conn; + SMB_ACL_T posix_acl = NULL; + int ret = -1; + + if ((posix_acl = conn->vfs_ops.sys_acl_get_fd(fsp, fd)) == NULL) + return -1; + + if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1) + goto done; + + ret = conn->vfs_ops.sys_acl_set_fd(fsp, fd, posix_acl); + + done: + + conn->vfs_ops.sys_acl_free_acl(conn, posix_acl); + return ret; +} + +BOOL directory_has_default_acl(connection_struct *conn, const char *fname) +{ + SMB_ACL_T dir_acl = conn->vfs_ops.sys_acl_get_file( conn, fname, SMB_ACL_TYPE_DEFAULT); + BOOL has_acl = False; + SMB_ACL_ENTRY_T entry; + + if (dir_acl != NULL && (conn->vfs_ops.sys_acl_get_entry(conn, dir_acl, SMB_ACL_FIRST_ENTRY, &entry) == 1)) + has_acl = True; + + conn->vfs_ops.sys_acl_free_acl(conn, dir_acl); + return has_acl; +} diff --git a/source3/smbd/process.c b/source3/smbd/process.c new file mode 100644 index 0000000000..007621f6bb --- /dev/null +++ b/source3/smbd/process.c @@ -0,0 +1,1301 @@ +/* + Unix SMB/CIFS implementation. + process incoming packets - main loop + Copyright (C) Andrew Tridgell 1992-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +struct timeval smb_last_time; + +static char *InBuffer = NULL; +char *OutBuffer = NULL; +char *last_inbuf = NULL; + +/* + * Size of data we can send to client. Set + * by the client for all protocols above CORE. + * Set by us for CORE protocol. + */ +int max_send = BUFFER_SIZE; +/* + * Size of the data we can receive. Set by us. + * Can be modified by the max xmit parameter. + */ +int max_recv = BUFFER_SIZE; + +extern int last_message; +extern int global_oplock_break; +extern userdom_struct current_user_info; +extern int smb_read_error; +extern VOLATILE sig_atomic_t reload_after_sighup; +extern VOLATILE sig_atomic_t got_sig_term; +extern BOOL global_machine_password_needs_changing; +extern fstring global_myworkgroup; +extern pstring global_myname; +extern int max_send; + +/**************************************************************************** + structure to hold a linked list of queued messages. + for processing. +****************************************************************************/ + +typedef struct { + ubi_slNode msg_next; + char *msg_buf; + int msg_len; +} pending_message_list; + +static ubi_slList smb_oplock_queue = { NULL, (ubi_slNodePtr)&smb_oplock_queue, 0}; + +/**************************************************************************** + Function to push a message onto the tail of a linked list of smb messages ready + for processing. +****************************************************************************/ + +static BOOL push_message(ubi_slList *list_head, char *buf, int msg_len) +{ + pending_message_list *msg = (pending_message_list *) + malloc(sizeof(pending_message_list)); + + if(msg == NULL) + { + DEBUG(0,("push_message: malloc fail (1)\n")); + return False; + } + + msg->msg_buf = (char *)malloc(msg_len); + if(msg->msg_buf == NULL) + { + DEBUG(0,("push_message: malloc fail (2)\n")); + SAFE_FREE(msg); + return False; + } + + memcpy(msg->msg_buf, buf, msg_len); + msg->msg_len = msg_len; + + ubi_slAddTail( list_head, msg); + + return True; +} + +/**************************************************************************** + Function to push a smb message onto a linked list of local smb messages ready + for processing. +****************************************************************************/ + +BOOL push_oplock_pending_smb_message(char *buf, int msg_len) +{ + return push_message(&smb_oplock_queue, buf, msg_len); +} + +/**************************************************************************** + Do all async processing in here. This includes UDB oplock messages, kernel + oplock messages, change notify events etc. +****************************************************************************/ + +static void async_processing(fd_set *fds, char *buffer, int buffer_len) +{ + /* check for oplock messages (both UDP and kernel) */ + if (receive_local_message(fds, buffer, buffer_len, 0)) { + process_local_message(buffer, buffer_len); + } + + if (got_sig_term) { + exit_server("Caught TERM signal"); + } + + /* check for async change notify events */ + process_pending_change_notify_queue(0); + + /* check for sighup processing */ + if (reload_after_sighup) { + change_to_root_user(); + DEBUG(1,("Reloading services after SIGHUP\n")); + reload_services(False); + reload_after_sighup = 0; + } +} + +/**************************************************************************** + Do a select on an two fd's - with timeout. + + If a local udp message has been pushed onto the + queue (this can only happen during oplock break + processing) call async_processing() + + If a pending smb message has been pushed onto the + queue (this can only happen during oplock break + processing) return this next. + + If the first smbfd is ready then read an smb from it. + if the second (loopback UDP) fd is ready then read a message + from it and setup the buffer header to identify the length + and from address. + Returns False on timeout or error. + Else returns True. + +The timeout is in milli seconds +****************************************************************************/ + +static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout) +{ + fd_set fds; + int selrtn; + struct timeval to; + int maxfd; + + smb_read_error = 0; + + again: + + /* + * Note that this call must be before processing any SMB + * messages as we need to synchronously process any messages + * we may have sent to ourselves from the previous SMB. + */ + message_dispatch(); + + /* + * Check to see if we already have a message on the smb queue. + * If so - copy and return it. + */ + if(ubi_slCount(&smb_oplock_queue) != 0) { + pending_message_list *msg = (pending_message_list *)ubi_slRemHead(&smb_oplock_queue); + memcpy(buffer, msg->msg_buf, MIN(buffer_len, msg->msg_len)); + + /* Free the message we just copied. */ + SAFE_FREE(msg->msg_buf); + SAFE_FREE(msg); + + DEBUG(5,("receive_message_or_smb: returning queued smb message.\n")); + return True; + } + + + /* + * Setup the select read fd set. + */ + + FD_ZERO(&fds); + FD_SET(smbd_server_fd(),&fds); + maxfd = setup_oplock_select_set(&fds); + + to.tv_sec = timeout / 1000; + to.tv_usec = (timeout % 1000) * 1000; + + selrtn = sys_select(MAX(maxfd,smbd_server_fd())+1,&fds,NULL,NULL,timeout>0?&to:NULL); + + /* if we get EINTR then maybe we have received an oplock + signal - treat this as select returning 1. This is ugly, but + is the best we can do until the oplock code knows more about + signals */ + if (selrtn == -1 && errno == EINTR) { + async_processing(&fds, buffer, buffer_len); + /* + * After async processing we must go and do the select again, as + * the state of the flag in fds for the server file descriptor is + * indeterminate - we may have done I/O on it in the oplock processing. JRA. + */ + goto again; + } + + /* Check if error */ + if (selrtn == -1) { + /* something is wrong. Maybe the socket is dead? */ + smb_read_error = READ_ERROR; + return False; + } + + /* Did we timeout ? */ + if (selrtn == 0) { + smb_read_error = READ_TIMEOUT; + return False; + } + + /* + * Ensure we process oplock break messages by preference. + * This is IMPORTANT ! Otherwise we can starve other processes + * sending us an oplock break message. JRA. + */ + + if (oplock_message_waiting(&fds)) { + async_processing(&fds, buffer, buffer_len); + /* + * After async processing we must go and do the select again, as + * the state of the flag in fds for the server file descriptor is + * indeterminate - we may have done I/O on it in the oplock processing. JRA. + */ + goto again; + } + + return receive_smb(smbd_server_fd(), buffer, 0); +} + +/**************************************************************************** +Get the next SMB packet, doing the local message processing automatically. +****************************************************************************/ + +BOOL receive_next_smb(char *inbuf, int bufsize, int timeout) +{ + BOOL got_keepalive; + BOOL ret; + + do { + ret = receive_message_or_smb(inbuf,bufsize,timeout); + + got_keepalive = (ret && (CVAL(inbuf,0) == SMBkeepalive)); + } while (ret && got_keepalive); + + return ret; +} + +/**************************************************************************** + We're terminating and have closed all our files/connections etc. + If there are any pending local messages we need to respond to them + before termination so that other smbds don't think we just died whilst + holding oplocks. +****************************************************************************/ + +void respond_to_all_remaining_local_messages(void) +{ + char buffer[1024]; + fd_set fds; + + /* + * Assert we have no exclusive open oplocks. + */ + + if(get_number_of_exclusive_open_oplocks()) { + DEBUG(0,("respond_to_all_remaining_local_messages: PANIC : we have %d exclusive oplocks.\n", + get_number_of_exclusive_open_oplocks() )); + return; + } + + /* + * Setup the select read fd set. + */ + + FD_ZERO(&fds); + if(!setup_oplock_select_set(&fds)) + return; + + /* + * Keep doing receive_local_message with a 1 ms timeout until + * we have no more messages. + */ + while(receive_local_message(&fds, buffer, sizeof(buffer), 1)) { + /* Deal with oplock break requests from other smbd's. */ + process_local_message(buffer, sizeof(buffer)); + + FD_ZERO(&fds); + (void)setup_oplock_select_set(&fds); + } + + return; +} + + +/* +These flags determine some of the permissions required to do an operation + +Note that I don't set NEED_WRITE on some write operations because they +are used by some brain-dead clients when printing, and I don't want to +force write permissions on print services. +*/ +#define AS_USER (1<<0) +#define NEED_WRITE (1<<1) +#define TIME_INIT (1<<2) +#define CAN_IPC (1<<3) +#define AS_GUEST (1<<5) +#define QUEUE_IN_OPLOCK (1<<6) + +/* + define a list of possible SMB messages and their corresponding + functions. Any message that has a NULL function is unimplemented - + please feel free to contribute implementations! +*/ +static struct smb_message_struct +{ + char *name; + int (*fn)(connection_struct *conn, char *, char *, int, int); + int flags; +} + smb_messages[256] = { + +/* 0x00 */ { "SMBmkdir",reply_mkdir,AS_USER | NEED_WRITE}, +/* 0x01 */ { "SMBrmdir",reply_rmdir,AS_USER | NEED_WRITE}, +/* 0x02 */ { "SMBopen",reply_open,AS_USER | QUEUE_IN_OPLOCK }, +/* 0x03 */ { "SMBcreate",reply_mknew,AS_USER}, +/* 0x04 */ { "SMBclose",reply_close,AS_USER | CAN_IPC }, +/* 0x05 */ { "SMBflush",reply_flush,AS_USER}, +/* 0x06 */ { "SMBunlink",reply_unlink,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK}, +/* 0x07 */ { "SMBmv",reply_mv,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK}, +/* 0x08 */ { "SMBgetatr",reply_getatr,AS_USER}, +/* 0x09 */ { "SMBsetatr",reply_setatr,AS_USER | NEED_WRITE}, +/* 0x0a */ { "SMBread",reply_read,AS_USER}, +/* 0x0b */ { "SMBwrite",reply_write,AS_USER | CAN_IPC }, +/* 0x0c */ { "SMBlock",reply_lock,AS_USER}, +/* 0x0d */ { "SMBunlock",reply_unlock,AS_USER}, +/* 0x0e */ { "SMBctemp",reply_ctemp,AS_USER | QUEUE_IN_OPLOCK }, +/* 0x0f */ { "SMBmknew",reply_mknew,AS_USER}, +/* 0x10 */ { "SMBchkpth",reply_chkpth,AS_USER}, +/* 0x11 */ { "SMBexit",reply_exit,0}, +/* 0x12 */ { "SMBlseek",reply_lseek,AS_USER}, +/* 0x13 */ { "SMBlockread",reply_lockread,AS_USER}, +/* 0x14 */ { "SMBwriteunlock",reply_writeunlock,AS_USER}, +/* 0x15 */ { NULL, NULL, 0 }, +/* 0x16 */ { NULL, NULL, 0 }, +/* 0x17 */ { NULL, NULL, 0 }, +/* 0x18 */ { NULL, NULL, 0 }, +/* 0x19 */ { NULL, NULL, 0 }, +/* 0x1a */ { "SMBreadbraw",reply_readbraw,AS_USER}, +/* 0x1b */ { "SMBreadBmpx",reply_readbmpx,AS_USER}, +/* 0x1c */ { "SMBreadBs",NULL,0 }, +/* 0x1d */ { "SMBwritebraw",reply_writebraw,AS_USER}, +/* 0x1e */ { "SMBwriteBmpx",reply_writebmpx,AS_USER}, +/* 0x1f */ { "SMBwriteBs",reply_writebs,AS_USER}, +/* 0x20 */ { "SMBwritec",NULL,0}, +/* 0x21 */ { NULL, NULL, 0 }, +/* 0x22 */ { "SMBsetattrE",reply_setattrE,AS_USER | NEED_WRITE }, +/* 0x23 */ { "SMBgetattrE",reply_getattrE,AS_USER }, +/* 0x24 */ { "SMBlockingX",reply_lockingX,AS_USER }, +/* 0x25 */ { "SMBtrans",reply_trans,AS_USER | CAN_IPC | QUEUE_IN_OPLOCK}, +/* 0x26 */ { "SMBtranss",NULL,AS_USER | CAN_IPC}, +/* 0x27 */ { "SMBioctl",reply_ioctl,0}, +/* 0x28 */ { "SMBioctls",NULL,AS_USER}, +/* 0x29 */ { "SMBcopy",reply_copy,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK }, +/* 0x2a */ { "SMBmove",NULL,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK }, +/* 0x2b */ { "SMBecho",reply_echo,0}, +/* 0x2c */ { "SMBwriteclose",reply_writeclose,AS_USER}, +/* 0x2d */ { "SMBopenX",reply_open_and_X,AS_USER | CAN_IPC | QUEUE_IN_OPLOCK }, +/* 0x2e */ { "SMBreadX",reply_read_and_X,AS_USER | CAN_IPC }, +/* 0x2f */ { "SMBwriteX",reply_write_and_X,AS_USER | CAN_IPC }, +/* 0x30 */ { NULL, NULL, 0 }, +/* 0x31 */ { NULL, NULL, 0 }, +/* 0x32 */ { "SMBtrans2", reply_trans2, AS_USER | QUEUE_IN_OPLOCK | CAN_IPC }, +/* 0x33 */ { "SMBtranss2", reply_transs2, AS_USER}, +/* 0x34 */ { "SMBfindclose", reply_findclose,AS_USER}, +/* 0x35 */ { "SMBfindnclose", reply_findnclose, AS_USER}, +/* 0x36 */ { NULL, NULL, 0 }, +/* 0x37 */ { NULL, NULL, 0 }, +/* 0x38 */ { NULL, NULL, 0 }, +/* 0x39 */ { NULL, NULL, 0 }, +/* 0x3a */ { NULL, NULL, 0 }, +/* 0x3b */ { NULL, NULL, 0 }, +/* 0x3c */ { NULL, NULL, 0 }, +/* 0x3d */ { NULL, NULL, 0 }, +/* 0x3e */ { NULL, NULL, 0 }, +/* 0x3f */ { NULL, NULL, 0 }, +/* 0x40 */ { NULL, NULL, 0 }, +/* 0x41 */ { NULL, NULL, 0 }, +/* 0x42 */ { NULL, NULL, 0 }, +/* 0x43 */ { NULL, NULL, 0 }, +/* 0x44 */ { NULL, NULL, 0 }, +/* 0x45 */ { NULL, NULL, 0 }, +/* 0x46 */ { NULL, NULL, 0 }, +/* 0x47 */ { NULL, NULL, 0 }, +/* 0x48 */ { NULL, NULL, 0 }, +/* 0x49 */ { NULL, NULL, 0 }, +/* 0x4a */ { NULL, NULL, 0 }, +/* 0x4b */ { NULL, NULL, 0 }, +/* 0x4c */ { NULL, NULL, 0 }, +/* 0x4d */ { NULL, NULL, 0 }, +/* 0x4e */ { NULL, NULL, 0 }, +/* 0x4f */ { NULL, NULL, 0 }, +/* 0x50 */ { NULL, NULL, 0 }, +/* 0x51 */ { NULL, NULL, 0 }, +/* 0x52 */ { NULL, NULL, 0 }, +/* 0x53 */ { NULL, NULL, 0 }, +/* 0x54 */ { NULL, NULL, 0 }, +/* 0x55 */ { NULL, NULL, 0 }, +/* 0x56 */ { NULL, NULL, 0 }, +/* 0x57 */ { NULL, NULL, 0 }, +/* 0x58 */ { NULL, NULL, 0 }, +/* 0x59 */ { NULL, NULL, 0 }, +/* 0x5a */ { NULL, NULL, 0 }, +/* 0x5b */ { NULL, NULL, 0 }, +/* 0x5c */ { NULL, NULL, 0 }, +/* 0x5d */ { NULL, NULL, 0 }, +/* 0x5e */ { NULL, NULL, 0 }, +/* 0x5f */ { NULL, NULL, 0 }, +/* 0x60 */ { NULL, NULL, 0 }, +/* 0x61 */ { NULL, NULL, 0 }, +/* 0x62 */ { NULL, NULL, 0 }, +/* 0x63 */ { NULL, NULL, 0 }, +/* 0x64 */ { NULL, NULL, 0 }, +/* 0x65 */ { NULL, NULL, 0 }, +/* 0x66 */ { NULL, NULL, 0 }, +/* 0x67 */ { NULL, NULL, 0 }, +/* 0x68 */ { NULL, NULL, 0 }, +/* 0x69 */ { NULL, NULL, 0 }, +/* 0x6a */ { NULL, NULL, 0 }, +/* 0x6b */ { NULL, NULL, 0 }, +/* 0x6c */ { NULL, NULL, 0 }, +/* 0x6d */ { NULL, NULL, 0 }, +/* 0x6e */ { NULL, NULL, 0 }, +/* 0x6f */ { NULL, NULL, 0 }, +/* 0x70 */ { "SMBtcon",reply_tcon,0}, +/* 0x71 */ { "SMBtdis",reply_tdis,0}, +/* 0x72 */ { "SMBnegprot",reply_negprot,0}, +/* 0x73 */ { "SMBsesssetupX",reply_sesssetup_and_X,0}, +/* 0x74 */ { "SMBulogoffX", reply_ulogoffX, 0}, /* ulogoff doesn't give a valid TID */ +/* 0x75 */ { "SMBtconX",reply_tcon_and_X,0}, +/* 0x76 */ { NULL, NULL, 0 }, +/* 0x77 */ { NULL, NULL, 0 }, +/* 0x78 */ { NULL, NULL, 0 }, +/* 0x79 */ { NULL, NULL, 0 }, +/* 0x7a */ { NULL, NULL, 0 }, +/* 0x7b */ { NULL, NULL, 0 }, +/* 0x7c */ { NULL, NULL, 0 }, +/* 0x7d */ { NULL, NULL, 0 }, +/* 0x7e */ { NULL, NULL, 0 }, +/* 0x7f */ { NULL, NULL, 0 }, +/* 0x80 */ { "SMBdskattr",reply_dskattr,AS_USER}, +/* 0x81 */ { "SMBsearch",reply_search,AS_USER}, +/* 0x82 */ { "SMBffirst",reply_search,AS_USER}, +/* 0x83 */ { "SMBfunique",reply_search,AS_USER}, +/* 0x84 */ { "SMBfclose",reply_fclose,AS_USER}, +/* 0x85 */ { NULL, NULL, 0 }, +/* 0x86 */ { NULL, NULL, 0 }, +/* 0x87 */ { NULL, NULL, 0 }, +/* 0x88 */ { NULL, NULL, 0 }, +/* 0x89 */ { NULL, NULL, 0 }, +/* 0x8a */ { NULL, NULL, 0 }, +/* 0x8b */ { NULL, NULL, 0 }, +/* 0x8c */ { NULL, NULL, 0 }, +/* 0x8d */ { NULL, NULL, 0 }, +/* 0x8e */ { NULL, NULL, 0 }, +/* 0x8f */ { NULL, NULL, 0 }, +/* 0x90 */ { NULL, NULL, 0 }, +/* 0x91 */ { NULL, NULL, 0 }, +/* 0x92 */ { NULL, NULL, 0 }, +/* 0x93 */ { NULL, NULL, 0 }, +/* 0x94 */ { NULL, NULL, 0 }, +/* 0x95 */ { NULL, NULL, 0 }, +/* 0x96 */ { NULL, NULL, 0 }, +/* 0x97 */ { NULL, NULL, 0 }, +/* 0x98 */ { NULL, NULL, 0 }, +/* 0x99 */ { NULL, NULL, 0 }, +/* 0x9a */ { NULL, NULL, 0 }, +/* 0x9b */ { NULL, NULL, 0 }, +/* 0x9c */ { NULL, NULL, 0 }, +/* 0x9d */ { NULL, NULL, 0 }, +/* 0x9e */ { NULL, NULL, 0 }, +/* 0x9f */ { NULL, NULL, 0 }, +/* 0xa0 */ { "SMBnttrans", reply_nttrans, AS_USER | CAN_IPC | QUEUE_IN_OPLOCK}, +/* 0xa1 */ { "SMBnttranss", reply_nttranss, AS_USER | CAN_IPC }, +/* 0xa2 */ { "SMBntcreateX", reply_ntcreate_and_X, AS_USER | CAN_IPC | QUEUE_IN_OPLOCK }, +/* 0xa3 */ { NULL, NULL, 0 }, +/* 0xa4 */ { "SMBntcancel", reply_ntcancel, 0 }, +/* 0xa5 */ { NULL, NULL, 0 }, +/* 0xa6 */ { NULL, NULL, 0 }, +/* 0xa7 */ { NULL, NULL, 0 }, +/* 0xa8 */ { NULL, NULL, 0 }, +/* 0xa9 */ { NULL, NULL, 0 }, +/* 0xaa */ { NULL, NULL, 0 }, +/* 0xab */ { NULL, NULL, 0 }, +/* 0xac */ { NULL, NULL, 0 }, +/* 0xad */ { NULL, NULL, 0 }, +/* 0xae */ { NULL, NULL, 0 }, +/* 0xaf */ { NULL, NULL, 0 }, +/* 0xb0 */ { NULL, NULL, 0 }, +/* 0xb1 */ { NULL, NULL, 0 }, +/* 0xb2 */ { NULL, NULL, 0 }, +/* 0xb3 */ { NULL, NULL, 0 }, +/* 0xb4 */ { NULL, NULL, 0 }, +/* 0xb5 */ { NULL, NULL, 0 }, +/* 0xb6 */ { NULL, NULL, 0 }, +/* 0xb7 */ { NULL, NULL, 0 }, +/* 0xb8 */ { NULL, NULL, 0 }, +/* 0xb9 */ { NULL, NULL, 0 }, +/* 0xba */ { NULL, NULL, 0 }, +/* 0xbb */ { NULL, NULL, 0 }, +/* 0xbc */ { NULL, NULL, 0 }, +/* 0xbd */ { NULL, NULL, 0 }, +/* 0xbe */ { NULL, NULL, 0 }, +/* 0xbf */ { NULL, NULL, 0 }, +/* 0xc0 */ { "SMBsplopen",reply_printopen,AS_USER | QUEUE_IN_OPLOCK }, +/* 0xc1 */ { "SMBsplwr",reply_printwrite,AS_USER}, +/* 0xc2 */ { "SMBsplclose",reply_printclose,AS_USER}, +/* 0xc3 */ { "SMBsplretq",reply_printqueue,AS_USER}, +/* 0xc4 */ { NULL, NULL, 0 }, +/* 0xc5 */ { NULL, NULL, 0 }, +/* 0xc6 */ { NULL, NULL, 0 }, +/* 0xc7 */ { NULL, NULL, 0 }, +/* 0xc8 */ { NULL, NULL, 0 }, +/* 0xc9 */ { NULL, NULL, 0 }, +/* 0xca */ { NULL, NULL, 0 }, +/* 0xcb */ { NULL, NULL, 0 }, +/* 0xcc */ { NULL, NULL, 0 }, +/* 0xcd */ { NULL, NULL, 0 }, +/* 0xce */ { NULL, NULL, 0 }, +/* 0xcf */ { NULL, NULL, 0 }, +/* 0xd0 */ { "SMBsends",reply_sends,AS_GUEST}, +/* 0xd1 */ { "SMBsendb",NULL,AS_GUEST}, +/* 0xd2 */ { "SMBfwdname",NULL,AS_GUEST}, +/* 0xd3 */ { "SMBcancelf",NULL,AS_GUEST}, +/* 0xd4 */ { "SMBgetmac",NULL,AS_GUEST}, +/* 0xd5 */ { "SMBsendstrt",reply_sendstrt,AS_GUEST}, +/* 0xd6 */ { "SMBsendend",reply_sendend,AS_GUEST}, +/* 0xd7 */ { "SMBsendtxt",reply_sendtxt,AS_GUEST}, +/* 0xd8 */ { NULL, NULL, 0 }, +/* 0xd9 */ { NULL, NULL, 0 }, +/* 0xda */ { NULL, NULL, 0 }, +/* 0xdb */ { NULL, NULL, 0 }, +/* 0xdc */ { NULL, NULL, 0 }, +/* 0xdd */ { NULL, NULL, 0 }, +/* 0xde */ { NULL, NULL, 0 }, +/* 0xdf */ { NULL, NULL, 0 }, +/* 0xe0 */ { NULL, NULL, 0 }, +/* 0xe1 */ { NULL, NULL, 0 }, +/* 0xe2 */ { NULL, NULL, 0 }, +/* 0xe3 */ { NULL, NULL, 0 }, +/* 0xe4 */ { NULL, NULL, 0 }, +/* 0xe5 */ { NULL, NULL, 0 }, +/* 0xe6 */ { NULL, NULL, 0 }, +/* 0xe7 */ { NULL, NULL, 0 }, +/* 0xe8 */ { NULL, NULL, 0 }, +/* 0xe9 */ { NULL, NULL, 0 }, +/* 0xea */ { NULL, NULL, 0 }, +/* 0xeb */ { NULL, NULL, 0 }, +/* 0xec */ { NULL, NULL, 0 }, +/* 0xed */ { NULL, NULL, 0 }, +/* 0xee */ { NULL, NULL, 0 }, +/* 0xef */ { NULL, NULL, 0 }, +/* 0xf0 */ { NULL, NULL, 0 }, +/* 0xf1 */ { NULL, NULL, 0 }, +/* 0xf2 */ { NULL, NULL, 0 }, +/* 0xf3 */ { NULL, NULL, 0 }, +/* 0xf4 */ { NULL, NULL, 0 }, +/* 0xf5 */ { NULL, NULL, 0 }, +/* 0xf6 */ { NULL, NULL, 0 }, +/* 0xf7 */ { NULL, NULL, 0 }, +/* 0xf8 */ { NULL, NULL, 0 }, +/* 0xf9 */ { NULL, NULL, 0 }, +/* 0xfa */ { NULL, NULL, 0 }, +/* 0xfb */ { NULL, NULL, 0 }, +/* 0xfc */ { NULL, NULL, 0 }, +/* 0xfd */ { NULL, NULL, 0 }, +/* 0xfe */ { NULL, NULL, 0 }, +/* 0xff */ { NULL, NULL, 0 } + +}; + +/******************************************************************* +dump a prs to a file + ********************************************************************/ +static void smb_dump(char *name, int type, char *data, ssize_t len) +{ + int fd, i; + pstring fname; + if (DEBUGLEVEL < 50) return; + + if (len < 4) len = smb_len(data)+4; + for (i=1;i<100;i++) { + slprintf(fname,sizeof(fname)-1, "/tmp/%s.%d.%s", name, i, + type ? "req" : "resp"); + fd = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0644); + if (fd != -1 || errno != EEXIST) break; + } + if (fd != -1) { + ssize_t ret = write(fd, data, len); + if (ret != len) + DEBUG(0,("smb_dump: problem: write returned %d\n", (int)ret )); + close(fd); + DEBUG(0,("created %s len %d\n", fname, len)); + } +} + + +/**************************************************************************** +do a switch on the message type, and return the response size +****************************************************************************/ +static int switch_message(int type,char *inbuf,char *outbuf,int size,int bufsize) +{ + static pid_t pid= (pid_t)-1; + int outsize = 0; + extern uint16 global_smbpid; + + type &= 0xff; + + if (pid == (pid_t)-1) + pid = sys_getpid(); + + errno = 0; + last_message = type; + + /* make sure this is an SMB packet */ + if (strncmp(smb_base(inbuf),"\377SMB",4) != 0) + { + DEBUG(2,("Non-SMB packet of length %d\n",smb_len(inbuf))); + return(-1); + } + + /* yuck! this is an interim measure before we get rid of our + current inbuf/outbuf system */ + global_smbpid = SVAL(inbuf,smb_pid); + + if (smb_messages[type].fn == NULL) + { + DEBUG(0,("Unknown message type %d!\n",type)); + smb_dump("Unknown", 1, inbuf, size); + outsize = reply_unknown(inbuf,outbuf); + } + else + { + int flags = smb_messages[type].flags; + static uint16 last_session_tag = UID_FIELD_INVALID; + /* In share mode security we must ignore the vuid. */ + uint16 session_tag = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID : SVAL(inbuf,smb_uid); + connection_struct *conn = conn_find(SVAL(inbuf,smb_tid)); + + DEBUG(3,("switch message %s (pid %d)\n",smb_fn_name(type),(int)pid)); + + smb_dump(smb_fn_name(type), 1, inbuf, size); + if(global_oplock_break) + { + if(flags & QUEUE_IN_OPLOCK) + { + /* + * Queue this message as we are the process of an oplock break. + */ + + DEBUG( 2, ( "switch_message: queueing message due to being in " ) ); + DEBUGADD( 2, ( "oplock break state.\n" ) ); + + push_oplock_pending_smb_message( inbuf, size ); + return -1; + } + } + + /* Ensure this value is replaced in the incoming packet. */ + SSVAL(inbuf,smb_uid,session_tag); + + /* + * Ensure the correct username is in current_user_info. + * This is a really ugly bugfix for problems with + * multiple session_setup_and_X's being done and + * allowing %U and %G substitutions to work correctly. + * There is a reason this code is done here, don't + * move it unless you know what you're doing... :-). + * JRA. + */ + + if (session_tag != last_session_tag) { + user_struct *vuser = NULL; + + last_session_tag = session_tag; + if(session_tag != UID_FIELD_INVALID) + vuser = get_valid_user_struct(session_tag); + if(vuser != NULL) + current_user_info = vuser->user; + } + + /* does this protocol need to be run as root? */ + if (!(flags & AS_USER)) + change_to_root_user(); + + /* does this protocol need a valid tree connection? */ + if ((flags & AS_USER) && !conn) { + return ERROR_DOS(ERRSRV, ERRinvnid); + } + + + /* does this protocol need to be run as the connected user? */ + if ((flags & AS_USER) && !change_to_user(conn,session_tag)) { + if (flags & AS_GUEST) + flags &= ~AS_USER; + else + return(ERROR_DOS(ERRSRV,ERRaccess)); + } + + /* this code is to work around a bug is MS client 3 without + introducing a security hole - it needs to be able to do + print queue checks as guest if it isn't logged in properly */ + if (flags & AS_USER) + flags &= ~AS_GUEST; + + /* does it need write permission? */ + if ((flags & NEED_WRITE) && !CAN_WRITE(conn)) + return(ERROR_DOS(ERRSRV,ERRaccess)); + + /* ipc services are limited */ + if (IS_IPC(conn) && (flags & AS_USER) && !(flags & CAN_IPC)) { + return(ERROR_DOS(ERRSRV,ERRaccess)); + } + + /* load service specific parameters */ + if (conn && !set_current_service(conn,(flags & AS_USER)?True:False)) { + return(ERROR_DOS(ERRSRV,ERRaccess)); + } + + /* does this protocol need to be run as guest? */ + if ((flags & AS_GUEST) && + (!change_to_guest() || + !check_access(smbd_server_fd(), lp_hostsallow(-1), lp_hostsdeny(-1)))) { + return(ERROR_DOS(ERRSRV,ERRaccess)); + } + + last_inbuf = inbuf; + + outsize = smb_messages[type].fn(conn, inbuf,outbuf,size,bufsize); + } + + smb_dump(smb_fn_name(type), 0, outbuf, outsize); + + return(outsize); +} + + +/**************************************************************************** + construct a reply to the incoming packet +****************************************************************************/ +static int construct_reply(char *inbuf,char *outbuf,int size,int bufsize) +{ + int type = CVAL(inbuf,smb_com); + int outsize = 0; + int msg_type = CVAL(inbuf,0); + + GetTimeOfDay(&smb_last_time); + + chain_size = 0; + file_chain_reset(); + reset_chain_p(); + + if (msg_type != 0) + return(reply_special(inbuf,outbuf)); + + construct_reply_common(inbuf, outbuf); + + outsize = switch_message(type,inbuf,outbuf,size,bufsize); + + outsize += chain_size; + + if(outsize > 4) + smb_setlen(outbuf,outsize - 4); + return(outsize); +} + +/**************************************************************************** + Keep track of the number of running smbd's. This functionality is used to + 'hard' limit Samba overhead on resource constrained systems. +****************************************************************************/ +static BOOL smbd_process_limit(void) +{ + int32 total_smbds; + + if (lp_max_smbd_processes()) { + + /* Always add one to the smbd process count, as exit_server() always + * subtracts one. + */ + + total_smbds = 1; /* In case we need to create the entry. */ + + if (!conn_tdb_ctx()) { + DEBUG(0,("smbd_process_limit: max smbd processes parameter set with status parameter not \ +set. Ignoring max smbd restriction.\n")); + return False; + } + + if (tdb_change_int32_atomic(conn_tdb_ctx(), "INFO/total_smbds", &total_smbds, 1) == -1) + return True; + + return total_smbds > lp_max_smbd_processes(); + } + else + return False; +} + +/**************************************************************************** + process an smb from the client - split out from the smbd_process() code so + it can be used by the oplock break code. +****************************************************************************/ +void process_smb(char *inbuf, char *outbuf) +{ +#ifdef WITH_SSL + extern BOOL sslEnabled; /* don't use function for performance reasons */ + static int sslConnected = 0; +#endif /* WITH_SSL */ + static int trans_num; + int msg_type = CVAL(inbuf,0); + int32 len = smb_len(inbuf); + int nread = len + 4; + + DO_PROFILE_INC(smb_count); + + if (trans_num == 0) { + /* on the first packet, check the global hosts allow/ hosts + deny parameters before doing any parsing of the packet + passed to us by the client. This prevents attacks on our + parsing code from hosts not in the hosts allow list */ + if (smbd_process_limit() || + !check_access(smbd_server_fd(), lp_hostsallow(-1), lp_hostsdeny(-1))) { + /* send a negative session response "not listening on calling + name" */ + static unsigned char buf[5] = {0x83, 0, 0, 1, 0x81}; + DEBUG( 1, ( "Connection denied from %s\n", + client_addr() ) ); + (void)send_smb(smbd_server_fd(),(char *)buf); + exit_server("connection denied"); + } + } + + DEBUG( 6, ( "got message type 0x%x of len 0x%x\n", msg_type, len ) ); + DEBUG( 3, ( "Transaction %d of length %d\n", trans_num, nread ) ); + +#ifdef WITH_SSL + if(sslEnabled && !sslConnected){ + sslConnected = sslutil_negotiate_ssl(smbd_server_fd(), msg_type); + if(sslConnected < 0){ /* an error occured */ + exit_server("SSL negotiation failed"); + }else if(sslConnected){ + trans_num++; + return; + } + } +#endif /* WITH_SSL */ + + if (msg_type == 0) + show_msg(inbuf); + else if(msg_type == SMBkeepalive) + return; /* Keepalive packet. */ + + nread = construct_reply(inbuf,outbuf,nread,max_send); + + if(nread > 0) + { + if (CVAL(outbuf,0) == 0) + show_msg(outbuf); + + if (nread != smb_len(outbuf) + 4) + { + DEBUG(0,("ERROR: Invalid message response size! %d %d\n", + nread, smb_len(outbuf))); + } + else + if (!send_smb(smbd_server_fd(),outbuf)) + exit_server("process_smb: send_smb failed."); + } + trans_num++; +} + + + +/**************************************************************************** +return a string containing the function name of a SMB command +****************************************************************************/ +char *smb_fn_name(int type) +{ + static char *unknown_name = "SMBunknown"; + + if (smb_messages[type].name == NULL) + return(unknown_name); + + return(smb_messages[type].name); +} + + +/**************************************************************************** + Helper function for contruct_reply. +****************************************************************************/ + +void construct_reply_common(char *inbuf,char *outbuf) +{ + memset(outbuf,'\0',smb_size); + + set_message(outbuf,0,0,True); + SCVAL(outbuf,smb_com,CVAL(inbuf,smb_com)); + + memcpy(outbuf+4,inbuf+4,4); + SCVAL(outbuf,smb_rcls,SMB_SUCCESS); + SCVAL(outbuf,smb_reh,0); + SCVAL(outbuf,smb_flg, FLAG_REPLY | (CVAL(inbuf,smb_flg) & FLAG_CASELESS_PATHNAMES)); + SSVAL(outbuf,smb_flg2, + (SVAL(inbuf,smb_flg2) & FLAGS2_UNICODE_STRINGS) | + FLAGS2_LONG_PATH_COMPONENTS | + FLAGS2_32_BIT_ERROR_CODES | FLAGS2_EXTENDED_SECURITY); + + SSVAL(outbuf,smb_err,SMB_SUCCESS); + SSVAL(outbuf,smb_tid,SVAL(inbuf,smb_tid)); + SSVAL(outbuf,smb_pid,SVAL(inbuf,smb_pid)); + SSVAL(outbuf,smb_uid,SVAL(inbuf,smb_uid)); + SSVAL(outbuf,smb_mid,SVAL(inbuf,smb_mid)); +} + +/**************************************************************************** + construct a chained reply and add it to the already made reply + **************************************************************************/ +int chain_reply(char *inbuf,char *outbuf,int size,int bufsize) +{ + static char *orig_inbuf; + static char *orig_outbuf; + int smb_com1, smb_com2 = CVAL(inbuf,smb_vwv0); + unsigned smb_off2 = SVAL(inbuf,smb_vwv1); + char *inbuf2, *outbuf2; + int outsize2; + char inbuf_saved[smb_wct]; + char outbuf_saved[smb_wct]; + int wct = CVAL(outbuf,smb_wct); + int outsize = smb_size + 2*wct + SVAL(outbuf,smb_vwv0+2*wct); + + /* maybe its not chained */ + if (smb_com2 == 0xFF) { + SCVAL(outbuf,smb_vwv0,0xFF); + return outsize; + } + + if (chain_size == 0) { + /* this is the first part of the chain */ + orig_inbuf = inbuf; + orig_outbuf = outbuf; + } + + /* + * The original Win95 redirector dies on a reply to + * a lockingX and read chain unless the chain reply is + * 4 byte aligned. JRA. + */ + + outsize = (outsize + 3) & ~3; + + /* we need to tell the client where the next part of the reply will be */ + SSVAL(outbuf,smb_vwv1,smb_offset(outbuf+outsize,outbuf)); + SCVAL(outbuf,smb_vwv0,smb_com2); + + /* remember how much the caller added to the chain, only counting stuff + after the parameter words */ + chain_size += outsize - smb_wct; + + /* work out pointers into the original packets. The + headers on these need to be filled in */ + inbuf2 = orig_inbuf + smb_off2 + 4 - smb_wct; + outbuf2 = orig_outbuf + SVAL(outbuf,smb_vwv1) + 4 - smb_wct; + + /* remember the original command type */ + smb_com1 = CVAL(orig_inbuf,smb_com); + + /* save the data which will be overwritten by the new headers */ + memcpy(inbuf_saved,inbuf2,smb_wct); + memcpy(outbuf_saved,outbuf2,smb_wct); + + /* give the new packet the same header as the last part of the SMB */ + memmove(inbuf2,inbuf,smb_wct); + + /* create the in buffer */ + SCVAL(inbuf2,smb_com,smb_com2); + + /* create the out buffer */ + construct_reply_common(inbuf2, outbuf2); + + DEBUG(3,("Chained message\n")); + show_msg(inbuf2); + + /* process the request */ + outsize2 = switch_message(smb_com2,inbuf2,outbuf2,size-chain_size, + bufsize-chain_size); + + /* copy the new reply and request headers over the old ones, but + preserve the smb_com field */ + memmove(orig_outbuf,outbuf2,smb_wct); + SCVAL(orig_outbuf,smb_com,smb_com1); + + /* restore the saved data, being careful not to overwrite any + data from the reply header */ + memcpy(inbuf2,inbuf_saved,smb_wct); + { + int ofs = smb_wct - PTR_DIFF(outbuf2,orig_outbuf); + if (ofs < 0) ofs = 0; + memmove(outbuf2+ofs,outbuf_saved+ofs,smb_wct-ofs); + } + + return outsize2; +} + +/**************************************************************************** + Setup the needed select timeout. +****************************************************************************/ + +static int setup_select_timeout(void) +{ + int select_timeout; + int t; + + /* + * Increase the select timeout back to SMBD_SELECT_TIMEOUT if we + * have removed any blocking locks. JRA. + */ + + select_timeout = blocking_locks_pending() ? SMBD_SELECT_TIMEOUT_WITH_PENDING_LOCKS*1000 : + SMBD_SELECT_TIMEOUT*1000; + + t = change_notify_timeout(); + if (t != -1) select_timeout = MIN(select_timeout, t*1000); + + return select_timeout; +} + +/**************************************************************************** + Check if services need reloading. +****************************************************************************/ + +void check_reload(int t) +{ + static time_t last_smb_conf_reload_time = 0; + + if(last_smb_conf_reload_time == 0) + last_smb_conf_reload_time = t; + + if (reload_after_sighup || (t >= last_smb_conf_reload_time+SMBD_RELOAD_CHECK)) + { + reload_services(True); + reload_after_sighup = False; + last_smb_conf_reload_time = t; + } +} + +/**************************************************************************** + Process any timeout housekeeping. Return False if the caller should exit. +****************************************************************************/ + +static BOOL timeout_processing(int deadtime, int *select_timeout, time_t *last_timeout_processing_time) +{ + static time_t last_keepalive_sent_time = 0; + static time_t last_idle_closed_check = 0; + time_t t; + BOOL allidle = True; + extern int keepalive; + + if (smb_read_error == READ_EOF) + { + DEBUG(3,("end of file from client\n")); + return False; + } + + if (smb_read_error == READ_ERROR) + { + DEBUG(3,("receive_smb error (%s) exiting\n", + strerror(errno))); + return False; + } + + *last_timeout_processing_time = t = time(NULL); + + if(last_keepalive_sent_time == 0) + last_keepalive_sent_time = t; + + if(last_idle_closed_check == 0) + last_idle_closed_check = t; + + /* become root again if waiting */ + change_to_root_user(); + + /* check if we need to reload services */ + check_reload(t); + + /* automatic timeout if all connections are closed */ + if (conn_num_open()==0 && (t - last_idle_closed_check) >= IDLE_CLOSED_TIMEOUT) + { + DEBUG( 2, ( "Closing idle connection\n" ) ); + return False; + } + else + last_idle_closed_check = t; + + if (keepalive && (t - last_keepalive_sent_time)>keepalive) + { + extern struct auth_context *negprot_global_auth_context; + if (!send_keepalive(smbd_server_fd())) { + DEBUG( 2, ( "Keepalive failed - exiting.\n" ) ); + return False; + } + + /* send a keepalive for a password server or the like. + This is attached to the auth_info created in the + negprot */ + if (negprot_global_auth_context + && negprot_global_auth_context->challenge_set_method + && negprot_global_auth_context->challenge_set_method->send_keepalive) { + negprot_global_auth_context->challenge_set_method->send_keepalive + (&negprot_global_auth_context->challenge_set_method->private_data); + } + + last_keepalive_sent_time = t; + } + + /* check for connection timeouts */ + allidle = conn_idle_all(t, deadtime); + + if (allidle && conn_num_open()>0) { + DEBUG(2,("Closing idle connection 2.\n")); + return False; + } + + if(global_machine_password_needs_changing && + /* for ADS we need to do a regular ADS password change, not a domain + password change */ + lp_security() == SEC_DOMAIN) + { + unsigned char trust_passwd_hash[16]; + time_t lct; + pstring remote_machine_list; + + /* + * We're in domain level security, and the code that + * read the machine password flagged that the machine + * password needs changing. + */ + + /* + * First, open the machine password file with an exclusive lock. + */ + + if(!secrets_fetch_trust_account_password(global_myworkgroup, trust_passwd_hash, &lct)) { + DEBUG(0,("process: unable to read the machine account password for \ +machine %s in domain %s.\n", global_myname, global_myworkgroup )); + return True; + } + + /* + * Make sure someone else hasn't already done this. + */ + + if(t < lct + lp_machine_password_timeout()) { + global_machine_password_needs_changing = False; + return True; + } + + pstrcpy(remote_machine_list, lp_passwordserver()); + + change_trust_account_password( global_myworkgroup, remote_machine_list); + global_machine_password_needs_changing = False; + } + + /* + * Check to see if we have any blocking locks + * outstanding on the queue. + */ + process_blocking_lock_queue(t); + + /* + * Check to see if we have any change notifies + * outstanding on the queue. + */ + process_pending_change_notify_queue(t); + + /* + * Now we are root, check if the log files need pruning. + * Force a log file check. + */ + force_check_log_size(); + check_log_size(); + + /* + * Modify the select timeout depending upon + * what we have remaining in our queues. + */ + + *select_timeout = setup_select_timeout(); + + return True; +} + +/**************************************************************************** + process commands from the client +****************************************************************************/ + +void smbd_process(void) +{ + extern int smb_echo_count; + time_t last_timeout_processing_time = time(NULL); + unsigned int num_smbs = 0; + + InBuffer = (char *)malloc(BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN); + OutBuffer = (char *)malloc(BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN); + if ((InBuffer == NULL) || (OutBuffer == NULL)) + return; + + max_recv = MIN(lp_maxxmit(),BUFFER_SIZE); + + /* re-initialise the timezone */ + TimeInit(); + + /* register our message handlers */ + message_register(MSG_SMB_FORCE_TDIS, msg_force_tdis); + talloc_init_named("dummy!"); + + while (True) { + int deadtime = lp_deadtime()*60; + int select_timeout = setup_select_timeout(); + int num_echos; + + if (deadtime <= 0) + deadtime = DEFAULT_SMBD_TIMEOUT; + + errno = 0; + + /* free up temporary memory */ + lp_talloc_free(); + main_loop_talloc_free(); + + while (!receive_message_or_smb(InBuffer,BUFFER_SIZE+LARGE_WRITEX_HDR_SIZE,select_timeout)) { + if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time)) + return; + num_smbs = 0; /* Reset smb counter. */ + } + + /* + * Ensure we do timeout processing if the SMB we just got was + * only an echo request. This allows us to set the select + * timeout in 'receive_message_or_smb()' to any value we like + * without worrying that the client will send echo requests + * faster than the select timeout, thus starving out the + * essential processing (change notify, blocking locks) that + * the timeout code does. JRA. + */ + num_echos = smb_echo_count; + + process_smb(InBuffer, OutBuffer); + + if (smb_echo_count != num_echos) { + if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time)) + return; + num_smbs = 0; /* Reset smb counter. */ + } + + num_smbs++; + + /* + * If we are getting smb requests in a constant stream + * with no echos, make sure we attempt timeout processing + * every select_timeout milliseconds - but only check for this + * every 200 smb requests. + */ + + if ((num_smbs % 200) == 0) { + time_t new_check_time = time(NULL); + if(new_check_time - last_timeout_processing_time >= (select_timeout/1000)) { + if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time)) + return; + num_smbs = 0; /* Reset smb counter. */ + last_timeout_processing_time = new_check_time; /* Reset time. */ + } + } + } +} diff --git a/source3/smbd/quotas.c b/source3/smbd/quotas.c new file mode 100644 index 0000000000..39cb8f2432 --- /dev/null +++ b/source3/smbd/quotas.c @@ -0,0 +1,1097 @@ +/* + Unix SMB/CIFS implementation. + support for quotas + Copyright (C) Andrew Tridgell 1992-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + 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 is one of the most system dependent parts of Samba, and its + * done a litle differently. Each system has its own way of doing + * things :-( + */ + +#include "includes.h" + +#if defined(VXFS_QUOTA) + +/* + * In addition to their native filesystems, some systems have Veritas VxFS. + * Declare here, define at end: reduces likely "include" interaction problems. + * David Lee <T.D.Lee@durham.ac.uk> + */ +BOOL disk_quotas_vxfs(const pstring name, char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize); + +#endif /* VXFS_QUOTA */ + +#ifdef LINUX + +#include <sys/types.h> +#include <asm/types.h> + +/* + * This shouldn't be neccessary - it should be /usr/include/sys/quota.h + * Unfortunately, RH7.1 ships with a different quota system using struct mem_dqblk + * rather than the struct dqblk defined in /usr/include/sys/quota.h. + * This means we must include linux/quota.h to have a hope of working on + * RH7.1 systems. And it also means this breaks if the kernel is upgraded + * to a Linus 2.4.x (where x > the minor number shipped with RH7.1) until + * Linus synchronises with the AC patches. Sometimes I *hate* Linux :-). JRA. + */ + +#include <linux/quota.h> +#ifdef HAVE_LINUX_XQM_H +#include <linux/xqm.h> +#endif + +#include <mntent.h> +#include <linux/unistd.h> + + +#define LINUX_QUOTAS_2 + +typedef struct _LINUX_SMB_DISK_QUOTA { + SMB_BIG_UINT bsize; + SMB_BIG_UINT hardlimit; /* In bsize units. */ + SMB_BIG_UINT softlimit; /* In bsize units. */ + SMB_BIG_UINT curblocks; /* In bsize units. */ + SMB_BIG_UINT ihardlimit; /* inode hard limit. */ + SMB_BIG_UINT isoftlimit; /* inode soft limit. */ + SMB_BIG_UINT curinodes; /* Current used inodes. */ +} LINUX_SMB_DISK_QUOTA; + +/**************************************************************************** + Abstract out the XFS Quota Manager quota get call. +****************************************************************************/ + +static int get_smb_linux_xfs_quota(char *path, uid_t euser_id, LINUX_SMB_DISK_QUOTA *dp) +{ + int ret = -1; +#ifdef HAVE_LINUX_XQM_H + struct fs_disk_quota D; + ZERO_STRUCT(D); + + if ((ret = quotactl(QCMD(Q_XGETQUOTA,USRQUOTA), path, euser_id, (caddr_t)&D))) + return ret; + + dp->bsize = (SMB_BIG_UINT)512; + dp->softlimit = (SMB_BIG_UINT)D.d_blk_softlimit; + dp->hardlimit = (SMB_BIG_UINT)D.d_blk_hardlimit; + dp->ihardlimit = (SMB_BIG_UINT)D.d_ino_hardlimit; + dp->isoftlimit = (SMB_BIG_UINT)D.d_ino_softlimit; + dp->curinodes = (SMB_BIG_UINT)D.d_icount; + dp->curblocks = (SMB_BIG_UINT)D.d_bcount; +#endif + return ret; +} + +/**************************************************************************** + Abstract out the old and new Linux quota get calls. +****************************************************************************/ + +static int get_smb_linux_vfs_quota(char *path, uid_t euser_id, LINUX_SMB_DISK_QUOTA *dp) +{ + int ret; +#ifdef LINUX_QUOTAS_1 + struct dqblk D; + ZERO_STRUCT(D); + dp->bsize = (SMB_BIG_UINT)1024; +#else /* LINUX_QUOTAS_2 */ + struct mem_dqblk D; + ZERO_STRUCT(D); +#ifndef QUOTABLOCK_SIZE +#define QUOTABLOCK_SIZE 1024 +#endif + dp->bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE; +#endif + + if ((ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), path, euser_id, (caddr_t)&D))) + return -1; + + dp->softlimit = (SMB_BIG_UINT)D.dqb_bsoftlimit; + dp->hardlimit = (SMB_BIG_UINT)D.dqb_bhardlimit; + dp->ihardlimit = (SMB_BIG_UINT)D.dqb_ihardlimit; + dp->isoftlimit = (SMB_BIG_UINT)D.dqb_isoftlimit; + dp->curinodes = (SMB_BIG_UINT)D.dqb_curinodes; + +#ifdef LINUX_QUOTAS_1 + dp->curblocks = (SMB_BIG_UINT)D.dqb_curblocks; +#else /* LINUX_QUOTAS_2 */ + dp->curblocks = ((SMB_BIG_UINT)D.dqb_curspace)/ dp->bsize; +#endif + + return 0; +} + +/**************************************************************************** +try to get the disk space from disk quotas (LINUX version) +****************************************************************************/ + +BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize) +{ + int r; + SMB_STRUCT_STAT S; + FILE *fp; + LINUX_SMB_DISK_QUOTA D; + struct mntent *mnt; + SMB_DEV_T devno; + int found; + uid_t euser_id; + + euser_id = geteuid(); + + /* find the block device file */ + + if ( sys_stat(path, &S) == -1 ) + return(False) ; + + devno = S.st_dev ; + + fp = setmntent(MOUNTED,"r"); + found = False ; + + while ((mnt = getmntent(fp))) { + if ( sys_stat(mnt->mnt_dir,&S) == -1 ) + continue ; + + if (S.st_dev == devno) { + found = True ; + break; + } + } + + endmntent(fp) ; + + if (!found) + return(False); + + save_re_uid(); + set_effective_uid(0); + if (strcmp(mnt->mnt_type, "xfs") == 0) + r=get_smb_linux_xfs_quota(mnt->mnt_fsname, euser_id, &D); + else + r=get_smb_linux_vfs_quota(mnt->mnt_fsname, euser_id, &D); + restore_re_uid(); + + /* Use softlimit to determine disk space, except when it has been exceeded */ + *bsize = D.bsize; + if (r == -1) { + if (errno == EDQUOT) { + *dfree =0; + *dsize =D.curblocks; + return (True); + } else { + return(False); + } + } + + /* Use softlimit to determine disk space, except when it has been exceeded */ + if ( + (D.softlimit && D.curblocks >= D.softlimit) || + (D.hardlimit && D.curblocks >= D.hardlimit) || + (D.isoftlimit && D.curinodes >= D.isoftlimit) || + (D.ihardlimit && D.curinodes>=D.ihardlimit) + ) { + *dfree = 0; + *dsize = D.curblocks; + } else if (D.softlimit==0 && D.hardlimit==0) { + return(False); + } else { + if (D.softlimit == 0) + D.softlimit = D.hardlimit; + *dfree = D.softlimit - D.curblocks; + *dsize = D.softlimit; + } + + return (True); +} + +#elif defined(CRAY) + +#include <sys/quota.h> +#include <mntent.h> + +/**************************************************************************** +try to get the disk space from disk quotas (CRAY VERSION) +****************************************************************************/ + +BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize) +{ + struct mntent *mnt; + FILE *fd; + SMB_STRUCT_STAT sbuf; + SMB_DEV_T devno ; + static SMB_DEV_T devno_cached = 0 ; + static pstring name; + struct q_request request ; + struct qf_header header ; + static int quota_default = 0 ; + int found ; + + if ( sys_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 ( sys_stat(mnt->mnt_dir,&sbuf) == -1 ) + continue ; + + if (sbuf.st_dev == devno) { + + found = True ; + break ; + + } + + } + + pstrcpy(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 < *dsize ) + *dfree = 0 ; + else + *dfree -= *dsize ; + + *bsize = 4096 ; /* Cray blocksize */ + + return(True) ; + +} + + +#elif defined(SUNOS5) || defined(SUNOS4) + +#include <fcntl.h> +#include <sys/param.h> +#if defined(SUNOS5) +#include <sys/fs/ufs_quota.h> +#include <sys/mnttab.h> +#include <sys/mntent.h> +#else /* defined(SUNOS4) */ +#include <ufs/quota.h> +#include <mntent.h> +#endif + +#if defined(SUNOS5) + +/**************************************************************************** + Allows querying of remote hosts for quotas on NFS mounted shares. + Supports normal NFS and AMD mounts. + Alan Romeril <a.romeril@ic.ac.uk> July 2K. +****************************************************************************/ + +#include <rpc/rpc.h> +#include <rpc/types.h> +#include <rpcsvc/rquota.h> +#include <rpc/nettype.h> +#include <rpc/xdr.h> + +static int quotastat; + +static int xdr_getquota_args(XDR *xdrsp, struct getquota_args *args) +{ + if (!xdr_string(xdrsp, &args->gqa_pathp, RQ_PATHLEN )) + return(0); + if (!xdr_int(xdrsp, &args->gqa_uid)) + return(0); + return (1); +} + +static int xdr_getquota_rslt(XDR *xdrsp, struct getquota_rslt *gqr) +{ + if (!xdr_int(xdrsp, "astat)) { + DEBUG(6,("nfs_quotas: Status bad or zero\n")); + return 0; + } + if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bsize)) { + DEBUG(6,("nfs_quotas: Block size bad or zero\n")); + return 0; + } + if (!xdr_bool(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_active)) { + DEBUG(6,("nfs_quotas: Active bad or zero\n")); + return 0; + } + if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bhardlimit)) { + DEBUG(6,("nfs_quotas: Hardlimit bad or zero\n")); + return 0; + } + if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bsoftlimit)) { + DEBUG(6,("nfs_quotas: Softlimit bad or zero\n")); + return 0; + } + if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_curblocks)) { + DEBUG(6,("nfs_quotas: Currentblocks bad or zero\n")); + return 0; + } + return (1); +} + +/* Restricted to SUNOS5 for the moment, I haven`t access to others to test. */ +static BOOL nfs_quotas(char *nfspath, uid_t euser_id, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize) +{ + uid_t uid = euser_id; + struct dqblk D; + char *mnttype = nfspath; + CLIENT *clnt; + struct getquota_rslt gqr; + struct getquota_args args; + char *cutstr, *pathname, *host, *testpath; + int len; + static struct timeval timeout = {2,0}; + enum clnt_stat clnt_stat; + BOOL ret = True; + + *bsize = *dfree = *dsize = (SMB_BIG_UINT)0; + + len=strcspn(mnttype, ":"); + pathname=strstr(mnttype, ":"); + cutstr = (char *) malloc(sizeof(char) * len ); + if (!cutstr) + return False; + + host = strncat(cutstr,mnttype, sizeof(char) * len ); + DEBUG(5,("nfs_quotas: looking for mount on \"%s\"\n", cutstr)); + DEBUG(5,("nfs_quotas: of path \"%s\"\n", mnttype)); + testpath=strchr_m(mnttype, ':'); + args.gqa_pathp = testpath+1; + args.gqa_uid = uid; + + DEBUG(5,("nfs_quotas: Asking for host \"%s\" rpcprog \"%i\" rpcvers \"%i\" network \"%s\"\n", host, RQUOTAPROG, RQUOTAVERS, "udp")); + + if ((clnt = clnt_create(host, RQUOTAPROG, RQUOTAVERS, "udp")) == NULL) { + ret = False; + goto out; + } + + clnt->cl_auth = authunix_create_default(); + DEBUG(9,("nfs_quotas: auth_success\n")); + + clnt_stat=clnt_call(clnt, RQUOTAPROC_GETQUOTA, xdr_getquota_args, (caddr_t)&args, xdr_getquota_rslt, (caddr_t)&gqr, timeout); + + if (clnt_stat != RPC_SUCCESS) { + DEBUG(9,("nfs_quotas: clnt_call fail\n")); + ret = False; + goto out; + } + + /* + * quotastat returns 0 if the rpc call fails, 1 if quotas exist, 2 if there is + * no quota set, and 3 if no permission to get the quota. If 0 or 3 return + * something sensible. + */ + + switch ( quotastat ) { + case 0: + DEBUG(9,("nfs_quotas: Remote Quotas Failed! Error \"%i\" \n", quotastat )); + ret = False; + goto out; + + case 1: + DEBUG(9,("nfs_quotas: Good quota data\n")); + D.dqb_bsoftlimit = gqr.getquota_rslt_u.gqr_rquota.rq_bsoftlimit; + D.dqb_bhardlimit = gqr.getquota_rslt_u.gqr_rquota.rq_bhardlimit; + D.dqb_curblocks = gqr.getquota_rslt_u.gqr_rquota.rq_curblocks; + break; + + case 2: + case 3: + D.dqb_bsoftlimit = 1; + D.dqb_curblocks = 1; + DEBUG(9,("nfs_quotas: Remote Quotas returned \"%i\" \n", quotastat )); + break; + + default: + DEBUG(9,("nfs_quotas: Remote Quotas Questionable! Error \"%i\" \n", quotastat )); + break; + } + + DEBUG(10,("nfs_quotas: Let`s look at D a bit closer... status \"%i\" bsize \"%i\" active? \"%i\" bhard \"%i\" bsoft \"%i\" curb \"%i\" \n", + quotastat, + gqr.getquota_rslt_u.gqr_rquota.rq_bsize, + gqr.getquota_rslt_u.gqr_rquota.rq_active, + gqr.getquota_rslt_u.gqr_rquota.rq_bhardlimit, + gqr.getquota_rslt_u.gqr_rquota.rq_bsoftlimit, + gqr.getquota_rslt_u.gqr_rquota.rq_curblocks)); + + *bsize = gqr.getquota_rslt_u.gqr_rquota.rq_bsize; + *dsize = D.dqb_bsoftlimit; + + if (D.dqb_curblocks == D.dqb_curblocks == 1) + *bsize = 512; + + if (D.dqb_curblocks > D.dqb_bsoftlimit) { + *dfree = 0; + *dsize = D.dqb_curblocks; + } else + *dfree = D.dqb_bsoftlimit - D.dqb_curblocks; + + out: + + if (clnt) { + if (clnt->cl_auth) + auth_destroy(clnt->cl_auth); + clnt_destroy(clnt); + } + + DEBUG(5,("nfs_quotas: For path \"%s\" returning bsize %.0f, dfree %.0f, dsize %.0f\n",args.gqa_pathp,(double)*bsize,(double)*dfree,(double)*dsize)); + + SAFE_FREE(cutstr); + DEBUG(10,("nfs_quotas: End of nfs_quotas\n" )); + return ret; +} +#endif + +/**************************************************************************** +try to get the disk space from disk quotas (SunOS & Solaris2 version) +Quota code by Peter Urbanec (amiga@cse.unsw.edu.au). +****************************************************************************/ + +BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize) +{ + uid_t euser_id; + int ret; + struct dqblk D; +#if defined(SUNOS5) + struct quotctl command; + int file; + static struct mnttab mnt; + static pstring name; + pstring devopt; +#else /* SunOS4 */ + struct mntent *mnt; + static pstring name; +#endif + FILE *fd; + SMB_STRUCT_STAT sbuf; + SMB_DEV_T devno ; + static SMB_DEV_T devno_cached = 0 ; + static int found ; + + euser_id = geteuid(); + + if ( sys_stat(path,&sbuf) == -1 ) + return(False) ; + + devno = sbuf.st_dev ; + DEBUG(5,("disk_quotas: looking for path \"%s\" devno=%x\n", path,(unsigned int)devno)); + if ( devno != devno_cached ) { + devno_cached = devno ; +#if defined(SUNOS5) + if ((fd = sys_fopen(MNTTAB, "r")) == NULL) + return(False) ; + + found = False ; + slprintf(devopt, sizeof(devopt) - 1, "dev=%x", (unsigned int)devno); + while (getmntent(fd, &mnt) == 0) { + if( !hasmntopt(&mnt, devopt) ) + continue; + + DEBUG(5,("disk_quotas: testing \"%s\" %s\n", mnt.mnt_mountp,devopt)); + + /* quotas are only on vxfs, UFS or NFS */ + if ( strcmp( mnt.mnt_fstype, MNTTYPE_UFS ) == 0 || + strcmp( mnt.mnt_fstype, "nfs" ) == 0 || + strcmp( mnt.mnt_fstype, "vxfs" ) == 0 ) { + found = True ; + break; + } + } + + pstrcpy(name,mnt.mnt_mountp) ; + pstrcat(name,"/quotas") ; + fclose(fd) ; +#else /* SunOS4 */ + if ((fd = setmntent(MOUNTED, "r")) == NULL) + return(False) ; + + found = False ; + while ((mnt = getmntent(fd)) != NULL) { + if ( sys_stat(mnt->mnt_dir,&sbuf) == -1 ) + continue ; + DEBUG(5,("disk_quotas: testing \"%s\" devno=%x\n", mnt->mnt_dir,(unsigned int)sbuf.st_dev)); + if (sbuf.st_dev == devno) { + found = True ; + break; + } + } + + pstrcpy(name,mnt->mnt_fsname) ; + endmntent(fd) ; +#endif + } + + if ( ! found ) + return(False) ; + + save_re_uid(); + set_effective_uid(0); + +#if defined(SUNOS5) + if ( strcmp( mnt.mnt_fstype, "nfs" ) == 0) { + BOOL retval; + DEBUG(5,("disk_quotas: looking for mountpath (NFS) \"%s\"\n", mnt.mnt_special)); + retval = nfs_quotas(mnt.mnt_special, euser_id, bsize, dfree, dsize); + restore_re_uid(); + return retval; + } + + DEBUG(5,("disk_quotas: looking for quotas file \"%s\"\n", name)); + if((file=sys_open(name, O_RDONLY,0))<0) { + restore_re_uid(); + return(False); + } + command.op = Q_GETQUOTA; + command.uid = euser_id; + command.addr = (caddr_t) &D; + ret = ioctl(file, Q_QUOTACTL, &command); + close(file); +#else + DEBUG(5,("disk_quotas: trying quotactl on device \"%s\"\n", name)); + ret = quotactl(Q_GETQUOTA, name, euser_id, &D); +#endif + + restore_re_uid(); + + if (ret < 0) { + DEBUG(5,("disk_quotas ioctl (Solaris) failed. Error = %s\n", strerror(errno) )); + +#if defined(SUNOS5) && defined(VXFS_QUOTA) + /* If normal quotactl() fails, try vxfs private calls */ + set_effective_uid(euser_id); + DEBUG(5,("disk_quotas: mount type \"%s\"\n", mnt.mnt_fstype)); + if ( 0 == strcmp ( mnt.mnt_fstype, "vxfs" )) { + BOOL retval; + retval = disk_quotas_vxfs(name, path, bsize, dfree, dsize); + return(retval); + } +#else + return(False); +#endif + } + + /* If softlimit is zero, set it equal to hardlimit. + */ + + if (D.dqb_bsoftlimit==0) + D.dqb_bsoftlimit = D.dqb_bhardlimit; + + /* Use softlimit to determine disk space. A user exceeding the quota is told + * that there's no space left. Writes might actually work for a bit if the + * hardlimit is set higher than softlimit. Effectively the disk becomes + * made of rubber latex and begins to expand to accommodate the user :-) + */ + + if (D.dqb_bsoftlimit==0) + return(False); + *bsize = DEV_BSIZE; + *dsize = D.dqb_bsoftlimit; + + if (D.dqb_curblocks > D.dqb_bsoftlimit) { + *dfree = 0; + *dsize = D.dqb_curblocks; + } else + *dfree = D.dqb_bsoftlimit - D.dqb_curblocks; + + DEBUG(5,("disk_quotas for path \"%s\" returning bsize %.0f, dfree %.0f, dsize %.0f\n", + path,(double)*bsize,(double)*dfree,(double)*dsize)); + + return(True); +} + + +#elif defined(OSF1) +#include <ufs/quota.h> + +/**************************************************************************** +try to get the disk space from disk quotas - OSF1 version +****************************************************************************/ + +BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize) +{ + int r, save_errno; + struct dqblk D; + SMB_STRUCT_STAT S; + uid_t euser_id; + + /* + * This code presumes that OSF1 will only + * give out quota info when the real uid + * matches the effective uid. JRA. + */ + euser_id = geteuid(); + save_re_uid(); + if (set_re_uid() != 0) return False; + + r= quotactl(path,QCMD(Q_GETQUOTA, USRQUOTA),euser_id,(char *) &D); + if (r) { + save_errno = errno; + } + + restore_re_uid(); + + *bsize = DEV_BSIZE; + + if (r) + { + if (save_errno == EDQUOT) /* disk quota exceeded */ + { + *dfree = 0; + *dsize = D.dqb_curblocks; + return (True); + } + else + return (False); + } + + /* If softlimit is zero, set it equal to hardlimit. + */ + + if (D.dqb_bsoftlimit==0) + D.dqb_bsoftlimit = D.dqb_bhardlimit; + + /* Use softlimit to determine disk space, except when it has been exceeded */ + + if (D.dqb_bsoftlimit==0) + return(False); + + if ((D.dqb_curblocks>D.dqb_bsoftlimit)) { + *dfree = 0; + *dsize = D.dqb_curblocks; + } else { + *dfree = D.dqb_bsoftlimit - D.dqb_curblocks; + *dsize = D.dqb_bsoftlimit; + } + return (True); +} + +#elif defined (IRIX6) +/**************************************************************************** +try to get the disk space from disk quotas (IRIX 6.2 version) +****************************************************************************/ + +#include <sys/quota.h> +#include <mntent.h> + +BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize) +{ + uid_t euser_id; + int r; + struct dqblk D; + struct fs_disk_quota F; + SMB_STRUCT_STAT S; + FILE *fp; + struct mntent *mnt; + SMB_DEV_T devno; + int found; + + /* find the block device file */ + + if ( sys_stat(path, &S) == -1 ) { + return(False) ; + } + + devno = S.st_dev ; + + fp = setmntent(MOUNTED,"r"); + found = False ; + + while ((mnt = getmntent(fp))) { + if ( sys_stat(mnt->mnt_dir,&S) == -1 ) + continue ; + if (S.st_dev == devno) { + found = True ; + break ; + } + } + endmntent(fp) ; + + if (!found) { + return(False); + } + + euser_id=geteuid(); + save_re_uid(); + set_effective_uid(0); + + /* Use softlimit to determine disk space, except when it has been exceeded */ + + *bsize = 512; + + if ( 0 == strcmp ( mnt->mnt_type, "efs" )) + { + r=quotactl (Q_GETQUOTA, mnt->mnt_fsname, euser_id, (caddr_t) &D); + + restore_re_uid(); + + if (r==-1) + return(False); + + /* Use softlimit to determine disk space, except when it has been exceeded */ + if ( + (D.dqb_bsoftlimit && D.dqb_curblocks>=D.dqb_bsoftlimit) || + (D.dqb_bhardlimit && D.dqb_curblocks>=D.dqb_bhardlimit) || + (D.dqb_fsoftlimit && D.dqb_curfiles>=D.dqb_fsoftlimit) || + (D.dqb_fhardlimit && D.dqb_curfiles>=D.dqb_fhardlimit) + ) + { + *dfree = 0; + *dsize = D.dqb_curblocks; + } + else if (D.dqb_bsoftlimit==0 && D.dqb_bhardlimit==0) + { + return(False); + } + else + { + *dfree = D.dqb_bsoftlimit - D.dqb_curblocks; + *dsize = D.dqb_bsoftlimit; + } + + } + else if ( 0 == strcmp ( mnt->mnt_type, "xfs" )) + { + r=quotactl (Q_XGETQUOTA, mnt->mnt_fsname, euser_id, (caddr_t) &F); + + restore_re_uid(); + + if (r==-1) + return(False); + + /* Use softlimit to determine disk space, except when it has been exceeded */ + if ( + (F.d_blk_softlimit && F.d_bcount>=F.d_blk_softlimit) || + (F.d_blk_hardlimit && F.d_bcount>=F.d_blk_hardlimit) || + (F.d_ino_softlimit && F.d_icount>=F.d_ino_softlimit) || + (F.d_ino_hardlimit && F.d_icount>=F.d_ino_hardlimit) + ) + { + *dfree = 0; + *dsize = F.d_bcount; + } + else if (F.d_blk_softlimit==0 && F.d_blk_hardlimit==0) + { + return(False); + } + else + { + *dfree = (F.d_blk_softlimit - F.d_bcount); + *dsize = F.d_blk_softlimit; + } + + } + else + { + restore_re_uid(); + return(False); + } + + return (True); + +} + +#else + +#if defined(__FreeBSD__) || defined(__OpenBSD__) +#include <ufs/ufs/quota.h> +#include <machine/param.h> +#elif AIX +/* AIX quota patch from Ole Holm Nielsen <ohnielse@fysik.dtu.dk> */ +#include <jfs/quota.h> +/* AIX 4.X: Rename members of the dqblk structure (ohnielse@fysik.dtu.dk) */ +#define dqb_curfiles dqb_curinodes +#define dqb_fhardlimit dqb_ihardlimit +#define dqb_fsoftlimit dqb_isoftlimit +#else /* !__FreeBSD__ && !AIX && !__OpenBSD__ */ +#include <sys/quota.h> +#include <devnm.h> +#endif + +/**************************************************************************** +try to get the disk space from disk quotas - default version +****************************************************************************/ + +BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize) +{ + int r; + struct dqblk D; + uid_t euser_id; +#if !defined(__FreeBSD__) && !defined(AIX) && !defined(__OpenBSD__) + char dev_disk[256]; + SMB_STRUCT_STAT S; + /* find the block device file */ + if ((sys_stat(path, &S)<0) || + (devnm(S_IFBLK, S.st_dev, dev_disk, 256, 0)<0)) return (False); +#endif + + euser_id = geteuid(); + +#ifdef HPUX + /* for HPUX, real uid must be same as euid to execute quotactl for euid */ + save_re_uid(); + if (set_re_uid() != 0) return False; + + r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D); + + restore_re_uid(); +#else +#if defined(__FreeBSD__) || defined(__OpenBSD__) + { + /* FreeBSD patches from Marty Moll <martym@arbor.edu> */ + gid_t egrp_id; + + save_re_uid(); + set_effective_uid(0); + + egrp_id = getegid(); + r= quotactl(path,QCMD(Q_GETQUOTA,USRQUOTA),euser_id,(char *) &D); + + /* As FreeBSD has group quotas, if getting the user + quota fails, try getting the group instead. */ + if (r) { + r= quotactl(path,QCMD(Q_GETQUOTA,GRPQUOTA),egrp_id,(char *) &D); + } + + restore_re_uid(); + } +#elif defined(AIX) + /* AIX has both USER and GROUP quotas: + Get the USER quota (ohnielse@fysik.dtu.dk) */ + r= quotactl(path,QCMD(Q_GETQUOTA,USRQUOTA),euser_id,(char *) &D); +#else /* !__FreeBSD__ && !AIX && !__OpenBSD__ */ + r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D); +#endif /* !__FreeBSD__ && !AIX && !__OpenBSD__ */ +#endif /* HPUX */ + + /* Use softlimit to determine disk space, except when it has been exceeded */ +#if defined(__FreeBSD__) || defined(__OpenBSD__) + *bsize = DEV_BSIZE; +#else /* !__FreeBSD__ && !__OpenBSD__ */ + *bsize = 1024; +#endif /*!__FreeBSD__ && !__OpenBSD__ */ + + if (r) + { + if (errno == EDQUOT) + { + *dfree =0; + *dsize =D.dqb_curblocks; + return (True); + } + else return(False); + } + + /* If softlimit is zero, set it equal to hardlimit. + */ + + if (D.dqb_bsoftlimit==0) + D.dqb_bsoftlimit = D.dqb_bhardlimit; + + if (D.dqb_bsoftlimit==0) + return(False); + /* Use softlimit to determine disk space, except when it has been exceeded */ + if ((D.dqb_curblocks>D.dqb_bsoftlimit) +#if !defined(__FreeBSD__) && !defined(__OpenBSD__) +||((D.dqb_curfiles>D.dqb_fsoftlimit) && (D.dqb_fsoftlimit != 0)) +#endif + ) { + *dfree = 0; + *dsize = D.dqb_curblocks; + } + else { + *dfree = D.dqb_bsoftlimit - D.dqb_curblocks; + *dsize = D.dqb_bsoftlimit; + } + return (True); +} + +#endif + +#if defined(VXFS_QUOTA) + +/**************************************************************************** +Try to get the disk space from Veritas disk quotas. + David Lee <T.D.Lee@durham.ac.uk> August 1999. + +Background assumptions: + Potentially under many Operating Systems. Initially Solaris 2. + + My guess is that Veritas is largely, though not entirely, + independent of OS. So I have separated it out. + + There may be some details. For example, OS-specific "include" files. + + It is understood that HPUX 10 somehow gets Veritas quotas without + any special effort; if so, this routine need not be compiled in. + Dirk De Wachter <Dirk.DeWachter@rug.ac.be> + +Warning: + It is understood that Veritas do not publicly support this ioctl interface. + Rather their preference would be for the user (us) to call the native + OS and then for the OS itself to call through to the VxFS filesystem. + Presumably HPUX 10, see above, does this. + +Hints for porting: + Add your OS to "IFLIST" below. + Get it to compile successfully: + Almost certainly "include"s require attention: see SUNOS5. + In the main code above, arrange for it to be called: see SUNOS5. + Test! + +****************************************************************************/ + +/* "IFLIST" + * This "if" is a list of ports: + * if defined(OS1) || defined(OS2) || ... + */ +#if defined(SUNOS5) + +#if defined(SUNOS5) +#include <sys/fs/vx_solaris.h> +#endif +#include <sys/fs/vx_machdep.h> +#include <sys/fs/vx_layout.h> +#include <sys/fs/vx_quota.h> +#include <sys/fs/vx_aioctl.h> +#include <sys/fs/vx_ioctl.h> + +BOOL disk_quotas_vxfs(const pstring name, char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize) +{ + uid_t user_id, euser_id; + int ret; + struct vx_dqblk D; + struct vx_quotctl quotabuf; + struct vx_genioctl genbuf; + pstring qfname; + int file; + + /* + * "name" may or may not include a trailing "/quotas". + * Arranging consistency of calling here in "quotas.c" may not be easy and + * it might be easier to examine and adjust it here. + * Fortunately, VxFS seems not to mind at present. + */ + pstrcpy(qfname, name) ; + /* pstrcat(qfname, "/quotas") ; */ /* possibly examine and adjust "name" */ + + euser_id = geteuid(); + set_effective_uid(0); + + DEBUG(5,("disk_quotas: looking for VxFS quotas file \"%s\"\n", qfname)); + if((file=sys_open(qfname, O_RDONLY,0))<0) { + set_effective_uid(euser_id); + return(False); + } + genbuf.ioc_cmd = VX_QUOTACTL; + genbuf.ioc_up = (void *) "abuf; + + quotabuf.cmd = VX_GETQUOTA; + quotabuf.uid = euser_id; + quotabuf.addr = (caddr_t) &D; + ret = ioctl(file, VX_ADMIN_IOCTL, &genbuf); + close(file); + + set_effective_uid(euser_id); + + if (ret < 0) { + DEBUG(5,("disk_quotas ioctl (VxFS) failed. Error = %s\n", strerror(errno) )); + return(False); + } + + /* If softlimit is zero, set it equal to hardlimit. + */ + + if (D.dqb_bsoftlimit==0) + D.dqb_bsoftlimit = D.dqb_bhardlimit; + + /* Use softlimit to determine disk space. A user exceeding the quota is told + * that there's no space left. Writes might actually work for a bit if the + * hardlimit is set higher than softlimit. Effectively the disk becomes + * made of rubber latex and begins to expand to accommodate the user :-) + */ + DEBUG(5,("disk_quotas for path \"%s\" block c/s/h %ld/%ld/%ld; file c/s/h %ld/%ld/%ld\n", + path, D.dqb_curblocks, D.dqb_bsoftlimit, D.dqb_bhardlimit, + D.dqb_curfiles, D.dqb_fsoftlimit, D.dqb_fhardlimit)); + + if (D.dqb_bsoftlimit==0) + return(False); + *bsize = DEV_BSIZE; + *dsize = D.dqb_bsoftlimit; + + if (D.dqb_curblocks > D.dqb_bsoftlimit) { + *dfree = 0; + *dsize = D.dqb_curblocks; + } else + *dfree = D.dqb_bsoftlimit - D.dqb_curblocks; + + DEBUG(5,("disk_quotas for path \"%s\" returning bsize %.0f, dfree %.0f, dsize %.0f\n", + path,(double)*bsize,(double)*dfree,(double)*dsize)); + + return(True); +} + +#endif /* SUNOS5 || ... */ + +#endif /* VXFS_QUOTA */ diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index b7b51775bb..fbb981781f 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -1,9 +1,9 @@ /* - Unix SMB/Netbios implementation. - Version 1.9. + Unix SMB/CIFS implementation. Main SMB reply routines - Copyright (C) Andrew Tridgell 1992-1995 - + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Andrew Bartlett 2001 + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or @@ -25,222 +25,282 @@ #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 int max_send; +extern int max_recv; extern char magic_char; -extern connection_struct Connections[]; -extern files_struct Files[]; extern BOOL case_sensitive; -extern pstring sesssetup_user; -extern int Client; +extern BOOL case_preserve; +extern BOOL short_case_preserve; +extern pstring global_myname; +extern int global_oplock_break; +unsigned int smb_echo_count = 0; -/* 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)) +extern fstring remote_machine; +extern BOOL global_encrypted_passwords_negotiated; /**************************************************************************** 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; + int outsize = 4; + int msg_type = CVAL(inbuf,0); + int msg_flags = CVAL(inbuf,1); + pstring name1,name2; + + extern fstring local_machine; + int len; + char name_type = 0; + + *name1 = *name2 = 0; + + memset(outbuf,'\0',smb_size); - *name1 = *name2 = 0; + smb_setlen(outbuf,0); + + switch (msg_type) { + case 0x81: /* session request */ + SCVAL(outbuf,0,0x82); + SCVAL(outbuf,3,0); + if (name_len(inbuf+4) > 50 || + name_len(inbuf+4 + 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)); + + fstrcpy(remote_machine,name2); + remote_machine[15] = 0; + trim_string(remote_machine," "," "); + strlower(remote_machine); + alpha_strcpy(remote_machine,remote_machine,SAFE_NETBIOS_CHARS,sizeof(remote_machine)-1); + + fstrcpy(local_machine,name1); + len = strlen(local_machine); + if (len == 16) { + name_type = local_machine[15]; + local_machine[15] = 0; + } + trim_string(local_machine," "," "); + strlower(local_machine); + alpha_strcpy(local_machine,local_machine,SAFE_NETBIOS_CHARS,sizeof(local_machine)-1); + + DEBUG(2,("netbios connect: local=%s remote=%s\n", + local_machine, remote_machine )); + + if (name_type == 'R') { + /* We are being asked for a pathworks session --- + no thanks! */ + SCVAL(outbuf, 0,0x83); + break; + } - smb_setlen(outbuf,0); + /* only add the client's machine name to the list + of possibly valid usernames if we are operating + in share mode security */ + if (lp_security() == SEC_SHARE) { + add_session_user(remote_machine); + } - 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); + reload_services(True); + reopen_logs(); + + claim_connection(NULL,"",MAXSTATUS,True); + + break; + + case 0x89: /* session keepalive request + (some old clients produce this?) */ + SCVAL(outbuf,0,SMBkeepalive); + SCVAL(outbuf,3,0); + break; + + case 0x82: /* positive session response */ + case 0x83: /* negative session response */ + case 0x84: /* retarget session response */ + DEBUG(0,("Unexpected session response\n")); + break; + + case SMBkeepalive: /* session keepalive */ + default: + 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)); + + DEBUG(5,("init msg_type=0x%x msg_flags=0x%x\n", + msg_type, msg_flags)); + + return(outsize); } /**************************************************************************** - reply to a tcon + Reply to a tcon. ****************************************************************************/ -int reply_tcon(char *inbuf,char *outbuf) + +int reply_tcon(connection_struct *conn, + char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { - pstring service; - pstring user; - pstring password; - pstring dev; - int connection_num; - int outsize = 0; - int uid = SVAL(inbuf,smb_uid); - int vuid; - int pwlen; + pstring service; + pstring password; + pstring dev; + int outsize = 0; + uint16 vuid = SVAL(inbuf,smb_uid); + int pwlen=0; + NTSTATUS nt_status; + char *p; + DATA_BLOB password_blob; + + START_PROFILE(SMBtcon); - *service = *user = *password = *dev = 0; + *service = *password = *dev = 0; - vuid = valid_uid(uid); - - parse_connect(inbuf,service,user,password,&pwlen,dev); + p = smb_buf(inbuf)+1; + p += srvstr_pull(inbuf, service, p, sizeof(service), -1, STR_TERMINATE) + 1; + pwlen = srvstr_pull(inbuf, password, p, sizeof(password), -1, STR_TERMINATE) + 1; + p += pwlen; + p += srvstr_pull(inbuf, dev, p, sizeof(dev), -1, STR_TERMINATE) + 1; + + p = strrchr_m(service,'\\'); + if (p) { + pstrcpy(service, p+1); + } + + password_blob = data_blob(password, pwlen+1); + + conn = make_connection(service,password_blob,dev,vuid,&nt_status); - connection_num = make_connection(service,user,password,pwlen,dev,vuid); + data_blob_clear_free(&password_blob); - if (connection_num < 0) - return(connection_error(inbuf,outbuf,connection_num)); + if (!conn) { + END_PROFILE(SMBtcon); + return ERROR_NT(nt_status); + } - outsize = set_message(outbuf,2,0,True); - SSVAL(outbuf,smb_vwv0,maxxmit); - SSVAL(outbuf,smb_vwv1,connection_num); - SSVAL(outbuf,smb_tid,connection_num); + outsize = set_message(outbuf,2,0,True); + SSVAL(outbuf,smb_vwv0,max_recv); + SSVAL(outbuf,smb_vwv1,conn->cnum); + SSVAL(outbuf,smb_tid,conn->cnum); - DEBUG(3,("%s tcon service=%s user=%s cnum=%d\n",timestring(),service,user,connection_num)); + DEBUG(3,("tcon service=%s cnum=%d\n", + service, conn->cnum)); - return(outsize); + END_PROFILE(SMBtcon); + return(outsize); } - /**************************************************************************** - reply to a tcon and X + Reply to a tcon and X. ****************************************************************************/ -int reply_tcon_and_X(char *inbuf,char *outbuf,int length,int bufsize) + +int reply_tcon_and_X(connection_struct *conn, 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); + fstring service; + DATA_BLOB password; + pstring devicename; + NTSTATUS nt_status; + uint16 vuid = SVAL(inbuf,smb_uid); + int passlen = SVAL(inbuf,smb_vwv3); + pstring path; + char *p, *q; + extern BOOL global_encrypted_passwords_negotiated; + START_PROFILE(SMBtconX); + + *service = *devicename = 0; + + /* we might have to close an old one */ + if ((SVAL(inbuf,smb_vwv2) & 0x1) && conn) { + close_cnum(conn,vuid); + } - *service = *user = *password = *devicename = 0; + if (passlen > MAX_PASS_LEN) { + return ERROR_DOS(ERRDOS,ERRbuftoosmall); + } + + if (global_encrypted_passwords_negotiated) { + password = data_blob(smb_buf(inbuf),passlen); + } else { + password = data_blob(smb_buf(inbuf),passlen+1); + /* Ensure correct termination */ + password.data[passlen]=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)); - } + p = smb_buf(inbuf) + passlen; + p += srvstr_pull(inbuf, path, p, sizeof(path), -1, STR_TERMINATE); + + /* + * the service name can be either: \\server\share + * or share directly like on the DELL PowerVault 705 + */ + if (*path=='\\') { + q = strchr_m(path+2,'\\'); + if (!q) { + END_PROFILE(SMBtconX); + return(ERROR_DOS(ERRDOS,ERRnosuchshare)); + } + fstrcpy(service,q+1); + } + else + fstrcpy(service,path); + + p += srvstr_pull(inbuf, devicename, p, sizeof(devicename), 6, STR_ASCII); - connection_num = make_connection(service,user,password,passlen,devicename,vuid); - - if (connection_num < 0) - return(connection_error(inbuf,outbuf,connection_num)); + DEBUG(4,("Got device type %s\n",devicename)); - 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); + conn = make_connection(service,password,devicename,vuid,&nt_status); + + data_blob_clear_free(&password); - CVAL(outbuf,smb_vwv0) = smb_com2; - SSVAL(outbuf,smb_vwv1,(chain_size + outsize)-4); + if (!conn) { + END_PROFILE(SMBtconX); + return ERROR_NT(nt_status); + } - strcpy(smb_buf(outbuf),devicename); + if (Protocol < PROTOCOL_NT1) { + set_message(outbuf,2,0,True); + p = smb_buf(outbuf); + p += srvstr_push(outbuf, p, devicename, -1, + STR_TERMINATE|STR_ASCII); + set_message_end(outbuf,p); + } else { + /* NT sets the fstype of IPC$ to the null string */ + char *fsname = IS_IPC(conn) ? "" : lp_fstype(SNUM(conn)); + + set_message(outbuf,3,0,True); + + p = smb_buf(outbuf); + p += srvstr_push(outbuf, p, devicename, -1, + STR_TERMINATE|STR_ASCII); + p += srvstr_push(outbuf, p, fsname, -1, + STR_TERMINATE); + + set_message_end(outbuf,p); + + /* what does setting this bit do? It is set by NT4 and + may affect the ability to autorun mounted cdroms */ + SSVAL(outbuf, smb_vwv2, SMB_SUPPORT_SEARCH_BITS| + (lp_csc_policy(SNUM(conn)) << 2)); + + init_dfsroot(conn, inbuf, outbuf); + } - if (smb_com2 != 0xFF) - outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4, - outbuf,outbuf+outsize, - length,bufsize); + + DEBUG(3,("tconX service=%s \n", + service)); + + /* set the incoming and outgoing tid to the just created one */ + SSVAL(inbuf,smb_tid,conn->cnum); + SSVAL(outbuf,smb_tid,conn->cnum); - return(outsize); + END_PROFILE(SMBtconX); + return chain_reply(inbuf,outbuf,length,bufsize); } @@ -249,245 +309,105 @@ int reply_tcon_and_X(char *inbuf,char *outbuf,int length,int bufsize) ****************************************************************************/ int reply_unknown(char *inbuf,char *outbuf) { - int cnum; - int type; - cnum = SVAL(inbuf,smb_tid); - type = CVAL(inbuf,smb_com); + int type; + 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)); + DEBUG(0,("unknown command type (%s): type=%d (0x%X)\n", + smb_fn_name(type), type, type)); - return(ERROR(ERRSRV,ERRunknownsmb)); + return(ERROR_DOS(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 reply_ioctl(connection_struct *conn, + char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { - 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); + uint16 device = SVAL(inbuf,smb_vwv1); + uint16 function = SVAL(inbuf,smb_vwv2); + uint32 ioctl_code = (device << 16) + function; + int replysize, outsize; + char *p; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBioctl); - add_session_user(user); + DEBUG(4, ("Received IOCTL (code 0x%x)\n", ioctl_code)); - - 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)) + switch (ioctl_code) { - 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; + case IOCTL_QUERY_JOB_INFO: + replysize = 32; + break; + default: + END_PROFILE(SMBioctl); + return(ERROR_DOS(ERRSRV,ERRnosupport)); } - } - - 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); + outsize = set_message(outbuf,8,replysize+1,True); + SSVAL(outbuf,smb_vwv1,replysize); /* Total data bytes returned */ + SSVAL(outbuf,smb_vwv5,replysize); /* Data bytes this buffer */ + SSVAL(outbuf,smb_vwv6,52); /* Offset to data */ + p = smb_buf(outbuf) + 1; /* Allow for alignment */ - 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); + switch (ioctl_code) + { + case IOCTL_QUERY_JOB_INFO: + SSVAL(p,0,fsp->print_jobid); /* Job number */ + srvstr_push(outbuf, p+2, global_myname, 15, STR_TERMINATE|STR_ASCII); + srvstr_push(outbuf, p+18, lp_servicename(SNUM(conn)), 13, STR_TERMINATE|STR_ASCII); + break; + } - return(outsize); + END_PROFILE(SMBioctl); + return outsize; } - /**************************************************************************** reply to a chkpth ****************************************************************************/ -int reply_chkpth(char *inbuf,char *outbuf) +int reply_chkpth(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { int outsize = 0; - int cnum,mode; + int mode; pstring name; BOOL ok = False; - - cnum = SVAL(inbuf,smb_tid); - - strcpy(name,smb_buf(inbuf) + 1); - unix_convert(name,cnum); + BOOL bad_path = False; + SMB_STRUCT_STAT sbuf; + START_PROFILE(SMBchkpth); + + srvstr_pull(inbuf, name, smb_buf(inbuf) + 1, sizeof(name), -1, STR_TERMINATE); + + RESOLVE_DFSPATH(name, conn, inbuf, outbuf); + + unix_convert(name,conn,0,&bad_path,&sbuf); mode = SVAL(inbuf,smb_vwv0); - if (check_name(name,cnum)) - ok = directory_exist(name,NULL); + if (check_name(name,conn)) { + if (VALID_STAT(sbuf) || vfs_stat(conn,name,&sbuf) == 0) + ok = S_ISDIR(sbuf.st_mode); + } + + if (!ok) { + /* We special case this - as when a Windows machine + is parsing a path is steps through the components + one at a time - if a component fails it expects + ERRbadpath, not ERRbadfile. + */ + if(errno == ENOENT) { + return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND); + } + + return(UNIXERROR(ERRDOS,ERRbadpath)); + } - 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)); - + + DEBUG(3,("chkpth %s mode=%d\n", name, mode)); + + END_PROFILE(SMBchkpth); return(outsize); } @@ -495,67 +415,75 @@ int reply_chkpth(char *inbuf,char *outbuf) /**************************************************************************** reply to a getatr ****************************************************************************/ -int reply_getatr(char *inbuf,char *outbuf) +int reply_getatr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { pstring fname; - int cnum; int outsize = 0; - struct stat sbuf; + SMB_STRUCT_STAT sbuf; BOOL ok = False; int mode=0; - uint32 size=0; + SMB_OFF_T size=0; time_t mtime=0; - - cnum = SVAL(inbuf,smb_tid); + BOOL bad_path = False; + char *p; + START_PROFILE(SMBgetatr); - strcpy(fname,smb_buf(inbuf) + 1); - unix_convert(fname,cnum); + p = smb_buf(inbuf) + 1; + p += srvstr_pull(inbuf, fname, p, sizeof(fname), -1, STR_TERMINATE); + RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); + /* 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; - } + { + mode = aHIDDEN | aDIR; + if (!CAN_WRITE(conn)) mode |= aRONLY; + size = 0; + mtime = 0; + ok = True; + } else - if (check_name(fname,cnum)) + { + unix_convert(fname,conn,0,&bad_path,&sbuf); + if (check_name(fname,conn)) + { + if (VALID_STAT(sbuf) || vfs_stat(conn,fname,&sbuf) == 0) { - 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))); + mode = dos_mode(conn,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) + { + set_bad_path_error(errno, bad_path); + END_PROFILE(SMBgetatr); 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(lp_dos_filetime_resolution(SNUM(conn)) ) + put_dos_date3(outbuf,smb_vwv1,mtime & ~1); + else + put_dos_date3(outbuf,smb_vwv1,mtime); + SIVAL(outbuf,smb_vwv3,(uint32)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 */ - } + if (Protocol >= PROTOCOL_NT1) + SSVAL(outbuf,smb_flg2,SVAL(outbuf, smb_flg2) | FLAGS2_IS_LONG_NAME); - DEBUG(3,("%s getatr name=%s mode=%d size=%d\n",timestring(),fname,mode,size)); + DEBUG( 3, ( "getatr name=%s mode=%d size=%d\n", fname, mode, (uint32)size ) ); + END_PROFILE(SMBgetatr); return(outsize); } @@ -563,37 +491,48 @@ int reply_getatr(char *inbuf,char *outbuf) /**************************************************************************** reply to a setatr ****************************************************************************/ -int reply_setatr(char *inbuf,char *outbuf) +int reply_setatr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { 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); + SMB_STRUCT_STAT sbuf; + BOOL bad_path = False; + char *p; + + START_PROFILE(SMBsetatr); + + p = smb_buf(inbuf) + 1; + p += srvstr_pull(inbuf, fname, p, sizeof(fname), -1, STR_TERMINATE); + unix_convert(fname,conn,0,&bad_path,&sbuf); mode = SVAL(inbuf,smb_vwv0); mtime = make_unix_date3(inbuf+smb_vwv1); - if (directory_exist(fname,NULL)) + if (VALID_STAT_OF_DIR(sbuf)) mode |= aDIR; - if (check_name(fname,cnum)) - ok = (dos_chmod(cnum,fname,mode,NULL) == 0); + else + mode &= ~aDIR; + + if (check_name(fname,conn)) + ok = (file_chmod(conn,fname,mode,NULL) == 0); if (ok) - ok = set_filetime(fname,mtime); + ok = set_filetime(conn,fname,mtime); if (!ok) + { + set_bad_path_error(errno, bad_path); + END_PROFILE(SMBsetatr); return(UNIXERROR(ERRDOS,ERRnoaccess)); - + } + outsize = set_message(outbuf,0,0,True); - DEBUG(3,("%s setatr name=%s mode=%d\n",timestring(),fname,mode)); + DEBUG( 3, ( "setatr name=%s mode=%d\n", fname, mode ) ); + END_PROFILE(SMBsetatr); return(outsize); } @@ -601,15 +540,13 @@ int reply_setatr(char *inbuf,char *outbuf) /**************************************************************************** reply to a dskattr ****************************************************************************/ -int reply_dskattr(char *inbuf,char *outbuf) +int reply_dskattr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { - int cnum; int outsize = 0; - int dfree,dsize,bsize; + SMB_BIG_UINT dfree,dsize,bsize; + START_PROFILE(SMBdskattr); - cnum = SVAL(inbuf,smb_tid); - - sys_disk_free(".",&bsize,&dfree,&dsize); + conn->vfs_ops.disk_free(conn,".",True,&bsize,&dfree,&dsize); outsize = set_message(outbuf,5,0,True); @@ -617,9 +554,10 @@ int reply_dskattr(char *inbuf,char *outbuf) 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)); - + + DEBUG(3,("dskattr dfree=%d\n", (unsigned int)dfree)); + + END_PROFILE(SMBdskattr); return(outsize); } @@ -628,15 +566,15 @@ int reply_dskattr(char *inbuf,char *outbuf) reply to a search Can be called from SMBsearch, SMBffirst or SMBfunique. ****************************************************************************/ -int reply_search(char *inbuf,char *outbuf) +int reply_search(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { pstring mask; pstring directory; pstring fname; - int size,mode; + SMB_OFF_T size; + int mode; time_t date; int dirtype; - int cnum; int outsize = 0; int numentries = 0; BOOL finished = False; @@ -645,12 +583,14 @@ int reply_search(char *inbuf,char *outbuf) char *p; BOOL ok = False; int status_len; - char *path; + pstring path; char status[21]; int dptr_num= -1; BOOL check_descend = False; BOOL expect_close = False; BOOL can_open = True; + BOOL bad_path = False; + START_PROFILE(SMBsearch); *mask = *directory = *fname = 0; @@ -658,197 +598,171 @@ int reply_search(char *inbuf,char *outbuf) 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)); - + p = smb_buf(inbuf) + 1; + p += srvstr_pull(inbuf, path, p, sizeof(path), -1, STR_TERMINATE); + p++; + status_len = SVAL(p, 0); + p += 2; /* 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); + { + SMB_STRUCT_STAT sbuf; + pstring dir2; - if (!check_name(directory,cnum)) - can_open = False; + pstrcpy(directory,path); + pstrcpy(dir2,path); + unix_convert(directory,conn,0,&bad_path,&sbuf); + unix_format(dir2); - 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 (!check_name(directory,conn)) + can_open = False; - if (strlen(directory) == 0) - strcpy(directory,"./"); - bzero(status,21); - CVAL(status,0) = dirtype; + p = strrchr_m(dir2,'/'); + if (p == NULL) + { + pstrcpy(mask,dir2); + *dir2 = 0; } - else + 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); + *p = 0; + pstrcpy(mask,p+1); } - /* turn strings of spaces into a . */ + p = strrchr_m(directory,'/'); + if (!p) + *directory = 0; + else + *p = 0; + + if (strlen(directory) == 0) + pstrcpy(directory,"./"); + memset((char *)status,'\0',21); + SCVAL(status,0,dirtype); + } + else { - 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); - } + memcpy(status,p,21); + dirtype = CVAL(status,0) & 0x1F; + conn->dirptr = dptr_fetch(status+12,&dptr_num); + if (!conn->dirptr) + goto SearchEmpty; + string_set(&conn->dirpath,dptr_path(dptr_num)); + fstrcpy(mask, dptr_wcard(dptr_num)); } + if (can_open) { - for (p=mask; *p; p++) + p = smb_buf(outbuf) + 3; + + ok = True; + + if (status_len == 0) + { + dptr_num = dptr_create(conn,directory,True,expect_close,SVAL(inbuf,smb_pid)); + if (dptr_num < 0) { - if (*p != '?' && *p != '*' && !isdoschar(*p)) - { - DEBUG(5,("Invalid char [%c] in search mask?\n",*p)); - *p = '?'; - } + if(dptr_num == -2) + { + set_bad_path_error(errno, bad_path); + END_PROFILE(SMBsearch); + return (UNIXERROR(ERRDOS,ERRnofids)); + } + END_PROFILE(SMBsearch); + return ERROR_DOS(ERRDOS,ERRnofids); } - } - - if (!strchr(mask,'.') && strlen(mask)>8) - { - fstring tmp; - strcpy(tmp,&mask[8]); - mask[8] = '.'; - mask[9] = 0; - strcat(mask,tmp); + dptr_set_wcard(dptr_num, strdup(mask)); } - 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)); + DEBUG(4,("dptr_num is %d\n",dptr_num)); - if (ok) - { - if ((dirtype&0x1F) == aVOLID) - { - memcpy(p,status,21); - make_dir_struct(p,"???????????",volume_label(SNUM(cnum)),0,aVOLID,0); - dptr_fill(p+12,dptr_num); - if (dptr_zero(p+12) && (status_len==0)) - numentries = 1; - else - numentries = 0; - p += DIR_STRUCT_SIZE; - } - else - { - DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n",Connections[cnum].dirpath,lp_dontdescend(SNUM(cnum)))); - if (in_list(Connections[cnum].dirpath, - lp_dontdescend(SNUM(cnum)),True)) - check_descend = True; - - for (i=numentries;(i<maxentries) && !finished;i++) - { - finished = - !get_dir_entry(cnum,mask,dirtype,fname,&size,&mode,&date,check_descend); - if (!finished) - { - memcpy(p,status,21); - make_dir_struct(p,mask,fname,size,mode,date); - dptr_fill(p+12,dptr_num); - numentries++; - } - p += DIR_STRUCT_SIZE; - } - } - } - } + if (ok) + { + if ((dirtype&0x1F) == aVOLID) + { + memcpy(p,status,21); + make_dir_struct(p,"???????????",volume_label(SNUM(conn)),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", + conn->dirpath,lp_dontdescend(SNUM(conn)))); + if (in_list(conn->dirpath, lp_dontdescend(SNUM(conn)),True)) + check_descend = True; + + for (i=numentries;(i<maxentries) && !finished;i++) + { + finished = + !get_dir_entry(conn,mask,dirtype,fname,&size,&mode,&date,check_descend); + if (!finished) + { + memcpy(p,status,21); + make_dir_struct(p,mask,fname,size,mode,date); + dptr_fill(p+12,dptr_num); + numentries++; + } + p += DIR_STRUCT_SIZE; + } + } + } /* if (ok ) */ + } - SearchEmpty: + SearchEmpty: if (numentries == 0 || !ok) - { - CVAL(outbuf,smb_rcls) = ERRDOS; - SSVAL(outbuf,smb_err,ERRnofiles); - } + { + SCVAL(outbuf,smb_rcls,ERRDOS); + SSVAL(outbuf,smb_err,ERRnofiles); + dptr_close(&dptr_num); + } /* If we were called as SMBffirst with smb_search_id == NULL and no entries were found then return error and close dirptr (X/Open spec) */ if(ok && expect_close && numentries == 0 && status_len == 0) - { - CVAL(outbuf,smb_rcls) = ERRDOS; - SSVAL(outbuf,smb_err,ERRnofiles); - /* Also close the dptr - we know it's gone */ - dptr_close(dptr_num); - } + { + SCVAL(outbuf,smb_rcls,ERRDOS); + SSVAL(outbuf,smb_err,ERRnofiles); + /* Also close the dptr - we know it's gone */ + dptr_close(&dptr_num); + } /* If we were called as SMBfunique, then we can close the dirptr now ! */ if(dptr_num >= 0 && CVAL(inbuf,smb_com) == SMBfunique) - dptr_close(dptr_num); + dptr_close(&dptr_num); SSVAL(outbuf,smb_vwv0,numentries); SSVAL(outbuf,smb_vwv1,3 + numentries * DIR_STRUCT_SIZE); - CVAL(smb_buf(outbuf),0) = 5; + SCVAL(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 */ - } + if (Protocol >= PROTOCOL_NT1) + SSVAL(outbuf,smb_flg2,SVAL(outbuf, smb_flg2) | FLAGS2_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)); + slprintf(directory, sizeof(directory)-1, "(%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)); + DEBUG( 4, ( "%s mask=%s path=%s dtype=%d nument=%d of %d\n", + smb_fn_name(CVAL(inbuf,smb_com)), + mask, directory, dirtype, numentries, maxentries ) ); + END_PROFILE(SMBsearch); return(outsize); } @@ -856,36 +770,41 @@ int reply_search(char *inbuf,char *outbuf) /**************************************************************************** reply to a fclose (stop directory search) ****************************************************************************/ -int reply_fclose(char *inbuf,char *outbuf) +int reply_fclose(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { - int cnum; int outsize = 0; int status_len; - char *path; + pstring path; char status[21]; - int dptr_num= -1; + int dptr_num= -2; + char *p; - cnum = SVAL(inbuf,smb_tid); + START_PROFILE(SMBfclose); 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)); + p = smb_buf(inbuf) + 1; + p += srvstr_pull(inbuf, path, p, sizeof(path), -1, STR_TERMINATE); + p++; + status_len = SVAL(p,0); + p += 2; + + if (status_len == 0) { + END_PROFILE(SMBfclose); + return ERROR_DOS(ERRSRV,ERRsrverror); + } - memcpy(status,smb_buf(inbuf) + 1 + strlen(path) + 4,21); + memcpy(status,p,21); if(dptr_fetch(status+12,&dptr_num)) { /* Close the dptr - we know it's gone */ - dptr_close(dptr_num); + dptr_close(&dptr_num); } SSVAL(outbuf,smb_vwv0,0); - DEBUG(3,("%s search close cnum=%d\n",timestring(),cnum)); + DEBUG(3,("search close\n")); + END_PROFILE(SMBfclose); return(outsize); } @@ -893,63 +812,71 @@ int reply_fclose(char *inbuf,char *outbuf) /**************************************************************************** reply to an open ****************************************************************************/ -int reply_open(char *inbuf,char *outbuf) + +int reply_open(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { pstring fname; - int cnum; - int fnum = -1; int outsize = 0; int fmode=0; int share_mode; - int size = 0; + SMB_OFF_T size = 0; time_t mtime=0; - int unixmode; + mode_t unixmode; int rmode=0; - struct stat sbuf; - - cnum = SVAL(inbuf,smb_tid); - + SMB_STRUCT_STAT sbuf; + BOOL bad_path = False; + files_struct *fsp; + int oplock_request = CORE_OPLOCK_REQUEST(inbuf); + START_PROFILE(SMBopen); + 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)); + srvstr_pull(inbuf, fname, smb_buf(inbuf)+1, sizeof(fname), -1, STR_TERMINATE); - if (!check_name(fname,cnum)) - return(UNIXERROR(ERRDOS,ERRnoaccess)); - - unixmode = unix_mode(cnum,aARCH); + RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); + + unix_convert(fname,conn,0,&bad_path,&sbuf); + + unixmode = unix_mode(conn,aARCH,fname); - open_file_shared(fnum,cnum,fname,share_mode,3,unixmode,&rmode,NULL); + fsp = open_file_shared(conn,fname,&sbuf,share_mode,(FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), + unixmode, oplock_request,&rmode,NULL); - if (!Files[fnum].open) + if (!fsp) + { + set_bad_path_error(errno, bad_path); + END_PROFILE(SMBopen); 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); + fmode = dos_mode(conn,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)); + close_file(fsp,False); + END_PROFILE(SMBopen); + return ERROR_DOS(ERRDOS,ERRnoaccess); } outsize = set_message(outbuf,7,0,True); - SSVAL(outbuf,smb_vwv0,fnum); + SSVAL(outbuf,smb_vwv0,fsp->fnum); SSVAL(outbuf,smb_vwv1,fmode); - put_dos_date3(outbuf,smb_vwv2,mtime); - SIVAL(outbuf,smb_vwv4,size); + if(lp_dos_filetime_resolution(SNUM(conn)) ) + put_dos_date3(outbuf,smb_vwv2,mtime & ~1); + else + put_dos_date3(outbuf,smb_vwv2,mtime); + SIVAL(outbuf,smb_vwv4,(uint32)size); SSVAL(outbuf,smb_vwv6,rmode); + + if (oplock_request && lp_fake_oplocks(SNUM(conn))) { + SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED); + } + if(EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) + SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED); + END_PROFILE(SMBopen); return(outsize); } @@ -957,165 +884,211 @@ int reply_open(char *inbuf,char *outbuf) /**************************************************************************** reply to an open and X ****************************************************************************/ -int reply_open_and_X(char *inbuf,char *outbuf,int length,int bufsize) +int reply_open_and_X(connection_struct *conn, 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); + /* Breakout the oplock request bits so we can set the + reply bits separately. */ + BOOL ex_oplock_request = EXTENDED_OPLOCK_REQUEST(inbuf); + BOOL core_oplock_request = CORE_OPLOCK_REQUEST(inbuf); + BOOL oplock_request = ex_oplock_request | core_oplock_request; #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; + mode_t unixmode; + SMB_OFF_T size=0; + int fmode=0,mtime=0,rmode=0; + SMB_STRUCT_STAT sbuf; int smb_action = 0; + BOOL bad_path = False; + files_struct *fsp; + START_PROFILE(SMBopenX); + + /* If it's an IPC, pass off the pipe handler. */ + if (IS_IPC(conn)) { + if (lp_nt_pipe_support()) { + END_PROFILE(SMBopenX); + return reply_open_pipe_and_X(conn, inbuf,outbuf,length,bufsize); + } else { + END_PROFILE(SMBopenX); + return ERROR_DOS(ERRSRV,ERRaccess); + } + } /* XXXX we need to handle passed times, sattr and flags */ + srvstr_pull(inbuf, fname, smb_buf(inbuf), sizeof(fname), -1, STR_TERMINATE); - 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)); + RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); - unixmode = unix_mode(cnum,smb_attr | aARCH); + unix_convert(fname,conn,0,&bad_path,&sbuf); + + unixmode = unix_mode(conn,smb_attr | aARCH, fname); - open_file_shared(fnum,cnum,fname,smb_mode,smb_ofun,unixmode, - &rmode,&smb_action); + fsp = open_file_shared(conn,fname,&sbuf,smb_mode,smb_ofun,unixmode, + oplock_request, &rmode,&smb_action); - if (!Files[fnum].open) + if (!fsp) + { + set_bad_path_error(errno, bad_path); + END_PROFILE(SMBopenX); 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); + fmode = dos_mode(conn,fname,&sbuf); mtime = sbuf.st_mtime; if (fmode & aDIR) { - close_file(fnum); - return(ERROR(ERRDOS,ERRnoaccess)); + close_file(fsp,False); + END_PROFILE(SMBopenX); + return ERROR_DOS(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); + /* If the caller set the extended oplock request bit + and we granted one (by whatever means) - set the + correct bit for extended oplock reply. + */ + + if (ex_oplock_request && lp_fake_oplocks(SNUM(conn))) { + smb_action |= EXTENDED_OPLOCK_GRANTED; + } + + if(ex_oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { + smb_action |= EXTENDED_OPLOCK_GRANTED; + } + + /* If the caller set the core oplock request bit + and we granted one (by whatever means) - set the + correct bit for core oplock reply. + */ + + if (core_oplock_request && lp_fake_oplocks(SNUM(conn))) { + SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED); + } + + if(core_oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { + SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED); + } + + set_message(outbuf,15,0,True); + SSVAL(outbuf,smb_vwv2,fsp->fnum); SSVAL(outbuf,smb_vwv3,fmode); - put_dos_date3(outbuf,smb_vwv4,mtime); - SIVAL(outbuf,smb_vwv6,size); + if(lp_dos_filetime_resolution(SNUM(conn)) ) + put_dos_date3(outbuf,smb_vwv4,mtime & ~1); + else + put_dos_date3(outbuf,smb_vwv4,mtime); + SIVAL(outbuf,smb_vwv6,(uint32)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); + END_PROFILE(SMBopenX); + return chain_reply(inbuf,outbuf,length,bufsize); } /**************************************************************************** reply to a SMBulogoffX ****************************************************************************/ -int reply_ulogoffX(char *inbuf,char *outbuf,int length,int bufsize) +int reply_ulogoffX(connection_struct *conn, 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); + uint16 vuid = SVAL(inbuf,smb_uid); + user_struct *vuser = get_valid_user_struct(vuid); + START_PROFILE(SMBulogoffX); - invalidate_uid(uid); + if(vuser == 0) { + DEBUG(3,("ulogoff, vuser id %d does not map to user.\n", vuid)); + } - outsize = set_message(outbuf,2,0,True); - CVAL(outbuf,smb_vwv0) = smb_com2; - SSVAL(outbuf,smb_vwv1,(chain_size+outsize)-4); + /* in user level security we are supposed to close any files + open by this user */ + if ((vuser != 0) && (lp_security() != SEC_SHARE)) { + file_close_user(vuid); + } - DEBUG(3,("%s ulogoffX uid=%d\n",timestring(),uid)); + invalidate_vuid(vuid); - if (smb_com2 != 0xFF) - outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4, - outbuf,outbuf+outsize, - length,bufsize); + set_message(outbuf,2,0,True); - - return(outsize); + DEBUG( 3, ( "ulogoffX vuid=%d\n", vuid ) ); + + END_PROFILE(SMBulogoffX); + return chain_reply(inbuf,outbuf,length,bufsize); } /**************************************************************************** - reply to a mknew + reply to a mknew or a create ****************************************************************************/ -int reply_mknew(char *inbuf,char *outbuf) +int reply_mknew(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { pstring fname; - int cnum,com; - int fnum = -1; + int com; int outsize = 0; int createmode; mode_t unixmode; - + int ofun = 0; + BOOL bad_path = False; + files_struct *fsp; + int oplock_request = CORE_OPLOCK_REQUEST(inbuf); + SMB_STRUCT_STAT sbuf; + START_PROFILE(SMBcreate); + 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); + srvstr_pull(inbuf, fname, smb_buf(inbuf) + 1, sizeof(fname), -1, STR_TERMINATE); - if (createmode & aVOLID) - { + RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); + + unix_convert(fname,conn,0,&bad_path,&sbuf); + + if (createmode & aVOLID) { DEBUG(0,("Attempt to create file (%s) with volid set - please report this\n",fname)); - } + } - unixmode = unix_mode(cnum,createmode); + unixmode = unix_mode(conn,createmode,fname); - 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)); + if(com == SMBmknew) + { + /* We should fail if file exists. */ + ofun = FILE_CREATE_IF_NOT_EXIST; + } + else + { + /* SMBcreate - Create if file doesn't exist, truncate if it does. */ + ofun = FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_TRUNCATE; + } - open_file(fnum,cnum,fname,O_RDWR | O_CREAT | O_TRUNC,unixmode); + /* Open file in dos compatibility share mode. */ + fsp = open_file_shared(conn,fname,&sbuf,SET_DENY_MODE(DENY_FCB)|SET_OPEN_MODE(DOS_OPEN_FCB), + ofun, unixmode, oplock_request, NULL, NULL); - if (!Files[fnum].open) + if (!fsp) + { + set_bad_path_error(errno, bad_path); + END_PROFILE(SMBcreate); 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)); - + SSVAL(outbuf,smb_vwv0,fsp->fnum); + + if (oplock_request && lp_fake_oplocks(SNUM(conn))) { + SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED); + } + + if(EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) + SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED); + + DEBUG( 2, ( "new file %s\n", fname ) ); + DEBUG( 3, ( "mknew %s fd=%d dmode=%d umode=%o\n", + fname, fsp->fd, createmode, (int)unixmode ) ); + + END_PROFILE(SMBcreate); return(outsize); } @@ -1123,343 +1096,561 @@ int reply_mknew(char *inbuf,char *outbuf) /**************************************************************************** reply to a create temporary file ****************************************************************************/ -int reply_ctemp(char *inbuf,char *outbuf) +int reply_ctemp(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { pstring fname; - pstring fname2; - int cnum; - int fnum = -1; int outsize = 0; int createmode; mode_t unixmode; - - cnum = SVAL(inbuf,smb_tid); + BOOL bad_path = False; + files_struct *fsp; + int oplock_request = CORE_OPLOCK_REQUEST(inbuf); + int tmpfd; + SMB_STRUCT_STAT sbuf; + char *p, *s; + + START_PROFILE(SMBctemp); + createmode = SVAL(inbuf,smb_vwv0); - sprintf(fname,"%s/TMXXXXXX",smb_buf(inbuf)+1); - unix_convert(fname,cnum); + srvstr_pull(inbuf, fname, smb_buf(inbuf)+1, sizeof(fname), -1, STR_TERMINATE); + pstrcat(fname,"\\TMXXXXXX"); + + RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); + + unix_convert(fname,conn,0,&bad_path,&sbuf); - unixmode = unix_mode(cnum,createmode); + unixmode = unix_mode(conn,createmode,fname); - fnum = find_free_file(); - if (fnum < 0) - return(ERROR(ERRSRV,ERRnofids)); + tmpfd = smb_mkstemp(fname); + if (tmpfd == -1) { + END_PROFILE(SMBctemp); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } - if (!check_name(fname,cnum)) - return(UNIXERROR(ERRDOS,ERRnoaccess)); + vfs_stat(conn,fname,&sbuf); - strcpy(fname2,(char *)mktemp(fname)); + /* Open file in dos compatibility share mode. */ + /* We should fail if file does not exist. */ + fsp = open_file_shared(conn,fname,&sbuf, + SET_DENY_MODE(DENY_FCB)|SET_OPEN_MODE(DOS_OPEN_FCB), + FILE_EXISTS_OPEN|FILE_FAIL_IF_NOT_EXIST, + unixmode, oplock_request, NULL, NULL); - open_file(fnum,cnum,fname2,O_RDWR | O_CREAT | O_TRUNC,unixmode); + /* close fd from smb_mkstemp() */ + close(tmpfd); - if (!Files[fnum].open) + if (!fsp) { + set_bad_path_error(errno, bad_path); + END_PROFILE(SMBctemp); 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)); + outsize = set_message(outbuf,1,0,True); + SSVAL(outbuf,smb_vwv0,fsp->fnum); + + /* the returned filename is relative to the directory */ + s = strrchr_m(fname, '/'); + if (!s) { + s = fname; + } else { + s++; + } + + p = smb_buf(outbuf); + SSVALS(p, 0, -1); /* what is this? not in spec */ + SSVAL(p, 2, strlen(s)); + p += 4; + p += srvstr_push(outbuf, p, s, -1, STR_ASCII); + outsize = set_message_end(outbuf, p); + + if (oplock_request && lp_fake_oplocks(SNUM(conn))) { + SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED); + } + if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) + SCVAL(outbuf,smb_flg,CVAL(outbuf,smb_flg)|CORE_OPLOCK_GRANTED); + + DEBUG( 2, ( "created temp file %s\n", fname ) ); + DEBUG( 3, ( "ctemp %s fd=%d dmode=%d umode=%o\n", + fname, fsp->fd, createmode, (int)unixmode ) ); + + END_PROFILE(SMBctemp); return(outsize); } +/******************************************************************* + Check if a user is allowed to rename a file. +********************************************************************/ + +static NTSTATUS can_rename(char *fname,connection_struct *conn, SMB_STRUCT_STAT *pst) +{ + int smb_action; + int access_mode; + files_struct *fsp; + + if (!CAN_WRITE(conn)) + return NT_STATUS_MEDIA_WRITE_PROTECTED; + + if (S_ISDIR(pst->st_mode)) + return NT_STATUS_OK; + + /* We need a better way to return NT status codes from open... */ + unix_ERR_class = 0; + unix_ERR_code = 0; + + fsp = open_file_shared1(conn, fname, pst, DELETE_ACCESS, SET_DENY_MODE(DENY_ALL), + (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), 0, 0, &access_mode, &smb_action); + + if (!fsp) { + NTSTATUS ret = NT_STATUS_ACCESS_DENIED; + if (unix_ERR_class == ERRDOS && unix_ERR_code == ERRbadshare) + ret = NT_STATUS_SHARING_VIOLATION; + unix_ERR_class = 0; + unix_ERR_code = 0; + return ret; + } + close_file(fsp,False); + return NT_STATUS_OK; +} /******************************************************************* -check if a user is allowed to delete a file + Check if a user is allowed to delete a file. ********************************************************************/ -static BOOL can_delete(char *fname,int cnum,int dirtype) + +static NTSTATUS can_delete(char *fname,connection_struct *conn, 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); + SMB_STRUCT_STAT sbuf; + int fmode; + int smb_action; + int access_mode; + files_struct *fsp; + + if (!CAN_WRITE(conn)) + return NT_STATUS_MEDIA_WRITE_PROTECTED; + + if (conn->vfs_ops.lstat(conn,fname,&sbuf) != 0) + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + + fmode = dos_mode(conn,fname,&sbuf); + if (fmode & aDIR) + return NT_STATUS_FILE_IS_A_DIRECTORY; + if (!lp_delete_readonly(SNUM(conn))) { + if (fmode & aRONLY) + return NT_STATUS_CANNOT_DELETE; + } + if ((fmode & ~dirtype) & (aHIDDEN | aSYSTEM)) + return NT_STATUS_CANNOT_DELETE; + + /* We need a better way to return NT status codes from open... */ + unix_ERR_class = 0; + unix_ERR_code = 0; + + fsp = open_file_shared1(conn, fname, &sbuf, DELETE_ACCESS, SET_DENY_MODE(DENY_ALL), + (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), 0, 0, &access_mode, &smb_action); + + if (!fsp) { + NTSTATUS ret = NT_STATUS_ACCESS_DENIED; + if (unix_ERR_class == ERRDOS && unix_ERR_code == ERRbadshare) + ret = NT_STATUS_SHARING_VIOLATION; + unix_ERR_class = 0; + unix_ERR_code = 0; + return ret; + } + close_file(fsp,False); + return NT_STATUS_OK; } /**************************************************************************** - reply to a unlink + The guts of the unlink command, split out so it may be called by the NT SMB + code. ****************************************************************************/ -int reply_unlink(char *inbuf,char *outbuf) + +NTSTATUS unlink_internals(connection_struct *conn, int dirtype, char *name) { - 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; + pstring directory; + pstring mask; + char *p; + int count=0; + NTSTATUS error = NT_STATUS_OK; + BOOL has_wild; + BOOL bad_path = False; + BOOL rc = True; + SMB_STRUCT_STAT sbuf; + + *directory = *mask = 0; + + rc = unix_convert(name,conn,0,&bad_path,&sbuf); + + p = strrchr_m(name,'/'); + if (!p) { + pstrcpy(directory,"."); + pstrcpy(mask,name); + } else { + *p = 0; + pstrcpy(directory,name); + pstrcpy(mask,p+1); + } + + /* + * We should only check the mangled cache + * here if unix_convert failed. This means + * that the path in 'mask' doesn't exist + * on the file system and so we need to look + * for a possible mangle. This patch from + * Tine Smukavec <valentin.smukavec@hermes.si>. + */ + + if (!rc && mangle_is_mangled(mask)) + mangle_check_cache( mask ); + + has_wild = ms_has_wild(mask); + + if (!has_wild) { + pstrcat(directory,"/"); + pstrcat(directory,mask); + error = can_delete(directory,conn,dirtype); + if (!NT_STATUS_IS_OK(error)) return error; + + if (vfs_unlink(conn,directory) == 0) { + count++; + } + } else { + void *dirptr = NULL; + char *dname; + + if (check_name(directory,conn)) + dirptr = OpenDir(conn, directory, True); + + /* XXXX the CIFS spec says that if bit0 of the flags2 field is set then + the pattern matches against the long name, otherwise the short name + We don't implement this yet XXXX + */ + + if (dirptr) { + error = NT_STATUS_OBJECT_NAME_NOT_FOUND; + + if (strequal(mask,"????????.???")) + pstrcpy(mask,"*"); + + while ((dname = ReadDirName(dirptr))) { + pstring fname; + pstrcpy(fname,dname); + + if(!mask_match(fname, mask, case_sensitive)) continue; + + slprintf(fname,sizeof(fname)-1, "%s/%s",directory,dname); + error = can_delete(fname,conn,dirtype); + if (!NT_STATUS_IS_OK(error)) continue; + if (vfs_unlink(conn,fname) == 0) count++; + DEBUG(3,("unlink_internals: succesful unlink [%s]\n",fname)); + } + CloseDir(dirptr); + } + } + + if (count == 0 && NT_STATUS_IS_OK(error)) { + error = map_nt_error_from_unix(errno); + } - *directory = *mask = 0; + return error; +} - cnum = SVAL(inbuf,smb_tid); - dirtype = SVAL(inbuf,smb_vwv0); +/**************************************************************************** + Reply to a unlink +****************************************************************************/ + +int reply_unlink(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, + int dum_buffsize) +{ + int outsize = 0; + pstring name; + int dirtype; + NTSTATUS status; + START_PROFILE(SMBunlink); + + dirtype = SVAL(inbuf,smb_vwv0); + + srvstr_pull(inbuf, name, smb_buf(inbuf) + 1, sizeof(name), -1, STR_TERMINATE); + + RESOLVE_DFSPATH(name, conn, inbuf, outbuf); + + DEBUG(3,("reply_unlink : %s\n",name)); + + status = unlink_internals(conn, dirtype, name); + if (!NT_STATUS_IS_OK(status)) return ERROR_NT(status); + + /* + * Win2k needs a changenotify request response before it will + * update after a rename.. + */ + process_pending_change_notify_queue((time_t)0); + + outsize = set_message(outbuf,0,0,True); - strcpy(name,smb_buf(inbuf) + 1); - - DEBUG(3,("reply_unlink : %s\n",name)); - - unix_convert(name,cnum); + END_PROFILE(SMBunlink); + return outsize; +} - p = strrchr(name,'/'); - if (!p) { - strcpy(directory,"./"); - strcpy(mask,name); - } else { - *p = 0; - strcpy(directory,name); - strcpy(mask,p+1); - } +/**************************************************************************** + Fail for readbraw. +****************************************************************************/ - if (is_mangled(mask)) - check_mangled_stack(mask); +void fail_readraw(void) +{ + pstring errstr; + slprintf(errstr, sizeof(errstr)-1, "FAIL ! reply_readbraw: socket write fail (%s)", + strerror(errno) ); + exit_server(errstr); +} - has_wild = strchr(mask,'*') || strchr(mask,'?'); +/**************************************************************************** + Reply to a readbraw (core+ protocol). +****************************************************************************/ - 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; +int reply_readbraw(connection_struct *conn, char *inbuf, char *outbuf, int dum_size, int dum_buffsize) +{ + ssize_t maxcount,mincount; + size_t nread = 0; + SMB_OFF_T startpos; + char *header = outbuf; + ssize_t ret=0; + files_struct *fsp; + START_PROFILE(SMBreadbraw); + + /* + * Special check if an oplock break has been issued + * and the readraw request croses on the wire, we must + * return a zero length response here. + */ + + if(global_oplock_break) { + _smb_setlen(header,0); + if (write_data(smbd_server_fd(),header,4) != 4) + fail_readraw(); + DEBUG(5,("readbraw - oplock break finished\n")); + END_PROFILE(SMBreadbraw); + return -1; + } - if (check_name(directory,cnum)) - dirptr = OpenDir(directory); + fsp = file_fsp(inbuf,smb_vwv0); + + if (!FNUM_OK(fsp,conn) || !fsp->can_read) { + /* + * fsp could be NULL here so use the value from the packet. JRA. + */ + DEBUG(3,("fnum %d not open in readbraw - cache prime?\n",(int)SVAL(inbuf,smb_vwv0))); + _smb_setlen(header,0); + if (write_data(smbd_server_fd(),header,4) != 4) + fail_readraw(); + END_PROFILE(SMBreadbraw); + return(-1); + } - if (dirptr) - { - error = ERRbadfile; + CHECK_FSP(fsp,conn); - if (strequal(mask,"????????.???")) - strcpy(mask,"*"); + flush_write_cache(fsp, READRAW_FLUSH); - while ((dname = ReadDirName(dirptr))) - { - pstring fname; - strcpy(fname,dname); - - if(!mask_match(fname, mask, case_sensitive, False)) continue; + startpos = IVAL(inbuf,smb_vwv1); + if(CVAL(inbuf,smb_wct) == 10) { + /* + * This is a large offset (64 bit) read. + */ +#ifdef LARGE_SMB_OFF_T - 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); -} + startpos |= (((SMB_OFF_T)IVAL(inbuf,smb_vwv8)) << 32); +#else /* !LARGE_SMB_OFF_T */ -/**************************************************************************** - 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; - } + /* + * Ensure we haven't been sent a >32 bit offset. + */ + if(IVAL(inbuf,smb_vwv8) != 0) { + DEBUG(0,("readbraw - large offset (%x << 32) used and we don't support \ +64 bit offsets.\n", (unsigned int)IVAL(inbuf,smb_vwv8) )); + _smb_setlen(header,0); + if (write_data(smbd_server_fd(),header,4) != 4) + fail_readraw(); + END_PROFILE(SMBreadbraw); + return(-1); + } - 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; - } +#endif /* LARGE_SMB_OFF_T */ - nread = MIN(maxcount,size - startpos); - } + if(startpos < 0) { + DEBUG(0,("readbraw - negative 64 bit readraw offset (%.0f) !\n", (double)startpos )); + _smb_setlen(header,0); + if (write_data(smbd_server_fd(),header,4) != 4) + fail_readraw(); + END_PROFILE(SMBreadbraw); + return(-1); + } + } + maxcount = (SVAL(inbuf,smb_vwv3) & 0xFFFF); + mincount = (SVAL(inbuf,smb_vwv4) & 0xFFFF); + + /* ensure we don't overrun the packet size */ + maxcount = MIN(65535,maxcount); + maxcount = MAX(mincount,maxcount); + + if (!is_locked(fsp,conn,(SMB_BIG_UINT)maxcount,(SMB_BIG_UINT)startpos, READ_LOCK,False)) { + SMB_OFF_T size = fsp->size; + SMB_OFF_T sizeneeded = startpos + maxcount; + + if (size < sizeneeded) { + SMB_STRUCT_STAT st; + if (vfs_fstat(fsp,fsp->fd,&st) == 0) + size = st.st_size; + if (!fsp->can_write) + fsp->size = size; + } - if (nread < mincount) - nread = 0; + if (startpos >= size) + nread = 0; + else + 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)); + DEBUG( 3, ( "readbraw fnum=%d start=%.0f max=%d min=%d nread=%d\n", fsp->fnum, (double)startpos, + (int)maxcount, (int)mincount, (int)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)); + if (nread > 0) { + ret = read_file(fsp,header+4,startpos,nread); + if (ret < mincount) + ret = 0; + } -#else - ret = read_file(fnum,header+4,startpos,nread,nread,-1,False); - if (ret < mincount) ret = 0; + _smb_setlen(header,ret); + if (write_data(smbd_server_fd(),header,4+ret) != 4+ret) + fail_readraw(); - _smb_setlen(header,ret); - transfer_file(0,Client,0,header,4+ret,0); -#endif - - DEBUG(5,("readbraw finished\n")); - return -1; + DEBUG(5,("readbraw finished\n")); + END_PROFILE(SMBreadbraw); + return -1; } - /**************************************************************************** reply to a lockread (core+ protocol) ****************************************************************************/ -int reply_lockread(char *inbuf,char *outbuf) +int reply_lockread(connection_struct *conn, char *inbuf,char *outbuf, int length, int dum_buffsiz) { - 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); + ssize_t nread = -1; + char *data; + int outsize = 0; + SMB_OFF_T startpos; + size_t numtoread; + NTSTATUS status; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBlockread); + + CHECK_FSP(fsp,conn); + CHECK_READ(fsp); + + release_level_2_oplocks_on_change(fsp); + + 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; + + /* + * NB. Discovered by Menny Hamburger at Mainsoft. This is a core+ + * protocol request that predates the read/write lock concept. + * Thus instead of asking for a read lock here we need to ask + * for a write lock. JRA. + */ + + status = do_lock_spin(fsp, conn, SVAL(inbuf,smb_pid), + (SMB_BIG_UINT)numtoread, (SMB_BIG_UINT)startpos, WRITE_LOCK); + + if (NT_STATUS_V(status)) { + if (lp_blocking_locks(SNUM(conn))) { + /* + * A blocking lock was requested. Package up + * this smb into a queued request and push it + * onto the blocking lock queue. + */ + if(push_blocking_lock_request(inbuf, length, -1, 0)) + END_PROFILE(SMBlockread); + return -1; + } + END_PROFILE(SMBlockread); + return ERROR_NT(status); + } - CHECK_FNUM(fnum,cnum); - CHECK_READ(fnum); - CHECK_ERROR(fnum); + nread = read_file(fsp,data,startpos,numtoread); - 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)); + if (nread < 0) { + END_PROFILE(SMBlockread); + 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,("lockread fnum=%d num=%d nread=%d\n", + fsp->fnum, (int)numtoread, (int)nread)); - 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); + END_PROFILE(SMBlockread); + return(outsize); } /**************************************************************************** reply to a read ****************************************************************************/ -int reply_read(char *inbuf,char *outbuf) + +int reply_read(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) { - int cnum,numtoread,fnum; - int nread = 0; + size_t numtoread; + ssize_t nread = 0; char *data; - int startpos; + SMB_OFF_T startpos; int outsize = 0; - - cnum = SVAL(inbuf,smb_tid); - fnum = GETFNUM(inbuf,smb_vwv0); + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBread); - CHECK_FNUM(fnum,cnum); - CHECK_READ(fnum); - CHECK_ERROR(fnum); + CHECK_FSP(fsp,conn); + CHECK_READ(fsp); 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 (is_locked(fsp,conn,(SMB_BIG_UINT)numtoread,(SMB_BIG_UINT)startpos, READ_LOCK,False)) { + END_PROFILE(SMBread); + return ERROR_DOS(ERRDOS,ERRlock); + } if (numtoread > 0) - nread = read_file(fnum,data,startpos,numtoread,numtoread,-1,False); - - if (nread < 0) + nread = read_file(fsp,data,startpos,numtoread); + + if (nread < 0) { + END_PROFILE(SMBread); return(UNIXERROR(ERRDOS,ERRnoaccess)); + } outsize += nread; SSVAL(outbuf,smb_vwv0,nread); SSVAL(outbuf,smb_vwv5,nread+3); - CVAL(smb_buf(outbuf),0) = 1; + SCVAL(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)); - + DEBUG( 3, ( "read fnum=%d num=%d nread=%d\n", + fsp->fnum, (int)numtoread, (int)nread ) ); + + END_PROFILE(SMBread); return(outsize); } @@ -1467,605 +1658,813 @@ int reply_read(char *inbuf,char *outbuf) /**************************************************************************** reply to a read and X ****************************************************************************/ -int reply_read_and_X(char *inbuf,char *outbuf,int length,int bufsize) +int reply_read_and_X(connection_struct *conn, 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; + files_struct *fsp = file_fsp(inbuf,smb_vwv2); + SMB_OFF_T startpos = IVAL(inbuf,smb_vwv3); + size_t smb_maxcnt = SVAL(inbuf,smb_vwv5); + size_t smb_mincnt = SVAL(inbuf,smb_vwv6); + ssize_t nread = -1; char *data; - int outsize = 0; - BOOL ok = False; + START_PROFILE(SMBreadX); - cnum = SVAL(inbuf,smb_tid); + /* If it's an IPC, pass off the pipe handler. */ + if (IS_IPC(conn)) { + END_PROFILE(SMBreadX); + return reply_pipe_read_and_X(inbuf,outbuf,length,bufsize); + } - CHECK_FNUM(fnum,cnum); - CHECK_READ(fnum); - CHECK_ERROR(fnum); + CHECK_FSP(fsp,conn); + CHECK_READ(fsp); - outsize = set_message(outbuf,12,0,True); + 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) + if(CVAL(inbuf,smb_wct) == 12) { +#ifdef LARGE_SMB_OFF_T + /* + * This is a large offset (64 bit) read. + */ + startpos |= (((SMB_OFF_T)IVAL(inbuf,smb_vwv10)) << 32); + +#else /* !LARGE_SMB_OFF_T */ + + /* + * Ensure we haven't been sent a >32 bit offset. + */ + + if(IVAL(inbuf,smb_vwv10) != 0) { + DEBUG(0,("reply_read_and_X - large offset (%x << 32) used and we don't support \ +64 bit offsets.\n", (unsigned int)IVAL(inbuf,smb_vwv10) )); + END_PROFILE(SMBreadX); + return ERROR_DOS(ERRDOS,ERRbadaccess); + } + +#endif /* LARGE_SMB_OFF_T */ + + } + + if (is_locked(fsp,conn,(SMB_BIG_UINT)smb_maxcnt,(SMB_BIG_UINT)startpos, READ_LOCK,False)) { + END_PROFILE(SMBreadX); + return ERROR_DOS(ERRDOS,ERRlock); + } + nread = read_file(fsp,data,startpos,smb_maxcnt); + + if (nread < 0) { + END_PROFILE(SMBreadX); 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(outbuf,smb_vwv6,smb_offset(data,outbuf)); 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)); + DEBUG( 3, ( "readX fnum=%d min=%d max=%d nread=%d\n", + fsp->fnum, (int)smb_mincnt, (int)smb_maxcnt, (int)nread ) ); - 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); + END_PROFILE(SMBreadX); + return chain_reply(inbuf,outbuf,length,bufsize); } - /**************************************************************************** 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; +int reply_writebraw(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) +{ + ssize_t nwritten=0; + ssize_t total_written=0; + size_t numtowrite=0; + size_t tcount; + SMB_OFF_T startpos; + char *data=NULL; + BOOL write_through; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + int outsize = 0; + START_PROFILE(SMBwritebraw); + + CHECK_FSP(fsp,conn); + CHECK_WRITE(fsp); + + 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); + } - if (is_locked(fnum,cnum,tcount,startpos)) - return(ERROR(ERRDOS,ERRlock)); + /* force the error type */ + SCVAL(inbuf,smb_com,SMBwritec); + SCVAL(outbuf,smb_com,SMBwritec); - if (seek_file(fnum,startpos) != startpos) - DEBUG(0,("couldn't seek to %d in writebraw\n",startpos)); + if (is_locked(fsp,conn,(SMB_BIG_UINT)tcount,(SMB_BIG_UINT)startpos, WRITE_LOCK,False)) { + END_PROFILE(SMBwritebraw); + return(ERROR_DOS(ERRDOS,ERRlock)); + } - if (numtowrite>0) - nwritten = write_file(fnum,data,numtowrite); + if (numtowrite>0) + nwritten = write_file(fsp,data,startpos,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)); + DEBUG(3,("writebraw1 fnum=%d start=%.0f num=%d wrote=%d sync=%d\n", + fsp->fnum, (double)startpos, (int)numtowrite, (int)nwritten, (int)write_through)); - if (nwritten < numtowrite) - return(UNIXERROR(ERRHRD,ERRdiskfull)); + if (nwritten < numtowrite) { + END_PROFILE(SMBwritebraw); + return(UNIXERROR(ERRHRD,ERRdiskfull)); + } - total_written = nwritten; + 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); + /* Return a message to the redirector to tell it to send more bytes */ + SCVAL(outbuf,smb_com,SMBwritebraw); + SSVALS(outbuf,smb_vwv0,-1); + outsize = set_message(outbuf,Protocol>PROTOCOL_COREPLUS?1:0,0,True); + if (!send_smb(smbd_server_fd(),outbuf)) + exit_server("reply_writebraw: send_smb failed."); - /* Now read the raw data into the buffer and write it */ - if(read_smb_length(Client,inbuf,0) == -1) { - exit_server("secondary writebraw failed"); - } + /* Now read the raw data into the buffer and write it */ + if (read_smb_length(smbd_server_fd(),inbuf,SMB_SECONDARY_WAIT) == -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); + /* 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)); - } + /* Set up outbuf to return the correct return */ + outsize = set_message(outbuf,1,0,True); + SCVAL(outbuf,smb_com,SMBwritec); + SSVAL(outbuf,smb_vwv0,total_written); - 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 (numtowrite != 0) { - if (nwritten < numtowrite) { - CVAL(outbuf,smb_rcls) = ERRHRD; - SSVAL(outbuf,smb_err,ERRdiskfull); - } + if (numtowrite > BUFFER_SIZE) { + DEBUG(0,("reply_writebraw: Oversize secondary write raw requested (%u). Terminating\n", + (unsigned int)numtowrite )); + exit_server("secondary writebraw failed"); + } - if (lp_syncalways(SNUM(cnum)) || write_through) - sync_file(fnum); + if (tcount > nwritten+numtowrite) { + DEBUG(3,("Client overestimated the write %d %d %d\n", + (int)tcount,(int)nwritten,(int)numtowrite)); + } - DEBUG(3,("%s writebraw2 fnum=%d cnum=%d start=%d num=%d wrote=%d\n", - timestring(),fnum,cnum,startpos,numtowrite,total_written)); + if (read_data( smbd_server_fd(), inbuf+4, numtowrite) != numtowrite ) { + DEBUG(0,("reply_writebraw: Oversize secondary write raw read failed (%s). Terminating\n", + strerror(errno) )); + exit_server("secondary writebraw failed"); + } - /* 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); + nwritten = write_file(fsp,inbuf+4,startpos+nwritten,numtowrite); - return(outsize); -} + if (nwritten < (ssize_t)numtowrite) { + SCVAL(outbuf,smb_rcls,ERRHRD); + SSVAL(outbuf,smb_err,ERRdiskfull); + } + + if (nwritten > 0) + total_written += nwritten; + } + + if ((lp_syncalways(SNUM(conn)) || write_through) && lp_strict_sync(SNUM(conn))) + sync_file(conn,fsp); + + DEBUG(3,("writebraw2 fnum=%d start=%.0f num=%d wrote=%d\n", + fsp->fnum, (double)startpos, (int)numtowrite,(int)total_written)); + + /* we won't return a status if write through is not selected - this follows what WfWg does */ + END_PROFILE(SMBwritebraw); + if (!write_through && total_written==tcount) { + +#if RABBIT_PELLET_FIX + /* + * Fix for "rabbit pellet" mode, trigger an early TCP ack by + * sending a SMBkeepalive. Thanks to DaveCB at Sun for this. JRA. + */ + if (!send_keepalive(smbd_server_fd())) + exit_server("reply_writebraw: send of keepalive failed"); +#endif + 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); +int reply_writeunlock(connection_struct *conn, char *inbuf,char *outbuf, + int size, int dum_buffsize) +{ + ssize_t nwritten = -1; + size_t numtowrite; + SMB_OFF_T startpos; + char *data; + NTSTATUS status; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + int outsize = 0; + START_PROFILE(SMBwriteunlock); + + CHECK_FSP(fsp,conn); + CHECK_WRITE(fsp); - numtowrite = SVAL(inbuf,smb_vwv1); - startpos = IVAL(inbuf,smb_vwv2); - data = smb_buf(inbuf) + 3; + 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); + if (is_locked(fsp,conn,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, + WRITE_LOCK,False)) { + END_PROFILE(SMBwriteunlock); + return ERROR_DOS(ERRDOS,ERRlock); + } - /* 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); + /* 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(fsp,data,startpos,numtowrite); - if (lp_syncalways(SNUM(cnum))) - sync_file(fnum); - - if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) - return(UNIXERROR(ERRDOS,ERRnoaccess)); + if (lp_syncalways(SNUM(conn))) + sync_file(conn,fsp); - if(!do_unlock(fnum, cnum, numtowrite, startpos, &eclass, &ecode)) - return(ERROR(eclass,ecode)); + if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) { + END_PROFILE(SMBwriteunlock); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } - 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); + status = do_unlock(fsp, conn, SVAL(inbuf,smb_pid), (SMB_BIG_UINT)numtowrite, + (SMB_BIG_UINT)startpos); + if (NT_STATUS_V(status)) { + END_PROFILE(SMBwriteunlock); + return ERROR_NT(status); + } + + outsize = set_message(outbuf,1,0,True); + + SSVAL(outbuf,smb_vwv0,nwritten); + + DEBUG(3,("writeunlock fnum=%d num=%d wrote=%d\n", + fsp->fnum, (int)numtowrite, (int)nwritten)); + + END_PROFILE(SMBwriteunlock); + return outsize; } /**************************************************************************** - reply to a write + Reply to a write. ****************************************************************************/ -int reply_write(char *inbuf,char *outbuf,int dum1,int dum2) + +int reply_write(connection_struct *conn, char *inbuf,char *outbuf,int size,int dum_buffsize) { - int cnum,numtowrite,fnum; - int nwritten = -1; - int outsize = 0; - int startpos; - char *data; + size_t numtowrite; + ssize_t nwritten = -1; + SMB_OFF_T startpos; + char *data; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + int outsize = 0; + START_PROFILE(SMBwrite); + + /* If it's an IPC, pass off the pipe handler. */ + if (IS_IPC(conn)) { + END_PROFILE(SMBwrite); + return reply_pipe_write(inbuf,outbuf,size,dum_buffsize); + } - dum1 = dum2 = 0; + CHECK_FSP(fsp,conn); + CHECK_WRITE(fsp); + numtowrite = SVAL(inbuf,smb_vwv1); + startpos = IVAL(inbuf,smb_vwv2); + data = smb_buf(inbuf) + 3; - cnum = SVAL(inbuf,smb_tid); - fnum = GETFNUM(inbuf,smb_vwv0); - - CHECK_FNUM(fnum,cnum); - CHECK_WRITE(fnum); - CHECK_ERROR(fnum); + if (is_locked(fsp,conn,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK,False)) { + END_PROFILE(SMBwrite); + return ERROR_DOS(ERRDOS,ERRlock); + } - numtowrite = SVAL(inbuf,smb_vwv1); - startpos = IVAL(inbuf,smb_vwv2); - data = smb_buf(inbuf) + 3; + /* + * 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) { + /* + * This is actually an allocate call, and set EOF. JRA. + */ + nwritten = vfs_allocate_file_space(fsp, (SMB_OFF_T)startpos); + if (nwritten < 0) { + END_PROFILE(SMBwrite); + return ERROR_NT(NT_STATUS_DISK_FULL); + } + nwritten = vfs_set_filelen(fsp, (SMB_OFF_T)startpos); + if (nwritten < 0) { + END_PROFILE(SMBwrite); + return ERROR_NT(NT_STATUS_DISK_FULL); + } + } else + nwritten = write_file(fsp,data,startpos,numtowrite); - if (is_locked(fnum,cnum,numtowrite,startpos)) - return(ERROR(ERRDOS,ERRlock)); + if (lp_syncalways(SNUM(conn))) + sync_file(conn,fsp); - seek_file(fnum,startpos); + if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) { + END_PROFILE(SMBwrite); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } - /* 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); + outsize = set_message(outbuf,1,0,True); - if (lp_syncalways(SNUM(cnum))) - sync_file(fnum); - - if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) - return(UNIXERROR(ERRDOS,ERRnoaccess)); + SSVAL(outbuf,smb_vwv0,nwritten); - outsize = set_message(outbuf,1,0,True); + if (nwritten < (ssize_t)numtowrite) { + SCVAL(outbuf,smb_rcls,ERRHRD); + SSVAL(outbuf,smb_err,ERRdiskfull); + } - SSVAL(outbuf,smb_vwv0,nwritten); + DEBUG(3,("write fnum=%d num=%d wrote=%d\n", fsp->fnum, (int)numtowrite, (int)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); + END_PROFILE(SMBwrite); + return(outsize); } /**************************************************************************** reply to a write and X ****************************************************************************/ -int reply_write_and_X(char *inbuf,char *outbuf,int length,int bufsize) +int reply_write_and_X(connection_struct *conn, 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); + files_struct *fsp = file_fsp(inbuf,smb_vwv2); + SMB_OFF_T startpos = IVAL(inbuf,smb_vwv3); + size_t numtowrite = SVAL(inbuf,smb_vwv10); BOOL write_through = BITSETW(inbuf+smb_vwv7,0); - int cnum; - int nwritten = -1; - int outsize = 0; + ssize_t nwritten = -1; + unsigned int smb_doff = SVAL(inbuf,smb_vwv11); + unsigned int smblen = smb_len(inbuf); char *data; + BOOL large_writeX = ((CVAL(inbuf,smb_wct) == 14) && (smblen > 0xFFFF)); + START_PROFILE(SMBwriteX); - cnum = SVAL(inbuf,smb_tid); + /* If it's an IPC, pass off the pipe handler. */ + if (IS_IPC(conn)) { + END_PROFILE(SMBwriteX); + return reply_pipe_write_and_X(inbuf,outbuf,length,bufsize); + } - CHECK_FNUM(fnum,cnum); - CHECK_WRITE(fnum); - CHECK_ERROR(fnum); + CHECK_FSP(fsp,conn); + CHECK_WRITE(fsp); + + /* Deal with possible LARGE_WRITEX */ + if (large_writeX) + numtowrite |= ((((size_t)SVAL(inbuf,smb_vwv9)) & 1 )<<16); + + if(smb_doff > smblen || (smb_doff + numtowrite > smblen)) { + END_PROFILE(SMBwriteX); + return ERROR_DOS(ERRDOS,ERRbadmem); + } data = smb_base(inbuf) + smb_doff; - if (is_locked(fnum,cnum,smb_dsize,smb_offs)) - return(ERROR(ERRDOS,ERRlock)); + if(CVAL(inbuf,smb_wct) == 14) { +#ifdef LARGE_SMB_OFF_T + /* + * This is a large offset (64 bit) write. + */ + startpos |= (((SMB_OFF_T)IVAL(inbuf,smb_vwv12)) << 32); + +#else /* !LARGE_SMB_OFF_T */ + + /* + * Ensure we haven't been sent a >32 bit offset. + */ + + if(IVAL(inbuf,smb_vwv12) != 0) { + DEBUG(0,("reply_write_and_X - large offset (%x << 32) used and we don't support \ +64 bit offsets.\n", (unsigned int)IVAL(inbuf,smb_vwv12) )); + END_PROFILE(SMBwriteX); + return ERROR_DOS(ERRDOS,ERRbadaccess); + } + +#endif /* LARGE_SMB_OFF_T */ + } + + if (is_locked(fsp,conn,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK,False)) { + END_PROFILE(SMBwriteX); + return ERROR_DOS(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) + if(numtowrite == 0) nwritten = 0; else - nwritten = write_file(fnum,data,smb_dsize); + nwritten = write_file(fsp,data,startpos,numtowrite); - if(((nwritten == 0) && (smb_dsize != 0))||(nwritten < 0)) + if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) { + END_PROFILE(SMBwriteX); return(UNIXERROR(ERRDOS,ERRnoaccess)); + } - outsize = set_message(outbuf,6,0,True); + 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; + if (large_writeX) + SSVAL(outbuf,smb_vwv4,(nwritten>>16)&1); + + if (nwritten < (ssize_t)numtowrite) { + SCVAL(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)); + DEBUG(3,("writeX fnum=%d num=%d wrote=%d\n", + fsp->fnum, (int)numtowrite, (int)nwritten)); - chain_fnum = fnum; + if (lp_syncalways(SNUM(conn)) || write_through) + sync_file(conn,fsp); - 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); + END_PROFILE(SMBwriteX); + return chain_reply(inbuf,outbuf,length,bufsize); } /**************************************************************************** reply to a lseek ****************************************************************************/ -int reply_lseek(char *inbuf,char *outbuf) + +int reply_lseek(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) { - int cnum,fnum; - uint32 startpos; - int32 res= -1; + SMB_OFF_T startpos; + SMB_OFF_T res= -1; int mode,umode; int outsize = 0; - - cnum = SVAL(inbuf,smb_tid); - fnum = GETFNUM(inbuf,smb_vwv0); + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBlseek); - CHECK_FNUM(fnum,cnum); - CHECK_ERROR(fnum); + CHECK_FSP(fsp,conn); + + flush_write_cache(fsp, SEEK_FLUSH); mode = SVAL(inbuf,smb_vwv1) & 3; - startpos = IVAL(inbuf,smb_vwv2); + startpos = IVALS(inbuf,smb_vwv2); - switch (mode & 3) - { + switch (mode) { case 0: umode = SEEK_SET; break; case 1: umode = SEEK_CUR; break; case 2: umode = SEEK_END; break; default: umode = SEEK_SET; break; + } + + if((res = conn->vfs_ops.lseek(fsp,fsp->fd,startpos,umode)) == -1) { + /* + * Check for the special case where a seek before the start + * of the file sets the offset to zero. Added in the CIFS spec, + * section 4.2.7. + */ + + if(errno == EINVAL) { + SMB_OFF_T current_pos = startpos; + + if(umode == SEEK_CUR) { + + if((current_pos = conn->vfs_ops.lseek(fsp,fsp->fd,0,SEEK_CUR)) == -1) { + END_PROFILE(SMBlseek); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + + current_pos += startpos; + + } else if (umode == SEEK_END) { + + SMB_STRUCT_STAT sbuf; + + if(vfs_fstat(fsp,fsp->fd, &sbuf) == -1) { + END_PROFILE(SMBlseek); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + + current_pos += sbuf.st_size; + } + + if(current_pos < 0) + res = conn->vfs_ops.lseek(fsp,fsp->fd,0,SEEK_SET); } - - res = lseek(Files[fnum].fd,startpos,umode); - Files[fnum].pos = res; + + if(res == -1) { + END_PROFILE(SMBlseek); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + } + + fsp->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)); + SIVAL(outbuf,smb_vwv0,res); + DEBUG(3,("lseek fnum=%d ofs=%.0f newpos = %.0f mode=%d\n", + fsp->fnum, (double)startpos, (double)res, mode)); + + END_PROFILE(SMBlseek); return(outsize); } - /**************************************************************************** reply to a flush ****************************************************************************/ -int reply_flush(char *inbuf,char *outbuf) -{ - int cnum, fnum; - int outsize = set_message(outbuf,0,0,True); - - cnum = SVAL(inbuf,smb_tid); - fnum = GETFNUM(inbuf,smb_vwv0); - if (fnum != 0xFFFF) { - CHECK_FNUM(fnum,cnum); - CHECK_ERROR(fnum); - } - - if (fnum == 0xFFFF) - { - int i; - for (i=0;i<MAX_OPEN_FILES;i++) - if (OPEN_FNUM(i)) - sync_file(i); - } - else - sync_file(fnum); +int reply_flush(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) +{ + int outsize = set_message(outbuf,0,0,True); + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBflush); - DEBUG(3,("%s flush fnum=%d\n",timestring(),fnum)); - return(outsize); + CHECK_FSP(fsp,conn); + + if (!fsp) { + file_sync_all(conn); + } else { + sync_file(conn,fsp); + } + + DEBUG(3,("flush\n")); + END_PROFILE(SMBflush); + return(outsize); } /**************************************************************************** reply to a exit ****************************************************************************/ -int reply_exit(char *inbuf,char *outbuf) +int reply_exit(connection_struct *conn, + char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { - int outsize = set_message(outbuf,0,0,True); - DEBUG(3,("%s exit\n",timestring())); - - return(outsize); + int outsize; + START_PROFILE(SMBexit); + outsize = set_message(outbuf,0,0,True); + + DEBUG(3,("exit\n")); + + END_PROFILE(SMBexit); + return(outsize); } /**************************************************************************** - reply to a close + Reply to a close - has to deal with closing a directory opened by NT SMB's. ****************************************************************************/ -int reply_close(char *inbuf,char *outbuf) +int reply_close(connection_struct *conn, char *inbuf,char *outbuf, int size, + int dum_buffsize) { - int fnum,cnum; - int outsize = 0; - time_t mtime; - int32 eclass = 0, err = 0; + int outsize = 0; + time_t mtime; + int32 eclass = 0, err = 0; + files_struct *fsp = NULL; + START_PROFILE(SMBclose); + + outsize = set_message(outbuf,0,0,True); + + /* If it's an IPC, pass off to the pipe handler. */ + if (IS_IPC(conn)) { + END_PROFILE(SMBclose); + return reply_pipe_close(conn, inbuf,outbuf); + } - outsize = set_message(outbuf,0,0,True); + fsp = file_fsp(inbuf,smb_vwv0); - cnum = SVAL(inbuf,smb_tid); + /* + * We can only use CHECK_FSP if we know it's not a directory. + */ - fnum = GETFNUM(inbuf,smb_vwv0); - CHECK_FNUM(fnum,cnum); + if(!fsp || (fsp->conn != conn)) { + END_PROFILE(SMBclose); + return ERROR_DOS(ERRDOS,ERRbadfid); + } - if(HAS_CACHED_ERROR(fnum)) { - eclass = Files[fnum].wbmpx_ptr->wr_errclass; - err = Files[fnum].wbmpx_ptr->wr_error; - } + if(fsp->is_directory) { + /* + * Special case - close NT SMB directory handle. + */ + DEBUG(3,("close %s fnum=%d\n", fsp->is_directory ? "directory" : "stat file open", fsp->fnum)); + close_file(fsp,True); + } else { + /* + * Close ordinary file. + */ + int close_err; + pstring file_name; + + /* Save the name for time set in close. */ + pstrcpy( file_name, fsp->fsp_name); + + DEBUG(3,("close fd=%d fnum=%d (numopen=%d)\n", + fsp->fd, fsp->fnum, + conn->num_files_open)); + + /* + * close_file() returns the unix errno if an error + * was detected on close - normally this is due to + * a disk full error. If not then it was probably an I/O error. + */ + + if((close_err = close_file(fsp,True)) != 0) { + errno = close_err; + END_PROFILE(SMBclose); + return (UNIXERROR(ERRHRD,ERRgeneral)); + } - mtime = make_unix_date3(inbuf+smb_vwv1); + /* + * Now take care of any time sent in the close. + */ - close_file(fnum); + mtime = make_unix_date3(inbuf+smb_vwv1); + + /* try and set the date */ + set_filetime(conn, file_name, mtime); - /* try and set the date */ - set_filetime(Files[fnum].name,mtime); + } - /* We have a cached error */ - if(eclass || err) - return(ERROR(eclass,err)); + /* We have a cached error */ + if(eclass || err) { + END_PROFILE(SMBclose); + return ERROR_DOS(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); + END_PROFILE(SMBclose); + return(outsize); } /**************************************************************************** reply to a writeclose (Core+ protocol) ****************************************************************************/ -int reply_writeclose(char *inbuf,char *outbuf) + +int reply_writeclose(connection_struct *conn, + char *inbuf,char *outbuf, int size, int dum_buffsize) { - int cnum,numtowrite,fnum; - int nwritten = -1; - int outsize = 0; - int startpos; - char *data; - time_t mtime; + size_t numtowrite; + ssize_t nwritten = -1; + int outsize = 0; + int close_err = 0; + SMB_OFF_T startpos; + char *data; + time_t mtime; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBwriteclose); + + CHECK_FSP(fsp,conn); + CHECK_WRITE(fsp); + + 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(fsp,conn,(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK,False)) { + END_PROFILE(SMBwriteclose); + return ERROR_DOS(ERRDOS,ERRlock); + } - cnum = SVAL(inbuf,smb_tid); - fnum = GETFNUM(inbuf,smb_vwv0); - - CHECK_FNUM(fnum,cnum); - CHECK_WRITE(fnum); - CHECK_ERROR(fnum); + nwritten = write_file(fsp,data,startpos,numtowrite); - numtowrite = SVAL(inbuf,smb_vwv1); - startpos = IVAL(inbuf,smb_vwv2); - mtime = make_unix_date3(inbuf+smb_vwv4); - data = smb_buf(inbuf) + 1; + set_filetime(conn, fsp->fsp_name,mtime); - if (is_locked(fnum,cnum,numtowrite,startpos)) - return(ERROR(ERRDOS,ERRlock)); - - seek_file(fnum,startpos); - - nwritten = write_file(fnum,data,numtowrite); + close_err = close_file(fsp,True); - 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)); + DEBUG(3,("writeclose fnum=%d num=%d wrote=%d (numopen=%d)\n", + fsp->fnum, (int)numtowrite, (int)nwritten, + conn->num_files_open)); - if (nwritten <= 0) - return(UNIXERROR(ERRDOS,ERRnoaccess)); - - outsize = set_message(outbuf,1,0,True); + if (nwritten <= 0) { + END_PROFILE(SMBwriteclose); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + + if(close_err != 0) { + errno = close_err; + END_PROFILE(SMBwriteclose); + return(UNIXERROR(ERRHRD,ERRgeneral)); + } + + outsize = set_message(outbuf,1,0,True); - SSVAL(outbuf,smb_vwv0,nwritten); - return(outsize); + SSVAL(outbuf,smb_vwv0,nwritten); + END_PROFILE(SMBwriteclose); + return(outsize); } /**************************************************************************** reply to a lock ****************************************************************************/ -int reply_lock(char *inbuf,char *outbuf) +int reply_lock(connection_struct *conn, + char *inbuf,char *outbuf, int length, int dum_buffsize) { - 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)); + int outsize = set_message(outbuf,0,0,True); + SMB_BIG_UINT count,offset; + NTSTATUS status; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBlock); + + CHECK_FSP(fsp,conn); + + release_level_2_oplocks_on_change(fsp); + + count = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv1); + offset = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv3); + + DEBUG(3,("lock fd=%d fnum=%d offset=%.0f count=%.0f\n", + fsp->fd, fsp->fnum, (double)offset, (double)count)); + + status = do_lock_spin(fsp, conn, SVAL(inbuf,smb_pid), count, offset, WRITE_LOCK); + if (NT_STATUS_V(status)) { + if (lp_blocking_locks(SNUM(conn))) { + /* + * A blocking lock was requested. Package up + * this smb into a queued request and push it + * onto the blocking lock queue. + */ + if(push_blocking_lock_request(inbuf, length, -1, 0)) { + END_PROFILE(SMBlock); + return -1; + } + } + END_PROFILE(SMBlock); + return ERROR_NT(status); + } - if(!do_lock( fnum, cnum, count, offset, &eclass, &ecode)) - return (ERROR(eclass,ecode)); - - return(outsize); + END_PROFILE(SMBlock); + return(outsize); } /**************************************************************************** reply to a unlock ****************************************************************************/ -int reply_unlock(char *inbuf,char *outbuf) +int reply_unlock(connection_struct *conn, char *inbuf,char *outbuf, int size, + int dum_buffsize) { - 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); + int outsize = set_message(outbuf,0,0,True); + SMB_BIG_UINT count,offset; + NTSTATUS status; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBunlock); - if(!do_unlock(fnum, cnum, count, offset, &eclass, &ecode)) - return (ERROR(eclass,ecode)); + CHECK_FSP(fsp,conn); + + count = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv1); + offset = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv3); + + status = do_unlock(fsp, conn, SVAL(inbuf,smb_pid), count, offset); + if (NT_STATUS_V(status)) { + END_PROFILE(SMBunlock); + return ERROR_NT(status); + } - 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); + DEBUG( 3, ( "unlock fd=%d fnum=%d offset=%.0f count=%.0f\n", + fsp->fd, fsp->fnum, (double)offset, (double)count ) ); + + END_PROFILE(SMBunlock); + return(outsize); } /**************************************************************************** reply to a tdis ****************************************************************************/ -int reply_tdis(char *inbuf,char *outbuf) +int reply_tdis(connection_struct *conn, + char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { - int cnum, uid; - int outsize = set_message(outbuf,0,0,True); - - cnum = SVAL(inbuf,smb_tid); - uid = SVAL(inbuf,smb_uid); + int outsize = set_message(outbuf,0,0,True); + uint16 vuid; + START_PROFILE(SMBtdis); - Connections[cnum].used = False; + vuid = SVAL(inbuf,smb_uid); - close_cnum(cnum,uid); - - DEBUG(3,("%s tdis cnum=%d\n",timestring(),cnum)); + if (!conn) { + DEBUG(4,("Invalid connection in tdis\n")); + END_PROFILE(SMBtdis); + return ERROR_DOS(ERRSRV,ERRinvnid); + } + + conn->used = False; - return outsize; + close_cnum(conn,vuid); + + END_PROFILE(SMBtdis); + return outsize; } @@ -2073,316 +2472,438 @@ int reply_tdis(char *inbuf,char *outbuf) /**************************************************************************** reply to a echo ****************************************************************************/ -int reply_echo(char *inbuf,char *outbuf) +int reply_echo(connection_struct *conn, + char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { - 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); + int smb_reverb = SVAL(inbuf,smb_vwv0); + int seq_num; + unsigned int data_len = smb_buflen(inbuf); + int outsize = set_message(outbuf,1,data_len,True); + START_PROFILE(SMBecho); - cnum = SVAL(inbuf,smb_tid); + data_len = MIN(data_len, (sizeof(inbuf)-(smb_buf(inbuf)-inbuf))); - 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); - /* 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; + } - 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); - for (seq_num =1 ; seq_num <= smb_reverb ; seq_num++) - { - SSVAL(outbuf,smb_vwv0,seq_num); + smb_setlen(outbuf,outsize - 4); - smb_setlen(outbuf,outsize - 4); + if (!send_smb(smbd_server_fd(),outbuf)) + exit_server("reply_echo: send_smb failed."); + } - send_smb(Client,outbuf); - } + DEBUG(3,("echo %d times\n", smb_reverb)); - DEBUG(3,("%s echo %d times cnum=%d\n",timestring(),smb_reverb,cnum)); + smb_echo_count++; - return -1; + END_PROFILE(SMBecho); + return -1; } /**************************************************************************** reply to a printopen ****************************************************************************/ -int reply_printopen(char *inbuf,char *outbuf) +int reply_printopen(connection_struct *conn, + char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { - 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)); + int outsize = 0; + files_struct *fsp; + START_PROFILE(SMBsplopen); + + if (!CAN_PRINT(conn)) { + END_PROFILE(SMBsplopen); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } - open_file(fnum,cnum,fname2,O_WRONLY | O_CREAT | O_TRUNC, - unix_mode(cnum,0)); + /* Open for exclusive use, write only. */ + fsp = print_fsp_open(conn, NULL); - if (!Files[fnum].open) - return(UNIXERROR(ERRDOS,ERRnoaccess)); + if (!fsp) { + END_PROFILE(SMBsplopen); + 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)); + outsize = set_message(outbuf,1,0,True); + SSVAL(outbuf,smb_vwv0,fsp->fnum); - return(outsize); + DEBUG(3,("openprint fd=%d fnum=%d\n", + fsp->fd, fsp->fnum)); + + END_PROFILE(SMBsplopen); + return(outsize); } /**************************************************************************** reply to a printclose ****************************************************************************/ -int reply_printclose(char *inbuf,char *outbuf) +int reply_printclose(connection_struct *conn, + char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { - int fnum,cnum; - int outsize = set_message(outbuf,0,0,True); - - cnum = SVAL(inbuf,smb_tid); - fnum = GETFNUM(inbuf,smb_vwv0); + int outsize = set_message(outbuf,0,0,True); + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + int close_err = 0; + START_PROFILE(SMBsplclose); - CHECK_FNUM(fnum,cnum); - CHECK_ERROR(fnum); + CHECK_FSP(fsp,conn); - if (!CAN_PRINT(cnum)) - return(ERROR(ERRDOS,ERRnoaccess)); - - close_file(fnum); + if (!CAN_PRINT(conn)) { + END_PROFILE(SMBsplclose); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } - DEBUG(3,("%s printclose fd=%d fnum=%d cnum=%d\n",timestring(),Files[fnum].fd,fnum,cnum)); + DEBUG(3,("printclose fd=%d fnum=%d\n", + fsp->fd,fsp->fnum)); - return(outsize); + close_err = close_file(fsp,True); + + if(close_err != 0) { + errno = close_err; + END_PROFILE(SMBsplclose); + return(UNIXERROR(ERRHRD,ERRgeneral)); + } + + END_PROFILE(SMBsplclose); + return(outsize); } /**************************************************************************** reply to a printqueue ****************************************************************************/ -int reply_printqueue(char *inbuf,char *outbuf) +int reply_printqueue(connection_struct *conn, + char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { - 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 + int outsize = set_message(outbuf,2,3,True); + int max_count = SVAL(inbuf,smb_vwv0); + int start_index = SVAL(inbuf,smb_vwv1); + START_PROFILE(SMBsplretq); + + /* we used to allow the client to get the cnum wrong, but that + is really quite gross and only worked when there was only + one printer - I think we should now only accept it if they + get it right (tridge) */ + if (!CAN_PRINT(conn)) { + END_PROFILE(SMBsplretq); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } - SSVAL(outbuf,smb_vwv0,0); - SSVAL(outbuf,smb_vwv1,0); - CVAL(smb_buf(outbuf),0) = 1; - SSVAL(smb_buf(outbuf),1,0); + SSVAL(outbuf,smb_vwv0,0); + SSVAL(outbuf,smb_vwv1,0); + SCVAL(smb_buf(outbuf),0,1); + SSVAL(smb_buf(outbuf),1,0); - DEBUG(3,("%s printqueue cnum=%d start_index=%d max_count=%d\n", - timestring(),cnum,start_index,max_count)); - - if (!OPEN_CNUM(cnum) || !Connections[cnum].printer) - { - int i; - cnum = -1; - - for (i=0;i<MAX_CONNECTIONS;i++) - if (CAN_PRINT(i) && Connections[i].printer) - cnum = i; + DEBUG(3,("printqueue start_index=%d max_count=%d\n", + start_index, max_count)); - if (cnum == -1) - for (i=0;i<MAX_CONNECTIONS;i++) - if (OPEN_CNUM(i)) - cnum = i; - - if (!OPEN_CNUM(cnum)) - return(ERROR(ERRSRV,ERRinvnid)); - - DEBUG(5,("connection not open or not a printer, using cnum %d\n",cnum)); - } - - if (!become_user(cnum,uid)) - return(ERROR(ERRSRV,ERRinvnid)); - - { - print_queue_struct *queue = NULL; - char *p = smb_buf(outbuf) + 3; - int count = get_printqueue(SNUM(cnum),cnum,&queue,NULL); - int num_to_get = ABS(max_count); - int first = (max_count>0?start_index:start_index+max_count+1); - int i; - - if (first >= count) - num_to_get = 0; - else - num_to_get = MIN(num_to_get,count-first); + { + print_queue_struct *queue = NULL; + print_status_struct status; + char *p = smb_buf(outbuf) + 3; + int count = print_queue_status(SNUM(conn), &queue, &status); + int num_to_get = ABS(max_count); + int first = (max_count>0?start_index:start_index+max_count+1); + int i; + + if (first >= count) + num_to_get = 0; + else + num_to_get = MIN(num_to_get,count-first); - for (i=first;i<first+num_to_get;i++) - { - put_dos_date2(p,0,queue[i].time); - CVAL(p,4) = (queue[i].status==LPQ_PRINTING?2:3); - SSVAL(p,5,queue[i].job); - SIVAL(p,7,queue[i].size); - CVAL(p,11) = 0; - StrnCpy(p+12,queue[i].user,16); - p += 28; - } + for (i=first;i<first+num_to_get;i++) { + put_dos_date2(p,0,queue[i].time); + SCVAL(p,4,(queue[i].status==LPQ_PRINTING?2:3)); + SSVAL(p,5, queue[i].job); + SIVAL(p,7,queue[i].size); + SCVAL(p,11,0); + srvstr_push(outbuf, p+12, queue[i].fs_user, 16, STR_ASCII); + p += 28; + } - if (count > 0) - { - outsize = set_message(outbuf,2,28*count+3,False); - SSVAL(outbuf,smb_vwv0,count); - SSVAL(outbuf,smb_vwv1,(max_count>0?first+count:first-1)); - CVAL(smb_buf(outbuf),0) = 1; - SSVAL(smb_buf(outbuf),1,28*count); - } + if (count > 0) { + outsize = set_message(outbuf,2,28*count+3,False); + SSVAL(outbuf,smb_vwv0,count); + SSVAL(outbuf,smb_vwv1,(max_count>0?first+count:first-1)); + SCVAL(smb_buf(outbuf),0,1); + SSVAL(smb_buf(outbuf),1,28*count); + } - if (queue) free(queue); + SAFE_FREE(queue); - DEBUG(3,("%d entries returned in queue\n",count)); - } + DEBUG(3,("%d entries returned in queue\n",count)); + } - return(outsize); + END_PROFILE(SMBsplretq); + return(outsize); } /**************************************************************************** reply to a printwrite ****************************************************************************/ -int reply_printwrite(char *inbuf,char *outbuf) +int reply_printwrite(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { - int cnum,numtowrite,fnum; + int numtowrite; int outsize = set_message(outbuf,0,0,True); char *data; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBsplwr); - cnum = SVAL(inbuf,smb_tid); - - if (!CAN_PRINT(cnum)) - return(ERROR(ERRDOS,ERRnoaccess)); - - fnum = GETFNUM(inbuf,smb_vwv0); + if (!CAN_PRINT(conn)) { + END_PROFILE(SMBsplwr); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } - CHECK_FNUM(fnum,cnum); - CHECK_WRITE(fnum); - CHECK_ERROR(fnum); + CHECK_FSP(fsp,conn); + CHECK_WRITE(fsp); numtowrite = SVAL(smb_buf(inbuf),1); data = smb_buf(inbuf) + 3; - if (write_file(fnum,data,numtowrite) != numtowrite) + if (write_file(fsp,data,-1,numtowrite) != numtowrite) { + END_PROFILE(SMBsplwr); return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + + DEBUG( 3, ( "printwrite fnum=%d num=%d\n", fsp->fnum, numtowrite ) ); - DEBUG(3,("%s printwrite fnum=%d cnum=%d num=%d\n",timestring(),fnum,cnum,numtowrite)); - + END_PROFILE(SMBsplwr); return(outsize); } /**************************************************************************** - reply to a mkdir + The guts of the mkdir command, split out so it may be called by the NT SMB + code. ****************************************************************************/ -int reply_mkdir(char *inbuf,char *outbuf) +NTSTATUS mkdir_internal(connection_struct *conn, pstring directory) { - 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); + BOOL bad_path = False; + SMB_STRUCT_STAT sbuf; + int ret= -1; + + unix_convert(directory,conn,0,&bad_path,&sbuf); + + if (check_name(directory, conn)) + ret = vfs_mkdir(conn,directory,unix_mode(conn,aDIR,directory)); + + if (ret == -1) { + NTSTATUS nterr = set_bad_path_error(errno, bad_path); + if (!NT_STATUS_IS_OK(nterr)) + return nterr; + return map_nt_error_from_unix(errno); + } + + return NT_STATUS_OK; } +/**************************************************************************** + Reply to a mkdir. +****************************************************************************/ + +int reply_mkdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) +{ + pstring directory; + int outsize; + NTSTATUS status; + START_PROFILE(SMBmkdir); + + srvstr_pull(inbuf, directory, smb_buf(inbuf) + 1, sizeof(directory), -1, STR_TERMINATE); + + status = mkdir_internal(conn, directory); + if (!NT_STATUS_IS_OK(status)) + return ERROR_NT(status); + + outsize = set_message(outbuf,0,0,True); + + DEBUG( 3, ( "mkdir %s ret=%d\n", directory, outsize ) ); + + END_PROFILE(SMBmkdir); + return(outsize); +} /**************************************************************************** - reply to a rmdir + Static function used by reply_rmdir to delete an entire directory + tree recursively. Return False on ok, True on fail. ****************************************************************************/ -int reply_rmdir(char *inbuf,char *outbuf) + +static BOOL recursive_rmdir(connection_struct *conn, char *directory) +{ + char *dname = NULL; + BOOL ret = False; + void *dirptr = OpenDir(conn, directory, False); + + if(dirptr == NULL) + return True; + + while((dname = ReadDirName(dirptr))) { + pstring fullname; + SMB_STRUCT_STAT st; + + if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0)) + continue; + + /* Construct the full name. */ + if(strlen(directory) + strlen(dname) + 1 >= sizeof(fullname)) { + errno = ENOMEM; + ret = True; + break; + } + + pstrcpy(fullname, directory); + pstrcat(fullname, "/"); + pstrcat(fullname, dname); + + if(conn->vfs_ops.lstat(conn,fullname, &st) != 0) { + ret = True; + break; + } + + if(st.st_mode & S_IFDIR) { + if(recursive_rmdir(conn, fullname)!=0) { + ret = True; + break; + } + if(vfs_rmdir(conn,fullname) != 0) { + ret = True; + break; + } + } else if(vfs_unlink(conn,fullname) != 0) { + ret = True; + break; + } + } + CloseDir(dirptr); + return ret; +} + +/**************************************************************************** + The internals of the rmdir code - called elsewhere. +****************************************************************************/ + +BOOL rmdir_internals(connection_struct *conn, char *directory) +{ + BOOL ok; + + ok = (vfs_rmdir(conn,directory) == 0); + if(!ok && ((errno == ENOTEMPTY)||(errno == EEXIST)) && lp_veto_files(SNUM(conn))) { + /* + * Check to see if the only thing in this directory are + * vetoed files/directories. If so then delete them and + * retry. If we fail to delete any of them (and we *don't* + * do a recursive delete) then fail the rmdir. + */ + BOOL all_veto_files = True; + char *dname; + void *dirptr = OpenDir(conn, directory, False); + + if(dirptr != NULL) { + int dirpos = TellDir(dirptr); + while ((dname = ReadDirName(dirptr))) { + if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0)) + continue; + if(!IS_VETO_PATH(conn, dname)) { + all_veto_files = False; + break; + } + } + + if(all_veto_files) { + SeekDir(dirptr,dirpos); + while ((dname = ReadDirName(dirptr))) { + pstring fullname; + SMB_STRUCT_STAT st; + + if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0)) + continue; + + /* Construct the full name. */ + if(strlen(directory) + strlen(dname) + 1 >= sizeof(fullname)) { + errno = ENOMEM; + break; + } + + pstrcpy(fullname, directory); + pstrcat(fullname, "/"); + pstrcat(fullname, dname); + + if(conn->vfs_ops.lstat(conn,fullname, &st) != 0) + break; + if(st.st_mode & S_IFDIR) { + if(lp_recursive_veto_delete(SNUM(conn))) { + if(recursive_rmdir(conn, fullname) != 0) + break; + } + if(vfs_rmdir(conn,fullname) != 0) + break; + } else if(vfs_unlink(conn,fullname) != 0) + break; + } + CloseDir(dirptr); + /* Retry the rmdir */ + ok = (vfs_rmdir(conn,directory) == 0); + } else { + CloseDir(dirptr); + } + } else { + errno = ENOTEMPTY; + } + } + + if (!ok) + DEBUG(3,("rmdir_internals: couldn't remove directory %s : %s\n", directory,strerror(errno))); + + return ok; +} + +/**************************************************************************** + Reply to a rmdir. +****************************************************************************/ + +int reply_rmdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { pstring directory; - int cnum; int outsize = 0; BOOL ok = False; + BOOL bad_path = False; + SMB_STRUCT_STAT sbuf; + START_PROFILE(SMBrmdir); + + srvstr_pull(inbuf, directory, smb_buf(inbuf) + 1, sizeof(directory), -1, STR_TERMINATE); + + RESOLVE_DFSPATH(directory, conn, inbuf, outbuf) + + unix_convert(directory,conn, NULL,&bad_path,&sbuf); - 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 (check_name(directory,conn)) + { + dptr_closepath(directory,SVAL(inbuf,smb_pid)); + ok = rmdir_internals(conn, directory); + } if (!ok) + { + set_bad_path_error(errno, bad_path); + END_PROFILE(SMBrmdir); return(UNIXERROR(ERRDOS,ERRbadpath)); - + } + outsize = set_message(outbuf,0,0,True); - DEBUG(3,("%s rmdir %s\n",timestring(),directory)); + DEBUG( 3, ( "rmdir %s\n", directory ) ); + END_PROFILE(SMBrmdir); return(outsize); } @@ -2396,26 +2917,26 @@ static BOOL resolve_wildcards(char *name1,char *name2) fstring ext1,ext2; char *p,*p2; - name1 = strrchr(name1,'/'); - name2 = strrchr(name2,'/'); + name1 = strrchr_m(name1,'/'); + name2 = strrchr_m(name2,'/'); if (!name1 || !name2) return(False); - strcpy(root1,name1); - strcpy(root2,name2); - p = strrchr(root1,'.'); + fstrcpy(root1,name1); + fstrcpy(root2,name2); + p = strrchr_m(root1,'.'); if (p) { *p = 0; - strcpy(ext1,p+1); + fstrcpy(ext1,p+1); } else { - strcpy(ext1,""); + fstrcpy(ext1,""); } - p = strrchr(root2,'.'); + p = strrchr_m(root2,'.'); if (p) { *p = 0; - strcpy(ext2,p+1); + fstrcpy(ext2,p+1); } else { - strcpy(ext2,""); + fstrcpy(ext2,""); } p = root1; @@ -2442,769 +2963,1289 @@ static BOOL resolve_wildcards(char *name1,char *name2) if (*p) p++; } - strcpy(name2,root2); + pstrcpy(name2,root2); if (ext2[0]) { - strcat(name2,"."); - strcat(name2,ext2); + pstrcat(name2,"."); + pstrcat(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 + The guts of the rename command, split out so it may be called by the NT SMB + code. ****************************************************************************/ -int reply_mv(char *inbuf,char *outbuf) + +NTSTATUS rename_internals(connection_struct *conn, char *name, char *newname, BOOL replace_if_exists) { - 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; + pstring directory; + pstring mask; + pstring newname_last_component; + char *p; + BOOL has_wild; + BOOL bad_path1 = False; + BOOL bad_path2 = False; + int count=0; + NTSTATUS error = NT_STATUS_OK; + BOOL rc = True; + SMB_STRUCT_STAT sbuf1, sbuf2; + + *directory = *mask = 0; + + rc = unix_convert(name,conn,0,&bad_path1,&sbuf1); + unix_convert(newname,conn,newname_last_component,&bad_path2,&sbuf2); + + /* + * Split the old name into directory and last component + * strings. Note that unix_convert may have stripped off a + * leading ./ from both name and newname if the rename is + * at the root of the share. We need to make sure either both + * name and newname contain a / character or neither of them do + * as this is checked in resolve_wildcards(). + */ + + p = strrchr_m(name,'/'); + if (!p) { + pstrcpy(directory,"."); + pstrcpy(mask,name); + } else { + *p = 0; + pstrcpy(directory,name); + pstrcpy(mask,p+1); + *p = '/'; /* Replace needed for exceptional test below. */ + } - *directory = *mask = 0; + /* + * We should only check the mangled cache + * here if unix_convert failed. This means + * that the path in 'mask' doesn't exist + * on the file system and so we need to look + * for a possible mangle. This patch from + * Tine Smukavec <valentin.smukavec@hermes.si>. + */ + + if (!rc && mangle_is_mangled(mask)) + mangle_check_cache( mask ); + + has_wild = ms_has_wild(mask); + + if (!has_wild) { + /* + * No wildcards - just process the one file. + */ + BOOL is_short_name = mangle_is_8_3(name, True); + + /* Add a terminating '/' to the directory name. */ + pstrcat(directory,"/"); + pstrcat(directory,mask); + + /* Ensure newname contains a '/' also */ + if(strrchr_m(newname,'/') == 0) { + pstring tmpstr; + + pstrcpy(tmpstr, "./"); + pstrcat(tmpstr, newname); + pstrcpy(newname, tmpstr); + } + + DEBUG(3,("rename_internals: case_sensitive = %d, case_preserve = %d, short case preserve = %d, \ +directory = %s, newname = %s, newname_last_component = %s, is_8_3 = %d\n", + case_sensitive, case_preserve, short_case_preserve, directory, + newname, newname_last_component, is_short_name)); + + /* + * Check for special case with case preserving and not + * case sensitive, if directory and newname are identical, + * and the old last component differs from the original + * last component only by case, then we should allow + * the rename (user is trying to change the case of the + * filename). + */ + if((case_sensitive == False) && + (((case_preserve == True) && + (is_short_name == False)) || + ((short_case_preserve == True) && + (is_short_name == True))) && + strcsequal(directory, newname)) { + pstring newname_modified_last_component; + + /* + * Get the last component of the modified name. + * Note that we guarantee that newname contains a '/' + * character above. + */ + p = strrchr_m(newname,'/'); + pstrcpy(newname_modified_last_component,p+1); + + if(strcsequal(newname_modified_last_component, + newname_last_component) == False) { + /* + * Replace the modified last component with + * the original. + */ + pstrcpy(p+1, newname_last_component); + } + } + + resolve_wildcards(directory,newname); + + /* + * The source object must exist. + */ + + if (!vfs_object_exist(conn, directory, &sbuf1)) { + DEBUG(3,("rename_internals: source doesn't exist doing rename %s -> %s\n", + directory,newname)); + + if (errno == ENOTDIR || errno == EISDIR || errno == ENOENT) { + /* + * Must return different errors depending on whether the parent + * directory existed or not. + */ + + p = strrchr_m(directory, '/'); + if (!p) + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + *p = '\0'; + if (vfs_object_exist(conn, directory, NULL)) + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + return NT_STATUS_OBJECT_PATH_NOT_FOUND; + } + error = map_nt_error_from_unix(errno); + DEBUG(3,("rename_internals: Error %s rename %s -> %s\n", + nt_errstr(error), directory,newname)); + + return error; + } - 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); + error = can_rename(directory,conn,&sbuf1); - p = strrchr(name,'/'); - if (!p) { - strcpy(directory,"./"); - strcpy(mask,name); - } else { - *p = 0; - strcpy(directory,name); - strcpy(mask,p+1); - } + if (!NT_STATUS_IS_OK(error)) { + DEBUG(3,("rename_internals: Error %s rename %s -> %s\n", + nt_errstr(error), directory,newname)); + return error; + } + + /* + * If the src and dest names are identical - including case, + * don't do the rename, just return success. + */ - if (is_mangled(mask)) - check_mangled_stack(mask); + if (strcsequal(directory, newname)) { + DEBUG(3,("rename_internals: identical names in rename %s - returning success\n", directory)); + return NT_STATUS_OK; + } - has_wild = strchr(mask,'*') || strchr(mask,'?'); + if(!replace_if_exists && vfs_object_exist(conn,newname,NULL)) { + DEBUG(3,("rename_internals: dest exists doing rename %s -> %s\n", + directory,newname)); + return NT_STATUS_OBJECT_NAME_COLLISION; + } - 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(conn->vfs_ops.rename(conn,directory, newname) == 0) { + DEBUG(3,("rename_internals: succeeded doing rename on %s -> %s\n", + directory,newname)); + return NT_STATUS_OK; + } - if (check_name(directory,cnum)) - dirptr = OpenDir(directory); + if (errno == ENOTDIR || errno == EISDIR) + error = NT_STATUS_OBJECT_NAME_COLLISION; + else + error = map_nt_error_from_unix(errno); + + DEBUG(3,("rename_internals: Error %s rename %s -> %s\n", + nt_errstr(error), directory,newname)); + + return error; + } else { + /* + * Wildcards - process each file that matches. + */ + void *dirptr = NULL; + char *dname; + pstring destname; + + if (check_name(directory,conn)) + dirptr = OpenDir(conn, directory, True); + + if (dirptr) { + error = NT_STATUS_OBJECT_NAME_NOT_FOUND; + + if (strequal(mask,"????????.???")) + pstrcpy(mask,"*"); + + while ((dname = ReadDirName(dirptr))) { + pstring fname; + + pstrcpy(fname,dname); + + if(!mask_match(fname, mask, case_sensitive)) + continue; + + error = NT_STATUS_ACCESS_DENIED; + slprintf(fname,sizeof(fname)-1,"%s/%s",directory,dname); + if (!vfs_object_exist(conn, fname, &sbuf1)) { + error = NT_STATUS_OBJECT_NAME_NOT_FOUND; + DEBUG(6,("rename %s failed. Error %s\n", fname, nt_errstr(error))); + continue; + } + error = can_rename(fname,conn,&sbuf1); + if (!NT_STATUS_IS_OK(error)) { + DEBUG(6,("rename %s refused\n", fname)); + continue; + } + pstrcpy(destname,newname); + + if (!resolve_wildcards(fname,destname)) { + DEBUG(6,("resolve_wildcards %s %s failed\n", + fname, destname)); + continue; + } + + if (!replace_if_exists && + vfs_file_exist(conn,destname, NULL)) { + DEBUG(6,("file_exist %s\n", destname)); + error = NT_STATUS_OBJECT_NAME_COLLISION; + continue; + } + + if (!conn->vfs_ops.rename(conn,fname,destname)) + count++; + DEBUG(3,("rename_internals: doing rename on %s -> %s\n",fname,destname)); + } + CloseDir(dirptr); + } + } + + if (count == 0 && NT_STATUS_IS_OK(error)) { + error = map_nt_error_from_unix(errno); + } + + return error; +} - if (dirptr) - { - error = ERRbadfile; +/**************************************************************************** + Reply to a mv. +****************************************************************************/ - if (strequal(mask,"????????.???")) - strcpy(mask,"*"); +int reply_mv(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, + int dum_buffsize) +{ + int outsize = 0; + pstring name; + pstring newname; + char *p; + NTSTATUS status; - while ((dname = ReadDirName(dirptr))) - { - pstring fname; - strcpy(fname,dname); - - if(!mask_match(fname, mask, case_sensitive, False)) continue; + START_PROFILE(SMBmv); - 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); + p = smb_buf(inbuf) + 1; + p += srvstr_pull(inbuf, name, p, sizeof(name), -1, STR_TERMINATE); + p++; + p += srvstr_pull(inbuf, newname, p, sizeof(newname), -1, STR_TERMINATE); + + RESOLVE_DFSPATH(name, conn, inbuf, outbuf); + RESOLVE_DFSPATH(newname, conn, inbuf, outbuf); + + DEBUG(3,("reply_mv : %s -> %s\n",name,newname)); + + status = rename_internals(conn, name, newname, False); + if (!NT_STATUS_IS_OK(status)) { + return ERROR_NT(status); + } + + /* + * Win2k needs a changenotify request response before it will + * update after a rename.. + */ + process_pending_change_notify_queue((time_t)0); + outsize = set_message(outbuf,0,0,True); - return(outsize); + END_PROFILE(SMBmv); + 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) + Copy a file as part of a reply_copy. +******************************************************************/ + +static BOOL copy_file(char *src,char *dest1,connection_struct *conn, int ofun, + int count,BOOL target_is_directory, int *err_ret) { - 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); - } + int Access,action; + SMB_STRUCT_STAT src_sbuf, sbuf2; + SMB_OFF_T ret=-1; + files_struct *fsp1,*fsp2; + pstring dest; + + *err_ret = 0; + + pstrcpy(dest,dest1); + if (target_is_directory) { + char *p = strrchr_m(src,'/'); + if (p) + p++; + else + p = src; + pstrcat(dest,"/"); + pstrcat(dest,p); + } - if (!file_exist(src,&st)) return(False); + if (!vfs_file_exist(conn,src,&src_sbuf)) + return(False); - fnum1 = find_free_file(); - if (fnum1<0) return(False); - open_file_shared(fnum1,cnum,src,(DENY_NONE<<4), - 1,0,&Access,&action); + fsp1 = open_file_shared(conn,src,&src_sbuf,SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_RDONLY), + (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),0,0,&Access,&action); - if (!Files[fnum1].open) return(False); + if (!fsp1) + return(False); - if (!target_is_directory && count) - ofun = 1; + if (!target_is_directory && count) + ofun = FILE_EXISTS_OPEN; - 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 (vfs_stat(conn,dest,&sbuf2) == -1) + ZERO_STRUCTP(&sbuf2); - if (!Files[fnum2].open) { - close_file(fnum1); - return(False); - } + fsp2 = open_file_shared(conn,dest,&sbuf2,SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_WRONLY), + ofun,src_sbuf.st_mode,0,&Access,&action); - if ((ofun&3) == 1) { - lseek(Files[fnum2].fd,0,SEEK_END); - } + if (!fsp2) { + close_file(fsp1,False); + return(False); + } + + if ((ofun&3) == 1) { + if(conn->vfs_ops.lseek(fsp2,fsp2->fd,0,SEEK_END) == -1) { + DEBUG(0,("copy_file: error - vfs lseek returned error %s\n", strerror(errno) )); + /* + * Stop the copy from occurring. + */ + ret = -1; + src_sbuf.st_size = 0; + } + } - if (st.st_size) - ret = transfer_file(Files[fnum1].fd,Files[fnum2].fd,st.st_size,NULL,0,0); + if (src_sbuf.st_size) + ret = vfs_transfer_file(fsp1, fsp2, src_sbuf.st_size); - close_file(fnum1); - close_file(fnum2); + close_file(fsp1,False); - return(ret == st.st_size); -} + /* Ensure the modtime is set correctly on the destination file. */ + fsp2->pending_modtime = src_sbuf.st_mtime; + /* + * As we are opening fsp1 read-only we only expect + * an error on close on fsp2 if we are out of space. + * Thus we don't look at the error return from the + * close of fsp1. + */ + *err_ret = close_file(fsp2,False); + return(ret == (SMB_OFF_T)src_sbuf.st_size); +} /**************************************************************************** reply to a file copy. ****************************************************************************/ -int reply_copy(char *inbuf,char *outbuf) +int reply_copy(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { int outsize = 0; pstring name; - int cnum; pstring directory; pstring mask,newname; char *p; int count=0; int error = ERRnoaccess; + int err = 0; 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; + BOOL bad_path1 = False; + BOOL bad_path2 = False; + BOOL rc = True; + SMB_STRUCT_STAT sbuf1, sbuf2; + START_PROFILE(SMBcopy); *directory = *mask = 0; - cnum = SVAL(inbuf,smb_tid); - - strcpy(name,smb_buf(inbuf)); - strcpy(newname,smb_buf(inbuf) + 1 + strlen(name)); + p = smb_buf(inbuf); + p += srvstr_pull(inbuf, name, p, sizeof(name), -1, STR_TERMINATE); + p += srvstr_pull(inbuf, newname, p, sizeof(newname), -1, STR_TERMINATE); DEBUG(3,("reply_copy : %s -> %s\n",name,newname)); - if (tid2 != cnum) { + if (tid2 != conn->cnum) { /* can't currently handle inter share copies XXXX */ DEBUG(3,("Rejecting inter-share copy\n")); - return(ERROR(ERRSRV,ERRinvdevice)); + END_PROFILE(SMBcopy); + return ERROR_DOS(ERRSRV,ERRinvdevice); } - unix_convert(name,cnum); - unix_convert(newname,cnum); + RESOLVE_DFSPATH(name, conn, inbuf, outbuf); + RESOLVE_DFSPATH(newname, conn, inbuf, outbuf); - target_is_directory = directory_exist(newname,NULL); + rc = unix_convert(name,conn,0,&bad_path1,&sbuf1); + unix_convert(newname,conn,0,&bad_path2,&sbuf2); + + target_is_directory = VALID_STAT_OF_DIR(sbuf2); if ((flags&1) && target_is_directory) { - return(ERROR(ERRDOS,ERRbadfile)); + END_PROFILE(SMBcopy); + return ERROR_DOS(ERRDOS,ERRbadfile); } if ((flags&2) && !target_is_directory) { - return(ERROR(ERRDOS,ERRbadpath)); + END_PROFILE(SMBcopy); + return ERROR_DOS(ERRDOS,ERRbadpath); } - if ((flags&(1<<5)) && directory_exist(name,NULL)) { + if ((flags&(1<<5)) && VALID_STAT_OF_DIR(sbuf1)) { /* wants a tree copy! XXXX */ DEBUG(3,("Rejecting tree copy\n")); - return(ERROR(ERRSRV,ERRerror)); + END_PROFILE(SMBcopy); + return ERROR_DOS(ERRSRV,ERRerror); } - p = strrchr(name,'/'); + p = strrchr_m(name,'/'); if (!p) { - strcpy(directory,"./"); - strcpy(mask,name); + pstrcpy(directory,"./"); + pstrcpy(mask,name); } else { *p = 0; - strcpy(directory,name); - strcpy(mask,p+1); + pstrcpy(directory,name); + pstrcpy(mask,p+1); } - if (is_mangled(mask)) - check_mangled_stack(mask); + /* + * We should only check the mangled cache + * here if unix_convert failed. This means + * that the path in 'mask' doesn't exist + * on the file system and so we need to look + * for a possible mangle. This patch from + * Tine Smukavec <valentin.smukavec@hermes.si>. + */ + + if (!rc && mangle_is_mangled(mask)) + mangle_check_cache( mask ); - has_wild = strchr(mask,'*') || strchr(mask,'?'); + has_wild = ms_has_wild(mask); if (!has_wild) { - strcat(directory,"/"); - strcat(directory,mask); + pstrcat(directory,"/"); + pstrcat(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); + copy_file(directory,newname,conn,ofun, + count,target_is_directory,&err)) count++; + if(!count && err) { + errno = err; + END_PROFILE(SMBcopy); + return(UNIXERROR(ERRHRD,ERRgeneral)); + } + if (!count) exists = vfs_file_exist(conn,directory,NULL); } else { void *dirptr = NULL; char *dname; pstring destname; - if (check_name(directory,cnum)) - dirptr = OpenDir(directory); + if (check_name(directory,conn)) + dirptr = OpenDir(conn, directory, True); - if (dirptr) - { + if (dirptr) { error = ERRbadfile; if (strequal(mask,"????????.???")) - strcpy(mask,"*"); + pstrcpy(mask,"*"); - while ((dname = ReadDirName(dirptr))) - { + while ((dname = ReadDirName(dirptr))) { pstring fname; - strcpy(fname,dname); + pstrcpy(fname,dname); - if(!mask_match(fname, mask, case_sensitive, False)) continue; + if(!mask_match(fname, mask, case_sensitive)) + continue; error = ERRnoaccess; - sprintf(fname,"%s/%s",directory,dname); - strcpy(destname,newname); + slprintf(fname,sizeof(fname)-1, "%s/%s",directory,dname); + pstrcpy(destname,newname); if (resolve_wildcards(fname,destname) && - copy_file(directory,newname,cnum,ofun, - count,target_is_directory)) count++; + copy_file(fname,destname,conn,ofun, + count,target_is_directory,&err)) 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 + if(err) { + /* Error on close... */ + errno = err; + END_PROFILE(SMBcopy); + return(UNIXERROR(ERRHRD,ERRgeneral)); + } + + if (exists) { + END_PROFILE(SMBcopy); + return ERROR_DOS(ERRDOS,error); + } else + { + if((errno == ENOENT) && (bad_path1 || bad_path2)) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } + END_PROFILE(SMBcopy); return(UNIXERROR(ERRDOS,error)); + } } outsize = set_message(outbuf,1,0,True); SSVAL(outbuf,smb_vwv0,count); + END_PROFILE(SMBcopy); return(outsize); } - - /**************************************************************************** reply to a setdir ****************************************************************************/ -int reply_setdir(char *inbuf,char *outbuf) +int reply_setdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { - int cnum,snum; + int snum; int outsize = 0; BOOL ok = False; pstring newdir; + START_PROFILE(pathworks_setdir); - 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); + snum = SNUM(conn); + if (!CAN_SETDIR(snum)) { + END_PROFILE(pathworks_setdir); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } + + srvstr_pull(inbuf, newdir, smb_buf(inbuf) + 1, sizeof(newdir), -1, STR_TERMINATE); - if (strlen(newdir) == 0) - ok = True; - else - { - ok = directory_exist(newdir,NULL); - if (ok) - string_set(&Connections[cnum].connectpath,newdir); - } + if (strlen(newdir) == 0) { + ok = True; + } else { + ok = vfs_directory_exist(conn,newdir,NULL); + if (ok) { + string_set(&conn->connectpath,newdir); + } + } - if (!ok) - return(ERROR(ERRDOS,ERRbadpath)); + if (!ok) { + END_PROFILE(pathworks_setdir); + return ERROR_DOS(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)); + SCVAL(outbuf,smb_reh,CVAL(inbuf,smb_reh)); + DEBUG(3,("setdir %s\n", newdir)); + + END_PROFILE(pathworks_setdir); return(outsize); } +/**************************************************************************** + Get a lock pid, dealing with large count requests. +****************************************************************************/ + +uint16 get_lock_pid( char *data, int data_offset, BOOL large_file_format) +{ + if(!large_file_format) + return SVAL(data,SMB_LPID_OFFSET(data_offset)); + else + return SVAL(data,SMB_LARGE_LPID_OFFSET(data_offset)); +} /**************************************************************************** - reply to a lockingX request + Get a lock count, dealing with large count requests. ****************************************************************************/ -int reply_lockingX(char *inbuf,char *outbuf,int length,int bufsize) + +SMB_BIG_UINT get_lock_count( char *data, int data_offset, BOOL large_file_format) { - 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); - } + SMB_BIG_UINT count = 0; - 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)); + if(!large_file_format) { + count = (SMB_BIG_UINT)IVAL(data,SMB_LKLEN_OFFSET(data_offset)); + } else { - chain_fnum = fnum; +#if defined(HAVE_LONGLONG) + count = (((SMB_BIG_UINT) IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset))) << 32) | + ((SMB_BIG_UINT) IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(data_offset))); +#else /* HAVE_LONGLONG */ - if (smb_com2 != 0xFF) - outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4, - outbuf,outbuf+outsize, - length,bufsize); - - chain_fnum = -1; - - return(outsize); + /* + * NT4.x seems to be broken in that it sends large file (64 bit) + * lockingX calls even if the CAP_LARGE_FILES was *not* + * negotiated. For boxes without large unsigned ints truncate the + * lock count by dropping the top 32 bits. + */ + + if(IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset)) != 0) { + DEBUG(3,("get_lock_count: truncating lock count (high)0x%x (low)0x%x to just low count.\n", + (unsigned int)IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset)), + (unsigned int)IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(data_offset)) )); + SIVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset),0); + } + + count = (SMB_BIG_UINT)IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(data_offset)); +#endif /* HAVE_LONGLONG */ + } + + return count; } +#if !defined(HAVE_LONGLONG) +/**************************************************************************** + Pathetically try and map a 64 bit lock offset into 31 bits. I hate Windows :-). +****************************************************************************/ +static uint32 map_lock_offset(uint32 high, uint32 low) +{ + unsigned int i; + uint32 mask = 0; + uint32 highcopy = high; + + /* + * Try and find out how many significant bits there are in high. + */ + + for(i = 0; highcopy; i++) + highcopy >>= 1; + + /* + * We use 31 bits not 32 here as POSIX + * lock offsets may not be negative. + */ + + mask = (~0) << (31 - i); + + if(low & mask) + return 0; /* Fail. */ + + high <<= (31 - i); + + return (high|low); +} +#endif /* !defined(HAVE_LONGLONG) */ /**************************************************************************** - reply to a SMBreadbmpx (read block multiplex) request + Get a lock offset, dealing with large offset requests. ****************************************************************************/ -int reply_readbmpx(char *inbuf,char *outbuf,int length,int bufsize) + +SMB_BIG_UINT get_lock_offset( char *data, int data_offset, BOOL large_file_format, BOOL *err) { - 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; + SMB_BIG_UINT offset = 0; + + *err = False; - /* this function doesn't seem to work - disable by default */ - if (!lp_readbmpx()) - return(ERROR(ERRSRV,ERRuseSTD)); + if(!large_file_format) { + offset = (SMB_BIG_UINT)IVAL(data,SMB_LKOFF_OFFSET(data_offset)); + } else { - outsize = set_message(outbuf,8,0,True); +#if defined(HAVE_LONGLONG) + offset = (((SMB_BIG_UINT) IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset))) << 32) | + ((SMB_BIG_UINT) IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset))); +#else /* HAVE_LONGLONG */ - cnum = SVAL(inbuf,smb_tid); - fnum = GETFNUM(inbuf,smb_vwv0); + /* + * NT4.x seems to be broken in that it sends large file (64 bit) + * lockingX calls even if the CAP_LARGE_FILES was *not* + * negotiated. For boxes without large unsigned ints mangle the + * lock offset by mapping the top 32 bits onto the lower 32. + */ + + if(IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset)) != 0) { + uint32 low = IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset)); + uint32 high = IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset)); + uint32 new_low = 0; + + if((new_low = map_lock_offset(high, low)) == 0) { + *err = True; + return (SMB_BIG_UINT)-1; + } - CHECK_FNUM(fnum,cnum); - CHECK_READ(fnum); - CHECK_ERROR(fnum); + DEBUG(3,("get_lock_offset: truncating lock offset (high)0x%x (low)0x%x to offset 0x%x.\n", + (unsigned int)high, (unsigned int)low, (unsigned int)new_low )); + SIVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset),0); + SIVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset),new_low); + } - startpos = IVAL(inbuf,smb_vwv1); - maxcount = SVAL(inbuf,smb_vwv3); - mincount = SVAL(inbuf,smb_vwv4); + offset = (SMB_BIG_UINT)IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset)); +#endif /* HAVE_LONGLONG */ + } - data = smb_buf(outbuf); - pad = ((int)data)%4; - if (pad) pad = 4 - pad; - data += pad; + return offset; +} - max_per_packet = bufsize-(outsize+pad); - tcount = maxcount; - total_read = 0; +/**************************************************************************** + reply to a lockingX request +****************************************************************************/ - if (is_locked(fnum,cnum,maxcount,startpos)) - return(ERROR(ERRDOS,ERRlock)); +int reply_lockingX(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize) +{ + files_struct *fsp = file_fsp(inbuf,smb_vwv2); + unsigned char locktype = CVAL(inbuf,smb_vwv3); + unsigned char oplocklevel = CVAL(inbuf,smb_vwv3+1); + uint16 num_ulocks = SVAL(inbuf,smb_vwv6); + uint16 num_locks = SVAL(inbuf,smb_vwv7); + SMB_BIG_UINT count = 0, offset = 0; + uint16 lock_pid; + int32 lock_timeout = IVAL(inbuf,smb_vwv4); + int i; + char *data; + BOOL large_file_format = (locktype & LOCKING_ANDX_LARGE_FILES)?True:False; + BOOL err; + NTSTATUS status; + + START_PROFILE(SMBlockingX); - do - { - int N = MIN(max_per_packet,tcount-total_read); - - nread = read_file(fnum,data,startpos,N,N,-1,False); + CHECK_FSP(fsp,conn); + + data = smb_buf(inbuf); - if (nread <= 0) nread = 0; + if (locktype & (LOCKING_ANDX_CANCEL_LOCK | LOCKING_ANDX_CHANGE_LOCKTYPE)) { + /* we don't support these - and CANCEL_LOCK makes w2k + and XP reboot so I don't really want to be + compatible! (tridge) */ + return ERROR_NT(NT_STATUS_NOT_SUPPORTED); + } + + /* Check if this is an oplock break on a file + we have granted an oplock on. + */ + if ((locktype & LOCKING_ANDX_OPLOCK_RELEASE)) { + /* Client can insist on breaking to none. */ + BOOL break_to_none = (oplocklevel == 0); + + DEBUG(5,("reply_lockingX: oplock break reply (%u) from client for fnum = %d\n", + (unsigned int)oplocklevel, fsp->fnum )); + + /* + * Make sure we have granted an exclusive or batch oplock on this file. + */ + + if(!EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { + DEBUG(0,("reply_lockingX: Error : oplock break from client for fnum = %d and \ +no oplock granted on this file (%s).\n", fsp->fnum, fsp->fsp_name)); + + /* if this is a pure oplock break request then don't send a reply */ + if (num_locks == 0 && num_ulocks == 0) { + END_PROFILE(SMBlockingX); + return -1; + } else { + END_PROFILE(SMBlockingX); + return ERROR_DOS(ERRDOS,ERRlock); + } + } - if (nread < N) - tcount = total_read + nread; + if (remove_oplock(fsp, break_to_none) == False) { + DEBUG(0,("reply_lockingX: error in removing oplock on file %s\n", + fsp->fsp_name )); + } + + /* if this is a pure oplock break request then don't send a reply */ + if (num_locks == 0 && num_ulocks == 0) { + /* Sanity check - ensure a pure oplock break is not a + chained request. */ + if(CVAL(inbuf,smb_vwv0) != 0xff) + DEBUG(0,("reply_lockingX: Error : pure oplock break is a chained %d request !\n", + (unsigned int)CVAL(inbuf,smb_vwv0) )); + END_PROFILE(SMBlockingX); + return -1; + } + } - 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)); + /* + * We do this check *after* we have checked this is not a oplock break + * response message. JRA. + */ + + release_level_2_oplocks_on_change(fsp); + + /* Data now points at the beginning of the list + of smb_unlkrng structs */ + for(i = 0; i < (int)num_ulocks; i++) { + lock_pid = get_lock_pid( data, i, large_file_format); + count = get_lock_count( data, i, large_file_format); + offset = get_lock_offset( data, i, large_file_format, &err); + + /* + * There is no error code marked "stupid client bug".... :-). + */ + if(err) { + END_PROFILE(SMBlockingX); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } - send_smb(Client,outbuf); + DEBUG(10,("reply_lockingX: unlock start=%.0f, len=%.0f for pid %u, file %s\n", + (double)offset, (double)count, (unsigned int)lock_pid, fsp->fsp_name )); + + status = do_unlock(fsp,conn,lock_pid,count,offset); + if (NT_STATUS_V(status)) { + END_PROFILE(SMBlockingX); + return ERROR_NT(status); + } + } - total_read += nread; - startpos += nread; - } - while (total_read < tcount); + /* Setup the timeout in seconds. */ - return(-1); -} + lock_timeout = ((lock_timeout == -1) ? -1 : lock_timeout/1000); + + /* Now do any requested locks */ + data += ((large_file_format ? 20 : 10)*num_ulocks); + + /* Data now points at the beginning of the list + of smb_lkrng structs */ + + for(i = 0; i < (int)num_locks; i++) { + lock_pid = get_lock_pid( data, i, large_file_format); + count = get_lock_count( data, i, large_file_format); + offset = get_lock_offset( data, i, large_file_format, &err); + + /* + * There is no error code marked "stupid client bug".... :-). + */ + if(err) { + END_PROFILE(SMBlockingX); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } + + DEBUG(10,("reply_lockingX: lock start=%.0f, len=%.0f for pid %u, file %s timeout = %d\n", + (double)offset, (double)count, (unsigned int)lock_pid, + fsp->fsp_name, (int)lock_timeout )); + + status = do_lock_spin(fsp,conn,lock_pid, count,offset, + ((locktype & 1) ? READ_LOCK : WRITE_LOCK)); + if (NT_STATUS_V(status)) { + if ((lock_timeout != 0) && lp_blocking_locks(SNUM(conn))) { + /* + * A blocking lock was requested. Package up + * this smb into a queued request and push it + * onto the blocking lock queue. + */ + if(push_blocking_lock_request(inbuf, length, lock_timeout, i)) { + END_PROFILE(SMBlockingX); + return -1; + } + } + 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) { + /* + * Ensure we don't do a remove on the lock that just failed, + * as under POSIX rules, if we have a lock already there, we + * will delete it (and we shouldn't) ..... + */ + for(i--; i >= 0; i--) { + lock_pid = get_lock_pid( data, i, large_file_format); + count = get_lock_count( data, i, large_file_format); + offset = get_lock_offset( data, i, large_file_format, &err); + + /* + * There is no error code marked "stupid client bug".... :-). + */ + if(err) { + END_PROFILE(SMBlockingX); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } + + do_unlock(fsp,conn,lock_pid,count,offset); + } + END_PROFILE(SMBlockingX); + return ERROR_NT(status); + } + set_message(outbuf,2,0,True); + + DEBUG( 3, ( "lockingX fnum=%d type=%d num_locks=%d num_ulocks=%d\n", + fsp->fnum, (unsigned int)locktype, num_locks, num_ulocks ) ); + + END_PROFILE(SMBlockingX); + return chain_reply(inbuf,outbuf,length,bufsize); +} /**************************************************************************** - reply to a SMBwritebmpx (write block multiplex primary) request + Reply to a SMBreadbmpx (read block multiplex) request. ****************************************************************************/ -int reply_writebmpx(char *inbuf,char *outbuf) + +int reply_readbmpx(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize) { - 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); + ssize_t nread = -1; + ssize_t total_read; + char *data; + SMB_OFF_T startpos; + int outsize; + size_t maxcount; + int max_per_packet; + size_t tcount; + int pad; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBreadBmpx); + + /* this function doesn't seem to work - disable by default */ + if (!lp_readbmpx()) { + END_PROFILE(SMBreadBmpx); + return ERROR_DOS(ERRSRV,ERRuseSTD); + } - CHECK_FNUM(fnum,cnum); - CHECK_WRITE(fnum); - CHECK_ERROR(fnum); + outsize = set_message(outbuf,8,0,True); - 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); + CHECK_FSP(fsp,conn); + CHECK_READ(fsp); - data = smb_base(inbuf) + smb_doff; + startpos = IVAL(inbuf,smb_vwv1); + maxcount = SVAL(inbuf,smb_vwv3); - /* 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; + data = smb_buf(outbuf); + pad = ((long)data)%4; + if (pad) + pad = 4 - pad; + data += pad; - if (is_locked(fnum,cnum,tcount,startpos)) - return(ERROR(ERRDOS,ERRlock)); + max_per_packet = bufsize-(outsize+pad); + tcount = maxcount; + total_read = 0; - seek_file(fnum,startpos); - nwritten = write_file(fnum,data,numtowrite); + if (is_locked(fsp,conn,(SMB_BIG_UINT)maxcount,(SMB_BIG_UINT)startpos, READ_LOCK,False)) { + END_PROFILE(SMBreadBmpx); + return ERROR_DOS(ERRDOS,ERRlock); + } - if(lp_syncalways(SNUM(cnum)) || write_through) - sync_file(fnum); + do { + size_t N = MIN(max_per_packet,tcount-total_read); - if(nwritten < numtowrite) - return(UNIXERROR(ERRHRD,ERRdiskfull)); + nread = read_file(fsp,data,startpos,N); - /* 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; - } + if (nread <= 0) + nread = 0; - /* 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); + if (nread < (ssize_t)N) + tcount = total_read + nread; - /* now the secondary */ - outsize = set_message(outbuf,1,0,True); - CVAL(outbuf,smb_com) = SMBwritec; - SSVAL(outbuf,smb_vwv0,nwritten); - } + 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)); - return(outsize); -} + if (!send_smb(smbd_server_fd(),outbuf)) + exit_server("reply_readbmpx: send_smb failed."); + total_read += nread; + startpos += nread; + } while (total_read < (ssize_t)tcount); + + END_PROFILE(SMBreadBmpx); + return(-1); +} /**************************************************************************** - reply to a SMBwritebs (write block multiplex secondary) request + Reply to a SMBsetattrE. ****************************************************************************/ -int reply_writebs(char *inbuf,char *outbuf) + +int reply_setattrE(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) { - 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; + struct utimbuf unix_times; + int outsize = 0; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBsetattrE); + + outsize = set_message(outbuf,0,0,True); + + if(!fsp || (fsp->conn != conn)) { + END_PROFILE(SMBgetattrE); + return ERROR_DOS(ERRDOS,ERRbadfid); + } + + /* + * 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); - cnum = SVAL(inbuf,smb_tid); - fnum = GETFNUM(inbuf,smb_vwv0); - CHECK_FNUM(fnum,cnum); - CHECK_WRITE(fnum); + /* + * Patch from Ray Frush <frush@engr.colostate.edu> + * Sometimes times are sent as zero - ignore them. + */ - tcount = SVAL(inbuf,smb_vwv1); - startpos = IVAL(inbuf,smb_vwv2); - numtowrite = SVAL(inbuf,smb_vwv6); - smb_doff = SVAL(inbuf,smb_vwv7); + if ((unix_times.actime == 0) && (unix_times.modtime == 0)) { + /* Ignore request */ + if( DEBUGLVL( 3 ) ) { + dbgtext( "reply_setattrE fnum=%d ", fsp->fnum); + dbgtext( "ignoring zero request - not setting timestamps of 0\n" ); + } + END_PROFILE(SMBsetattrE); + return(outsize); + } else if ((unix_times.actime != 0) && (unix_times.modtime == 0)) { + /* set modify time = to access time if modify time was 0 */ + unix_times.modtime = unix_times.actime; + } - data = smb_base(inbuf) + smb_doff; + /* Set the date on this file */ + if(file_utime(conn, fsp->fsp_name, &unix_times)) { + END_PROFILE(SMBsetattrE); + return ERROR_DOS(ERRDOS,ERRnoaccess); + } + + DEBUG( 3, ( "reply_setattrE fnum=%d actime=%d modtime=%d\n", + fsp->fnum, (int)unix_times.actime, (int)unix_times.modtime ) ); - /* We need to send an SMBwriteC response, not an SMBwritebs */ - CVAL(outbuf,smb_com) = SMBwritec; + END_PROFILE(SMBsetattrE); + return(outsize); +} - /* 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; +/* Back from the dead for OS/2..... JRA. */ - /* Check for an earlier error */ - if(wbms->wr_discard) - return -1; /* Just discard the packet */ +/**************************************************************************** + Reply to a SMBwritebmpx (write block multiplex primary) request. +****************************************************************************/ + +int reply_writebmpx(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) +{ + size_t numtowrite; + ssize_t nwritten = -1; + int outsize = 0; + SMB_OFF_T startpos; + size_t tcount; + BOOL write_through; + int smb_doff; + char *data; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBwriteBmpx); + + CHECK_FSP(fsp,conn); + CHECK_WRITE(fsp); + CHECK_ERROR(fsp); + + 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 */ + SCVAL(outbuf,smb_com,SMBwritec); + + if (is_locked(fsp,conn,(SMB_BIG_UINT)tcount,(SMB_BIG_UINT)startpos,WRITE_LOCK,False)) { + END_PROFILE(SMBwriteBmpx); + return(ERROR_DOS(ERRDOS,ERRlock)); + } - seek_file(fnum,startpos); - nwritten = write_file(fnum,data,numtowrite); + nwritten = write_file(fsp,data,startpos,numtowrite); - if(lp_syncalways(SNUM(cnum)) || write_through) - sync_file(fnum); + if(lp_syncalways(SNUM(conn)) || write_through) + sync_file(conn,fsp); - 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)); - } + if(nwritten < (ssize_t)numtowrite) { + END_PROFILE(SMBwriteBmpx); + return(UNIXERROR(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) { + /* 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((ssize_t)tcount > nwritten) { + write_bmpx_struct *wbms; + if(fsp->wbmpx_ptr != NULL) + wbms = fsp->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")); + END_PROFILE(SMBwriteBmpx); + return(ERROR_DOS(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; + fsp->wbmpx_ptr = wbms; + } + + /* We are returning successfully, set the message type back to + SMBwritebmpx */ + SCVAL(outbuf,smb_com,SMBwriteBmpx); + outsize = set_message(outbuf,1,0,True); - SSVAL(outbuf,smb_vwv0,wbms->wr_total_written); - send_response = True; - } + + SSVALS(outbuf,smb_vwv0,-1); /* We don't support smb_remaining */ + + DEBUG( 3, ( "writebmpx fnum=%d num=%d wrote=%d\n", + fsp->fnum, (int)numtowrite, (int)nwritten ) ); - free((char *)wbms); - Files[fnum].wbmpx_ptr = NULL; - } + if (write_through && tcount==nwritten) { + /* We need to send both a primary and a secondary response */ + smb_setlen(outbuf,outsize - 4); + if (!send_smb(smbd_server_fd(),outbuf)) + exit_server("reply_writebmpx: send_smb failed."); - if(send_response) - return(outsize); + /* Now the secondary */ + outsize = set_message(outbuf,1,0,True); + SCVAL(outbuf,smb_com,SMBwritec); + SSVAL(outbuf,smb_vwv0,nwritten); + } - return(-1); + END_PROFILE(SMBwriteBmpx); + return(outsize); } - /**************************************************************************** - reply to a SMBsetattrE + Reply to a SMBwritebs (write block multiplex secondary) request. ****************************************************************************/ -int reply_setattrE(char *inbuf,char *outbuf) + +int reply_writebs(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { - int cnum,fnum; - struct utimbuf unix_times; - int outsize = 0; + size_t numtowrite; + ssize_t nwritten = -1; + int outsize = 0; + SMB_OFF_T startpos; + size_t tcount; + BOOL write_through; + int smb_doff; + char *data; + write_bmpx_struct *wbms; + BOOL send_response = False; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBwriteBs); + + CHECK_FSP(fsp,conn); + CHECK_WRITE(fsp); + + 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 */ + SCVAL(outbuf,smb_com,SMBwritec); + + /* This fd should have an auxiliary struct attached, + check that it does */ + wbms = fsp->wbmpx_ptr; + if(!wbms) { + END_PROFILE(SMBwriteBs); + return(-1); + } - outsize = set_message(outbuf,0,0,True); + /* If write through is set we can return errors, else we must cache them */ + write_through = wbms->wr_mode; - cnum = SVAL(inbuf,smb_tid); - fnum = GETFNUM(inbuf,smb_vwv0); + /* Check for an earlier error */ + if(wbms->wr_discard) { + END_PROFILE(SMBwriteBs); + return -1; /* Just discard the packet */ + } - CHECK_FNUM(fnum,cnum); - CHECK_ERROR(fnum); + nwritten = write_file(fsp,data,startpos,numtowrite); - /* 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); + if(lp_syncalways(SNUM(conn)) || write_through) + sync_file(conn,fsp); - /* 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)); + if (nwritten < (ssize_t)numtowrite) { + if(write_through) { + /* We are returning an error - we can delete the aux struct */ + if (wbms) + free((char *)wbms); + fsp->wbmpx_ptr = NULL; + END_PROFILE(SMBwriteBs); + return(ERROR_DOS(ERRHRD,ERRdiskfull)); + } + END_PROFILE(SMBwriteBs); + return(CACHE_ERROR(wbms,ERRHRD,ERRdiskfull)); + } - return(outsize); -} + /* 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); + fsp->wbmpx_ptr = NULL; + } + + if(send_response) { + END_PROFILE(SMBwriteBs); + return(outsize); + } + + END_PROFILE(SMBwriteBs); + return(-1); +} /**************************************************************************** - reply to a SMBgetattrE + 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); +int reply_getattrE(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) +{ + SMB_STRUCT_STAT sbuf; + int outsize = 0; + int mode; + files_struct *fsp = file_fsp(inbuf,smb_vwv0); + START_PROFILE(SMBgetattrE); - cnum = SVAL(inbuf,smb_tid); - fnum = GETFNUM(inbuf,smb_vwv0); + outsize = set_message(outbuf,11,0,True); - CHECK_FNUM(fnum,cnum); - CHECK_ERROR(fnum); + if(!fsp || (fsp->conn != conn)) { + END_PROFILE(SMBgetattrE); + return ERROR_DOS(ERRDOS,ERRbadfid); + } - /* Do an fstat on this file */ - if(fstat(Files[fnum].fd, &sbuf)) - return(UNIXERROR(ERRDOS,ERRnoaccess)); + /* Do an fstat on this file */ + if(fsp_stat(fsp, &sbuf)) { + END_PROFILE(SMBgetattrE); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } - mode = dos_mode(cnum,Files[fnum].name,&sbuf); + mode = dos_mode(conn,fsp->fsp_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); + /* + * 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,get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn)))); + 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,(uint32)sbuf.st_size); + SIVAL(outbuf,smb_vwv8,SMB_ROUNDUP(sbuf.st_size,1024)); + } + SSVAL(outbuf,smb_vwv10, mode); - DEBUG(3,("%s reply_getattrE fnum=%d cnum=%d\n",timestring(),fnum,cnum)); + DEBUG( 3, ( "reply_getattrE fnum=%d\n", fsp->fnum)); - return(outsize); + END_PROFILE(SMBgetattrE); + return(outsize); } - - - - - diff --git a/source3/smbd/sec_ctx.c b/source3/smbd/sec_ctx.c new file mode 100644 index 0000000000..87bf8b1744 --- /dev/null +++ b/source3/smbd/sec_ctx.c @@ -0,0 +1,424 @@ +/* + Unix SMB/CIFS implementation. + uid/user handling + Copyright (C) Tim Potter 2000 + + 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" + +extern struct current_user current_user; + +struct sec_ctx { + uid_t uid; + uid_t gid; + int ngroups; + gid_t *groups; + NT_USER_TOKEN *token; +}; + +/* A stack of security contexts. We include the current context as being + the first one, so there is room for another MAX_SEC_CTX_DEPTH more. */ + +static struct sec_ctx sec_ctx_stack[MAX_SEC_CTX_DEPTH + 1]; +static int sec_ctx_stack_ndx; + +/**************************************************************************** + Become the specified uid. +****************************************************************************/ + +static BOOL become_uid(uid_t uid) +{ + /* Check for dodgy uid values */ + + if (uid == (uid_t)-1 || + ((sizeof(uid_t) == 2) && (uid == (uid_t)65535))) { + static int done; + + if (!done) { + DEBUG(1,("WARNING: using uid %d is a security risk\n", + (int)uid)); + done = 1; + } + } + + /* Set effective user id */ + + set_effective_uid(uid); + + DO_PROFILE_INC(uid_changes); + return True; +} + +/**************************************************************************** + Become the specified gid. +****************************************************************************/ + +static BOOL become_gid(gid_t gid) +{ + /* Check for dodgy gid values */ + + if (gid == (gid_t)-1 || ((sizeof(gid_t) == 2) && + (gid == (gid_t)65535))) { + static int done; + + if (!done) { + DEBUG(1,("WARNING: using gid %d is a security risk\n", + (int)gid)); + done = 1; + } + } + + /* Set effective group id */ + + set_effective_gid(gid); + return True; +} + +/**************************************************************************** + Become the specified uid and gid. +****************************************************************************/ + +static BOOL become_id(uid_t uid, gid_t gid) +{ + return become_gid(gid) && become_uid(uid); +} + +/**************************************************************************** + Drop back to root privileges in order to change to another user. +****************************************************************************/ + +static void gain_root(void) +{ + if (non_root_mode()) { + return; + } + + if (geteuid() != 0) { + set_effective_uid(0); + + if (geteuid() != 0) { + DEBUG(0, + ("Warning: You appear to have a trapdoor " + "uid system\n")); + } + } + + if (getegid() != 0) { + set_effective_gid(0); + + if (getegid() != 0) { + DEBUG(0, + ("Warning: You appear to have a trapdoor " + "gid system\n")); + } + } +} + +/**************************************************************************** + Get the list of current groups. +****************************************************************************/ + +int get_current_groups(int *p_ngroups, gid_t **p_groups) +{ + int i; + gid_t grp; + int ngroups = sys_getgroups(0,&grp); + gid_t *groups; + + (*p_ngroups) = 0; + (*p_groups) = NULL; + + if (ngroups <= 0) + return -1; + + if((groups = (gid_t *)malloc(sizeof(gid_t)*ngroups)) == NULL) { + DEBUG(0,("setup_groups malloc fail !\n")); + return -1; + } + + if ((ngroups = sys_getgroups(ngroups,groups)) == -1) { + SAFE_FREE(groups); + return -1; + } + + (*p_ngroups) = ngroups; + (*p_groups) = groups; + + DEBUG( 3, ( "get_current_groups: user is in %u groups: ", ngroups)); + for (i = 0; i < ngroups; i++ ) { + DEBUG( 3, ( "%s%d", (i ? ", " : ""), (int)groups[i] ) ); + } + DEBUG( 3, ( "\n" ) ); + + return ngroups; +} + +/**************************************************************************** + Initialize the groups a user belongs to. +****************************************************************************/ + +BOOL initialise_groups(char *user, uid_t uid, gid_t gid) +{ + struct sec_ctx *prev_ctx_p; + BOOL result = True; + + if (non_root_mode()) { + return True; + } + + become_root(); + + /* Call initgroups() to get user groups */ + + if (initgroups(user,gid) == -1) { + DEBUG(0,("Unable to initgroups. Error was %s\n", strerror(errno) )); + if (getuid() == 0) { + if (gid < 0 || gid > 32767 || uid < 0 || uid > 32767) { + DEBUG(0,("This is probably a problem with the account %s\n", user)); + } + } + result = False; + goto done; + } + + /* Store groups in previous user's security context. This will + always work as the become_root() call increments the stack + pointer. */ + + prev_ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx - 1]; + + SAFE_FREE(prev_ctx_p->groups); + prev_ctx_p->ngroups = 0; + + get_current_groups(&prev_ctx_p->ngroups, &prev_ctx_p->groups); + + done: + unbecome_root(); + + return result; +} + +/**************************************************************************** + Create a new security context on the stack. It is the same as the old + one. User changes are done using the set_sec_ctx() function. +****************************************************************************/ + +BOOL push_sec_ctx(void) +{ + struct sec_ctx *ctx_p; + + /* Check we don't overflow our stack */ + + if (sec_ctx_stack_ndx == MAX_SEC_CTX_DEPTH) { + DEBUG(0, ("Security context stack overflow!\n")); + smb_panic("Security context stack overflow!\n"); + } + + /* Store previous user context */ + + sec_ctx_stack_ndx++; + + ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx]; + + ctx_p->uid = geteuid(); + ctx_p->gid = getegid(); + + DEBUG(3, ("push_sec_ctx(%u, %u) : sec_ctx_stack_ndx = %d\n", + (unsigned int)ctx_p->uid, (unsigned int)ctx_p->gid, sec_ctx_stack_ndx )); + + ctx_p->token = dup_nt_token(sec_ctx_stack[sec_ctx_stack_ndx-1].token); + + ctx_p->ngroups = sys_getgroups(0, NULL); + + if (ctx_p->ngroups != 0) { + if (!(ctx_p->groups = malloc(ctx_p->ngroups * sizeof(gid_t)))) { + DEBUG(0, ("Out of memory in push_sec_ctx()\n")); + delete_nt_token(&ctx_p->token); + return False; + } + + sys_getgroups(ctx_p->ngroups, ctx_p->groups); + } else { + ctx_p->groups = NULL; + } + + return True; +} + +/**************************************************************************** + Set the current security context to a given user. +****************************************************************************/ + +void set_sec_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups, NT_USER_TOKEN *token) +{ + struct sec_ctx *ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx]; + + /* Set the security context */ + + DEBUG(3, ("setting sec ctx (%u, %u) - sec_ctx_stack_ndx = %d\n", + (unsigned int)uid, (unsigned int)gid, sec_ctx_stack_ndx)); + + if (ngroups) { + int i; + + DEBUG(3, ("%d user groups: \n", ngroups)); + for (i = 0; i < ngroups; i++) { + DEBUGADD(3, ("%u ", (unsigned int)groups[i])); + } + + DEBUG(3, ("\n")); + } + + + gain_root(); + +#ifdef HAVE_SETGROUPS + sys_setgroups(ngroups, groups); +#endif + + ctx_p->ngroups = ngroups; + + SAFE_FREE(ctx_p->groups); + if (token && (token == ctx_p->token)) + smb_panic("DUPLICATE_TOKEN"); + + delete_nt_token(&ctx_p->token); + + ctx_p->groups = memdup(groups, sizeof(gid_t) * ngroups); + ctx_p->token = dup_nt_token(token); + + become_id(uid, gid); + + ctx_p->uid = uid; + ctx_p->gid = gid; + + /* Update current_user stuff */ + + current_user.uid = uid; + current_user.gid = gid; + current_user.ngroups = ngroups; + current_user.groups = groups; + current_user.nt_user_token = ctx_p->token; +} + +/**************************************************************************** + Become root context. +****************************************************************************/ + +void set_root_sec_ctx(void) +{ + /* May need to worry about supplementary groups at some stage */ + + set_sec_ctx(0, 0, 0, NULL, NULL); +} + +/**************************************************************************** + Pop a security context from the stack. +****************************************************************************/ + +BOOL pop_sec_ctx(void) +{ + struct sec_ctx *ctx_p; + struct sec_ctx *prev_ctx_p; + + /* Check for stack underflow */ + + if (sec_ctx_stack_ndx == 0) { + DEBUG(0, ("Security context stack underflow!\n")); + smb_panic("Security context stack underflow!\n"); + } + + ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx]; + + /* Clear previous user info */ + + ctx_p->uid = (uid_t)-1; + ctx_p->gid = (gid_t)-1; + + SAFE_FREE(ctx_p->groups); + ctx_p->ngroups = 0; + + delete_nt_token(&ctx_p->token); + + /* Pop back previous user */ + + sec_ctx_stack_ndx--; + + gain_root(); + + prev_ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx]; + +#ifdef HAVE_SETGROUPS + sys_setgroups(prev_ctx_p->ngroups, prev_ctx_p->groups); +#endif + + become_id(prev_ctx_p->uid, prev_ctx_p->gid); + + /* Update current_user stuff */ + + current_user.uid = prev_ctx_p->uid; + current_user.gid = prev_ctx_p->gid; + current_user.ngroups = prev_ctx_p->ngroups; + current_user.groups = prev_ctx_p->groups; + current_user.nt_user_token = prev_ctx_p->token; + + DEBUG(3, ("pop_sec_ctx (%u, %u) - sec_ctx_stack_ndx = %d\n", + (unsigned int)geteuid(), (unsigned int)getegid(), sec_ctx_stack_ndx)); + + return True; +} + +/* Initialise the security context system */ + +void init_sec_ctx(void) +{ + int i; + struct sec_ctx *ctx_p; + + /* Initialise security context stack */ + + memset(sec_ctx_stack, 0, sizeof(struct sec_ctx) * MAX_SEC_CTX_DEPTH); + + for (i = 0; i < MAX_SEC_CTX_DEPTH; i++) { + sec_ctx_stack[i].uid = (uid_t)-1; + sec_ctx_stack[i].gid = (gid_t)-1; + } + + /* Initialise first level of stack. It is the current context */ + ctx_p = &sec_ctx_stack[0]; + + ctx_p->uid = geteuid(); + ctx_p->gid = getegid(); + + get_current_groups(&ctx_p->ngroups, &ctx_p->groups); + + ctx_p->token = NULL; /* Maps to guest user. */ + + /* Initialise current_user global */ + + current_user.uid = ctx_p->uid; + current_user.gid = ctx_p->gid; + current_user.ngroups = ctx_p->ngroups; + current_user.groups = ctx_p->groups; + + /* The conn and vuid are usually taken care of by other modules. + We initialise them here. */ + + current_user.conn = NULL; + current_user.vuid = UID_FIELD_INVALID; + current_user.nt_user_token = NULL; +} diff --git a/source3/smbd/server.c b/source3/smbd/server.c index 5d8facef33..c759f56e0c 100644 --- a/source3/smbd/server.c +++ b/source3/smbd/server.c @@ -1,8 +1,8 @@ /* - Unix SMB/Netbios implementation. - Version 1.9. + Unix SMB/CIFS implementation. Main SMB server routines - Copyright (C) Andrew Tridgell 1992-1995 + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Martin Pool 2002 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 @@ -20,29 +20,11 @@ */ #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; +extern fstring global_myworkgroup; +extern pstring global_myname; -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; +int am_parent = 1; /* the last message the was processed */ int last_message = -1; @@ -50,4251 +32,869 @@ 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; +#ifdef WITH_DFS +extern int dcelogin_atmost_once; +#endif /* WITH_DFS */ extern fstring remote_machine; +/* really we should have a top level context structure that has the + client file descriptor as an element. That would require a major rewrite :( -/* 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; + the following 2 functions are an alternative - they make the file + descriptor private to smbd + */ +static int server_fd = -1; -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) +int smbd_server_fd(void) { - 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); + return server_fd; } +void smbd_set_server_fd(int fd) +{ + server_fd = fd; + client_setfd(fd); +} /**************************************************************************** - change a unix mode to a dos mode + Terminate signal. ****************************************************************************/ -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; +VOLATILE sig_atomic_t got_sig_term = 0; - 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) +static void sig_term(void) { - 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)); + got_sig_term = 1; + sys_select_signal(); } - /**************************************************************************** -check if two filenames are equal - -this needs to be careful about whether we are case sensitive + Catch a sighup. ****************************************************************************/ -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)); -} +VOLATILE sig_atomic_t reload_after_sighup = 0; -/**************************************************************************** -mangle the 2nd name and check if it is then equal to the first name -****************************************************************************/ -static BOOL mangled_equal(char *name1, char *name2) +static void sig_hup(int sig) { - pstring tmpname; - - if (is_8_3(name2)) - return(False); - - strcpy(tmpname,name2); - mangle_name_83(tmpname); - - return(strequal(name1,tmpname)); + reload_after_sighup = 1; + sys_select_signal(); } - /**************************************************************************** -scan a directory to find a filename, matching without case sensitivity + Send a SIGTERM to our process group. +*****************************************************************************/ -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) +static void killkids(void) { - 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); + if(am_parent) kill(0,SIGTERM); } /**************************************************************************** -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 + Process a sam sync message - not sure whether to do this here or + somewhere else. ****************************************************************************/ -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) +static void msg_sam_sync(int UNUSED(msg_type), pid_t UNUSED(pid), + void *UNUSED(buf), size_t UNUSED(len)) { - 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); + DEBUG(10, ("** sam sync message received, ignoring\n")); } -#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 + Process a sam sync replicate message - not sure whether to do this here or + somewhere else. ****************************************************************************/ -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) +static void msg_sam_repl(int msg_type, pid_t pid, void *buf, size_t len) { - 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 - + uint32 low_serial; - /* 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 + if (len != sizeof(uint32)) + return; -/* handle rediculous bsize values - some OSes are broken */ -if ((*bsize) < 512 || (*bsize)>0xFFFF) *bsize = 1024; + low_serial = *((uint32 *)buf); - 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 + DEBUG(3, ("received sam replication message, serial = 0x%04x\n", + low_serial)); } - /**************************************************************************** -wrap it to get filenames right + Open the socket communication - inetd. ****************************************************************************/ -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) +static BOOL open_sockets_inetd(void) { - 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)); + /* Started from inetd. fd 0 is the socket. */ + /* We will abort gracefully when the client or remote system + goes away */ + smbd_set_server_fd(dup(0)); + + /* close our standard file descriptors */ + close_low_fds(); + + set_socket_options(smbd_server_fd(),"SO_KEEPALIVE"); + set_socket_options(smbd_server_fd(), user_socket_options); - return(ret); + return True; } -/**************************************************************************** -check a filename - possibly caling reducename -****************************************************************************/ -static void check_for_pipe(char *fname) +static void msg_exit_server(int msg_type, pid_t src, void *buf, size_t len) { - /* 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; - } + exit_server("Got a SHUTDOWN message"); } /**************************************************************************** -open a file + Open the socket communication. ****************************************************************************/ -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]; +static BOOL open_sockets(BOOL is_daemon,int port) +{ + int num_interfaces = iface_count(); + int fd_listenset[FD_SETSIZE]; + fd_set listen_set; + int s; + int i; - p[max_len] = '\0'; - if ((Files[fnum].fd = sys_open(fname,flags,mode)) == -1) - p[max_len] = tmp; + if (!is_daemon) { + return open_sockets_inetd(); } - } -#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) + +#ifdef HAVE_ATEXIT { - DEBUG(3,("Failed to mmap() %s - %s\n",fname,strerror(errno))); - Files[fnum].mmap_ptr = NULL; + static int atexit_set; + if(atexit_set == 0) { + atexit_set=1; + atexit(killkids); + } } - } -#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); + /* Stop zombies */ + CatchChild(); + + + FD_ZERO(&listen_set); + + if(lp_interfaces() && lp_bind_interfaces_only()) { + /* We have been given an interfaces line, and been + told to only bind to those interfaces. Create a + socket per interface and bind to only these. + */ + + if(num_interfaces > FD_SETSIZE) { + DEBUG(0,("open_sockets: Too many interfaces specified to bind to. Number was %d \ +max can be %d\n", + num_interfaces, FD_SETSIZE)); + return False; + } + + /* Now open a listen socket for each of the + interfaces. */ + for(i = 0; i < num_interfaces; i++) { + struct in_addr *ifip = iface_n_ip(i); + + if(ifip == NULL) { + DEBUG(0,("open_sockets: interface %d has NULL IP address !\n", i)); + continue; + } + s = fd_listenset[i] = open_socket_in(SOCK_STREAM, port, 0, ifip->s_addr, True); + if(s == -1) + return False; + + /* ready to listen */ + set_socket_options(s,"SO_KEEPALIVE"); + set_socket_options(s,user_socket_options); + + if (listen(s, 5) == -1) { + DEBUG(0,("listen: %s\n",strerror(errno))); + close(s); + return False; + } + FD_SET(s,&listen_set); + } + } else { + /* Just bind to 0.0.0.0 - accept connections + from anywhere. */ + num_interfaces = 1; + + /* open an incoming socket */ + s = open_socket_in(SOCK_STREAM, port, 0, + interpret_addr(lp_socket_address()),True); + if (s == -1) + return(False); + + /* ready to listen */ + set_socket_options(s,"SO_KEEPALIVE"); + set_socket_options(s,user_socket_options); + + if (listen(s, 5) == -1) { + DEBUG(0,("open_sockets: listen: %s\n", + strerror(errno))); + close(s); + return False; + } + + fd_listenset[0] = s; + FD_SET(s,&listen_set); + } + + /* Listen to messages */ + + message_register(MSG_SMB_SAM_SYNC, msg_sam_sync); + message_register(MSG_SMB_SAM_REPL, msg_sam_repl); + message_register(MSG_SHUTDOWN, msg_exit_server); + + /* now accept incoming connections - forking a new process + for each incoming connection */ + DEBUG(2,("waiting for a connection\n")); + while (1) { + fd_set lfds; + int num; + + /* Free up temporary memory from the main smbd. */ + lp_talloc_free(); + + /* Ensure we respond to PING and DEBUG messages from the main smbd. */ + message_dispatch(); + + memcpy((char *)&lfds, (char *)&listen_set, + sizeof(listen_set)); + + num = sys_select(FD_SETSIZE,&lfds,NULL,NULL,NULL); + + if (num == -1 && errno == EINTR) { + if (got_sig_term) { + exit_server("Caught TERM signal"); + } + + /* check for sighup processing */ + if (reload_after_sighup) { + change_to_root_user(); + DEBUG(1,("Reloading services after SIGHUP\n")); + reload_services(False); + reload_after_sighup = 0; + } + + continue; + } + + /* check if we need to reload services */ + check_reload(time(NULL)); + + /* Find the sockets that are read-ready - + accept on these. */ + for( ; num > 0; num--) { + struct sockaddr addr; + socklen_t in_addrlen = sizeof(addr); + + s = -1; + for(i = 0; i < num_interfaces; i++) { + if(FD_ISSET(fd_listenset[i],&lfds)) { + s = fd_listenset[i]; + /* Clear this so we don't look + at it again. */ + FD_CLR(fd_listenset[i],&lfds); + break; + } + } + + smbd_set_server_fd(accept(s,&addr,&in_addrlen)); + + if (smbd_server_fd() == -1 && errno == EINTR) + continue; + + if (smbd_server_fd() == -1) { + DEBUG(0,("open_sockets: accept: %s\n", + strerror(errno))); + continue; + } + + if (smbd_server_fd() != -1 && sys_fork()==0) { + /* Child code ... */ + + /* close the listening socket(s) */ + for(i = 0; i < num_interfaces; i++) + close(fd_listenset[i]); + + /* close our standard file + descriptors */ + close_low_fds(); + am_parent = 0; + + set_socket_options(smbd_server_fd(),"SO_KEEPALIVE"); + set_socket_options(smbd_server_fd(),user_socket_options); + + /* Reset global variables in util.c so + that client substitutions will be + done correctly in the process. */ + reset_globals_after_fork(); + + /* tdb needs special fork handling */ + tdb_reopen_all(); + + return True; + } + /* The parent doesn't need this socket */ + close(smbd_server_fd()); + + /* Sun May 6 18:56:14 2001 ackley@cs.unm.edu: + Clear the closed fd info out of server_fd -- + and more importantly, out of client_fd in + util_sock.c, to avoid a possible + getpeername failure if we reopen the logs + and use %I in the filename. + */ + + smbd_set_server_fd(-1); + + /* Force parent to check log size after + * spawning child. Fix from + * klausr@ITAP.Physik.Uni-Stuttgart.De. The + * parent smbd will log to logserver.smb. It + * writes only two messages for each child + * started/finished. But each child writes, + * say, 50 messages also in logserver.smb, + * begining with the debug_count of the + * parent, before the child opens its own log + * file logserver.client. In a worst case + * scenario the size of logserver.smb would be + * checked after about 50*50=2500 messages + * (ca. 100kb). + * */ + force_check_log_size(); - if (share_mode == DENY_DOS) - return(pid == getpid()); + } /* end for num */ + } /* end while 1 */ - /* XXXX exactly what share mode combinations should be allowed for - deleting/renaming? */ - return(False); +/* NOTREACHED return True; */ } /**************************************************************************** - 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); - } -} + Reload the services file. +**************************************************************************/ - -/**************************************************************************** -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) +BOOL reload_services(BOOL test) { - 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; + BOOL ret; - if (access_allowed == AWRITE) - flags = O_WRONLY; - } - } - } - - DEBUG(4,("calling open_file with flags=0x%X flags2=0x%X mode=0%o\n", - flags,flags2,mode)); - - open_file(fnum,cnum,fname,flags|(flags2&~(O_TRUNC)),mode); - if (!Files[fnum].open && flags==O_RDWR && errno!=ENOENT && fcbopen) { - flags = O_RDONLY; - open_file(fnum,cnum,fname,flags,mode); - } - - if (Files[fnum].open) { - int open_mode=0; - switch (flags) { - case O_RDONLY: - open_mode = 0; - break; - case O_RDWR: - open_mode = 2; - break; - case O_WRONLY: - open_mode = 1; - break; - } - - Files[fnum].share_mode = (deny_mode<<4) | open_mode; - Files[fnum].share_pending = True; - - if (Access) { - (*Access) = open_mode; - } - - if (action) { - if (file_existed && !(flags2 & O_TRUNC)) *action = 1; - if (!file_existed) *action = 2; - if (file_existed && (flags2 & O_TRUNC)) *action = 3; - } - - if (!share_pid) - share_mode_pending = True; - - if ((flags2&O_TRUNC) && file_existed) - truncate_unless_locked(fnum,cnum); - } -} - - - -/******************************************************************* -check for files that we should now set our share modes on -********************************************************************/ -static void check_share_modes(void) -{ - int i; - for (i=0;i<MAX_OPEN_FILES;i++) - if(Files[i].open && Files[i].share_pending) { - if (lp_share_modes(SNUM(Files[i].cnum))) { - int pid=0; - get_share_mode_by_fnum(Files[i].cnum,i,&pid); - if (!pid) { - set_share_mode(i,Files[i].share_mode); - Files[i].share_pending = False; - } - } else { - Files[i].share_pending = False; - } - } -} - - -/**************************************************************************** -seek a file. Try to avoid the seek if possible -****************************************************************************/ -int seek_file(int fnum,int pos) -{ - int offset = 0; - if (Files[fnum].print_file && POSTSCRIPT(Files[fnum].cnum)) - offset = 3; - - Files[fnum].pos = lseek(Files[fnum].fd,pos+offset,SEEK_SET) - offset; - return(Files[fnum].pos); -} - -/**************************************************************************** -read from a file -****************************************************************************/ -int read_file(int fnum,char *data,int pos,int mincnt,int maxcnt,int timeout,BOOL exact) -{ - int ret=0; - - if (!Files[fnum].can_write) - { - ret = read_predict(Files[fnum].fd, - pos, - data, - NULL, - maxcnt); - - data += ret; - maxcnt -= ret; - mincnt = MAX(mincnt-ret,0); - pos += ret; - } - -#if USE_MMAP - if (Files[fnum].mmap_ptr) - { - int num = MIN(maxcnt,Files[fnum].mmap_size-pos); - if (num > 0) - { - memcpy(data,Files[fnum].mmap_ptr+pos,num); - data += num; - pos += num; - maxcnt -= num; - mincnt = MAX(mincnt-num,0); - ret += num; + if (lp_loaded()) { + pstring fname; + pstrcpy(fname,lp_configfile()); + if (file_exist(fname, NULL) && + !strcsequal(fname, dyn_CONFIGFILE)) { + pstrcpy(dyn_CONFIGFILE, fname); + test = False; + } } - } -#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 + reopen_logs(); -#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); -} + if (test && !lp_file_list_changed()) + return(True); + lp_killunused(conn_snum_used); + + ret = lp_load(dyn_CONFIGFILE, False, False, True); -/**************************************************************************** - become the specified uid and gid -****************************************************************************/ -static BOOL become_id(int uid,int gid) -{ - return(become_gid(gid) && become_uid(uid)); -} + load_printers(); -/**************************************************************************** -become the guest user -****************************************************************************/ -static BOOL become_guest(void) -{ - BOOL ret; - static struct passwd *pass=NULL; + /* perhaps the config filename is now set */ + if (!test) + reload_services(True); - if (initial_uid != 0) - return(True); + reopen_logs(); - if (!pass) - pass = Get_Pwnam(lp_guestaccount(-1),True); - if (!pass) return(False); + load_interfaces(); - ret = become_id(pass->pw_uid,pass->pw_gid); + { + if (smbd_server_fd() != -1) { + set_socket_options(smbd_server_fd(),"SO_KEEPALIVE"); + set_socket_options(smbd_server_fd(), user_socket_options); + } + } - if (!ret) - DEBUG(1,("Failed to become guest. Invalid guest account?\n")); + mangle_reset_cache(); + reset_stat_cache(); - last_user.cnum = -2; + /* this forces service parameters to be flushed */ + set_current_service(NULL,True); - return(ret); + return(ret); } +#if DUMP_CORE /******************************************************************* -check if a username is OK +prepare to dump a core file - carefully! ********************************************************************/ -static BOOL check_user_ok(int cnum,user_struct *vuser,int snum) -{ - int i; - for (i=0;i<Connections[cnum].uid_cache.entries;i++) - if (Connections[cnum].uid_cache.list[i] == vuser->uid) return(True); - - if (!user_ok(vuser->name,snum)) return(False); - - i = Connections[cnum].uid_cache.entries % UID_CACHE_SIZE; - Connections[cnum].uid_cache.list[i] = vuser->uid; - - if (Connections[cnum].uid_cache.entries < UID_CACHE_SIZE) - Connections[cnum].uid_cache.entries++; - - return(True); -} - - -/**************************************************************************** - become the user of a connection number -****************************************************************************/ -BOOL become_user(int cnum, int uid) -{ - int new_umask; - user_struct *vuser; - int snum,gid; - int ngroups; - gid_t *groups; - - if (last_user.cnum == cnum && last_user.uid == uid) { - DEBUG(4,("Skipping become_user - already user\n")); - return(True); - } - - unbecome_user(); - - if (!OPEN_CNUM(cnum)) { - DEBUG(2,("Connection %d not open\n",cnum)); - return(False); - } - - snum = Connections[cnum].service; - - if (Connections[cnum].force_user || - lp_security() == SEC_SHARE || - !(vuser = get_valid_user_struct(uid)) || - !check_user_ok(cnum,vuser,snum)) { - uid = Connections[cnum].uid; - gid = Connections[cnum].gid; - groups = Connections[cnum].groups; - ngroups = Connections[cnum].ngroups; - } else { - if (!vuser) { - DEBUG(2,("Invalid vuid used %d\n",uid)); - return(False); - } - uid = vuser->uid; - if(!*lp_force_group(snum)) - gid = vuser->gid; - else - gid = Connections[cnum].gid; - groups = vuser->user_groups; - ngroups = vuser->user_ngroups; - } - - if (initial_uid == 0) - { - if (!become_gid(gid)) return(False); - -#ifndef NO_SETGROUPS - if (!IS_IPC(cnum)) { - /* groups stuff added by ih/wreu */ - if (ngroups > 0) - if (setgroups(ngroups,groups)<0) - DEBUG(0,("setgroups call failed!\n")); - } -#endif - - if (!Connections[cnum].admin_user && !become_uid(uid)) - return(False); - } - - new_umask = 0777 & ~CREATE_MODE(cnum); - old_umask = umask(new_umask); - - last_user.cnum = cnum; - last_user.uid = uid; - - DEBUG(5,("become_user uid=(%d,%d) gid=(%d,%d) new_umask=0%o\n", - getuid(),geteuid(),getgid(),getegid(),new_umask)); - - return(True); -} - -/**************************************************************************** - unbecome the user of a connection number -****************************************************************************/ -BOOL unbecome_user(void ) +static BOOL dump_core(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); + char *p; + pstring dname; + + pstrcpy(dname,lp_logfile()); + if ((p=strrchr_m(dname,'/'))) *p=0; + pstrcat(dname,"/corefiles"); + mkdir(dname,0700); + sys_chown(dname,getuid(),getgid()); + chmod(dname,0700); + if (chdir(dname)) return(False); + umask(~(0700)); + +#ifdef HAVE_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", + (int)rlp.rlim_cur,(int)rlp.rlim_max)); + } #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); + DEBUG(0,("Dumping core in %s\n", dname)); + abort(); + return(True); } +#endif /**************************************************************************** - find a service entry +update the current smbd process count ****************************************************************************/ -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) +static void decrement_smbd_process_count(void) { - write_bmpx_struct *wbmpx = Files[fnum].wbmpx_ptr; + int32 total_smbds; - 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++; + if (lp_max_smbd_processes()) { + total_smbds = 0; + tdb_change_int32_atomic(conn_tdb_ctx(), "INFO/total_smbds", &total_smbds, -1); } - } - - return(error_packet(inbuf,outbuf,eclass,ecode,line)); } - /**************************************************************************** - create an error packet. Normally called using the ERROR() macro + Exit the server. ****************************************************************************/ -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() +void exit_server(char *reason) { - 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 + static int firsttime=1; + extern char *last_inbuf; + extern struct auth_context *negprot_global_auth_context; - /* 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 + if (!firsttime) + exit(0); + firsttime = 0; -#ifndef USE_WAITPID - while (wait3(WAIT3_CAST1 NULL, WNOHANG, WAIT3_CAST2 NULL) > 0); -#endif - depth--; - BlockSignals(False); - return 0; -} -#endif + change_to_root_user(); + DEBUG(2,("Closing connections\n")); -/**************************************************************************** - 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 + if (negprot_global_auth_context) { + (negprot_global_auth_context->free)(&negprot_global_auth_context); + } - /* open an incoming socket */ - s = open_socket_in(SOCK_STREAM, port, 0); - if (s == -1) - return(False); + conn_close_all(); - /* 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); + invalidate_all_vuids(); - if (Client == -1 && errno == EINTR) - continue; + /* delete our entry in the connections database. */ + yield_connection(NULL,""); - if (Client == -1) - { - DEBUG(0,("accept: %s",strerror(errno))); - return False; - } + respond_to_all_remaining_local_messages(); + decrement_smbd_process_count(); -#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 +#ifdef WITH_DFS + if (dcelogin_atmost_once) { + dfs_unlogin(); } - } - 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(); + if (!reason) { + int oldlevel = DEBUGLEVEL; + DEBUGLEVEL = 10; + DEBUG(0,("Last message was %s\n",smb_fn_name(last_message))); + if (last_inbuf) + show_msg(last_inbuf); + DEBUGLEVEL = oldlevel; + DEBUG(0,("===============================================================\n")); +#if DUMP_CORE + if (dump_core()) return; +#endif + } - set_socket_options(Client,"SO_KEEPALIVE"); - set_socket_options(Client,user_socket_options); - } + locking_end(); - return True; + DEBUG(3,("Server exit (%s)\n", (reason ? reason : ""))); + exit(0); } - /**************************************************************************** -check if a snum is in use + Initialise connect, service and file structs. ****************************************************************************/ -BOOL snum_used(int snum) -{ - int i; - for (i=0;i<MAX_CONNECTIONS;i++) - if (OPEN_CNUM(i) && (SNUM(i) == snum)) - return(True); - return(False); -} -/**************************************************************************** - reload the services file - **************************************************************************/ -BOOL reload_services(BOOL test) +static void init_structs(void ) { - BOOL ret; + /* + * Set the machine NETBIOS name if not already + * set from the config file. + */ - if (lp_loaded()) - { - pstring fname; - strcpy(fname,lp_configfile()); - if (file_exist(fname,NULL) && !strcsequal(fname,servicesf)) - { - strcpy(servicesf,fname); - test = False; + if (!*global_myname) { + char *p; + pstrcpy( global_myname, myhostname() ); + p = strchr_m(global_myname, '.' ); + if (p) + *p = 0; } - } - reopen_logs(); + strupper(global_myname); - if (test && !lp_file_list_changed()) - return(True); + conn_init(); - lp_killunused(snum_used); + file_init(); - ret = lp_load(servicesf,False); + /* for RPC pipes */ + init_rpc_pipe_hnd(); - /* perhaps the config filename is now set */ - if (!test) - reload_services(True); + init_dptrs(); - reopen_logs(); + secrets_init(); - { - extern int Client; - if (Client != -1) { - set_socket_options(Client,"SO_KEEPALIVE"); - set_socket_options(Client,user_socket_options); - } - } - - create_mangled_stack(lp_mangledstack()); - - /* this forces service parameters to be flushed */ - become_service(-1,True); - - return(ret); -} - - - -/**************************************************************************** -this prevents zombie child processes -****************************************************************************/ -static int sig_hup() -{ - BlockSignals(True); - DEBUG(0,("Got SIGHUP\n")); - reload_services(False); -#ifndef DONT_REINSTALL_SIG - signal(SIGHUP,SIGNAL_CAST sig_hup); -#endif - BlockSignals(False); - return(0); } /**************************************************************************** -Setup the groups a user belongs to. + Usage on the program. ****************************************************************************/ -int setup_groups(char *user, int uid, int gid, int *p_ngroups, - int **p_igroups, gid_t **p_groups) -{ - if (-1 == initgroups(user,gid)) - { - if (getuid() == 0) - { - DEBUG(0,("Unable to initgroups!\n")); - if (gid < 0 || gid > 16000 || uid < 0 || uid > 16000) - DEBUG(0,("This is probably a problem with the account %s\n",user)); - } - } - else - { - int i,ngroups; - int *igroups; - gid_t grp = 0; - ngroups = getgroups(0,&grp); - if (ngroups <= 0) - ngroups = 32; - igroups = (int *)malloc(sizeof(int)*ngroups); - for (i=0;i<ngroups;i++) - igroups[i] = 0x42424242; - ngroups = getgroups(ngroups,(gid_t *)igroups); - - if (igroups[0] == 0x42424242) - ngroups = 0; - - *p_ngroups = ngroups; - - /* The following bit of code is very strange. It is due to the - fact that some OSes use int* and some use gid_t* for - getgroups, and some (like SunOS) use both, one in prototypes, - and one in man pages and the actual code. Thus we detect it - dynamically using some very ugly code */ - if (ngroups > 0) - { - /* does getgroups return ints or gid_t ?? */ - static BOOL groups_use_ints = True; - - if (groups_use_ints && - ngroups == 1 && - SVAL(igroups,2) == 0x4242) - groups_use_ints = False; - - for (i=0;groups_use_ints && i<ngroups;i++) - if (igroups[i] == 0x42424242) - groups_use_ints = False; - - if (groups_use_ints) - { - *p_igroups = igroups; - *p_groups = (gid_t *)igroups; - } - else - { - gid_t *groups = (gid_t *)igroups; - igroups = (int *)malloc(sizeof(int)*ngroups); - for (i=0;i<ngroups;i++) - igroups[i] = groups[i]; - *p_igroups = igroups; - *p_groups = (gid_t *)groups; - } - } - DEBUG(3,("%s is in %d groups\n",user,ngroups)); - for (i=0;i<ngroups;i++) - DEBUG(3,("%d ",igroups[i])); - DEBUG(3,("\n")); - } - return 0; -} -/**************************************************************************** - make a connection to a service -****************************************************************************/ -int make_connection(char *service,char *user,char *password, int pwlen, char *dev,int vuid) +static void usage(char *pname) { - int cnum; - int snum; - struct passwd *pass = NULL; - connection_struct *pcon; - BOOL guest = False; - BOOL force = False; - static BOOL first_connection = True; - - strlower(service); - - snum = find_service(service); - if (snum < 0) - { - if (strequal(service,"IPC$")) - { - DEBUG(3,("%s refusing IPC connection\n",timestring())); - return(-3); - } - - DEBUG(0,("%s couldn't find service %s\n",timestring(),service)); - return(-2); - } - - if (strequal(service,HOMES_NAME)) - { - if (*user && Get_Pwnam(user,True)) - return(make_connection(user,user,password,pwlen,dev,vuid)); - if (validated_username(vuid)) - { - strcpy(user,validated_username(vuid)); - return(make_connection(user,user,password,pwlen,dev,vuid)); - } - } - - if (!lp_snum_ok(snum) || !check_access(snum)) { - return(-4); - } - - /* you can only connect to the IPC$ service as an ipc device */ - if (strequal(service,"IPC$")) - strcpy(dev,"IPC"); - - if (*dev == '?' || !*dev) - { - if (lp_print_ok(snum)) - strcpy(dev,"LPT1:"); - else - strcpy(dev,"A:"); - } - - /* if the request is as a printer and you can't print then refuse */ - strupper(dev); - if (!lp_print_ok(snum) && (strncmp(dev,"LPT",3) == 0)) { - DEBUG(1,("Attempt to connect to non-printer as a printer\n")); - return(-6); - } - - /* lowercase the user name */ - strlower(user); - - /* add it as a possible user name */ - add_session_user(service); - - /* shall we let them in? */ - if (!authorise_login(snum,user,password,pwlen,&guest,&force,vuid)) - { - DEBUG(2,("%s invalid username/password for %s\n",timestring(),service)); - return(-1); - } - - cnum = find_free_connection(str_checksum(service) + str_checksum(user)); - if (cnum < 0) - { - DEBUG(0,("%s couldn't find free connection\n",timestring())); - return(-1); - } - - pcon = &Connections[cnum]; - bzero((char *)pcon,sizeof(*pcon)); - - /* find out some info about the user */ - pass = Get_Pwnam(user,True); - - if (pass == NULL) - { - DEBUG(0,("%s couldn't find account %s\n",timestring(),user)); - return(-7); - } - - pcon->read_only = lp_readonly(snum); - - { - pstring list; - StrnCpy(list,lp_readlist(snum),sizeof(pstring)-1); - string_sub(list,"%S",service); - - if (user_in_list(user,list)) - pcon->read_only = True; - - StrnCpy(list,lp_writelist(snum),sizeof(pstring)-1); - string_sub(list,"%S",service); - - if (user_in_list(user,list)) - pcon->read_only = False; - } - - /* admin user check */ - if (user_in_list(user,lp_admin_users(snum)) && - !pcon->read_only) - { - pcon->admin_user = True; - DEBUG(0,("%s logged in as admin user (root privileges)\n",user)); - } - else - pcon->admin_user = False; - - pcon->force_user = force; - pcon->uid = pass->pw_uid; - pcon->gid = pass->pw_gid; - pcon->num_files_open = 0; - pcon->lastused = time(NULL); - pcon->service = snum; - pcon->used = True; - pcon->printer = (strncmp(dev,"LPT",3) == 0); - pcon->ipc = (strncmp(dev,"IPC",3) == 0); - pcon->dirptr = NULL; - string_set(&pcon->dirpath,""); - string_set(&pcon->user,user); - -#if HAVE_GETGRNAM - if (*lp_force_group(snum)) - { - struct group *gptr = (struct group *)getgrnam(lp_force_group(snum)); - if (gptr) - { - pcon->gid = gptr->gr_gid; - DEBUG(3,("Forced group %s\n",lp_force_group(snum))); - } - else - DEBUG(1,("Couldn't find group %s\n",lp_force_group(snum))); - } -#endif - - if (*lp_force_user(snum)) - { - struct passwd *pass2; - fstring fuser; - strcpy(fuser,lp_force_user(snum)); - pass2 = (struct passwd *)Get_Pwnam(fuser,True); - if (pass2) - { - pcon->uid = pass2->pw_uid; - string_set(&pcon->user,fuser); - strcpy(user,fuser); - pcon->force_user = True; - DEBUG(3,("Forced user %s\n",fuser)); - } - else - DEBUG(1,("Couldn't find user %s\n",fuser)); - } - - { - pstring s; - strcpy(s,lp_pathname(snum)); - standard_sub(cnum,s); - string_set(&pcon->connectpath,s); - DEBUG(3,("Connect path is %s\n",s)); - } - - /* groups stuff added by ih */ - pcon->ngroups = 0; - pcon->groups = NULL; - - if (!IS_IPC(cnum)) - { - /* Find all the groups this uid is in and store them. Used by become_user() */ - setup_groups(pcon->user,pcon->uid,pcon->gid,&pcon->ngroups,&pcon->igroups,&pcon->groups); - - /* check number of connections */ - if (!claim_connection(cnum, - lp_servicename(SNUM(cnum)), - lp_max_connections(SNUM(cnum)),False)) - { - DEBUG(1,("too many connections - rejected\n")); - return(-8); - } - - if (lp_status(SNUM(cnum))) - claim_connection(cnum,"STATUS.",MAXSTATUS,first_connection); - - first_connection = False; - } /* IS_IPC */ - - pcon->open = True; - - /* execute any "root preexec = " line */ - if (*lp_rootpreexec(SNUM(cnum))) - { - pstring cmd; - strcpy(cmd,lp_rootpreexec(SNUM(cnum))); - standard_sub(cnum,cmd); - DEBUG(5,("cmd=%s\n",cmd)); - smbrun(cmd,NULL); - } - - if (!become_user(cnum,pcon->uid)) - { - DEBUG(0,("Can't become connected user!\n")); - pcon->open = False; - if (!IS_IPC(cnum)) { - yield_connection(cnum, - lp_servicename(SNUM(cnum)), - lp_max_connections(SNUM(cnum))); - if (lp_status(SNUM(cnum))) yield_connection(cnum,"STATUS.",MAXSTATUS); - } - return(-1); - } - - if (ChDir(pcon->connectpath) != 0) - { - DEBUG(0,("Can't change directory to %s\n",pcon->connectpath)); - pcon->open = False; - unbecome_user(); - if (!IS_IPC(cnum)) { - yield_connection(cnum, - lp_servicename(SNUM(cnum)), - lp_max_connections(SNUM(cnum))); - if (lp_status(SNUM(cnum))) yield_connection(cnum,"STATUS.",MAXSTATUS); - } - return(-5); - } - - string_set(&pcon->origpath,pcon->connectpath); - -#if SOFTLINK_OPTIMISATION - /* resolve any soft links early */ - { - pstring s; - strcpy(s,pcon->connectpath); - GetWd(s); - string_set(&pcon->connectpath,s); - ChDir(pcon->connectpath); - } -#endif - - num_connections_open++; - add_session_user(user); - - /* execute any "preexec = " line */ - if (*lp_preexec(SNUM(cnum))) - { - pstring cmd; - strcpy(cmd,lp_preexec(SNUM(cnum))); - standard_sub(cnum,cmd); - smbrun(cmd,NULL); - } - - /* we've finished with the sensitive stuff */ - unbecome_user(); - - { - extern struct from_host Client_info; - DEBUG(IS_IPC(cnum)?3:1,("%s %s (%s) connect to service %s as user %s (uid=%d,gid=%d) (pid %d)\n", - timestring(), - Client_info.name,Client_info.addr, - lp_servicename(SNUM(cnum)),user, - pcon->uid, - pcon->gid, - (int)getpid())); - } - - return(cnum); + d_printf("Usage: %s [-DaioPh?Vb] [-d debuglevel] [-l log basename] [-p port]\n", pname); + d_printf(" [-O socket options] [-s services file]\n"); + d_printf("\t-D Become a daemon (default)\n"); + d_printf("\t-a Append to log file (default)\n"); + d_printf("\t-i Run interactive (not a daemon)\n" ); + d_printf("\t-o Overwrite log file, don't append\n"); + d_printf("\t-h Print usage\n"); + d_printf("\t-? Print usage\n"); + d_printf("\t-V Print version\n"); + d_printf("\t-b Print build options\n"); + d_printf("\t-d debuglevel Set the debuglevel\n"); + d_printf("\t-l log basename. Basename for log/debug files\n"); + d_printf("\t-p port Listen on the specified port\n"); + d_printf("\t-O socket options Socket options\n"); + d_printf("\t-s services file. Filename of services file\n"); + d_printf("\n"); } - /**************************************************************************** - find first available file slot + main program. ****************************************************************************/ -int find_free_file(void ) -{ - int i; - for (i=1;i<MAX_OPEN_FILES;i++) - if (!Files[i].open) - return(i); - DEBUG(1,("ERROR! Out of file structures - perhaps increase MAX_OPEN_FILES?\n")); - return(-1); -} -/**************************************************************************** - find first available connection slot, starting from a random position. -The randomisation stops problems with the server dieing and clients -thinking the server is still available. -****************************************************************************/ -static int find_free_connection(int hash ) + int main(int argc,char *argv[]) { - int i; - BOOL used=False; - hash = (hash % (MAX_CONNECTIONS-2))+1; + extern BOOL append_log; + extern BOOL AllowDebugChange; + extern char *optarg; + /* shall I run as a daemon */ + BOOL is_daemon = False; + BOOL interactive = False; + BOOL specified_logfile = False; + int port = SMB_PORT; + int opt; + pstring logfile; - again: +#ifdef HAVE_SET_AUTH_PARAMETERS + set_auth_parameters(argc,argv); +#endif - for (i=hash+1;i!=hash;) - { - if (!Connections[i].open && Connections[i].used == used) - { - DEBUG(3,("found free connection number %d\n",i)); - return(i); + /* this is for people who can't start the program correctly */ + while (argc > 1 && (*argv[1] != '-')) { + argv++; + argc--; } - i++; - if (i == MAX_CONNECTIONS) - i = 1; - } - - if (!used) - { - used = !used; - goto again; - } - - DEBUG(1,("ERROR! Out of connection structures\n")); - return(-1); -} + while ( EOF != (opt = getopt(argc, argv, "O:l:s:d:Dp:h?bVaiof:")) ) + switch (opt) { + case 'O': + pstrcpy(user_socket_options,optarg); + break; -/**************************************************************************** -reply for the core protocol -****************************************************************************/ -int reply_corep(char *outbuf) -{ - int outsize = set_message(outbuf,1,0,True); + case 's': + pstrcpy(dyn_CONFIGFILE,optarg); + break; - Protocol = PROTOCOL_CORE; - - return outsize; -} + case 'l': + specified_logfile = True; + pstr_sprintf(logfile, "%s/log.smbd", optarg); + lp_set_logfile(logfile); + break; + case 'a': + append_log = True; + break; -/**************************************************************************** -reply for the coreplus protocol -****************************************************************************/ -int reply_coreplus(char *outbuf) -{ - int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0); - int outsize = set_message(outbuf,13,0,True); - SSVAL(outbuf,smb_vwv5,raw); /* tell redirector we support - readbraw and writebraw (possibly) */ - CVAL(outbuf,smb_flg) = 0x81; /* Reply, SMBlockread, SMBwritelock supported */ - SSVAL(outbuf,smb_vwv1,0x1); /* user level security, don't encrypt */ + case 'i': + interactive = True; + break; - Protocol = PROTOCOL_COREPLUS; + case 'o': + append_log = False; + break; - return outsize; -} + case 'D': + is_daemon = True; + break; + case 'd': + if (*optarg == 'A') + DEBUGLEVEL = 10000; + else + DEBUGLEVEL = atoi(optarg); + AllowDebugChange = False; + break; -/**************************************************************************** -reply for the lanman 1.0 protocol -****************************************************************************/ -int reply_lanman1(char *outbuf) -{ - int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0); - int secword=0; - BOOL doencrypt = SMBENCRYPT(); - time_t t = time(NULL); - - if (lp_security()>=SEC_USER) secword |= 1; - if (doencrypt) secword |= 2; - - set_message(outbuf,13,doencrypt?8:0,True); - SSVAL(outbuf,smb_vwv1,secword); -#ifdef SMB_PASSWD - /* Create a token value and add it to the outgoing packet. */ - if (doencrypt) - generate_next_challenge(smb_buf(outbuf)); -#endif + case 'p': + port = atoi(optarg); + break; - Protocol = PROTOCOL_LANMAN1; + case 'h': + case '?': + usage(argv[0]); + exit(0); + break; + + case 'V': + d_printf("Version %s\n",VERSION); + exit(0); + break; + case 'b': + build_options(True); /* Display output to screen as well as debug */ + exit(0); + break; + default: + DEBUG(0,("Incorrect program usage - are you sure the command line is correct?\n")); + usage(argv[0]); + exit(1); + } - 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)); +#ifdef HAVE_SETLUID + /* needed for SecureWare on SCO */ + setluid(0); #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); + sec_init(); - put_dos_date(outbuf,smb_vwv8,t); + load_case_tables(); - return (smb_len(outbuf)+4); -} + append_log = True; + if(!specified_logfile) { + pstr_sprintf(logfile, "%s/log.smbd", dyn_LOGFILEBASE); + lp_set_logfile(logfile); + } -/**************************************************************************** -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 + fstrcpy(remote_machine, "smbd"); - SIVAL(outbuf,smb_vwv6,getpid()); + setup_logging(argv[0],interactive); - Protocol = PROTOCOL_LANMAN2; + /* we want to re-seed early to prevent time delays causing + client problems at a later date. (tridge) */ + generate_random_buffer(NULL, 0, False); - 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 - } + /* make absolutely sure we run as root - to handle cases where people + are crazy enough to have it setuid */ - 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); + gain_root_privilege(); + gain_root_group_privilege(); - return (smb_len(outbuf)+4); -} + fault_setup((void (*)(void *))exit_server); + CatchSignal(SIGTERM , SIGNAL_CAST sig_term); + CatchSignal(SIGHUP,SIGNAL_CAST sig_hup); + + /* we are never interested in SIGPIPE */ + BlockSignals(True,SIGPIPE); -/**************************************************************************** -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); - } +#if defined(SIGFPE) + /* we are never interested in SIGFPE */ + BlockSignals(True,SIGFPE); #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)); +#if defined(SIGUSR2) + /* We are no longer interested in USR2 */ + BlockSignals(True,SIGUSR2); #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); - } -} + /* POSIX demands that signals are inherited. If the invoking process has + * these signals masked, we will have problems, as we won't recieve them. */ + BlockSignals(False, SIGHUP); + BlockSignals(False, SIGUSR1); + BlockSignals(False, SIGTERM); -/**************************************************************************** -close all open files for a connection -****************************************************************************/ -static void close_open_files(int cnum) -{ - int i; - for (i=0;i<MAX_OPEN_FILES;i++) - if( Files[i].cnum == cnum && Files[i].open) { - close_file(i); - } -} + /* we want total control over the permissions on created files, + so set our umask to 0 */ + umask(0); + init_sec_ctx(); + reopen_logs(); -/**************************************************************************** -close a cnum -****************************************************************************/ -void close_cnum(int cnum, int uid) -{ - extern struct from_host Client_info; - - DirCacheFlush(SNUM(cnum)); - - unbecome_user(); - - if (!OPEN_CNUM(cnum)) - { - DEBUG(0,("Can't close cnum %d\n",cnum)); - return; - } - - DEBUG(IS_IPC(cnum)?3:1,("%s %s (%s) closed connection to service %s\n", - timestring(), - Client_info.name,Client_info.addr, - lp_servicename(SNUM(cnum)))); - - yield_connection(cnum, - lp_servicename(SNUM(cnum)), - lp_max_connections(SNUM(cnum))); - - if (lp_status(SNUM(cnum))) - yield_connection(cnum,"STATUS.",MAXSTATUS); - - close_open_files(cnum); - dptr_closecnum(cnum); - - /* execute any "postexec = " line */ - if (*lp_postexec(SNUM(cnum)) && become_user(cnum,uid)) - { - pstring cmd; - strcpy(cmd,lp_postexec(SNUM(cnum))); - standard_sub(cnum,cmd); - smbrun(cmd,NULL); - unbecome_user(); - } - - unbecome_user(); - /* execute any "root postexec = " line */ - if (*lp_rootpostexec(SNUM(cnum))) - { - pstring cmd; - strcpy(cmd,lp_rootpostexec(SNUM(cnum))); - standard_sub(cnum,cmd); - smbrun(cmd,NULL); - } - - Connections[cnum].open = False; - num_connections_open--; - if (Connections[cnum].ngroups && Connections[cnum].groups) - { - if (Connections[cnum].igroups != (int *)Connections[cnum].groups) - free(Connections[cnum].groups); - free(Connections[cnum].igroups); - Connections[cnum].groups = NULL; - Connections[cnum].igroups = NULL; - Connections[cnum].ngroups = 0; - } - - string_set(&Connections[cnum].user,""); - string_set(&Connections[cnum].dirpath,""); - string_set(&Connections[cnum].connectpath,""); -} + DEBUG(0,( "smbd version %s started.\n", VERSION)); + DEBUGADD(0,( "Copyright Andrew Tridgell and the Samba Team 1992-2002\n")); + DEBUG(2,("uid=%d gid=%d euid=%d egid=%d\n", + (int)getuid(),(int)getgid(),(int)geteuid(),(int)getegid())); -/**************************************************************************** -simple routines to do connection counting -****************************************************************************/ -BOOL yield_connection(int cnum,char *name,int max_connections) -{ - struct connect_record crec; - pstring fname; - FILE *f; - int mypid = getpid(); - int i; - - DEBUG(3,("Yielding connection to %d %s\n",cnum,name)); - - if (max_connections <= 0) - return(True); - - bzero(&crec,sizeof(crec)); - - strcpy(fname,lp_lockdir()); - standard_sub(cnum,fname); - trim_string(fname,"","/"); + /* Output the build options to the debug log */ + build_options(False); - strcat(fname,"/"); - strcat(fname,name); - strcat(fname,".LCK"); - - f = fopen(fname,"r+"); - if (!f) - { - DEBUG(2,("Coudn't open lock file %s (%s)\n",fname,strerror(errno))); - return(False); - } - - fseek(f,0,SEEK_SET); - - /* find a free spot */ - for (i=0;i<max_connections;i++) - { - if (fread(&crec,sizeof(crec),1,f) != 1) - { - DEBUG(2,("Entry not found in lock file %s\n",fname)); - fclose(f); - return(False); + if (sizeof(uint16) < 2 || sizeof(uint32) < 4) { + DEBUG(0,("ERROR: Samba is not configured correctly for the word size on your machine\n")); + exit(1); } - if (crec.pid == mypid && crec.cnum == cnum) - break; - } - - if (crec.pid != mypid || crec.cnum != cnum) - { - fclose(f); - DEBUG(2,("Entry not found in lock file %s\n",fname)); - return(False); - } - - bzero((void *)&crec,sizeof(crec)); - - /* remove our mark */ - if (fseek(f,i*sizeof(crec),SEEK_SET) != 0 || - fwrite(&crec,sizeof(crec),1,f) != 1) - { - DEBUG(2,("Couldn't update lock file %s (%s)\n",fname,strerror(errno))); - fclose(f); - return(False); - } - - DEBUG(3,("Yield successful\n")); - - fclose(f); - return(True); -} + /* + * Do this before reload_services. + */ -/**************************************************************************** -simple routines to do connection counting -****************************************************************************/ -BOOL claim_connection(int cnum,char *name,int max_connections,BOOL Clear) -{ - struct connect_record crec; - pstring fname; - FILE *f; - int snum = SNUM(cnum); - int i,foundi= -1; - int total_recs; - - if (max_connections <= 0) - return(True); - - DEBUG(5,("trying claim %s %s %d\n",lp_lockdir(),name,max_connections)); - - strcpy(fname,lp_lockdir()); - standard_sub(cnum,fname); - trim_string(fname,"","/"); - - if (!directory_exist(fname,NULL)) - mkdir(fname,0755); - - strcat(fname,"/"); - strcat(fname,name); - strcat(fname,".LCK"); - - if (!file_exist(fname,NULL)) - { - f = fopen(fname,"w"); - if (f) fclose(f); - } - - total_recs = file_size(fname) / sizeof(crec); + if (!reload_services(False)) + return(-1); - f = fopen(fname,"r+"); + init_structs(); - if (!f) - { - DEBUG(1,("couldn't open lock file %s\n",fname)); - return(False); - } - - /* find a free spot */ - for (i=0;i<max_connections;i++) - { - - if (i>=total_recs || - fseek(f,i*sizeof(crec),SEEK_SET) != 0 || - fread(&crec,sizeof(crec),1,f) != 1) - { - if (foundi < 0) foundi = i; - break; - } - - if (Clear && crec.pid && !process_exists(crec.pid)) - { - fseek(f,i*sizeof(crec),SEEK_SET); - bzero((void *)&crec,sizeof(crec)); - fwrite(&crec,sizeof(crec),1,f); - if (foundi < 0) foundi = i; - continue; + /* don't call winbind for our domain if we are the DC */ + if (lp_domain_logons()) { + winbind_exclude_domain(lp_workgroup()); } - if (foundi < 0 && (!crec.pid || !process_exists(crec.pid))) - { - foundi=i; - if (!Clear) break; + +#ifdef WITH_PROFILE + if (!profile_setup(False)) { + DEBUG(0,("ERROR: failed to setup profiling\n")); + return -1; } - } - - if (foundi < 0) - { - DEBUG(3,("no free locks in %s\n",fname)); - fclose(f); - return(False); - } - - /* fill in the crec */ - bzero((void *)&crec,sizeof(crec)); - crec.magic = 0x280267; - crec.pid = getpid(); - crec.cnum = cnum; - crec.uid = Connections[cnum].uid; - crec.gid = Connections[cnum].gid; - StrnCpy(crec.name,lp_servicename(snum),sizeof(crec.name)-1); - crec.start = time(NULL); - - { - extern struct from_host Client_info; - StrnCpy(crec.machine,Client_info.name,sizeof(crec.machine)-1); - StrnCpy(crec.addr,Client_info.addr,sizeof(crec.addr)-1); - } - - /* make our mark */ - if (fseek(f,foundi*sizeof(crec),SEEK_SET) != 0 || - fwrite(&crec,sizeof(crec),1,f) != 1) - { - fclose(f); - return(False); - } - - fclose(f); - return(True); -} - -#if DUMP_CORE -/******************************************************************* -prepare to dump a core file - carefully! -********************************************************************/ -static BOOL dump_core(void) -{ - char *p; - pstring dname; - strcpy(dname,debugf); - if ((p=strrchr(dname,'/'))) *p=0; - strcat(dname,"/corefiles"); - mkdir(dname,0700); - sys_chown(dname,getuid(),getgid()); - chmod(dname,0700); - if (chdir(dname)) return(False); - umask(~(0700)); - -#ifndef NO_GETRLIMIT -#ifdef RLIMIT_CORE - { - struct rlimit rlp; - getrlimit(RLIMIT_CORE, &rlp); - rlp.rlim_cur = MAX(4*1024*1024,rlp.rlim_cur); - setrlimit(RLIMIT_CORE, &rlp); - getrlimit(RLIMIT_CORE, &rlp); - DEBUG(3,("Core limits now %d %d\n",rlp.rlim_cur,rlp.rlim_max)); - } -#endif -#endif - - - DEBUG(0,("Dumping core in %s\n",dname)); - return(True); -} -#endif - -/**************************************************************************** -exit the server -****************************************************************************/ -void exit_server(char *reason) -{ - static int firsttime=1; - int i; - - if (!firsttime) exit(0); - firsttime = 0; - - unbecome_user(); - DEBUG(2,("Closing connections\n")); - for (i=0;i<MAX_CONNECTIONS;i++) - if (Connections[i].open) - close_cnum(i,-1); -#ifdef DFS_AUTH - if (dcelogin_atmost_once) - dfs_unlogin(); -#endif - if (!reason) { - int oldlevel = DEBUGLEVEL; - DEBUGLEVEL = 10; - DEBUG(0,("Last message was %s\n",smb_fn_name(last_message))); - if (last_inbuf) - show_msg(last_inbuf); - DEBUGLEVEL = oldlevel; - DEBUG(0,("===============================================================\n")); -#if DUMP_CORE - if (dump_core()) return; -#endif - } - DEBUG(3,("%s Server exit (%s)\n",timestring(),reason?reason:"")); - exit(0); -} - -/**************************************************************************** -do some standard substitutions in a string -****************************************************************************/ -void standard_sub(int cnum,char *s) -{ - if (!strchr(s,'%')) return; - - if (VALID_CNUM(cnum)) - { - string_sub(s,"%S",lp_servicename(Connections[cnum].service)); - string_sub(s,"%P",Connections[cnum].connectpath); - string_sub(s,"%u",Connections[cnum].user); - if (strstr(s,"%H")) { - char *home = get_home_dir(Connections[cnum].user); - if (home) string_sub(s,"%H",home); - } - string_sub(s,"%g",gidtoname(Connections[cnum].gid)); - } - standard_sub_basic(s); -} - -/* -These flags determine some of the permissions required to do an operation - -Note that I don't set NEED_WRITE on some write operations because they -are used by some brain-dead clients when printing, and I don't want to -force write permissions on print services. -*/ -#define AS_USER (1<<0) -#define NEED_WRITE (1<<1) -#define TIME_INIT (1<<2) -#define CAN_IPC (1<<3) -#define AS_GUEST (1<<5) - - -/* - define a list of possible SMB messages and their corresponding - functions. Any message that has a NULL function is unimplemented - - please feel free to contribute implementations! -*/ -struct smb_message_struct -{ - int code; - char *name; - int (*fn)(); - int flags; -#if PROFILING - unsigned long time; -#endif -} - smb_messages[] = { - - /* CORE PROTOCOL */ - - {SMBnegprot,"SMBnegprot",reply_negprot,0}, - {SMBtcon,"SMBtcon",reply_tcon,0}, - {SMBtdis,"SMBtdis",reply_tdis,0}, - {SMBexit,"SMBexit",reply_exit,0}, - {SMBioctl,"SMBioctl",reply_ioctl,0}, - {SMBecho,"SMBecho",reply_echo,0}, - {SMBsesssetupX,"SMBsesssetupX",reply_sesssetup_and_X,0}, - {SMBtconX,"SMBtconX",reply_tcon_and_X,0}, - {SMBulogoffX, "SMBulogoffX", reply_ulogoffX, 0}, - {SMBgetatr,"SMBgetatr",reply_getatr,AS_USER}, - {SMBsetatr,"SMBsetatr",reply_setatr,AS_USER | NEED_WRITE}, - {SMBchkpth,"SMBchkpth",reply_chkpth,AS_USER}, - {SMBsearch,"SMBsearch",reply_search,AS_USER}, - {SMBopen,"SMBopen",reply_open,AS_USER}, - - /* note that SMBmknew and SMBcreate are deliberately overloaded */ - {SMBcreate,"SMBcreate",reply_mknew,AS_USER}, - {SMBmknew,"SMBmknew",reply_mknew,AS_USER}, - - {SMBunlink,"SMBunlink",reply_unlink,AS_USER | NEED_WRITE}, - {SMBread,"SMBread",reply_read,AS_USER}, - {SMBwrite,"SMBwrite",reply_write,AS_USER}, - {SMBclose,"SMBclose",reply_close,AS_USER}, - {SMBmkdir,"SMBmkdir",reply_mkdir,AS_USER | NEED_WRITE}, - {SMBrmdir,"SMBrmdir",reply_rmdir,AS_USER | NEED_WRITE}, - {SMBdskattr,"SMBdskattr",reply_dskattr,AS_USER}, - {SMBmv,"SMBmv",reply_mv,AS_USER | NEED_WRITE}, - - /* this is a Pathworks specific call, allowing the - changing of the root path */ - {pSETDIR,"pSETDIR",reply_setdir,AS_USER}, - - {SMBlseek,"SMBlseek",reply_lseek,AS_USER}, - {SMBflush,"SMBflush",reply_flush,AS_USER}, - {SMBctemp,"SMBctemp",reply_ctemp,AS_USER}, - {SMBsplopen,"SMBsplopen",reply_printopen,AS_USER}, - {SMBsplclose,"SMBsplclose",reply_printclose,AS_USER}, - {SMBsplretq,"SMBsplretq",reply_printqueue,AS_USER}, - {SMBsplwr,"SMBsplwr",reply_printwrite,AS_USER}, - {SMBlock,"SMBlock",reply_lock,AS_USER}, - {SMBunlock,"SMBunlock",reply_unlock,AS_USER}, - - /* CORE+ PROTOCOL FOLLOWS */ - - {SMBreadbraw,"SMBreadbraw",reply_readbraw,AS_USER}, - {SMBwritebraw,"SMBwritebraw",reply_writebraw,AS_USER}, - {SMBwriteclose,"SMBwriteclose",reply_writeclose,AS_USER}, - {SMBlockread,"SMBlockread",reply_lockread,AS_USER}, - {SMBwriteunlock,"SMBwriteunlock",reply_writeunlock,AS_USER}, - - /* LANMAN1.0 PROTOCOL FOLLOWS */ - - {SMBreadBmpx,"SMBreadBmpx",reply_readbmpx,AS_USER}, - {SMBreadBs,"SMBreadBs",NULL,AS_USER}, - {SMBwriteBmpx,"SMBwriteBmpx",reply_writebmpx,AS_USER}, - {SMBwriteBs,"SMBwriteBs",reply_writebs,AS_USER}, - {SMBwritec,"SMBwritec",NULL,AS_USER}, - {SMBsetattrE,"SMBsetattrE",reply_setattrE,AS_USER | NEED_WRITE}, - {SMBgetattrE,"SMBgetattrE",reply_getattrE,AS_USER}, - {SMBtrans,"SMBtrans",reply_trans,AS_USER | CAN_IPC}, - {SMBtranss,"SMBtranss",NULL,AS_USER | CAN_IPC}, - {SMBioctls,"SMBioctls",NULL,AS_USER}, - {SMBcopy,"SMBcopy",reply_copy,AS_USER | NEED_WRITE}, - {SMBmove,"SMBmove",NULL,AS_USER | NEED_WRITE}, - - {SMBopenX,"SMBopenX",reply_open_and_X,AS_USER}, - {SMBreadX,"SMBreadX",reply_read_and_X,AS_USER}, - {SMBwriteX,"SMBwriteX",reply_write_and_X,AS_USER}, - {SMBlockingX,"SMBlockingX",reply_lockingX,AS_USER}, - - {SMBffirst,"SMBffirst",reply_search,AS_USER}, - {SMBfunique,"SMBfunique",reply_search,AS_USER}, - {SMBfclose,"SMBfclose",reply_fclose,AS_USER}, - - /* LANMAN2.0 PROTOCOL FOLLOWS */ - {SMBfindnclose, "SMBfindnclose", reply_findnclose, AS_USER}, - {SMBfindclose, "SMBfindclose", reply_findclose,AS_USER}, - {SMBtrans2, "SMBtrans2", reply_trans2, AS_USER}, - {SMBtranss2, "SMBtranss2", reply_transs2, AS_USER}, - - /* messaging routines */ - {SMBsends,"SMBsends",reply_sends,AS_GUEST}, - {SMBsendstrt,"SMBsendstrt",reply_sendstrt,AS_GUEST}, - {SMBsendend,"SMBsendend",reply_sendend,AS_GUEST}, - {SMBsendtxt,"SMBsendtxt",reply_sendtxt,AS_GUEST}, - - /* NON-IMPLEMENTED PARTS OF THE CORE PROTOCOL */ - - {SMBsendb,"SMBsendb",NULL,AS_GUEST}, - {SMBfwdname,"SMBfwdname",NULL,AS_GUEST}, - {SMBcancelf,"SMBcancelf",NULL,AS_GUEST}, - {SMBgetmac,"SMBgetmac",NULL,AS_GUEST} - }; - -/**************************************************************************** -return a string containing the function name of a SMB command -****************************************************************************/ -char *smb_fn_name(int type) -{ - static char *unknown_name = "SMBunknown"; - static int num_smb_messages = - sizeof(smb_messages) / sizeof(struct smb_message_struct); - int match; - - for (match=0;match<num_smb_messages;match++) - if (smb_messages[match].code == type) - break; - - if (match == num_smb_messages) - return(unknown_name); - - return(smb_messages[match].name); -} - - -/**************************************************************************** -do a switch on the message type, and return the response size -****************************************************************************/ -static int switch_message(int type,char *inbuf,char *outbuf,int size,int bufsize) -{ - static int pid= -1; - int outsize = 0; - static int num_smb_messages = - sizeof(smb_messages) / sizeof(struct smb_message_struct); - int match; - -#if PROFILING - struct timeval msg_start_time; - struct timeval msg_end_time; - static unsigned long total_time = 0; - - GetTimeOfDay(&msg_start_time); #endif - if (pid == -1) - pid = getpid(); - - errno = 0; - last_message = type; - - /* make sure this is an SMB packet */ - if (strncmp(smb_base(inbuf),"\377SMB",4) != 0) - { - DEBUG(2,("Non-SMB packet of length %d\n",smb_len(inbuf))); - return(-1); - } - - for (match=0;match<num_smb_messages;match++) - if (smb_messages[match].code == type) - break; - - if (match == num_smb_messages) - { - DEBUG(0,("Unknown message type %d!\n",type)); - outsize = reply_unknown(inbuf,outbuf); - } - else - { - DEBUG(3,("switch message %s (pid %d)\n",smb_messages[match].name,pid)); - if (smb_messages[match].fn) +#ifdef WITH_SSL { - int cnum = SVAL(inbuf,smb_tid); - int flags = smb_messages[match].flags; - int uid = SVAL(inbuf,smb_uid); - - /* does this protocol need to be run as root? */ - if (!(flags & AS_USER)) - unbecome_user(); - - /* does this protocol need to be run as the connected user? */ - if ((flags & AS_USER) && !become_user(cnum,uid)) - return(ERROR(ERRSRV,ERRinvnid)); - - /* does it need write permission? */ - if ((flags & NEED_WRITE) && !CAN_WRITE(cnum)) - return(ERROR(ERRSRV,ERRaccess)); - - /* ipc services are limited */ - if (IS_IPC(cnum) && (flags & AS_USER) && !(flags & CAN_IPC)) - return(ERROR(ERRSRV,ERRaccess)); - - /* load service specific parameters */ - if (OPEN_CNUM(cnum) && !become_service(cnum,(flags & AS_USER)?True:False)) - return(ERROR(ERRSRV,ERRaccess)); - - /* does this protocol need to be run as guest? */ - if ((flags & AS_GUEST) && (!become_guest() || !check_access(-1))) - return(ERROR(ERRSRV,ERRaccess)); - - last_inbuf = inbuf; - - outsize = smb_messages[match].fn(inbuf,outbuf,size,bufsize); - } - else - { - outsize = reply_unknown(inbuf,outbuf); + extern BOOL sslEnabled; + sslEnabled = lp_ssl_enabled(); + if(sslEnabled) + sslutil_init(True); } - } - -#if PROFILING - GetTimeOfDay(&msg_end_time); - if (!(smb_messages[match].flags & TIME_INIT)) - { - smb_messages[match].time = 0; - smb_messages[match].flags |= TIME_INIT; - } - { - unsigned long this_time = - (msg_end_time.tv_sec - msg_start_time.tv_sec)*1e6 + - (msg_end_time.tv_usec - msg_start_time.tv_usec); - smb_messages[match].time += this_time; - total_time += this_time; - } - DEBUG(2,("TIME %s %d usecs %g pct\n", - smb_fn_name(type),smb_messages[match].time, - (100.0*smb_messages[match].time) / total_time)); -#endif - - return(outsize); -} - - -/**************************************************************************** -construct a chained reply and add it to the already made reply - -inbuf points to the original message start. -inbuf2 points to the smb_wct part of the secondary message -type is the type of the secondary message -outbuf points to the original outbuffer -outbuf2 points to the smb_wct field of the new outbuffer -size is the total length of the incoming message (from inbuf1) -bufsize is the total buffer size - -return how many bytes were added to the response -****************************************************************************/ -int chain_reply(int type,char *inbuf,char *inbuf2,char *outbuf,char *outbuf2,int size,int bufsize) -{ - int outsize = 0; - char *ibuf,*obuf; - static BOOL in_chain = False; - static char *last_outbuf=NULL; - BOOL was_inchain = in_chain; - int insize_remaining; - static int insize_deleted; - - - chain_size += PTR_DIFF(outbuf2,outbuf) - smb_wct; - if (was_inchain) - outbuf = last_outbuf; - else - insize_deleted = 0; - - - insize_deleted = 0; - inbuf2 -= insize_deleted; - insize_remaining = size - PTR_DIFF(inbuf2,inbuf); - insize_deleted += size - (insize_remaining + smb_wct); - - in_chain = True; - last_outbuf = outbuf; +#endif /* WITH_SSL */ + fstrcpy(global_myworkgroup, lp_workgroup()); - /* allocate some space for the in and out buffers of the chained message */ - ibuf = (char *)malloc(size + SAFETY_MARGIN); - obuf = (char *)malloc(bufsize + SAFETY_MARGIN); + DEBUG(3,( "loaded services\n")); - if (!ibuf || !obuf) - { - DEBUG(0,("Out of memory in chain reply\n")); - return(ERROR(ERRSRV,ERRnoresource)); - } + if (!is_daemon && !is_a_socket(0)) { + if (!interactive) + DEBUG(0,("standard input is not a socket, assuming -D option\n")); - ibuf += SMB_ALIGNMENT; - obuf += SMB_ALIGNMENT; + /* + * Setting is_daemon here prevents us from eventually calling + * the open_sockets_inetd() + */ - /* create the in buffer */ - memcpy(ibuf,inbuf,smb_wct); - memcpy(ibuf+smb_wct,inbuf2,insize_remaining); - CVAL(ibuf,smb_com) = type; - - /* create the out buffer */ - bzero(obuf,smb_size); - - set_message(obuf,0,0,True); - CVAL(obuf,smb_com) = CVAL(ibuf,smb_com); - - memcpy(obuf+4,ibuf+4,4); - CVAL(obuf,smb_rcls) = SUCCESS; - CVAL(obuf,smb_reh) = 0; - CVAL(obuf,smb_flg) = 0x80 | (CVAL(ibuf,smb_flg) & 0x8); /* bit 7 set - means a reply */ - SSVAL(obuf,smb_flg2,1); /* say we support long filenames */ - SSVAL(obuf,smb_err,SUCCESS); - SSVAL(obuf,smb_tid,SVAL(inbuf,smb_tid)); - SSVAL(obuf,smb_pid,SVAL(inbuf,smb_pid)); - SSVAL(obuf,smb_uid,SVAL(inbuf,smb_uid)); - SSVAL(obuf,smb_mid,SVAL(inbuf,smb_mid)); - - DEBUG(3,("Chained message\n")); - show_msg(ibuf); - - /* process the request */ - outsize = switch_message(type,ibuf,obuf,smb_wct+insize_remaining, - bufsize-chain_size); - - /* copy the new reply header over the old one, but preserve - the smb_com field */ - memcpy(outbuf+smb_com+1,obuf+smb_com+1,smb_wct-(smb_com+1)); - - /* and copy the data from the reply to the right spot */ - memcpy(outbuf2,obuf+smb_wct,outsize - smb_wct); - - /* free the allocated buffers */ - if (ibuf) free(ibuf-SMB_ALIGNMENT); - if (obuf) free(obuf-SMB_ALIGNMENT); - - in_chain = was_inchain; - - /* return how much extra has been added to the packet */ - return(outsize - smb_wct); -} - - - -/**************************************************************************** - construct a reply to the incoming packet -****************************************************************************/ -int construct_reply(char *inbuf,char *outbuf,int size,int bufsize) -{ - int type = CVAL(inbuf,smb_com); - int outsize = 0; - int msg_type = CVAL(inbuf,0); - - smb_last_time = time(NULL); - - chain_size = 0; - - bzero(outbuf,smb_size); - - if (msg_type != 0) - return(reply_special(inbuf,outbuf)); - - CVAL(outbuf,smb_com) = CVAL(inbuf,smb_com); - set_message(outbuf,0,0,True); - - memcpy(outbuf+4,inbuf+4,4); - CVAL(outbuf,smb_rcls) = SUCCESS; - CVAL(outbuf,smb_reh) = 0; - CVAL(outbuf,smb_flg) = 0x80 | (CVAL(inbuf,smb_flg) & 0x8); /* bit 7 set - means a reply */ - SSVAL(outbuf,smb_flg2,1); /* say we support long filenames */ - SSVAL(outbuf,smb_err,SUCCESS); - SSVAL(outbuf,smb_tid,SVAL(inbuf,smb_tid)); - SSVAL(outbuf,smb_pid,SVAL(inbuf,smb_pid)); - SSVAL(outbuf,smb_uid,SVAL(inbuf,smb_uid)); - SSVAL(outbuf,smb_mid,SVAL(inbuf,smb_mid)); - - outsize = switch_message(type,inbuf,outbuf,size,bufsize); - - if(outsize > 4) - smb_setlen(outbuf,outsize - 4); - return(outsize); -} - - -/**************************************************************************** - process commands from the client -****************************************************************************/ -void process(void ) -{ - static int trans_num = 0; - int nread; - extern struct from_host Client_info; - extern int Client; - - fromhost(Client,&Client_info); - - InBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); - OutBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); - if ((InBuffer == NULL) || (OutBuffer == NULL)) - return; - - InBuffer += SMB_ALIGNMENT; - OutBuffer += SMB_ALIGNMENT; - -#if PRIME_NMBD - DEBUG(3,("priming nmbd\n")); - { - struct in_addr ip; - ip = *interpret_addr2("localhost"); - if (zero_ip(ip)) ip = *interpret_addr2("127.0.0.1"); - *OutBuffer = 0; - send_one_packet(OutBuffer,1,ip,137,SOCK_DGRAM); - } -#endif - - last_user.cnum = -1; - - while (True) - { - int32 len; - int msg_type; - int msg_flags; - int type; - int deadtime = lp_deadtime()*60; - int counter; - int last_keepalive=0; - - if (deadtime <= 0) - deadtime = DEFAULT_SMBD_TIMEOUT; - - if (lp_readprediction()) - do_read_prediction(); - - { - extern pstring share_del_pending; - if (*share_del_pending) { - unbecome_user(); - if (!unlink(share_del_pending)) - DEBUG(3,("Share file deleted %s\n",share_del_pending)); - else - DEBUG(2,("Share del failed of %s\n",share_del_pending)); - share_del_pending[0] = 0; + is_daemon = True; } - } - - if (share_mode_pending) { - unbecome_user(); - check_share_modes(); - share_mode_pending=False; - } - - errno = 0; - for (counter=SMBD_SELECT_LOOP; - !receive_smb(Client,InBuffer,SMBD_SELECT_LOOP*1000); - counter += SMBD_SELECT_LOOP) - { - int i; - time_t t; - BOOL allidle = True; - extern int keepalive; - - /* check for socket failure */ - if (errno == EBADF) { - DEBUG(3,("%s Bad file descriptor - exiting\n",timestring())); - return; - } - - t = time(NULL); - - /* become root again if waiting */ - unbecome_user(); - - /* check for smb.conf reload */ - if (!(counter%SMBD_RELOAD_CHECK)) - reload_services(True); - - /* check the share modes every 10 secs */ - if (!(counter%SHARE_MODES_CHECK)) - check_share_modes(); - - /* clean the share modes every 5 minutes */ - if (!(counter%SHARE_MODES_CLEAN)) - clean_share_files(); - - /* automatic timeout if all connections are closed */ - if (num_connections_open==0 && counter >= IDLE_CLOSED_TIMEOUT) { - DEBUG(2,("%s Closing idle connection\n",timestring())); - return; - } - - if (keepalive && (counter-last_keepalive)>keepalive) { - if (!send_keepalive(Client)) { - DEBUG(2,("%s Keepalive failed - exiting\n",timestring())); - return; - } - last_keepalive = counter; - } - - /* check for connection timeouts */ - for (i=0;i<MAX_CONNECTIONS;i++) - if (Connections[i].open) - { - /* close dirptrs on connections that are idle */ - if ((t-Connections[i].lastused)>DPTR_IDLE_TIMEOUT) - dptr_idlecnum(i); - - if (Connections[i].num_files_open > 0 || - (t-Connections[i].lastused)<deadtime) - allidle = False; - } - - if (allidle && num_connections_open>0) { - DEBUG(2,("%s Closing idle connection 2\n",timestring())); - return; - } + if (is_daemon && !interactive) { + DEBUG( 3, ( "Becoming a daemon.\n" ) ); + become_daemon(); } - 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; - } +#if HAVE_SETPGID + /* + * If we're interactive we want to set our own process group for + * signal management. + */ + if (interactive) + setpgid( (pid_t)0, (pid_t)0); #endif + if (!directory_exist(lp_lockdir(), NULL)) { + mkdir(lp_lockdir(), 0755); + } - if (msg_type == 0) - show_msg(InBuffer); - - nread = construct_reply(InBuffer,OutBuffer,nread,maxxmit); - - if(nread > 0) { - if (CVAL(OutBuffer,0) == 0) - show_msg(OutBuffer); - - if (nread != smb_len(OutBuffer) + 4) - { - DEBUG(0,("ERROR: Invalid message response size! %d %d\n", - nread, - smb_len(OutBuffer))); - } - else - send_smb(Client,OutBuffer); - } - trans_num++; - } -} - - -/**************************************************************************** - initialise connect, service and file structs -****************************************************************************/ -static void init_structs(void ) -{ - int i; - get_myname(myhostname,&myip); - - for (i=0;i<MAX_CONNECTIONS;i++) - { - Connections[i].open = False; - Connections[i].num_files_open=0; - Connections[i].lastused=0; - Connections[i].used=False; - string_init(&Connections[i].user,""); - string_init(&Connections[i].dirpath,""); - string_init(&Connections[i].connectpath,""); - string_init(&Connections[i].origpath,""); - } - - for (i=0;i<MAX_OPEN_FILES;i++) - { - Files[i].open = False; - string_init(&Files[i].name,""); - } - - init_dptrs(); -} - -/**************************************************************************** -usage on the program -****************************************************************************/ -void usage(char *pname) -{ - DEBUG(0,("Incorrect program usage - are you sure the command line is correct?\n")); - - printf("Usage: %s [-D] [-p port] [-d debuglevel] [-l log basename] [-s services file]\n",pname); - printf("Version %s\n",VERSION); - printf("\t-D become a daemon\n"); - printf("\t-p port listen on the specified port\n"); - printf("\t-d debuglevel set the debuglevel\n"); - printf("\t-l log basename. Basename for log/debug files\n"); - printf("\t-s services file. Filename of services file\n"); - printf("\t-P passive only\n"); - printf("\t-a overwrite log file, don't append\n"); - printf("\n"); -} - - -/**************************************************************************** - main program -****************************************************************************/ -int main(int argc,char *argv[]) -{ - extern BOOL append_log; - /* shall I run as a daemon */ - BOOL is_daemon = False; - int port = 139; - int opt; - extern char *optarg; - -#ifdef NEED_AUTH_PARAMETERS - set_auth_parameters(argc,argv); -#endif + if (is_daemon) { + pidfile_create("smbd"); + } -#ifdef SecureWare - setluid(0); -#endif + if (!message_init()) { + exit(1); + } + register_msg_pool_usage(); + register_dmalloc_msgs(); - append_log = True; + /* Setup the main smbd so that we can get messages. */ + claim_connection(NULL,"",0,True); - TimeInit(); + /* + DO NOT ENABLE THIS TILL YOU COPE WITH KILLING THESE TASKS AND INETD + THIS *killed* LOTS OF BUILD FARM MACHINES. IT CREATED HUNDREDS OF + smbd PROCESSES THAT NEVER DIE + start_background_queue(); + */ - strcpy(debugf,SMBLOGFILE); + if (!open_sockets(is_daemon,port)) + exit(1); - setup_logging(argv[0],False); + /* + * everything after this point is run after the fork() + */ - charset_initialise(); + if (!locking_init(0)) + exit(1); - /* make absolutely sure we run as root - to handle cases whre people - are crazy enough to have it setuid */ -#ifdef USE_SETRES - setresuid(0,0,0); -#else - setuid(0); - seteuid(0); - setuid(0); - seteuid(0); -#endif + if (!print_backend_init()) + exit(1); - fault_setup(exit_server); + if (!share_info_db_init()) + exit(1); - umask(0777 & ~DEF_CREATE_MASK); + if(!initialize_password_db(False)) + exit(1); - initial_uid = geteuid(); - initial_gid = getegid(); + uni_group_cache_init(); /* Non-critical */ + + /* possibly reload the services file. */ + reload_services(True); - if (initial_gid != 0 && initial_uid == 0) - { -#ifdef HPUX - setresgid(0,0,0); -#else - setgid(0); - setegid(0); -#endif - } - - initial_uid = geteuid(); - initial_gid = getegid(); - - - /* this is for people who can't start the program correctly */ - while (argc > 1 && (*argv[1] != '-')) - { - argv++; - argc--; - } - - while ((opt = getopt(argc, argv, "O:i:l:s:d:Dp:hPa")) != EOF) - switch (opt) - { - case 'O': - strcpy(user_socket_options,optarg); - break; - case 'i': - strcpy(scope,optarg); - break; - case 'P': - { - extern BOOL passive; - passive = True; - } - break; - case 's': - strcpy(servicesf,optarg); - break; - case 'l': - strcpy(debugf,optarg); - break; - case 'a': - { - extern BOOL append_log; - append_log = !append_log; + if(!pdb_generate_sam_sid()) { + DEBUG(0,("ERROR: Samba cannot create a SAM SID.\n")); + exit(1); } - 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())); + if (!init_account_policy()) { + DEBUG(0,("Could not open account policy tdb.\n")); + exit(1); } - process(); - close_sockets(); - } - exit_server("normal exit"); - return(0); -} + if (*lp_rootdir()) { + if (sys_chroot(lp_rootdir()) == 0) + DEBUG(2,("Changed root to %s\n", lp_rootdir())); + } + /* Setup oplocks */ + if (!init_oplocks()) + exit(1); + + /* Setup change notify */ + if (!init_change_notify()) + exit(1); + smbd_process(); + + uni_group_cache_shutdown(); + exit_server("normal exit"); + return(0); +} diff --git a/source3/smbd/service.c b/source3/smbd/service.c new file mode 100644 index 0000000000..0ae49b7adf --- /dev/null +++ b/source3/smbd/service.c @@ -0,0 +1,738 @@ +/* + Unix SMB/CIFS implementation. + service (connection) opening and closing + Copyright (C) Andrew Tridgell 1992-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + 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" + +extern struct timeval smb_last_time; +extern int case_default; +extern BOOL case_preserve; +extern BOOL short_case_preserve; +extern BOOL case_mangle; +extern BOOL case_sensitive; +extern BOOL use_mangled_map; +extern fstring remote_machine; +extern userdom_struct current_user_info; +extern fstring remote_machine; + + +/**************************************************************************** + Load parameters specific to a connection/service. +****************************************************************************/ + +BOOL set_current_service(connection_struct *conn,BOOL do_chdir) +{ + extern char magic_char; + static connection_struct *last_conn; + int snum; + + if (!conn) { + last_conn = NULL; + return(False); + } + + conn->lastused = smb_last_time.tv_sec; + + snum = SNUM(conn); + + if (do_chdir && + vfs_ChDir(conn,conn->connectpath) != 0 && + vfs_ChDir(conn,conn->origpath) != 0) { + DEBUG(0,("chdir (%s) failed\n", + conn->connectpath)); + return(False); + } + + if (conn == last_conn) + return(True); + + last_conn = conn; + + 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); +} + +/**************************************************************************** + Add a home service. Returns the new service number or -1 if fail. +****************************************************************************/ + +int add_home_service(const char *service, const char *homedir) +{ + int iHomeService; + int iService; + fstring new_service; + fstring domain; + + if (!service || !homedir) + return -1; + + if ((iHomeService = lp_servicenumber(HOMES_NAME)) < 0) + return -1; + + /* + * If this is a winbindd provided username, remove + * the domain component before adding the service. + * Log a warning if the "path=" parameter does not + * include any macros. + */ + + split_domain_and_name(service, domain, new_service); + lp_add_home(new_service, iHomeService, homedir); + iService = lp_servicenumber(new_service); + + return iService; +} + + +/** + * Find a service entry. service is always in dos codepage. + * + * @param service is modified (to canonical form??) + **/ +int find_service(fstring service) +{ + int iService; + + all_string_sub(service,"\\","/",0); + + iService = lp_servicenumber(service); + + /* now handle the special case of a home directory */ + if (iService < 0) + { + char *phome_dir = get_user_service_home_dir(service); + + if(!phome_dir) + { + /* + * Try mapping the servicename, it may + * be a Windows to unix mapped user name. + */ + if(map_username(service)) + phome_dir = get_user_service_home_dir(service); + } + + DEBUG(3,("checking for home directory %s gave %s\n",service, + phome_dir?phome_dir:"(NULL)")); + + iService = add_home_service(service,phome_dir); + } + + /* 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)); + } + } + + /* Check for default vfs service? Unsure whether to implement this */ + if (iService < 0) + { + } + + /* just possibly it's a default service? */ + if (iService < 0) + { + char *pdefservice = lp_defaultservice(); + if (pdefservice && *pdefservice && + !strequal(pdefservice,service) && + !strstr(service,"..")) + { + /* + * We need to do a local copy here as lp_defaultservice() + * returns one of the rotating lp_string buffers that + * could get overwritten by the recursive find_service() call + * below. Fix from Josef Hinteregger <joehtg@joehtg.co.at>. + */ + pstring defservice; + pstrcpy(defservice, pdefservice); + iService = find_service(defservice); + if (iService >= 0) + { + all_string_sub(service, "_","/",0); + 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); +} + + +/**************************************************************************** + do some basic sainity checks on the share. + This function modifies dev, ecode. +****************************************************************************/ +static NTSTATUS share_sanity_checks(int snum, const char* service, pstring dev) +{ + + if (!lp_snum_ok(snum) || + !check_access(smbd_server_fd(), + lp_hostsallow(snum), lp_hostsdeny(snum))) { + return NT_STATUS_ACCESS_DENIED; + } + + /* you can only connect to the IPC$ service as an ipc device */ + if (strequal(service,"IPC$") || strequal(service,"ADMIN$")) + pstrcpy(dev,"IPC"); + + if (dev[0] == '?' || !dev[0]) { + if (lp_print_ok(snum)) { + pstrcpy(dev,"LPT1:"); + } else { + pstrcpy(dev,"A:"); + } + } + + /* if the request is as a printer and you can't print then refuse */ + strupper(dev); + if (!lp_print_ok(snum) && (strncmp(dev,"LPT",3) == 0)) { + DEBUG(1,("Attempt to connect to non-printer as a printer\n")); + return NT_STATUS_BAD_DEVICE_TYPE; + } + + /* Behave as a printer if we are supposed to */ + if (lp_print_ok(snum) && (strcmp(dev, "A:") == 0)) { + pstrcpy(dev, "LPT1:"); + } + + return NT_STATUS_OK; +} + + +/**************************************************************************** + readonly share? +****************************************************************************/ +static void set_read_only(connection_struct *conn) +{ + char **list; + char *service = lp_servicename(conn->service); + conn->read_only = lp_readonly(conn->service); + + if (!service) return; + + lp_list_copy(&list, lp_readlist(conn->service)); + if (list) { + if (!lp_list_substitute(list, "%S", service)) { + DEBUG(0, ("ERROR: read list substitution failed\n")); + } + if (user_in_list(conn->user, list)) + conn->read_only = True; + lp_list_free(&list); + } + + lp_list_copy(&list, lp_writelist(conn->service)); + if (list) { + if (!lp_list_substitute(list, "%S", service)) { + DEBUG(0, ("ERROR: write list substitution failed\n")); + } + if (user_in_list(conn->user, list)) + conn->read_only = False; + lp_list_free(&list); + } +} + + +/**************************************************************************** + admin user check +****************************************************************************/ +static void set_admin_user(connection_struct *conn) +{ + /* admin user check */ + + /* JRA - original code denied admin user if the share was + marked read_only. Changed as I don't think this is needed, + but old code left in case there is a problem here. + */ + if (user_in_list(conn->user,lp_admin_users(conn->service)) +#if 0 + && !conn->read_only +#endif + ) { + conn->admin_user = True; + DEBUG(0,("%s logged in as admin user (root privileges)\n",conn->user)); + } else { + conn->admin_user = False; + } + +#if 0 /* This done later, for now */ + /* admin users always run as uid=0 */ + if (conn->admin_user) { + conn->uid = 0; + } +#endif +} + +/**************************************************************************** + Make a connection to a service. + * + * @param service (May be modified to canonical form???) +****************************************************************************/ + +connection_struct *make_connection(char *service, DATA_BLOB password, + char *dev, uint16 vuid, NTSTATUS *status) +{ + int snum; + struct passwd *pass = NULL; + BOOL guest = False; + BOOL force = False; + connection_struct *conn; + uid_t euid; + + fstring user; + ZERO_STRUCT(user); + + /* This must ONLY BE CALLED AS ROOT. As it exits this function as root. */ + if (!non_root_mode() && (euid = geteuid()) != 0) { + DEBUG(0,("make_connection: PANIC ERROR. Called as nonroot (%u)\n", (unsigned int)euid )); + smb_panic("make_connection: PANIC ERROR. Called as nonroot\n"); + } + + strlower(service); + + snum = find_service(service); + + if (snum < 0) { + if (strequal(service,"IPC$") || strequal(service,"ADMIN$")) { + DEBUG(3,("refusing IPC connection\n")); + *status = NT_STATUS_ACCESS_DENIED; + return NULL; + } + + DEBUG(0,("%s (%s) couldn't find service %s\n", + remote_machine, client_addr(), service)); + *status = NT_STATUS_BAD_NETWORK_NAME; + return NULL; + } + + if (strequal(service,HOMES_NAME)) { + if(lp_security() != SEC_SHARE) { + if (validated_username(vuid)) { + fstring unix_username; + fstrcpy(unix_username,validated_username(vuid)); + return make_connection(unix_username, + password,dev,vuid,status); + } + } else { + /* Security = share. Try with current_user_info.smb_name + * as the username. */ + if (* current_user_info.smb_name) { + fstring unix_username; + fstrcpy(unix_username, + current_user_info.smb_name); + map_username(unix_username); + return make_connection(unix_username, + password,dev,vuid,status); + } + } + } + + if (NT_STATUS_IS_ERR(*status = share_sanity_checks(snum, service, dev))) { + return NULL; + } + + /* add it as a possible user name if we + are in share mode security */ + if (lp_security() == SEC_SHARE) { + add_session_user(service); + } + + + /* shall we let them in? */ + if (!authorise_login(snum,user,password,&guest,&force,vuid)) { + DEBUG( 2, ( "Invalid username/password for %s [%s]\n", service, user ) ); + *status = NT_STATUS_WRONG_PASSWORD; + return NULL; + } + + add_session_user(user); + + conn = conn_new(); + if (!conn) { + DEBUG(0,("Couldn't find free connection.\n")); + *status = NT_STATUS_INSUFFICIENT_RESOURCES; + return NULL; + } + + /* find out some info about the user */ + pass = smb_getpwnam(user,True); + + if (pass == NULL) { + DEBUG(0,( "Couldn't find account %s\n",user)); + *status = NT_STATUS_NO_SUCH_USER; + conn_free(conn); + return NULL; + } + + conn->force_user = force; + conn->vuid = vuid; + conn->uid = pass->pw_uid; + conn->gid = pass->pw_gid; + safe_strcpy(conn->client_address, client_addr(), + sizeof(conn->client_address)-1); + conn->num_files_open = 0; + conn->lastused = time(NULL); + conn->service = snum; + conn->used = True; + conn->printer = (strncmp(dev,"LPT",3) == 0); + conn->ipc = ((strncmp(dev,"IPC",3) == 0) || strequal(dev,"ADMIN$")); + conn->dirptr = NULL; + conn->veto_list = NULL; + conn->hide_list = NULL; + conn->veto_oplock_list = NULL; + string_set(&conn->dirpath,""); + string_set(&conn->user,user); + conn->nt_user_token = NULL; + + set_read_only(conn); + + set_admin_user(conn); + + /* + * If force user is true, then store the + * given userid and also the primary groupid + * of the user we're forcing. + */ + + if (*lp_force_user(snum)) { + struct passwd *pass2; + pstring fuser; + pstrcpy(fuser,lp_force_user(snum)); + + /* Allow %S to be used by force user. */ + pstring_sub(fuser,"%S",service); + + pass2 = (struct passwd *)Get_Pwnam_Modify(fuser); + if (pass2) { + conn->uid = pass2->pw_uid; + conn->gid = pass2->pw_gid; + string_set(&conn->user,fuser); + fstrcpy(user,fuser); + conn->force_user = True; + DEBUG(3,("Forced user %s\n",fuser)); + } else { + DEBUG(1,("Couldn't find user %s\n",fuser)); + } + } + + /* admin users always run as uid=0 */ + if (conn->admin_user) { + conn->uid = 0; + } + +#ifdef HAVE_GETGRNAM + /* + * If force group is true, then override + * any groupid stored for the connecting user. + */ + + if (*lp_force_group(snum)) { + gid_t gid; + pstring gname; + pstring tmp_gname; + BOOL user_must_be_member = False; + + StrnCpy(tmp_gname,lp_force_group(snum),sizeof(pstring)-1); + + if (tmp_gname[0] == '+') { + user_must_be_member = True; + StrnCpy(gname,&tmp_gname[1],sizeof(pstring)-2); + } else { + StrnCpy(gname,tmp_gname,sizeof(pstring)-1); + } + /* default service may be a group name */ + pstring_sub(gname,"%S",service); + gid = nametogid(gname); + + if (gid != (gid_t)-1) { + /* + * If the user has been forced and the forced group starts + * with a '+', then we only set the group to be the forced + * group if the forced user is a member of that group. + * Otherwise, the meaning of the '+' would be ignored. + */ + if (conn->force_user && user_must_be_member) { + if (user_in_group_list( user, gname )) { + conn->gid = gid; + DEBUG(3,("Forced group %s for member %s\n",gname,user)); + } + } else { + conn->gid = gid; + DEBUG(3,("Forced group %s\n",gname)); + } + } else { + DEBUG(1,("Couldn't find group %s\n",gname)); + } + } +#endif /* HAVE_GETGRNAM */ + + { + pstring s; + pstrcpy(s,lp_pathname(snum)); + standard_sub_conn(conn,s); + string_set(&conn->connectpath,s); + DEBUG(3,("Connect path is %s\n",s)); + } + + /* groups stuff added by ih */ + conn->ngroups = 0; + conn->groups = NULL; + + /* Find all the groups this uid is in and + store them. Used by change_to_user() */ + initialise_groups(conn->user, conn->uid, conn->gid); + get_current_groups(&conn->ngroups,&conn->groups); + + conn->nt_user_token = create_nt_token(conn->uid, conn->gid, + conn->ngroups, conn->groups, + guest, NULL); + + /* + * New code to check if there's a share security descripter + * added from NT server manager. This is done after the + * smb.conf checks are done as we need a uid and token. JRA. + */ + + { + BOOL can_write = share_access_check(conn, snum, vuid, FILE_WRITE_DATA); + + if (!can_write) { + if (!share_access_check(conn, snum, vuid, FILE_READ_DATA)) { + /* No access, read or write. */ + *status = NT_STATUS_ACCESS_DENIED; + DEBUG(0,( "make_connection: connection to %s denied due to security descriptor.\n", + service )); + conn_free(conn); + return NULL; + } else { + conn->read_only = True; + } + } + } + /* Initialise VFS function pointers */ + + if (!smbd_vfs_init(conn)) { + DEBUG(0, ("vfs_init failed for service %s\n", lp_servicename(SNUM(conn)))); + conn_free(conn); + return NULL; + } + +/* ROOT Activities: */ + /* check number of connections */ + if (!claim_connection(conn, + lp_servicename(SNUM(conn)), + lp_max_connections(SNUM(conn)), + False)) { + DEBUG(1,("too many connections - rejected\n")); + *status = NT_STATUS_INSUFFICIENT_RESOURCES; + conn_free(conn); + return NULL; + } + + /* Preexecs are done here as they might make the dir we are to ChDir to below */ + /* execute any "root preexec = " line */ + if (*lp_rootpreexec(SNUM(conn))) { + int ret; + pstring cmd; + pstrcpy(cmd,lp_rootpreexec(SNUM(conn))); + standard_sub_conn(conn,cmd); + DEBUG(5,("cmd=%s\n",cmd)); + ret = smbrun(cmd,NULL); + if (ret != 0 && lp_rootpreexec_close(SNUM(conn))) { + DEBUG(1,("root preexec gave %d - failing connection\n", ret)); + yield_connection(conn, lp_servicename(SNUM(conn))); + conn_free(conn); + *status = NT_STATUS_UNSUCCESSFUL; + return NULL; + } + } + +/* USER Activites: */ + if (!change_to_user(conn, conn->vuid)) { + /* No point continuing if they fail the basic checks */ + DEBUG(0,("Can't become connected user!\n")); + conn_free(conn); + *status = NT_STATUS_LOGON_FAILURE; + return NULL; + } + + /* Remember that a different vuid can connect later without these checks... */ + + /* Preexecs are done here as they might make the dir we are to ChDir to below */ + /* execute any "preexec = " line */ + if (*lp_preexec(SNUM(conn))) { + int ret; + pstring cmd; + pstrcpy(cmd,lp_preexec(SNUM(conn))); + standard_sub_conn(conn,cmd); + ret = smbrun(cmd,NULL); + if (ret != 0 && lp_preexec_close(SNUM(conn))) { + DEBUG(1,("preexec gave %d - failing connection\n", ret)); + change_to_root_user(); + yield_connection(conn, lp_servicename(SNUM(conn))); + conn_free(conn); + *status = NT_STATUS_UNSUCCESSFUL; + return NULL; + } + } + + if (vfs_ChDir(conn,conn->connectpath) != 0) { + DEBUG(0,("%s (%s) Can't change directory to %s (%s)\n", + remote_machine, conn->client_address, + conn->connectpath,strerror(errno))); + change_to_root_user(); + yield_connection(conn, lp_servicename(SNUM(conn))); + conn_free(conn); + *status = NT_STATUS_BAD_NETWORK_NAME; + return NULL; + } + + string_set(&conn->origpath,conn->connectpath); + +#if SOFTLINK_OPTIMISATION + /* resolve any soft links early */ + { + pstring s; + pstrcpy(s,conn->connectpath); + vfs_GetWd(conn,s); + string_set(&conn->connectpath,s); + vfs_ChDir(conn,conn->connectpath); + } +#endif + + /* + * Print out the 'connected as' stuff here as we need + * to know the effective uid and gid we will be using + * (at least initially). + */ + + if( DEBUGLVL( IS_IPC(conn) ? 3 : 1 ) ) { + dbgtext( "%s (%s) ", remote_machine, conn->client_address ); + dbgtext( "connect to service %s ", lp_servicename(SNUM(conn)) ); + dbgtext( "initially as user %s ", user ); + dbgtext( "(uid=%d, gid=%d) ", (int)geteuid(), (int)getegid() ); + dbgtext( "(pid %d)\n", (int)sys_getpid() ); + } + + /* Add veto/hide lists */ + if (!IS_IPC(conn) && !IS_PRINT(conn)) { + set_namearray( &conn->veto_list, lp_veto_files(SNUM(conn))); + set_namearray( &conn->hide_list, lp_hide_files(SNUM(conn))); + set_namearray( &conn->veto_oplock_list, lp_veto_oplocks(SNUM(conn))); + } + + /* Invoke VFS make connection hook */ + + if (conn->vfs_ops.connect) { + if (conn->vfs_ops.connect(conn, service, user) < 0) { + DEBUG(0,("make_connection: VFS make connection failed!\n")); + *status = NT_STATUS_UNSUCCESSFUL; + change_to_root_user(); + conn_free(conn); + return NULL; + } + } + + /* we've finished with the user stuff - go back to root */ + change_to_root_user(); + + return(conn); +} + + +/**************************************************************************** +close a cnum +****************************************************************************/ +void close_cnum(connection_struct *conn, uint16 vuid) +{ + DirCacheFlush(SNUM(conn)); + + change_to_root_user(); + + DEBUG(IS_IPC(conn)?3:1, ("%s (%s) closed connection to service %s\n", + remote_machine,conn->client_address, + lp_servicename(SNUM(conn)))); + + if (conn->vfs_ops.disconnect != NULL) { + + /* Call VFS disconnect hook */ + + conn->vfs_ops.disconnect(conn); + + } + + yield_connection(conn, lp_servicename(SNUM(conn))); + + file_close_conn(conn); + dptr_closecnum(conn); + + /* execute any "postexec = " line */ + if (*lp_postexec(SNUM(conn)) && + change_to_user(conn, vuid)) { + pstring cmd; + pstrcpy(cmd,lp_postexec(SNUM(conn))); + standard_sub_conn(conn,cmd); + smbrun(cmd,NULL); + change_to_root_user(); + } + + change_to_root_user(); + /* execute any "root postexec = " line */ + if (*lp_rootpostexec(SNUM(conn))) { + pstring cmd; + pstrcpy(cmd,lp_rootpostexec(SNUM(conn))); + standard_sub_conn(conn,cmd); + smbrun(cmd,NULL); + } + conn_free(conn); +} diff --git a/source3/smbd/session.c b/source3/smbd/session.c new file mode 100644 index 0000000000..05a7b24da2 --- /dev/null +++ b/source3/smbd/session.c @@ -0,0 +1,178 @@ +/* + Unix SMB/CIFS implementation. + session handling for utmp and PAM + Copyright (C) tridge@samba.org 2001 + Copyright (C) abartlet@pcug.org.au 2001 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* a "session" is claimed when we do a SessionSetupX operation + and is yielded when the corresponding vuid is destroyed. + + sessions are used to populate utmp and PAM session structures +*/ + +#include "includes.h" + +extern fstring remote_machine; + +static TDB_CONTEXT *tdb; +/* called when a session is created */ +BOOL session_claim(user_struct *vuser) +{ + int i; + TDB_DATA data; + struct sessionid sessionid; + uint32 pid = (uint32)sys_getpid(); + TDB_DATA key; + fstring keystr; + char * hostname; + + vuser->session_id = 0; + + /* don't register sessions for the guest user - its just too + expensive to go through pam session code for browsing etc */ + if (vuser->guest) { + return True; + } + + if (!tdb) { + tdb = tdb_open_log(lock_path("sessionid.tdb"), 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT, + O_RDWR | O_CREAT, 0644); + if (!tdb) { + DEBUG(1,("session_claim: failed to open sessionid tdb\n")); + return False; + } + } + + ZERO_STRUCT(sessionid); + + data.dptr = NULL; + data.dsize = 0; + + for (i=1;i<MAX_SESSION_ID;i++) { + slprintf(keystr, sizeof(keystr)-1, "ID/%d", i); + key.dptr = keystr; + key.dsize = strlen(keystr)+1; + + if (tdb_store(tdb, key, data, TDB_INSERT) == 0) break; + } + + if (i == MAX_SESSION_ID) { + DEBUG(1,("session_claim: out of session IDs (max is %d)\n", + MAX_SESSION_ID)); + return False; + } + + /* If 'hostname lookup' == yes, then do the DNS lookup. This is + needed becouse utmp and PAM both expect DNS names + + client_name() handles this case internally. + */ + + hostname = client_name(); + if (strcmp(hostname, "UNKNOWN") == 0) { + hostname = client_addr(); + } + + fstrcpy(sessionid.username, vuser->user.unix_name); + fstrcpy(sessionid.hostname, hostname); + slprintf(sessionid.id_str, sizeof(sessionid.id_str)-1, SESSION_TEMPLATE, i); + sessionid.id_num = i; + sessionid.pid = pid; + sessionid.uid = vuser->uid; + sessionid.gid = vuser->gid; + fstrcpy(sessionid.remote_machine, remote_machine); + fstrcpy(sessionid.ip_addr, client_addr()); + + if (!smb_pam_claim_session(sessionid.username, sessionid.id_str, sessionid.hostname)) { + DEBUG(1,("pam_session rejected the session for %s [%s]\n", + sessionid.username, sessionid.id_str)); + tdb_delete(tdb, key); + return False; + } + + data.dptr = (char *)&sessionid; + data.dsize = sizeof(sessionid); + if (tdb_store(tdb, key, data, TDB_MODIFY) != 0) { + DEBUG(1,("session_claim: unable to create session id record\n")); + return False; + } + +#if WITH_UTMP + if (lp_utmp()) { + sys_utmp_claim(sessionid.username, sessionid.hostname, + sessionid.id_str, sessionid.id_num); + } +#endif + + vuser->session_id = i; + return True; +} + +/* called when a session is destroyed */ +void session_yield(user_struct *vuser) +{ + TDB_DATA dbuf; + struct sessionid sessionid; + TDB_DATA key; + fstring keystr; + + if (!tdb) return; + + if (vuser->session_id == 0) { + return; + } + + slprintf(keystr, sizeof(keystr)-1, "ID/%d", vuser->session_id); + + key.dptr = keystr; + key.dsize = strlen(keystr)+1; + + dbuf = tdb_fetch(tdb, key); + + if (dbuf.dsize != sizeof(sessionid)) + return; + + memcpy(&sessionid, dbuf.dptr, sizeof(sessionid)); + + SAFE_FREE(dbuf.dptr); + +#if WITH_UTMP + if (lp_utmp()) { + sys_utmp_yield(sessionid.username, sessionid.hostname, + sessionid.id_str, sessionid.id_num); + } +#endif + + smb_pam_close_session(sessionid.username, sessionid.id_str, sessionid.hostname); + + tdb_delete(tdb, key); +} + +BOOL session_traverse(int (*fn)(TDB_CONTEXT *, TDB_DATA, TDB_DATA, void *), void *state) +{ + if (!tdb) { + DEBUG(3, ("No tdb opened\n")); + return False; + } + + tdb_traverse(tdb, fn, state); + return True; +} + + + diff --git a/source3/smbd/sesssetup.c b/source3/smbd/sesssetup.c new file mode 100644 index 0000000000..899c9174b2 --- /dev/null +++ b/source3/smbd/sesssetup.c @@ -0,0 +1,808 @@ +/* + Unix SMB/CIFS implementation. + handle SMBsessionsetup + Copyright (C) Andrew Tridgell 1998-2001 + Copyright (C) Andrew Bartlett 2001 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +uint32 global_client_caps = 0; +static struct auth_context *ntlmssp_auth_context = NULL; + +/* + on a logon error possibly map the error to success if "map to guest" + is set approriately +*/ +static NTSTATUS do_map_to_guest(NTSTATUS status, auth_serversupplied_info **server_info, + const char *user, const char *domain) +{ + if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) { + if ((lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_USER) || + (lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_PASSWORD)) { + DEBUG(3,("No such user %s [%s] - using guest account\n", + user, domain)); + make_server_info_guest(server_info); + status = NT_STATUS_OK; + } + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) { + if (lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_PASSWORD) { + DEBUG(3,("Registered username %s for guest access\n",user)); + make_server_info_guest(server_info); + status = NT_STATUS_OK; + } + } + + return status; +} + + +/**************************************************************************** + Add the standard 'Samba' signature to the end of the session setup. +****************************************************************************/ +static void add_signature(char *outbuf) +{ + char *p; + p = smb_buf(outbuf); + p += srvstr_push(outbuf, p, "Unix", -1, STR_TERMINATE); + p += srvstr_push(outbuf, p, "Samba", -1, STR_TERMINATE); + p += srvstr_push(outbuf, p, lp_workgroup(), -1, STR_TERMINATE); + set_message_end(outbuf,p); +} + +/**************************************************************************** + Do a 'guest' logon, getting back the +****************************************************************************/ +static NTSTATUS check_guest_password(auth_serversupplied_info **server_info) +{ + struct auth_context *auth_context; + auth_usersupplied_info *user_info = NULL; + + NTSTATUS nt_status; + unsigned char chal[8]; + + ZERO_STRUCT(chal); + + DEBUG(3,("Got anonymous request\n")); + + if (!NT_STATUS_IS_OK(nt_status = make_auth_context_fixed(&auth_context, chal))) { + return nt_status; + } + + if (!make_user_info_guest(&user_info)) { + (auth_context->free)(&auth_context); + return NT_STATUS_NO_MEMORY; + } + + nt_status = auth_context->check_ntlm_password(auth_context, user_info, server_info); + (auth_context->free)(&auth_context); + free_user_info(&user_info); + return nt_status; +} + + +#ifdef HAVE_KRB5 +/**************************************************************************** +reply to a session setup spnego negotiate packet for kerberos +****************************************************************************/ +static int reply_spnego_kerberos(connection_struct *conn, + char *inbuf, char *outbuf, + int length, int bufsize, + DATA_BLOB *secblob) +{ + DATA_BLOB ticket; + char *client, *p; + const struct passwd *pw; + char *user; + int sess_vuid; + NTSTATUS ret; + DATA_BLOB auth_data; + auth_serversupplied_info *server_info = NULL; + ADS_STRUCT *ads; + + if (!spnego_parse_krb5_wrap(*secblob, &ticket)) { + return ERROR_NT(NT_STATUS_LOGON_FAILURE); + } + + ads = ads_init(NULL, NULL, NULL, NULL); + + ret = ads_verify_ticket(ads, &ticket, &client, &auth_data); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(1,("Failed to verify incoming ticket!\n")); + ads_destroy(&ads); + return ERROR_NT(NT_STATUS_LOGON_FAILURE); + } + + DEBUG(3,("Ticket name is [%s]\n", client)); + + p = strchr_m(client, '@'); + if (!p) { + DEBUG(3,("Doesn't look like a valid principal\n")); + ads_destroy(&ads); + return ERROR_NT(NT_STATUS_LOGON_FAILURE); + } + + *p = 0; + if (strcasecmp(p+1, ads->realm) != 0) { + DEBUG(3,("Ticket for foreign realm %s@%s\n", client, p+1)); + if (!lp_allow_trusted_domains()) { + return ERROR_NT(NT_STATUS_LOGON_FAILURE); + } + /* this gives a fully qualified user name (ie. with full realm). + that leads to very long usernames, but what else can we do? */ + asprintf(&user, "%s%s%s", p+1, lp_winbind_separator(), client); + } else { + user = strdup(client); + } + ads_destroy(&ads); + + /* the password is good - let them in */ + pw = smb_getpwnam(user,False); + if (!pw && !strstr(user, lp_winbind_separator())) { + char *user2; + /* try it with a winbind domain prefix */ + asprintf(&user2, "%s%s%s", lp_workgroup(), lp_winbind_separator(), user); + pw = smb_getpwnam(user2,False); + if (pw) { + free(user); + user = user2; + } + } + + if (!pw) { + DEBUG(1,("Username %s is invalid on this system\n",user)); + return ERROR_NT(NT_STATUS_NO_SUCH_USER); + } + + if (!make_server_info_pw(&server_info,pw)) { + DEBUG(1,("make_server_info_from_pw failed!\n")); + return ERROR_NT(NT_STATUS_NO_MEMORY); + } + + sess_vuid = register_vuid(server_info, user); + + free(user); + free_server_info(&server_info); + + if (sess_vuid == -1) { + return ERROR_NT(NT_STATUS_LOGON_FAILURE); + } + + set_message(outbuf,4,0,True); + SSVAL(outbuf, smb_vwv3, 0); + add_signature(outbuf); + + SSVAL(outbuf,smb_uid,sess_vuid); + SSVAL(inbuf,smb_uid,sess_vuid); + + return chain_reply(inbuf,outbuf,length,bufsize); +} +#endif + + +/**************************************************************************** +send a security blob via a session setup reply +****************************************************************************/ +static BOOL reply_sesssetup_blob(connection_struct *conn, char *outbuf, + DATA_BLOB blob) +{ + char *p; + + set_message(outbuf,4,0,True); + + /* we set NT_STATUS_MORE_PROCESSING_REQUIRED to tell the other end + that we aren't finished yet */ + + SIVAL(outbuf, smb_rcls, NT_STATUS_V(NT_STATUS_MORE_PROCESSING_REQUIRED)); + SSVAL(outbuf, smb_vwv0, 0xFF); /* no chaining possible */ + SSVAL(outbuf, smb_vwv3, blob.length); + p = smb_buf(outbuf); + memcpy(p, blob.data, blob.length); + p += blob.length; + p += srvstr_push(outbuf, p, "Unix", -1, STR_TERMINATE); + p += srvstr_push(outbuf, p, "Samba", -1, STR_TERMINATE); + p += srvstr_push(outbuf, p, lp_workgroup(), -1, STR_TERMINATE); + set_message_end(outbuf,p); + + return send_smb(smbd_server_fd(),outbuf); +} + +/**************************************************************************** +reply to a session setup spnego negotiate packet +****************************************************************************/ +static int reply_spnego_negotiate(connection_struct *conn, + char *inbuf, + char *outbuf, + int length, int bufsize, + DATA_BLOB blob1) +{ + char *OIDs[ASN1_MAX_OIDS]; + DATA_BLOB secblob; + int i; + uint32 ntlmssp_command, neg_flags; + DATA_BLOB sess_key, chal, spnego_chal; + const uint8 *cryptkey; + BOOL got_kerberos = False; + NTSTATUS nt_status; + + /* parse out the OIDs and the first sec blob */ + if (!parse_negTokenTarg(blob1, OIDs, &secblob)) { + return ERROR_NT(NT_STATUS_LOGON_FAILURE); + } + + for (i=0;OIDs[i];i++) { + DEBUG(3,("Got OID %s\n", OIDs[i])); + if (strcmp(OID_KERBEROS5, OIDs[i]) == 0 || + strcmp(OID_KERBEROS5_OLD, OIDs[i]) == 0) { + got_kerberos = True; + } + free(OIDs[i]); + } + DEBUG(3,("Got secblob of size %d\n", secblob.length)); + +#ifdef HAVE_KRB5 + if (got_kerberos) { + int ret = reply_spnego_kerberos(conn, inbuf, outbuf, + length, bufsize, &secblob); + data_blob_free(&secblob); + return ret; + } +#endif + + /* parse the NTLMSSP packet */ +#if 0 + file_save("secblob.dat", secblob.data, secblob.length); +#endif + + if (!msrpc_parse(&secblob, "CddB", + "NTLMSSP", + &ntlmssp_command, + &neg_flags, + &sess_key)) { + return ERROR_NT(NT_STATUS_LOGON_FAILURE); + } + + data_blob_free(&secblob); + data_blob_free(&sess_key); + + if (ntlmssp_command != NTLMSSP_NEGOTIATE) { + return ERROR_NT(NT_STATUS_LOGON_FAILURE); + } + + DEBUG(3,("Got neg_flags=%08x\n", neg_flags)); + + if (ntlmssp_auth_context) { + (ntlmssp_auth_context->free)(&ntlmssp_auth_context); + } + + if (!NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(&ntlmssp_auth_context))) { + return ERROR_NT(nt_status); + } + + cryptkey = ntlmssp_auth_context->get_ntlm_challenge(ntlmssp_auth_context); + + /* Give them the challenge. For now, ignore neg_flags and just + return the flags we want. Obviously this is not correct */ + + neg_flags = NTLMSSP_NEGOTIATE_UNICODE | + NTLMSSP_NEGOTIATE_LM_KEY | + NTLMSSP_NEGOTIATE_NTLM; + + msrpc_gen(&chal, "Cddddbdddd", + "NTLMSSP", + NTLMSSP_CHALLENGE, + 0, + 0x30, /* ?? */ + neg_flags, + cryptkey, 8, + 0, 0, 0, + 0x3000); /* ?? */ + + if (!spnego_gen_challenge(&spnego_chal, &chal, &chal)) { + DEBUG(3,("Failed to generate challenge\n")); + return ERROR_NT(NT_STATUS_LOGON_FAILURE); + } + + /* now tell the client to send the auth packet */ + reply_sesssetup_blob(conn, outbuf, spnego_chal); + + data_blob_free(&chal); + data_blob_free(&spnego_chal); + + /* and tell smbd that we have already replied to this packet */ + return -1; +} + + +/**************************************************************************** +reply to a session setup spnego auth packet +****************************************************************************/ +static int reply_spnego_auth(connection_struct *conn, char *inbuf, char *outbuf, + int length, int bufsize, + DATA_BLOB blob1) +{ + DATA_BLOB auth; + char *workgroup = NULL, *user = NULL, *machine = NULL; + DATA_BLOB lmhash, nthash, sess_key; + DATA_BLOB plaintext_password = data_blob(NULL, 0); + uint32 ntlmssp_command, neg_flags; + NTSTATUS nt_status; + int sess_vuid; + BOOL as_guest; + uint32 auth_flags = AUTH_FLAG_NONE; + + auth_usersupplied_info *user_info = NULL; + auth_serversupplied_info *server_info = NULL; + + if (!spnego_parse_auth(blob1, &auth)) { +#if 0 + file_save("auth.dat", blob1.data, blob1.length); +#endif + return ERROR_NT(NT_STATUS_LOGON_FAILURE); + } + + /* now the NTLMSSP encoded auth hashes */ + if (!msrpc_parse(&auth, "CdBBUUUBd", + "NTLMSSP", + &ntlmssp_command, + &lmhash, + &nthash, + &workgroup, + &user, + &machine, + &sess_key, + &neg_flags)) { + return ERROR_NT(NT_STATUS_LOGON_FAILURE); + } + + data_blob_free(&auth); + data_blob_free(&sess_key); + + DEBUG(3,("Got user=[%s] workgroup=[%s] machine=[%s] len1=%d len2=%d\n", + user, workgroup, machine, lmhash.length, nthash.length)); + +#if 0 + file_save("nthash1.dat", nthash.data, nthash.length); + file_save("lmhash1.dat", lmhash.data, lmhash.length); +#endif + + if (lmhash.length) { + auth_flags |= AUTH_FLAG_LM_RESP; + } + + if (nthash.length == 24) { + auth_flags |= AUTH_FLAG_NTLM_RESP; + } else if (nthash.length > 24) { + auth_flags |= AUTH_FLAG_NTLMv2_RESP; + } + + if (!make_user_info_map(&user_info, + user, workgroup, + machine, + lmhash, nthash, + plaintext_password, + auth_flags, True)) { + return ERROR_NT(NT_STATUS_NO_MEMORY); + } + + nt_status = ntlmssp_auth_context->check_ntlm_password(ntlmssp_auth_context, user_info, &server_info); + + if (!NT_STATUS_IS_OK(nt_status)) { + nt_status = do_map_to_guest(nt_status, &server_info, user, workgroup); + } + + SAFE_FREE(workgroup); + SAFE_FREE(machine); + + (ntlmssp_auth_context->free)(&ntlmssp_auth_context); + + free_user_info(&user_info); + + data_blob_free(&lmhash); + + data_blob_free(&nthash); + + if (!NT_STATUS_IS_OK(nt_status)) { + SAFE_FREE(user); + return ERROR_NT(nt_status_squash(nt_status)); + } + + as_guest = server_info->guest; + + sess_vuid = register_vuid(server_info, user); + free_server_info(&server_info); + + SAFE_FREE(user); + + if (sess_vuid == -1) { + return ERROR_NT(NT_STATUS_LOGON_FAILURE); + } + + set_message(outbuf,4,0,True); + SSVAL(outbuf, smb_vwv3, 0); + + if (as_guest) { + SSVAL(outbuf,smb_vwv2,1); + } + + add_signature(outbuf); + + SSVAL(outbuf,smb_uid,sess_vuid); + SSVAL(inbuf,smb_uid,sess_vuid); + + return chain_reply(inbuf,outbuf,length,bufsize); +} + + +/**************************************************************************** +reply to a session setup spnego anonymous packet +****************************************************************************/ +static int reply_spnego_anonymous(connection_struct *conn, char *inbuf, char *outbuf, + int length, int bufsize) +{ + int sess_vuid; + auth_serversupplied_info *server_info = NULL; + NTSTATUS nt_status; + + nt_status = check_guest_password(&server_info); + + if (!NT_STATUS_IS_OK(nt_status)) { + return ERROR_NT(nt_status_squash(nt_status)); + } + + sess_vuid = register_vuid(server_info, lp_guestaccount()); + + free_server_info(&server_info); + + if (sess_vuid == -1) { + return ERROR_NT(NT_STATUS_LOGON_FAILURE); + } + + set_message(outbuf,4,0,True); + SSVAL(outbuf, smb_vwv3, 0); + add_signature(outbuf); + + SSVAL(outbuf,smb_uid,sess_vuid); + SSVAL(inbuf,smb_uid,sess_vuid); + + return chain_reply(inbuf,outbuf,length,bufsize); +} + + +/**************************************************************************** +reply to a session setup command +****************************************************************************/ +static int reply_sesssetup_and_X_spnego(connection_struct *conn, char *inbuf,char *outbuf, + int length,int bufsize) +{ + uint8 *p; + DATA_BLOB blob1; + int ret; + + DEBUG(3,("Doing spnego session setup\n")); + + if (global_client_caps == 0) { + global_client_caps = IVAL(inbuf,smb_vwv10); + } + + p = (uint8 *)smb_buf(inbuf); + + if (SVAL(inbuf, smb_vwv7) == 0) { + /* an anonymous request */ + return reply_spnego_anonymous(conn, inbuf, outbuf, length, bufsize); + } + + /* pull the spnego blob */ + blob1 = data_blob(p, SVAL(inbuf, smb_vwv7)); + +#if 0 + file_save("negotiate.dat", blob1.data, blob1.length); +#endif + + if (blob1.data[0] == ASN1_APPLICATION(0)) { + /* its a negTokenTarg packet */ + ret = reply_spnego_negotiate(conn, inbuf, outbuf, length, bufsize, blob1); + data_blob_free(&blob1); + return ret; + } + + if (blob1.data[0] == ASN1_CONTEXT(1)) { + /* its a auth packet */ + ret = reply_spnego_auth(conn, inbuf, outbuf, length, bufsize, blob1); + data_blob_free(&blob1); + return ret; + } + + /* what sort of packet is this? */ + DEBUG(1,("Unknown packet in reply_sesssetup_and_X_spnego\n")); + + data_blob_free(&blob1); + + return ERROR_NT(NT_STATUS_LOGON_FAILURE); +} + + +/**************************************************************************** +reply to a session setup command +****************************************************************************/ +int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf, + int length,int bufsize) +{ + int sess_vuid; + int smb_bufsize; + DATA_BLOB lm_resp; + DATA_BLOB nt_resp; + DATA_BLOB plaintext_password; + pstring user; + pstring sub_user; /* Sainitised username for substituion */ + fstring domain; + fstring native_os; + fstring native_lanman; + static BOOL done_sesssetup = False; + extern BOOL global_encrypted_passwords_negotiated; + extern BOOL global_spnego_negotiated; + extern int Protocol; + extern fstring remote_machine; + extern userdom_struct current_user_info; + extern int max_send; + + auth_usersupplied_info *user_info = NULL; + extern struct auth_context *negprot_global_auth_context; + auth_serversupplied_info *server_info = NULL; + + NTSTATUS nt_status; + + BOOL doencrypt = global_encrypted_passwords_negotiated; + + START_PROFILE(SMBsesssetupX); + + ZERO_STRUCT(lm_resp); + ZERO_STRUCT(nt_resp); + ZERO_STRUCT(plaintext_password); + + DEBUG(3,("wct=%d flg2=0x%x\n", CVAL(inbuf, smb_wct), SVAL(inbuf, smb_flg2))); + + /* a SPNEGO session setup has 12 command words, whereas a normal + NT1 session setup has 13. See the cifs spec. */ + if (CVAL(inbuf, smb_wct) == 12 && + (SVAL(inbuf, smb_flg2) & FLAGS2_EXTENDED_SECURITY)) { + if (!global_spnego_negotiated) { + DEBUG(0,("reply_sesssetup_and_X: Rejecting attempt at SPNEGO session setup when it was not negoitiated.\n")); + return ERROR_NT(NT_STATUS_UNSUCCESSFUL); + } + + return reply_sesssetup_and_X_spnego(conn, inbuf, outbuf, length, bufsize); + } + + smb_bufsize = SVAL(inbuf,smb_vwv2); + + if (Protocol < PROTOCOL_NT1) { + uint16 passlen1 = SVAL(inbuf,smb_vwv7); + if (passlen1 > MAX_PASS_LEN) { + return ERROR_DOS(ERRDOS,ERRbuftoosmall); + } + + if (doencrypt) { + lm_resp = data_blob(smb_buf(inbuf), passlen1); + } else { + plaintext_password = data_blob(smb_buf(inbuf), passlen1+1); + /* Ensure null termination */ + plaintext_password.data[passlen1] = 0; + } + + srvstr_pull(inbuf, user, smb_buf(inbuf)+passlen1, sizeof(user), -1, STR_TERMINATE); + *domain = 0; + + } else { + uint16 passlen1 = SVAL(inbuf,smb_vwv7); + uint16 passlen2 = SVAL(inbuf,smb_vwv8); + enum remote_arch_types ra_type = get_remote_arch(); + char *p = smb_buf(inbuf); + + if(global_client_caps == 0) + global_client_caps = IVAL(inbuf,smb_vwv11); + + /* client_caps is used as final determination if client is NT or Win95. + This is needed to return the correct error codes in some + circumstances. + */ + + if(ra_type == RA_WINNT || ra_type == RA_WIN2K || ra_type == RA_WIN95) { + if(!(global_client_caps & (CAP_NT_SMBS | CAP_STATUS32))) { + set_remote_arch( RA_WIN95); + } + } + + if (passlen1 > MAX_PASS_LEN) { + return ERROR_DOS(ERRDOS,ERRbuftoosmall); + } + + passlen1 = MIN(passlen1, MAX_PASS_LEN); + passlen2 = MIN(passlen2, MAX_PASS_LEN); + + if (!doencrypt) { + /* both Win95 and WinNT stuff up the password lengths for + non-encrypting systems. Uggh. + + if passlen1==24 its a win95 system, and its setting the + password length incorrectly. Luckily it still works with the + default code because Win95 will null terminate the password + anyway + + if passlen1>0 and passlen2>0 then maybe its a NT box and its + setting passlen2 to some random value which really stuffs + things up. we need to fix that one. */ + + if (passlen1 > 0 && passlen2 > 0 && passlen2 != 24 && passlen2 != 1) + passlen2 = 0; + } + + /* Save the lanman2 password and the NT md4 password. */ + + if ((doencrypt) && (passlen1 != 0) && (passlen1 != 24)) { + doencrypt = False; + } + + if (doencrypt) { + lm_resp = data_blob(p, passlen1); + nt_resp = data_blob(p+passlen1, passlen2); + } else { + plaintext_password = data_blob(p, passlen1+1); + /* Ensure null termination */ + plaintext_password.data[passlen1] = 0; + } + + p += passlen1 + passlen2; + p += srvstr_pull(inbuf, user, p, sizeof(user), -1, + STR_TERMINATE); + p += srvstr_pull(inbuf, domain, p, sizeof(domain), + -1, STR_TERMINATE); + p += srvstr_pull(inbuf, native_os, p, sizeof(native_os), + -1, STR_TERMINATE); + p += srvstr_pull(inbuf, native_lanman, p, sizeof(native_lanman), + -1, STR_TERMINATE); + DEBUG(3,("Domain=[%s] NativeOS=[%s] NativeLanMan=[%s]\n", + domain,native_os,native_lanman)); + } + + /* don't allow for weird usernames or domains */ + alpha_strcpy(user, user, ". _-$", sizeof(user)); + alpha_strcpy(domain, domain, ". _-", sizeof(domain)); + if (strstr(user, "..") || strstr(domain,"..")) { + return ERROR_NT(NT_STATUS_LOGON_FAILURE); + } + + DEBUG(3,("sesssetupX:name=[%s]\\[%s]@[%s]\n", domain, user, remote_machine)); + + if (*user) { + if (global_spnego_negotiated) { + + /* This has to be here, becouse this is a perfectly valid behaviour for guest logons :-( */ + + DEBUG(0,("reply_sesssetup_and_X: Rejecting attempt at 'normal' session setup after negotiating spnego.\n")); + return ERROR_NT(NT_STATUS_UNSUCCESSFUL); + } + pstrcpy(sub_user, user); + } else { + pstrcpy(sub_user, lp_guestaccount()); + } + + pstrcpy(current_user_info.smb_name,sub_user); + + reload_services(True); + + if (lp_security() == SEC_SHARE) { + /* in share level we should ignore any passwords */ + + data_blob_free(&lm_resp); + data_blob_free(&nt_resp); + data_blob_clear_free(&plaintext_password); + + map_username(sub_user); + add_session_user(sub_user); + /* Then force it to null for the benfit of the code below */ + *user = 0; + } + + if (!*user) { + + nt_status = check_guest_password(&server_info); + + } else if (doencrypt) { + if (!make_user_info_for_reply_enc(&user_info, + user, domain, + lm_resp, nt_resp)) { + nt_status = NT_STATUS_NO_MEMORY; + } else { + nt_status = negprot_global_auth_context->check_ntlm_password(negprot_global_auth_context, + user_info, + &server_info); + } + } else { + struct auth_context *plaintext_auth_context = NULL; + const uint8 *chal; + if (NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(&plaintext_auth_context))) { + chal = plaintext_auth_context->get_ntlm_challenge(plaintext_auth_context); + + if (!make_user_info_for_reply(&user_info, + user, domain, chal, + plaintext_password)) { + nt_status = NT_STATUS_NO_MEMORY; + } + + if (NT_STATUS_IS_OK(nt_status)) { + nt_status = plaintext_auth_context->check_ntlm_password(plaintext_auth_context, + user_info, + &server_info); + + (plaintext_auth_context->free)(&plaintext_auth_context); + } + } + } + + free_user_info(&user_info); + + data_blob_free(&lm_resp); + data_blob_free(&nt_resp); + data_blob_clear_free(&plaintext_password); + + if (!NT_STATUS_IS_OK(nt_status)) { + nt_status = do_map_to_guest(nt_status, &server_info, user, domain); + } + + if (!NT_STATUS_IS_OK(nt_status)) { + return ERROR_NT(nt_status_squash(nt_status)); + } + + /* it's ok - setup a reply */ + if (Protocol < PROTOCOL_NT1) { + set_message(outbuf,3,0,True); + } else { + set_message(outbuf,3,0,True); + add_signature(outbuf); + /* perhaps grab OS version here?? */ + } + + if (server_info->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 */ + + sess_vuid = register_vuid(server_info, sub_user); + + free_server_info(&server_info); + + if (sess_vuid == -1) { + return ERROR_NT(NT_STATUS_LOGON_FAILURE); + } + + + SSVAL(outbuf,smb_uid,sess_vuid); + SSVAL(inbuf,smb_uid,sess_vuid); + + if (!done_sesssetup) + max_send = MIN(max_send,smb_bufsize); + + done_sesssetup = True; + + END_PROFILE(SMBsesssetupX); + return chain_reply(inbuf,outbuf,length,bufsize); +} diff --git a/source3/smbd/smbrun.c b/source3/smbd/smbrun.c deleted file mode 100644 index df12ae1f85..0000000000 --- a/source3/smbd/smbrun.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - 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/srvstr.c b/source3/smbd/srvstr.c new file mode 100644 index 0000000000..90da422f13 --- /dev/null +++ b/source3/smbd/srvstr.c @@ -0,0 +1,32 @@ +/* + Unix SMB/CIFS implementation. + server specific string routines + Copyright (C) Andrew Tridgell 2001 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +int srvstr_push(void *base_ptr, void *dest, const char *src, int dest_len, int flags) +{ + return push_string(base_ptr, dest, src, dest_len, flags); +} + +int srvstr_pull(void *base_ptr, char *dest, const void *src, int dest_len, int src_len, + int flags) +{ + return pull_string(base_ptr, dest, src, dest_len, src_len, flags); +} diff --git a/source3/smbd/ssl.c b/source3/smbd/ssl.c new file mode 100644 index 0000000000..7fcb48a954 --- /dev/null +++ b/source3/smbd/ssl.c @@ -0,0 +1,286 @@ +/* + Unix SMB/CIFS implementation. + SSLeay utility functions + Copyright (C) Christian Starkjohann <cs@obdev.at> 1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + 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. +*/ + +/* + * since includes.h pulls in config.h which is were WITH_SSL will be + * defined, we want to include includes.h before testing for WITH_SSL + * RJS 26-Jan-1999 + */ + +#include "includes.h" + +#ifdef WITH_SSL /* should always be defined if this module is compiled */ + +#include <openssl/ssl.h> +#include <openssl/err.h> + +BOOL sslEnabled; +SSL *ssl = NULL; +int sslFd = -1; +static SSL_CTX *sslContext = NULL; +extern int DEBUGLEVEL; + +static int ssl_verify_cb(int ok, X509_STORE_CTX *ctx) +{ +char buffer[256]; + + X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), + buffer, sizeof(buffer)); + if(ok){ + DEBUG(0, ("SSL: Certificate OK: %s\n", buffer)); + }else{ + switch (ctx->error){ + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + DEBUG(0, ("SSL: Cert error: CA not known: %s\n", buffer)); + break; + case X509_V_ERR_CERT_NOT_YET_VALID: + DEBUG(0, ("SSL: Cert error: Cert not yet valid: %s\n", buffer)); + break; + case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: + DEBUG(0, ("SSL: Cert error: illegal \'not before\' field: %s\n", + buffer)); + break; + case X509_V_ERR_CERT_HAS_EXPIRED: + DEBUG(0, ("SSL: Cert error: Cert expired: %s\n", buffer)); + break; + case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: + DEBUG(0, ("SSL: Cert error: invalid \'not after\' field: %s\n", + buffer)); + break; + default: + DEBUG(0, ("SSL: Cert error: unknown error %d in %s\n", ctx->error, + buffer)); + break; + } + } + return ok; +} + +static RSA *ssl_temp_rsa_cb(SSL *ssl, int is_export, int keylength) +{ +static RSA *rsa = NULL; + + if(rsa == NULL) + rsa = RSA_generate_key(keylength, RSA_F4, NULL, NULL); + return rsa; +} + +/* This is called before we fork. It should ask the user for the pass phrase + * if necessary. Error output can still go to stderr because the process + * has a terminal. + */ +int sslutil_init(int isServer) +{ +int err, entropybytes; +char *certfile, *keyfile, *ciphers, *cacertDir, *cacertFile; +char *egdsocket, *entropyfile; + + SSL_load_error_strings(); + SSLeay_add_ssl_algorithms(); + egdsocket = lp_ssl_egdsocket(); + if (egdsocket != NULL && *egdsocket != 0) + RAND_egd(egdsocket); + entropyfile = lp_ssl_entropyfile(); + entropybytes = lp_ssl_entropybytes(); + if (entropyfile != NULL && *entropyfile != 0) + RAND_load_file(entropyfile, entropybytes); + switch(lp_ssl_version()){ + case SMB_SSL_V2: sslContext = SSL_CTX_new(SSLv2_method()); break; + case SMB_SSL_V3: sslContext = SSL_CTX_new(SSLv3_method()); break; + default: + case SMB_SSL_V23: sslContext = SSL_CTX_new(SSLv23_method()); break; + case SMB_SSL_TLS1: sslContext = SSL_CTX_new(TLSv1_method()); break; + } + if(sslContext == NULL){ + err = ERR_get_error(); + fprintf(stderr, "SSL: Error allocating context: %s\n", + ERR_error_string(err, NULL)); + exit(1); + } + if(lp_ssl_compatibility()){ + SSL_CTX_set_options(sslContext, SSL_OP_ALL); + } + certfile = isServer ? lp_ssl_server_cert() : lp_ssl_client_cert(); + if((certfile == NULL || *certfile == 0) && isServer){ + fprintf(stderr, "SSL: No cert file specified in config file!\n"); + fprintf(stderr, "The server MUST have a certificate!\n"); + exit(1); + } + keyfile = isServer ? lp_ssl_server_privkey() : lp_ssl_client_privkey(); + if(keyfile == NULL || *keyfile == 0) + keyfile = certfile; + if(certfile != NULL && *certfile != 0){ + if(!SSL_CTX_use_certificate_chain_file(sslContext, certfile)){ + err = ERR_get_error(); + fprintf(stderr, "SSL: error reading certificate from file %s: %s\n", + certfile, ERR_error_string(err, NULL)); + exit(1); + } + if(!SSL_CTX_use_PrivateKey_file(sslContext, keyfile, SSL_FILETYPE_PEM)){ + err = ERR_get_error(); + fprintf(stderr, "SSL: error reading private key from file %s: %s\n", + keyfile, ERR_error_string(err, NULL)); + exit(1); + } + if(!SSL_CTX_check_private_key(sslContext)){ + err = ERR_get_error(); + fprintf(stderr, "SSL: Private key does not match public key in cert!\n"); + exit(1); + } + } + cacertDir = lp_ssl_cacertdir(); + cacertFile = lp_ssl_cacertfile(); + if(cacertDir != NULL && *cacertDir == 0) + cacertDir = NULL; + if(cacertFile != NULL && *cacertFile == 0) + cacertFile = NULL; + if(!SSL_CTX_load_verify_locations(sslContext, cacertFile, cacertDir)){ + err = ERR_get_error(); + if (cacertFile || cacertDir) { + fprintf(stderr, "SSL: Error error setting CA cert locations: %s\n", + ERR_error_string(err, NULL)); + fprintf(stderr, "trying default locations.\n"); + } + cacertFile = cacertDir = NULL; + if(!SSL_CTX_set_default_verify_paths(sslContext)){ + err = ERR_get_error(); + fprintf(stderr, "SSL: Error error setting default CA cert location: %s\n", + ERR_error_string(err, NULL)); + exit(1); + } + } + SSL_CTX_set_tmp_rsa_callback(sslContext, ssl_temp_rsa_cb); + if((ciphers = lp_ssl_ciphers()) != NULL && *ciphers != 0) + SSL_CTX_set_cipher_list(sslContext, ciphers); + if((isServer && lp_ssl_reqClientCert()) || (!isServer && lp_ssl_reqServerCert())){ + SSL_CTX_set_verify(sslContext, + SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, ssl_verify_cb); + }else{ + SSL_CTX_set_verify(sslContext, SSL_VERIFY_NONE, ssl_verify_cb); + } +#if 1 /* don't know what this is good for, but s_server in SSLeay does it, too */ + if(isServer){ + SSL_CTX_set_client_CA_list(sslContext, SSL_load_client_CA_file(certfile)); + } +#endif + return 0; +} + +int sslutil_accept(int fd) +{ +int err; + + if(ssl != NULL){ + DEBUG(0, ("SSL: internal error: more than one SSL connection (server)\n")); + return -1; + } + if((ssl = SSL_new(sslContext)) == NULL){ + err = ERR_get_error(); + DEBUG(0, ("SSL: Error allocating handle: %s\n", + ERR_error_string(err, NULL))); + return -1; + } + SSL_set_fd(ssl, fd); + sslFd = fd; + if(SSL_accept(ssl) <= 0){ + err = ERR_get_error(); + DEBUG(0, ("SSL: Error accepting on socket: %s\n", + ERR_error_string(err, NULL))); + return -1; + } + DEBUG(0, ("SSL: negotiated cipher: %s\n", SSL_get_cipher(ssl))); + return 0; +} + +int sslutil_fd_is_ssl(int fd) +{ + return fd == sslFd; +} + +int sslutil_connect(int fd) +{ +int err; + + if(ssl != NULL){ + DEBUG(0, ("SSL: internal error: more than one SSL connection (client)\n")); + return -1; + } + if((ssl = SSL_new(sslContext)) == NULL){ + err = ERR_get_error(); + DEBUG(0, ("SSL: Error allocating handle: %s\n", + ERR_error_string(err, NULL))); + return -1; + } + SSL_set_fd(ssl, fd); + sslFd = fd; + if(SSL_connect(ssl) <= 0){ + err = ERR_get_error(); + DEBUG(0, ("SSL: Error conencting socket: %s\n", + ERR_error_string(err, NULL))); + return -1; + } + DEBUG(0, ("SSL: negotiated cipher: %s\n", SSL_get_cipher(ssl))); + return 0; +} + +int sslutil_disconnect(int fd) +{ + if(fd == sslFd && ssl != NULL){ + SSL_free(ssl); + ssl = NULL; + sslFd = -1; + } + return 0; +} + +int sslutil_negotiate_ssl(int fd, int msg_type) +{ +unsigned char buf[5] = {0x83, 0, 0, 1, 0x81}; +char *reqHosts, *resignHosts; + + reqHosts = lp_ssl_hosts(); + resignHosts = lp_ssl_hosts_resign(); + if(!allow_access(resignHosts, reqHosts, get_socket_name(fd), get_socket_addr(fd))){ + sslEnabled = False; + return 0; + } + if(msg_type != 0x81){ /* first packet must be a session request */ + DEBUG( 0, ( "Client %s did not use session setup; access denied\n", + client_addr() ) ); + if (!send_smb(fd, (char *)buf)) + DEBUG(0, ("sslutil_negotiate_ssl: send_smb failed.\n")); + return -1; + } + buf[4] = 0x8e; /* negative session response: use SSL */ + if (!send_smb(fd, (char *)buf)) { + DEBUG(0,("sslutil_negotiate_ssl: send_smb failed.\n")); + return -1; + } + if(sslutil_accept(fd) != 0){ + DEBUG( 0, ( "Client %s failed SSL negotiation!\n", client_addr() ) ); + return -1; + } + return 1; +} + +#else /* WITH_SSL */ + void ssl_dummy(void); + void ssl_dummy(void) {;} /* So some compilers don't complain. */ +#endif /* WITH_SSL */ diff --git a/source3/smbd/statcache.c b/source3/smbd/statcache.c new file mode 100644 index 0000000000..93782b9bb0 --- /dev/null +++ b/source3/smbd/statcache.c @@ -0,0 +1,230 @@ +/* + Unix SMB/CIFS implementation. + stat cache code + Copyright (C) Andrew Tridgell 1992-2000 + Copyright (C) Jeremy Allison 1999-2000 + + + 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" + +extern BOOL case_sensitive; + + +/**************************************************************************** + Stat cache code used in unix_convert. +*****************************************************************************/ + +typedef struct { + int name_len; + char names[2]; /* This is extended via malloc... */ +} stat_cache_entry; + +#define INIT_STAT_CACHE_SIZE 512 +static hash_table stat_cache; + +/**************************************************************************** + Add an entry into the stat cache. +*****************************************************************************/ + +void stat_cache_add( char *full_orig_name, char *orig_translated_path) +{ + stat_cache_entry *scp; + stat_cache_entry *found_scp; + pstring orig_name; + pstring translated_path; + int namelen; + hash_element *hash_elem; + + if (!lp_stat_cache()) return; + + namelen = strlen(orig_translated_path); + + /* + * Don't cache trivial valid directory entries. + */ + if((*full_orig_name == '\0') || (strcmp(full_orig_name, ".") == 0) || + (strcmp(full_orig_name, "..") == 0)) + return; + + /* + * If we are in case insentive mode, we need to + * store names that need no translation - else, it + * would be a waste. + */ + + if(case_sensitive && (strcmp(full_orig_name, orig_translated_path) == 0)) + return; + + /* + * Remove any trailing '/' characters from the + * translated path. + */ + + pstrcpy(translated_path, orig_translated_path); + if(translated_path[namelen-1] == '/') { + translated_path[namelen-1] = '\0'; + namelen--; + } + + /* + * We will only replace namelen characters + * of full_orig_name. + * StrnCpy always null terminates. + */ + + StrnCpy(orig_name, full_orig_name, namelen); + if(!case_sensitive) + strupper( orig_name ); + + /* + * Check this name doesn't exist in the cache before we + * add it. + */ + + if ((hash_elem = hash_lookup(&stat_cache, orig_name))) { + found_scp = (stat_cache_entry *)(hash_elem->value); + if (strcmp((found_scp->names+found_scp->name_len+1), translated_path) == 0) { + return; + } else { + hash_remove(&stat_cache, hash_elem); + if((scp = (stat_cache_entry *)malloc(sizeof(stat_cache_entry)+2*namelen)) == NULL) { + DEBUG(0,("stat_cache_add: Out of memory !\n")); + return; + } + pstrcpy(scp->names, orig_name); + pstrcpy((scp->names+namelen+1), translated_path); + scp->name_len = namelen; + hash_insert(&stat_cache, (char *)scp, orig_name); + } + return; + } else { + + /* + * New entry. + */ + + if((scp = (stat_cache_entry *)malloc(sizeof(stat_cache_entry)+2*namelen)) == NULL) { + DEBUG(0,("stat_cache_add: Out of memory !\n")); + return; + } + pstrcpy(scp->names, orig_name); + pstrcpy(scp->names+namelen+1, translated_path); + scp->name_len = namelen; + hash_insert(&stat_cache, (char *)scp, orig_name); + } + + DEBUG(5,("stat_cache_add: Added entry %s -> %s\n", scp->names, (scp->names+scp->name_len+1))); +} + +/**************************************************************************** + Look through the stat cache for an entry - promote it to the top if found. + Return True if we translated (and did a scuccessful stat on) the entire name. +*****************************************************************************/ + +BOOL stat_cache_lookup(connection_struct *conn, char *name, char *dirpath, + char **start, SMB_STRUCT_STAT *pst) +{ + stat_cache_entry *scp; + char *trans_name; + pstring chk_name; + int namelen; + hash_element *hash_elem; + char *sp; + + if (!lp_stat_cache()) + return False; + + namelen = strlen(name); + + *start = name; + + DO_PROFILE_INC(statcache_lookups); + + /* + * Don't lookup trivial valid directory entries. + */ + if((*name == '\0') || (strcmp(name, ".") == 0) || (strcmp(name, "..") == 0)) { + DO_PROFILE_INC(statcache_misses); + return False; + } + + pstrcpy(chk_name, name); + if(!case_sensitive) + strupper( chk_name ); + + while (1) { + hash_elem = hash_lookup(&stat_cache, chk_name); + if(hash_elem == NULL) { + /* + * Didn't find it - remove last component for next try. + */ + sp = strrchr_m(chk_name, '/'); + if (sp) { + *sp = '\0'; + } else { + /* + * We reached the end of the name - no match. + */ + DO_PROFILE_INC(statcache_misses); + return False; + } + if((*chk_name == '\0') || (strcmp(chk_name, ".") == 0) + || (strcmp(chk_name, "..") == 0)) { + DO_PROFILE_INC(statcache_misses); + return False; + } + } else { + scp = (stat_cache_entry *)(hash_elem->value); + DO_PROFILE_INC(statcache_hits); + trans_name = scp->names+scp->name_len+1; + if(vfs_stat(conn,trans_name, pst) != 0) { + /* Discard this entry - it doesn't exist in the filesystem. */ + hash_remove(&stat_cache, hash_elem); + return False; + } + memcpy(name, trans_name, scp->name_len); + *start = &name[scp->name_len]; + if(**start == '/') + ++*start; + StrnCpy( dirpath, trans_name, name - (*start)); + return (namelen == scp->name_len); + } + } +} + +/*************************************************************************** ** + * Initializes or clears the stat cache. + * + * Input: none. + * Output: none. + * + * ************************************************************************** ** + */ +BOOL reset_stat_cache( void ) +{ + static BOOL initialised; + if (!lp_stat_cache()) return True; + + if (initialised) { + hash_clear(&stat_cache); + } + + initialised = hash_table_init( &stat_cache, INIT_STAT_CACHE_SIZE, + (compare_function)(strcmp)); + return initialised; +} /* reset_stat_cache */ diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c index 9d02123cf8..1972e9c8c8 100644 --- a/source3/smbd/trans2.c +++ b/source3/smbd/trans2.c @@ -1,8 +1,7 @@ /* - Unix SMB/Netbios implementation. - Version 1.9. + Unix SMB/CIFS implementation. SMB transaction2 handling - Copyright (C) Jeremy Allison 1994 + Copyright (C) Jeremy Allison 1994-2001 Extensively modified by Andrew Tridgell, 1995 @@ -22,15 +21,14 @@ */ #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; +extern int smb_read_error; +extern fstring local_machine; +extern int global_oplock_break; +extern uint32 global_client_caps; +extern pstring global_myname; /**************************************************************************** Send the required number of replies back. @@ -38,23 +36,25 @@ extern int Client; 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) + int paramsize, char *pdata, int datasize) { - /* As we are using a protocol > LANMAN1 then the maxxmit + /* As we are using a protocol > LANMAN1 then the max_send 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; + extern int max_send; 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; + int alignment_offset = 1; /* JRA. This used to be 3. Set to 1 to make netmon parse ok. */ + int data_alignment_offset = 0; /* Initially set the wcnt area to be 10 - this is true for all trans2 replies */ @@ -63,107 +63,136 @@ static int send_trans2_replies(char *outbuf, int bufsize, char *params, /* 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; - } + { + if (!send_smb(smbd_server_fd(),outbuf)) + exit_server("send_trans2_replies: send_smb failed."); + return 0; + } + + /* When sending params and data ensure that both are nicely aligned */ + /* Only do this alignment when there is also data to send - else + can cause NT redirector problems. */ + if (((params_to_send % 4) != 0) && (data_to_send != 0)) + data_alignment_offset = 4 - (params_to_send % 4); /* 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 + /* The alignment_offset is to align the param 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? */ + useable_space = bufsize - ((smb_buf(outbuf)+ + alignment_offset+data_alignment_offset) - + outbuf); - 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); + /* useable_space can never be more than max_send minus the + alignment offset. */ + useable_space = MIN(useable_space, + max_send - (alignment_offset+data_alignment_offset)); - 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); + 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 + data_alignment_offset; + /* We can never send more than useable_space */ + /* + * Note that 'useable_space' does not include the alignment offsets, + * but we must include the alignment offsets in the calculation of + * the length of the data we send over the wire, as the alignment offsets + * are sent here. Fix from Marc_Jacobsen@hp.com. + */ + total_sent_thistime = MIN(total_sent_thistime, useable_space+ + alignment_offset + data_alignment_offset); + + 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); + + /* 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))); + + if(params_sent_thistime == 0) + SSVAL(outbuf,smb_prdisp,0); + else + /* 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 + data_alignment_offset); + SSVAL(outbuf,smb_drdisp, pd - pdata); + } - /* Calculate how many parameters and data we can fit into - this packet. Parameters get precedence */ + /* 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+ + data_alignment_offset,pd,data_sent_thistime); - 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); + 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)); - 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); - } + /* Send the packet */ + if (!send_smb(smbd_server_fd(),outbuf)) + exit_server("send_trans2_replies: send_smb failed."); - 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); - } + pp += params_sent_thistime; + pd += data_sent_thistime; - /* 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; - } + params_to_send -= params_sent_thistime; + data_to_send -= data_sent_thistime; + + /* Sanity check */ + if(params_to_send < 0 || data_to_send < 0) + { + DEBUG(0,("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 + Reply to a TRANSACT2_OPEN. ****************************************************************************/ -static int call_trans2open(char *inbuf, char *outbuf, int bufsize, int cnum, - char **pparams, char **ppdata) + +static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, + int bufsize, + char **pparams, char **ppdata) { char *params = *pparams; int16 open_mode = SVAL(params, 2); int16 open_attr = SVAL(params,6); + BOOL oplock_request = (((SVAL(params,0)|(1<<1))>>1) | ((SVAL(params,0)|(1<<2))>>1)); #if 0 BOOL return_additional_info = BITSETW(params,0); int16 open_sattr = SVAL(params, 4); @@ -172,68 +201,85 @@ static int call_trans2open(char *inbuf, char *outbuf, int bufsize, int cnum, 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; + mode_t unixmode; + SMB_OFF_T size=0; + int fmode=0,mtime=0,rmode; + SMB_INO_T inode = 0; + SMB_STRUCT_STAT sbuf; int smb_action = 0; + BOOL bad_path = False; + files_struct *fsp; + + srvstr_pull(inbuf, fname, pname, sizeof(fname), -1, STR_TERMINATE); - StrnCpy(fname,pname,namelen); + DEBUG(3,("trans2open %s mode=%d attr=%d ofun=%d size=%d\n", + fname,open_mode, open_attr, open_ofun, open_size)); - 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)); + if (IS_IPC(conn)) { + return(ERROR_DOS(ERRSRV,ERRaccess)); + } /* XXXX we need to handle passed times, sattr and flags */ - unix_convert(fname,cnum); + unix_convert(fname,conn,0,&bad_path,&sbuf); - fnum = find_free_file(); - if (fnum < 0) - return(ERROR(ERRSRV,ERRnofids)); - - if (!check_name(fname,cnum)) + if (!check_name(fname,conn)) + { + if((errno == ENOENT) && bad_path) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } return(UNIXERROR(ERRDOS,ERRnoaccess)); + } - unixmode = unix_mode(cnum,open_attr | aARCH); - + unixmode = unix_mode(conn,open_attr | aARCH, fname); - open_file_shared(fnum,cnum,fname,open_mode,open_ofun,unixmode, - &rmode,&smb_action); + fsp = open_file_shared(conn,fname,&sbuf,open_mode,open_ofun,unixmode, + oplock_request, &rmode,&smb_action); - if (!Files[fnum].open) + if (!fsp) + { + if((errno == ENOENT) && bad_path) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } 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); + fmode = dos_mode(conn,fname,&sbuf); mtime = sbuf.st_mtime; inode = sbuf.st_ino; if (fmode & aDIR) { - close_file(fnum); - return(ERROR(ERRDOS,ERRnoaccess)); + close_file(fsp,False); + return(ERROR_DOS(ERRDOS,ERRnoaccess)); } /* Realloc the size of parameters and data we will return */ - params = *pparams = Realloc(*pparams, 28); - if(params == NULL) - return(ERROR(ERRDOS,ERRnomem)); + params = Realloc(*pparams, 28); + if( params == NULL ) { + return(ERROR_DOS(ERRDOS,ERRnomem)); + } + *pparams = params; - bzero(params,28); - SSVAL(params,0,fnum); + memset((char *)params,'\0',28); + SSVAL(params,0,fsp->fnum); SSVAL(params,2,fmode); put_dos_date2(params,4, mtime); - SIVAL(params,8, size); + SIVAL(params,8, (uint32)size); SSVAL(params,12,rmode); + if (oplock_request && lp_fake_oplocks(SNUM(conn))) { + smb_action |= EXTENDED_OPLOCK_GRANTED; + } + SSVAL(params,18,smb_action); + /* + * WARNING - this may need to be changed if SMB_INO_T <> 4 bytes. + */ SIVAL(params,20,inode); /* Send the required number of replies */ @@ -242,292 +288,503 @@ static int call_trans2open(char *inbuf, char *outbuf, int bufsize, int cnum, return -1; } +/********************************************************* + Routine to check if a given string matches exactly. + as a special case a mask of "." does NOT match. That + is required for correct wildcard semantics + Case can be significant or not. +**********************************************************/ + +static BOOL exact_match(char *str,char *mask, BOOL case_sig) +{ + if (mask[0] == '.' && mask[1] == 0) + return False; + if (case_sig) + return strcmp(str,mask)==0; + return strcasecmp(str,mask) == 0; +} + +#if 0 + +Not finished yet - jra. + /**************************************************************************** - get a level dependent lanman2 dir entry. + Return the filetype for UNIX extensions. ****************************************************************************/ -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) + +static uint32 unix_filetype(mode_t mode) { - 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); + if(S_ISREG(mode)) + return UNIX_TYPE_FILE; + else if(S_ISDIR(mode)) + return UNIX_TYPE_DIR; +#ifdef S_ISLNK + else if(S_ISLNK(mode)) + return UNIX_TYPE_SYMLINK; +#endif +#ifdef S_ISCHR + else if(S_ISCHR(mode)) + return UNIX_TYPE_CHARDEV; +#endif +#ifdef S_ISBLK + else if(S_ISBLK(mode)) + return UNIX_TYPE_BLKDEV; +#endif +#ifdef S_ISFIFO + else if(S_ISFIFO(mode)) + return UNIX_TYPE_FIFO; +#endif +#ifdef S_ISSOCK + else if(S_ISSOCK(mode)) + return UNIX_TYPE_SOCKET; +#endif - while (!found) - { - /* Needed if we run out of space */ - prev_dirpos = TellDir(Connections[cnum].dirptr); - dname = ReadDirName(Connections[cnum].dirptr); + DEBUG(0,("unix_filetype: unknown filetype %u", (unsigned)mode)); + return UNIX_TYPE_UNKNOWN; +} - reskey = TellDir(Connections[cnum].dirptr); +/**************************************************************************** + Return the major devicenumber for UNIX extensions. +****************************************************************************/ - 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); +static uint32 unix_dev_major(SMB_DEV_T dev) +{ +#if defined(HAVE_DEVICE_MAJOR_FN) + return (uint32)major(dev); +#else + return (uint32)(dev >> 8); +#endif +} - matched = False; +/**************************************************************************** + Return the minor devicenumber for UNIX extensions. +****************************************************************************/ - strcpy(fname,dname); +static uint32 unix_dev_minor(SMB_DEV_T dev) +{ +#if defined(HAVE_DEVICE_MINOR_FN) + return (uint32)minor(dev); +#else + return (uint32)(dev & 0xff); +#endif +} - 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; - } +/**************************************************************************** + Map standard UNIX permissions onto wire representations. +****************************************************************************/ + +static uint32 unix_perms_to_wire(mode_t perms) +{ + uint ret = 0; + + ret |= ((perms & S_IXOTH) ? UNIX_X_OTH : 0); + ret |= ((perms & S_IWOTH) ? UNIX_W_OTH : 0); + ret |= ((perms & S_IROTH) ? UNIX_R_OTH : 0); + ret |= ((perms & S_IXGRP) ? UNIX_X_GRP : 0); + ret |= ((perms & S_IWGRP) ? UNIX_W_GRP : 0); + ret |= ((perms & S_IRGRP) ? UNIX_R_GRP : 0); + ret |= ((perms & S_IXUSR) ? UNIX_X_USR : 0); + ret |= ((perms & S_IWUSR) ? UNIX_W_USR : 0); + ret |= ((perms & S_IRUSR) ? UNIX_R_USR : 0); +#ifdef S_ISVTX + ret |= ((perms & S_ISVTX) ? UNIX_STICKY : 0); +#endif +#ifdef S_ISGID + ret |= ((perms & S_ISGID) ? UNIX_SET_GID : 0); +#endif +#ifdef S_ISUID + ret |= ((perms & S_ISVTX) ? UNIX_SET_UID : 0); +#endif + return ret; +} - mode = dos_mode(cnum,pathreal,&sbuf); +#endif - 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)); +/**************************************************************************** + Get a level dependent lanman2 dir entry. +****************************************************************************/ + +static BOOL get_lanman2_dir_entry(connection_struct *conn, + void *inbuf, void *outbuf, + 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, BOOL *got_exact_match, + int *last_name_off) +{ + char *dname; + BOOL found = False; + SMB_STRUCT_STAT sbuf; + pstring mask; + pstring pathreal; + pstring fname; + char *p, *q, *pdata = *ppdata; + uint32 reskey=0; + int prev_dirpos=0; + int mode=0; + SMB_OFF_T size = 0; + SMB_OFF_T allocation_size = 0; + uint32 len; + time_t mdate=0, adate=0, cdate=0; + char *nameptr; + BOOL was_8_3; + int nt_extmode; /* Used for NT connections instead of mode */ + BOOL needslash = ( conn->dirpath[strlen(conn->dirpath) -1] != '/'); + + *fname = 0; + *out_of_space = False; + *got_exact_match = False; + + if (!conn->dirptr) + return(False); + + p = strrchr_m(path_mask,'/'); + if(p != NULL) { + if(p[1] == '\0') + pstrcpy(mask,"*.*"); + else + pstrcpy(mask, p+1); + } else + pstrcpy(mask, path_mask); + + while (!found) { + BOOL got_match; + + /* Needed if we run out of space */ + prev_dirpos = TellDir(conn->dirptr); + dname = ReadDirName(conn->dirptr); + + /* + * Due to bugs in NT client redirectors we are not using + * resume keys any more - set them to zero. + * Check out the related comments in findfirst/findnext. + * JRA. + */ + + reskey = 0; + + DEBUG(8,("get_lanman2_dir_entry:readdir on dirptr 0x%lx now at offset %d\n", + (long)conn->dirptr,TellDir(conn->dirptr))); + + if (!dname) + return(False); + + pstrcpy(fname,dname); + + if(!(got_match = *got_exact_match = exact_match(fname, mask, case_sensitive))) + got_match = mask_match(fname, mask, case_sensitive); + + if(!got_match && !mangle_is_8_3(fname, False)) { + + /* + * It turns out that NT matches wildcards against + * both long *and* short names. This may explain some + * of the wildcard wierdness from old DOS clients + * that some people have been seeing.... JRA. + */ + + pstring newname; + pstrcpy( newname, fname); + mangle_map( newname, True, False, SNUM(conn)); + if(!(got_match = *got_exact_match = exact_match(newname, mask, case_sensitive))) + got_match = mask_match(newname, mask, case_sensitive); + } + + if(got_match) { + BOOL isdots = (strequal(fname,"..") || strequal(fname,".")); + if (dont_descend && !isdots) + continue; - found = True; + pstrcpy(pathreal,conn->dirpath); + if(needslash) + pstrcat(pathreal,"/"); + pstrcat(pathreal,dname); + + if (INFO_LEVEL_IS_UNIX(info_level)) { + if (vfs_lstat(conn,pathreal,&sbuf) != 0) { + DEBUG(5,("get_lanman2_dir_entry:Couldn't lstat [%s] (%s)\n", + pathreal,strerror(errno))); + continue; + } + } else if (vfs_stat(conn,pathreal,&sbuf) != 0) { + /* Needed to show the msdfs symlinks as directories */ + if(!lp_host_msdfs() || !lp_msdfs_root(SNUM(conn)) + || !is_msdfs_link(conn, pathreal)) { + DEBUG(5,("get_lanman2_dir_entry:Couldn't stat [%s] (%s)\n", + pathreal,strerror(errno))); + continue; + } else { + DEBUG(5,("get_lanman2_dir_entry: Masquerading msdfs link %s as a directory\n", + pathreal)); + sbuf.st_mode = (sbuf.st_mode & 0xFFF) | S_IFDIR; + } + } + + mode = dos_mode(conn,pathreal,&sbuf); + + if (!dir_check_ftype(conn,mode,&sbuf,dirtype)) { + DEBUG(5,("[%s] attribs didn't match %x\n",fname,dirtype)); + continue; + } + + size = sbuf.st_size; + allocation_size = SMB_ROUNDUP_ALLOCATION(sbuf.st_size); + mdate = sbuf.st_mtime; + adate = sbuf.st_atime; + cdate = get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn))); + + if (lp_dos_filetime_resolution(SNUM(conn))) { + cdate &= ~1; + mdate &= ~1; + adate &= ~1; + } + + 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); + mangle_map(fname,False,True,SNUM(conn)); + + p = pdata; + nameptr = p; + + nt_extmode = mode ? mode : FILE_ATTRIBUTE_NORMAL; + + 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,(uint32)size); + SIVAL(p,l1_cbFileAlloc,(uint32)allocation_size); + SSVAL(p,l1_attrFile,mode); + p += l1_achName; + nameptr = p; + p += align_string(outbuf, p, 0); + len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE); + SCVAL(nameptr, -1, len); + p += len; + break; + + case 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,(uint32)size); + SIVAL(p,l2_cbFileAlloc,(uint32)allocation_size); + SSVAL(p,l2_attrFile,mode); + SIVAL(p,l2_cbList,0); /* No extended attributes */ + p += l2_achName; + nameptr = p; + len = srvstr_push(outbuf, p, fname, -1, STR_NOALIGN); + SCVAL(p, -1, len); + p += len; + *p++ = 0; /* craig from unisys pointed out we need this */ + break; + + case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: + was_8_3 = mangle_is_8_3(fname, True); + 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; + SOFF_T(p,0,size); + SOFF_T(p,8,allocation_size); + p += 16; + SIVAL(p,0,nt_extmode); p += 4; + q = p; p += 4; + SIVAL(p,0,0); p += 4; + if (!was_8_3) { + pstring mangled_name; + pstrcpy(mangled_name, fname); + mangle_map(mangled_name,True,True,SNUM(conn)); + mangled_name[12] = 0; + len = srvstr_push(outbuf, p+2, mangled_name, 24, STR_UPPER); + SSVAL(p, 0, len); + } else { + SSVAL(p,0,0); + *(p+2) = 0; + } + p += 2 + 24; + len = srvstr_push(outbuf, p, fname, -1, 0); + SIVAL(q,0,len); + p += len; + len = PTR_DIFF(p, pdata); + len = (len + 3) & ~3; + SIVAL(pdata,0,len); + p = pdata + len; + break; + + case SMB_FIND_FILE_DIRECTORY_INFO: + 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; + SOFF_T(p,0,size); + SOFF_T(p,8,allocation_size); + p += 16; + SIVAL(p,0,nt_extmode); p += 4; + p += 4; + len = srvstr_push(outbuf, p, fname, -1, 0); + SIVAL(p, -4, len); + p += len; + len = PTR_DIFF(p, pdata); + len = (len + 3) & ~3; + SIVAL(pdata,0,len); + p = pdata + len; + break; + + case SMB_FIND_FILE_FULL_DIRECTORY_INFO: + 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; + SOFF_T(p,0,size); + SOFF_T(p,8,allocation_size); + p += 16; + SIVAL(p,0,nt_extmode); p += 4; + p += 4; + SIVAL(p,0,0); p += 4; + + len = srvstr_push(outbuf, p, fname, -1, 0); + SIVAL(p, -4, len); + p += len; + + len = PTR_DIFF(p, pdata); + len = (len + 3) & ~3; + SIVAL(pdata,0,len); + p = pdata + len; + break; + + case SMB_FIND_FILE_NAMES_INFO: + p += 4; + SIVAL(p,0,reskey); p += 4; + p += 4; + /* this must *not* be null terminated or w2k gets in a loop trying to set an + acl on a dir (tridge) */ + len = srvstr_push(outbuf, p, fname, -1, 0); + SIVAL(p, -4, len); + p += len; + len = PTR_DIFF(p, pdata); + len = (len + 3) & ~3; + SIVAL(pdata,0,len); + p = pdata + len; + break; + + /* CIFS UNIX Extension. */ + +#if 0 /* JRA - FIXME - NEEDS UNICODE CONVERSION !!! */ + case SMB_FIND_FILE_UNIX: + + len = 108+strlen(fname)+1; /* (length of SMB_QUERY_FILE_UNIX_BASIC = 100)+4+4+strlen(fname)*/ + /* +1 to be sure to transmit the termination of fname */ + len = (len + 3) & ~3; + + SIVAL(p,0,len); p+= 4; /* Offset from this structure to the beginning of the next one */ + SIVAL(p,0,reskey); p+= 4; /* Used for continuing search. */ + + /* Begin of SMB_QUERY_FILE_UNIX_BASIC */ + SOFF_T(p,0,sbuf.st_size); /* File size 64 Bit */ + p+= 8; + +#if defined(HAVE_STAT_ST_BLOCKS) && defined(STAT_ST_BLOCKSIZE) + SOFF_T(p,0,sbuf.st_blocks*STAT_ST_BLOCKSIZE); /* Number of bytes used on disk - 64 Bit */ +#else + /* Can't get the value - fake it using size. */ + SOFF_T(p,0,sbuf.st_size); /* Number of bytes used on disk - 64 Bit */ #endif + p+= 8; - p = pdata; - name_ptr = p; + put_long_date(p,sbuf.st_ctime); /* Creation Time 64 Bit */ + put_long_date(p+8,sbuf.st_atime); /* Last access time 64 Bit */ + put_long_date(p+16,sbuf.st_mtime); /* Last modification time 64 Bit */ + p+= 24; - name_map_mangle(fname,False,SNUM(cnum)); + SIVAL(p,0,sbuf.st_uid); /* user id for the owner */ + SIVAL(p,4,0); + p+= 8; - 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; + SIVAL(p,0,sbuf.st_gid); /* group id of owner */ + SIVAL(p,4,0); + p+= 8; - 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; + SIVAL(p,0,unix_filetype(sbuf.st_mode)); + p+= 4; - 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; + SIVAL(p,0,unix_dev_major(sbuf.st_rdev)); /* Major device number if type is device */ + SIVAL(p,4,0); + p+= 8; - 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; + SIVAL(p,0,unix_dev_minor(sbuf.st_rdev)); /* Minor device number if type is device */ + SIVAL(p,4,0); + p+= 8; - 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; + SINO_T(p,0,(SMB_INO_T)sbuf.st_ino); /* inode number */ + p+= 8; - 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; + SIVAL(p,0, unix_perms_to_wire(sbuf.st_mode)); /* Standard UNIX file permissions */ + SIVAL(p,4,0); + p+= 8; - 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; + SIVAL(p,0,sbuf.st_nlink); /* number of hard links */ + SIVAL(p,4,0); + p+= 8; - default: - return(False); - } + /* End of SMB_QUERY_FILE_UNIX_BASIC */ + pstrcpy(p,fname); + p=pdata+len; + break; +#endif - 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 */ - } + default: + return(False); + } + + + if (PTR_DIFF(p,pdata) > space_remaining) { + /* Move the dirptr back to prev_dirpos */ + SeekDir(conn->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(nameptr,base_data); + /* Advance the data pointer to the next slot */ + *ppdata = p; - /* 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); + return(found); } - + /**************************************************************************** - reply to a TRANS2_FINDFIRST + Reply to a TRANS2_FINDFIRST. ****************************************************************************/ -static int call_trans2findfirst(char *inbuf, char *outbuf, int bufsize, int cnum, - char **pparams, char **ppdata) + +static int call_trans2findfirst(connection_struct *conn, + char *inbuf, char *outbuf, int bufsize, + 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 @@ -554,6 +811,8 @@ static int call_trans2findfirst(char *inbuf, char *outbuf, int bufsize, int cnum BOOL dont_descend = False; BOOL out_of_space = False; int space_remaining; + BOOL bad_path = False; + SMB_STRUCT_STAT sbuf; *directory = *mask = 0; @@ -573,68 +832,69 @@ static int call_trans2findfirst(char *inbuf, char *outbuf, int bufsize, int cnum case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: break; default: - return(ERROR(ERRDOS,ERRunknownlevel)); + return(ERROR_DOS(ERRDOS,ERRunknownlevel)); } - strcpy(directory, params + 12); /* Complete directory path with - wildcard mask appended */ + srvstr_pull(inbuf, directory, params+12, sizeof(directory), -1, STR_TERMINATE); - DEBUG(5,("path=%s\n",directory)); + RESOLVE_FINDFIRST_DFSPATH(directory, conn, inbuf, outbuf); + + unix_convert(directory,conn,0,&bad_path,&sbuf); + if(!check_name(directory,conn)) { + if((errno == ENOENT) && bad_path) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } + +#if 0 + /* Ugly - NT specific hack - maybe not needed ? (JRA) */ + if((errno == ENOTDIR) && (Protocol >= PROTOCOL_NT1) && + (get_remote_arch() == RA_WINNT)) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbaddirectory; + } +#endif - unix_convert(directory,cnum); - if(!check_name(directory,cnum)) { - return(ERROR(ERRDOS,ERRbadpath)); + return(UNIXERROR(ERRDOS,ERRbadpath)); } - p = strrchr(directory,'/'); + p = strrchr_m(directory,'/'); if(p == NULL) { - strcpy(mask,directory); - strcpy(directory,"./"); + pstrcpy(mask,directory); + pstrcpy(directory,"./"); } else { - strcpy(mask,p+1); + pstrcpy(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); + pdata = Realloc(*ppdata, max_data_bytes + 1024); + if( pdata == NULL ) { + return(ERROR_DOS(ERRDOS,ERRnomem)); + } + *ppdata = pdata; + memset((char *)pdata,'\0',max_data_bytes + 1024); /* 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++; - } + params = Realloc(*pparams, 10); + if (params == NULL) { + return ERROR_DOS(ERRDOS,ERRnomem); } - - /* a special case for 16 bit apps */ - if (strequal(mask,"????????.???")) strcpy(mask,"*"); + *pparams = params; - /* handle broken clients that send us old 8.3 format */ - string_sub(mask,"????????","*"); - string_sub(mask,".???",".*"); + dptr_num = dptr_create(conn,directory, False, True ,SVAL(inbuf,smb_pid)); + if (dptr_num < 0) + return(UNIXERROR(ERRDOS,ERRbadfile)); /* 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_close(&dptr_num); + return ERROR_DOS(ERRDOS,ERRnomem); } dptr_set_wcard(dptr_num, wcard); @@ -646,8 +906,8 @@ static int call_trans2findfirst(char *inbuf, char *outbuf, int bufsize, int cnum 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)) + conn->dirpath,lp_dontdescend(SNUM(conn)))); + if (in_list(conn->dirpath,lp_dontdescend(SNUM(conn)),case_sensitive)) dont_descend = True; p = pdata; @@ -655,39 +915,61 @@ static int call_trans2findfirst(char *inbuf, char *outbuf, int bufsize, int cnum out_of_space = False; for (i=0;(i<maxentries) && !finished && !out_of_space;i++) + { + BOOL got_exact_match = False; + + /* 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(conn, + inbuf, outbuf, + mask,dirtype,info_level, + requires_resume_key,dont_descend, + &p,pdata,space_remaining, &out_of_space, &got_exact_match, + &last_name_off); + } - /* 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) - finished = False; + if (!finished && !out_of_space) + numentries++; - if (!finished && !out_of_space) - numentries++; - space_remaining = max_data_bytes - PTR_DIFF(p,pdata); - } + /* + * As an optimisation if we know we aren't looking + * for a wildcard name (ie. the name matches the wildcard exactly) + * then we can finish on any (first) match. + * This speeds up large directory searches. JRA. + */ + + if(got_exact_match) + finished = True; + + 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; - } + { + DEBUG(5,("call_trans2findfirst - (2) closing dptr_num %d\n", dptr_num)); + dptr_close(&dptr_num); + } + + /* + * If there are no matching entries we must return ERRDOS/ERRbadfile - + * from observation of NT. + */ + + if(numentries == 0) { + dptr_close(&dptr_num); + return ERROR_DOS(ERRDOS,ERRbadfile); + } /* At this point pdata points to numentries directory entries. */ @@ -701,22 +983,34 @@ static int call_trans2findfirst(char *inbuf, char *outbuf, int bufsize, int cnum 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)); + slprintf(directory,sizeof(directory)-1, "(%s)",dptr_path(dptr_num)); + + DEBUG( 4, ( "%s mask=%s directory=%s dirtype=%d numentries=%d\n", + smb_fn_name(CVAL(inbuf,smb_com)), + mask, directory, dirtype, numentries ) ); - 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)); + /* + * Force a name mangle here to ensure that the + * mask as an 8.3 name is top of the mangled cache. + * The reasons for this are subtle. Don't remove + * this code unless you know what you are doing + * (see PR#13758). JRA. + */ + + if(!mangle_is_8_3( mask, False)) + mangle_map(mask, True, True, SNUM(conn)); return(-1); } - /**************************************************************************** - reply to a TRANS2_FINDNEXT + Reply to a TRANS2_FINDNEXT. ****************************************************************************/ -static int call_trans2findnext(char *inbuf, char *outbuf, int length, int bufsize, - int cnum, char **pparams, char **ppdata) + +static int call_trans2findnext(connection_struct *conn, + char *inbuf, char *outbuf, + int length, int bufsize, + 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 @@ -726,7 +1020,7 @@ static int call_trans2findnext(char *inbuf, char *outbuf, int length, int bufsiz int max_data_bytes = SVAL(inbuf, smb_mdrcnt); char *params = *pparams; char *pdata = *ppdata; - int16 dptr_num = SVAL(params,0); + int dptr_num = SVAL(params,0); int maxentries = SVAL(params,2); uint16 info_level = SVAL(params,4); uint32 resume_key = IVAL(params,6); @@ -734,6 +1028,7 @@ static int call_trans2findnext(char *inbuf, char *outbuf, int length, int bufsiz BOOL close_if_end = BITSETW(params+10,1); BOOL requires_resume_key = BITSETW(params+10,2); BOOL continue_bit = BITSETW(params+10,3); + pstring resume_name; pstring mask; pstring directory; char *p; @@ -745,11 +1040,15 @@ static int call_trans2findnext(char *inbuf, char *outbuf, int length, int bufsiz BOOL out_of_space = False; int space_remaining; - *mask = *directory = 0; + *mask = *directory = *resume_name = 0; + + srvstr_pull(inbuf, resume_name, params+12, sizeof(resume_name), -1, STR_TERMINATE); - 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", + 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 resume name = %s 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)); + requires_resume_key, resume_key, resume_name, continue_bit, info_level)); switch (info_level) { @@ -763,89 +1062,179 @@ static int call_trans2findnext(char *inbuf, char *outbuf, int length, int bufsiz case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: break; default: - return(ERROR(ERRDOS,ERRunknownlevel)); + return ERROR_DOS(ERRDOS,ERRunknownlevel); } - pdata = *ppdata = Realloc( *ppdata, max_data_bytes + 1024); - if(!*ppdata) - return(ERROR(ERRDOS,ERRnomem)); - bzero(pdata,max_data_bytes); + pdata = Realloc( *ppdata, max_data_bytes + 1024); + if(pdata == NULL) { + return ERROR_DOS(ERRDOS,ERRnomem); + } + *ppdata = pdata; + memset((char *)pdata,'\0',max_data_bytes + 1024); /* Realloc the params space */ - params = *pparams = Realloc(*pparams, 6*SIZEOFWORD); - if(!params) - return(ERROR(ERRDOS,ERRnomem)); + params = Realloc(*pparams, 6*SIZEOFWORD); + if( params == NULL ) { + return ERROR_DOS(ERRDOS,ERRnomem); + } + *pparams = params; /* Check that the dptr is valid */ - if(!(Connections[cnum].dirptr = dptr_fetch_lanman2(params, dptr_num))) - return(ERROR(ERRDOS,ERRnofiles)); + if(!(conn->dirptr = dptr_fetch_lanman2(dptr_num))) + return ERROR_DOS(ERRDOS,ERRnofiles); - string_set(&Connections[cnum].dirpath,dptr_path(dptr_num)); + string_set(&conn->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)); + return ERROR_DOS(ERRDOS,ERRnofiles); } - strcpy(mask, p); - strcpy(directory,Connections[cnum].dirpath); + pstrcpy(mask, p); + pstrcpy(directory,conn->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", + DEBUG(3,("dptr_num is %d, mask = %s, attr = %x, dirptr=(0x%lX,%d)\n", dptr_num, mask, dirtype, - Connections[cnum].dirptr, - TellDir(Connections[cnum].dirptr))); + (long)conn->dirptr, + TellDir(conn->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)) + DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n",conn->dirpath,lp_dontdescend(SNUM(conn)))); + if (in_list(conn->dirpath,lp_dontdescend(SNUM(conn)),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); + /* + * Seek to the correct position. We no longer use the resume key but + * depend on the last file name instead. + */ + if(requires_resume_key && *resume_name && !continue_bit) + { + /* + * Fix for NT redirector problem triggered by resume key indexes + * changing between directory scans. We now return a resume key of 0 + * and instead look for the filename to continue from (also given + * to us by NT/95/smbfs/smbclient). If no other scans have been done between the + * findfirst/findnext (as is usual) then the directory pointer + * should already be at the correct place. Check this by scanning + * backwards looking for an exact (ie. case sensitive) filename match. + * If we get to the beginning of the directory and haven't found it then scan + * forwards again looking for a match. JRA. + */ + + int current_pos, start_pos; + char *dname = NULL; + void *dirptr = conn->dirptr; + start_pos = TellDir(dirptr); + for(current_pos = start_pos; current_pos >= 0; current_pos--) + { + DEBUG(7,("call_trans2findnext: seeking to pos %d\n", current_pos)); - for (i=0;(i<(int)maxentries) && !finished && !out_of_space ;i++) + SeekDir(dirptr, current_pos); + dname = ReadDirName(dirptr); + + /* + * Remember, mangle_map is called by + * get_lanman2_dir_entry(), so the resume name + * could be mangled. Ensure we do the same + * here. + */ + + if(dname != NULL) + mangle_map( dname, False, True, SNUM(conn)); + + if(dname && strcsequal( resume_name, dname)) + { + SeekDir(dirptr, current_pos+1); + DEBUG(7,("call_trans2findnext: got match at pos %d\n", current_pos+1 )); + break; + } + } + + /* + * Scan forward from start if not found going backwards. + */ + + if(current_pos < 0) { - /* 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); - } + DEBUG(7,("call_trans2findnext: notfound: seeking to pos %d\n", start_pos)); + SeekDir(dirptr, start_pos); + for(current_pos = start_pos; (dname = ReadDirName(dirptr)) != NULL; SeekDir(dirptr,++current_pos)) + { + /* + * Remember, mangle_map is called by + * get_lanman2_dir_entry(), so the resume name + * could be mangled. Ensure we do the same + * here. + */ + + if(dname != NULL) + mangle_map( dname, False, True, SNUM(conn)); + + if(dname && strcsequal( resume_name, dname)) + { + SeekDir(dirptr, current_pos+1); + DEBUG(7,("call_trans2findnext: got match at pos %d\n", current_pos+1 )); + break; + } + } /* end for */ + } /* end if current_pos */ + } /* end if requires_resume_key && !continue_bit */ - if (finished && out_of_space) - finished = False; + for (i=0;(i<(int)maxentries) && !finished && !out_of_space ;i++) + { + BOOL got_exact_match = False; - if (!finished && !out_of_space) - numentries++; - space_remaining = max_data_bytes - PTR_DIFF(p,pdata); + /* 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(conn, + inbuf, outbuf, + mask,dirtype,info_level, + requires_resume_key,dont_descend, + &p,pdata,space_remaining, &out_of_space, &got_exact_match, + &last_name_off); } + + if (finished && out_of_space) + finished = False; + + if (!finished && !out_of_space) + numentries++; + + /* + * As an optimisation if we know we aren't looking + * for a wildcard name (ie. the name matches the wildcard exactly) + * then we can finish on any (first) match. + * This speeds up large directory searches. JRA. + */ + + if(got_exact_match) + finished = True; + + 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; - } + { + DEBUG(5,("call_trans2findnext: closing dptr_num = %d\n", dptr_num)); + dptr_close(&dptr_num); /* This frees up the saved mask */ + } /* Set up the return parameter block */ @@ -857,124 +1246,166 @@ static int call_trans2findnext(char *inbuf, char *outbuf, int length, int bufsiz 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)); + slprintf(directory,sizeof(directory)-1, "(%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)); + DEBUG( 3, ( "%s mask=%s directory=%s dirtype=%d numentries=%d\n", + smb_fn_name(CVAL(inbuf,smb_com)), + mask, directory, dirtype, numentries ) ); return(-1); } /**************************************************************************** - reply to a TRANS2_QFSINFO (query filesystem info) + 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) + +static int call_trans2qfsinfo(connection_struct *conn, + char *inbuf, char *outbuf, + int length, int bufsize, + char **pparams, char **ppdata) { + int max_data_bytes = SVAL(inbuf, smb_mdrcnt); 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)); + int data_len, len; + SMB_STRUCT_STAT st; + char *vname = volume_label(SNUM(conn)); + int snum = SNUM(conn); + char *fstype = lp_fstype(SNUM(conn)); - if(sys_stat(".",&st)!=0) { + DEBUG(3,("call_trans2qfsinfo: level = %d\n", info_level)); + + if(vfs_stat(conn,".",&st)!=0) { DEBUG(2,("call_trans2qfsinfo: stat of . failed (%s)\n", strerror(errno))); - return (ERROR(ERRSRV,ERRinvdevice)); + return ERROR_DOS(ERRSRV,ERRinvdevice); } - pdata = *ppdata = Realloc(*ppdata, 1024); bzero(pdata,1024); + pdata = Realloc(*ppdata, max_data_bytes + 1024); + if ( pdata == NULL ) { + return ERROR_DOS(ERRDOS,ERRnomem); + } + *ppdata = pdata; + memset((char *)pdata,'\0',max_data_bytes + 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)); + { + SMB_BIG_UINT dfree,dsize,bsize; + data_len = 18; + conn->vfs_ops.disk_free(conn,".",False,&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=%u, id=%x, cSectorUnit=%u, cUnit=%u, cUnitAvail=%u, cbSector=%d\n", + (unsigned int)bsize, (unsigned int)st.st_dev, ((unsigned int)bsize)/512, (unsigned int)dsize, + (unsigned int)dfree, 512)); break; } + case 2: + /* Return volume name */ + /* + * Add volume serial number - hash of a combination of + * the called hostname and the service name. + */ + SIVAL(pdata,0,str_checksum(lp_servicename(snum)) ^ (str_checksum(local_machine)<<16) ); + len = srvstr_push(outbuf, pdata+l2_vol_szVolLabel, vname, -1, + STR_TERMINATE); + SCVAL(pdata,l2_vol_cch,len); + data_len = l2_vol_szVolLabel + len; + DEBUG(5,("call_trans2qfsinfo : time = %x, namelen = %d, name = %s\n", + (unsigned)st.st_ctime, len, vname)); + 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; + SIVAL(pdata,0,FILE_CASE_PRESERVED_NAMES|FILE_CASE_SENSITIVE_SEARCH| + (lp_nt_acl_support(SNUM(conn)) ? FILE_PERSISTENT_ACLS : 0)); /* FS ATTRIBUTES */ + SIVAL(pdata,4,255); /* Max filename component length */ + /* NOTE! the fstype must *not* be null terminated or win98 won't recognise it + and will think we can't do long filenames */ + len = srvstr_push(outbuf, pdata+12, fstype, -1, 0); + SIVAL(pdata,8,len); + data_len = 12 + len; + break; + case SMB_QUERY_FS_LABEL_INFO: - data_len = 4 + strlen(vname); - SIVAL(pdata,0,strlen(vname)); - strcpy(pdata+4,vname); - break; + len = srvstr_push(outbuf, pdata+4, vname, -1, STR_TERMINATE); + data_len = 4 + len; + SIVAL(pdata,0,len); + break; case SMB_QUERY_FS_VOLUME_INFO: - data_len = 17 + strlen(vname); - SIVAL(pdata,12,strlen(vname)); - strcpy(pdata+17,vname); - break; + /* + * Add volume serial number - hash of a combination of + * the called hostname and the service name. + */ + SIVAL(pdata,8,str_checksum(lp_servicename(snum)) ^ + (str_checksum(local_machine)<<16)); + + len = srvstr_push(outbuf, pdata+18, vname, -1, STR_TERMINATE); + SIVAL(pdata,12,len); + data_len = 18+len; + DEBUG(5,("call_trans2qfsinfo : SMB_QUERY_FS_VOLUME_INFO namelen = %d, vol=%s serv=%s\n", + (int)strlen(vname),vname, lp_servicename(snum))); + 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); - } + { + SMB_BIG_UINT dfree,dsize,bsize; + data_len = 24; + conn->vfs_ops.disk_free(conn,".",False,&bsize,&dfree,&dsize); + SBIG_UINT(pdata,0,dsize); + SBIG_UINT(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)); - } + case SMB_MAC_QUERY_FS_INFO: + /* + * Thursby MAC extension... ONLY on NTFS filesystems + * once we do streams then we don't need this + */ + if (strequal(lp_fstype(SNUM(conn)),"NTFS")) { + data_len = 88; + SIVAL(pdata,84,0x100); /* Don't support mac... */ + break; + } + /* drop through */ + default: + return ERROR_DOS(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)); + DEBUG( 4, ( "%s info_level = %d\n", + smb_fn_name(CVAL(inbuf,smb_com)), info_level) ); return -1; } /**************************************************************************** - reply to a TRANS2_SETFSINFO (set filesystem info) + 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) + +static int call_trans2setfsinfo(connection_struct *conn, + char *inbuf, char *outbuf, int length, + int bufsize, + 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)); + if (!CAN_WRITE(conn)) + return ERROR_DOS(ERRSRV,ERRaccess); outsize = set_message(outbuf,10,0,True); @@ -982,388 +1413,972 @@ static int call_trans2setfsinfo(char *inbuf, char *outbuf, int length, int bufsi } /**************************************************************************** - reply to a TRANS2_QFILEINFO (query file info by fileid) + * Utility function to set bad path error. + ****************************************************************************/ + +NTSTATUS set_bad_path_error(int err, BOOL bad_path) +{ + if((err == ENOENT) && bad_path) { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + return NT_STATUS_OBJECT_PATH_NOT_FOUND; + } + return NT_STATUS_OK; +} + +/**************************************************************************** + Reply to a TRANS2_QFILEPATHINFO or TRANSACT2_QFILEINFO (query file info by + file name or file id). ****************************************************************************/ -static int call_trans2qfilepathinfo(char *inbuf, char *outbuf, int length, - int bufsize,int cnum, + +static int call_trans2qfilepathinfo(connection_struct *conn, + char *inbuf, char *outbuf, int length, + int bufsize, 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; + int max_data_bytes = SVAL(inbuf, smb_mdrcnt); + char *params = *pparams; + char *pdata = *ppdata; + uint16 tran_call = SVAL(inbuf, smb_setup0); + uint16 info_level; + int mode=0; + SMB_OFF_T size=0; + SMB_OFF_T allocation_size=0; + unsigned int data_size; + SMB_STRUCT_STAT sbuf; + pstring fname, dos_fname; + char *base_name; + char *p; + SMB_OFF_T pos = 0; + BOOL bad_path = False; + BOOL delete_pending = False; + int len; + time_t c_time; + + if (!params) { + return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + } + if (tran_call == TRANSACT2_QFILEINFO) { + files_struct *fsp = file_fsp(params,0); + info_level = SVAL(params,2); + + DEBUG(3,("call_trans2qfilepathinfo: TRANSACT2_QFILEINFO: level = %d\n", info_level)); + + if(fsp && (fsp->is_directory || fsp->fd == -1)) { + /* + * This is actually a QFILEINFO on a directory + * handle (returned from an NT SMB). NT5.0 seems + * to do this call. JRA. + */ + pstrcpy(fname, fsp->fsp_name); + unix_convert(fname,conn,0,&bad_path,&sbuf); + if (!check_name(fname,conn) || + (!VALID_STAT(sbuf) && vfs_stat(conn,fname,&sbuf))) { + DEBUG(3,("fileinfo of %s failed (%s)\n",fname,strerror(errno))); + if((errno == ENOENT) && bad_path) { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } + return(UNIXERROR(ERRDOS,ERRbadpath)); + } + + delete_pending = fsp->directory_delete_on_close; + } else { + /* + * Original code - this is an open file. + */ + CHECK_FSP(fsp,conn); + + pstrcpy(fname, fsp->fsp_name); + if (vfs_fstat(fsp,fsp->fd,&sbuf) != 0) { + DEBUG(3,("fstat of fnum %d failed (%s)\n", fsp->fnum, strerror(errno))); + return(UNIXERROR(ERRDOS,ERRbadfid)); + } + if((pos = fsp->conn->vfs_ops.lseek(fsp,fsp->fd,0,SEEK_CUR)) == -1) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + delete_pending = fsp->delete_on_close; + } + } else { + /* qpathinfo */ + info_level = SVAL(params,0); - if (tran_call == TRANSACT2_QFILEINFO) { - int16 fnum = SVAL(params,0); - info_level = SVAL(params,2); + DEBUG(3,("call_trans2qfilepathinfo: TRANSACT2_QPATHINFO: level = %d\n", info_level)); - CHECK_FNUM(fnum,cnum); - CHECK_ERROR(fnum); + srvstr_pull(inbuf, fname, ¶ms[6], sizeof(fname), -1, STR_TERMINATE); - 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; - } + RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); + + unix_convert(fname,conn,0,&bad_path,&sbuf); + if (!check_name(fname,conn) || + (!VALID_STAT(sbuf) && vfs_stat(conn,fname,&sbuf))) { + DEBUG(3,("fileinfo of %s failed (%s)\n",fname,strerror(errno))); + if((errno == ENOENT) && bad_path) { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } + return(UNIXERROR(ERRDOS,ERRbadpath)); + } + } - DEBUG(3,("call_trans2qfilepathinfo %s level=%d call=%d total_data=%d\n", - fname,info_level,tran_call,total_data)); + DEBUG(3,("call_trans2qfilepathinfo %s level=%d call=%d total_data=%d\n", + fname,info_level,tran_call,total_data)); + + p = strrchr_m(fname,'/'); + if (!p) + base_name = fname; + else + base_name = p+1; + + mode = dos_mode(conn,fname,&sbuf); + size = sbuf.st_size; + allocation_size = SMB_ROUNDUP_ALLOCATION(sbuf.st_size); + + if (mode & aDIR) + size = 0; + + params = Realloc(*pparams,2); + if (params == NULL) + return ERROR_DOS(ERRDOS,ERRnomem); + *pparams = params; + memset((char *)params,'\0',2); + data_size = max_data_bytes + 1024; + pdata = Realloc(*ppdata, data_size); + if ( pdata == NULL ) + return ERROR_DOS(ERRDOS,ERRnomem); + *ppdata = pdata; + + 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)); + return ERROR_DOS(ERRDOS,ERReasnotsupported); + } - 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); + memset((char *)pdata,'\0',data_size); + + c_time = get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn))); + + if (lp_dos_filetime_resolution(SNUM(conn))) { + c_time &= ~1; + sbuf.st_atime &= ~1; + sbuf.st_mtime &= ~1; + sbuf.st_mtime &= ~1; + } + + /* NT expects the name to be in an exact form */ + if (strequal(fname,".")) { + pstrcpy(dos_fname, "\\"); + } else { + snprintf(dos_fname, sizeof(dos_fname), "\\%s", fname); + string_replace( dos_fname, '/', '\\'); + } + + switch (info_level) { + case SMB_INFO_STANDARD: + case SMB_INFO_QUERY_EA_SIZE: + data_size = (info_level==1?22:26); + put_dos_date2(pdata,l1_fdateCreation,c_time); + put_dos_date2(pdata,l1_fdateLastAccess,sbuf.st_atime); + put_dos_date2(pdata,l1_fdateLastWrite,sbuf.st_mtime); /* write time */ + SIVAL(pdata,l1_cbFile,(uint32)size); + SIVAL(pdata,l1_cbFileAlloc,(uint32)allocation_size); + SSVAL(pdata,l1_attrFile,mode); + SIVAL(pdata,l1_attrFile+2,4); /* this is what OS2 does */ + break; + + case SMB_INFO_QUERY_EAS_FROM_LIST: + data_size = 24; + put_dos_date2(pdata,0,c_time); + put_dos_date2(pdata,4,sbuf.st_atime); + put_dos_date2(pdata,8,sbuf.st_mtime); + SIVAL(pdata,12,(uint32)size); + SIVAL(pdata,16,(uint32)allocation_size); + SIVAL(pdata,20,mode); + break; + + case SMB_INFO_QUERY_ALL_EAS: + data_size = 4; + SIVAL(pdata,0,data_size); + break; + + case 6: + return ERROR_DOS(ERRDOS,ERRbadfunc); /* os/2 needs this */ + + case SMB_FILE_BASIC_INFORMATION: + case SMB_QUERY_FILE_BASIC_INFO: + + if (info_level == SMB_QUERY_FILE_BASIC_INFO) + data_size = 36; /* w95 returns 40 bytes not 36 - why ?. */ + else { + data_size = 40; + SIVAL(pdata,36,0); + } + put_long_date(pdata,c_time); + put_long_date(pdata+8,sbuf.st_atime); + put_long_date(pdata+16,sbuf.st_mtime); /* write time */ + put_long_date(pdata+24,sbuf.st_mtime); /* change time */ + SIVAL(pdata,32,mode); + + DEBUG(5,("SMB_QFBI - ")); + { + time_t create_time = c_time; + DEBUG(5,("create: %s ", ctime(&create_time))); + } + DEBUG(5,("access: %s ", ctime(&sbuf.st_atime))); + DEBUG(5,("write: %s ", ctime(&sbuf.st_mtime))); + DEBUG(5,("change: %s ", ctime(&sbuf.st_mtime))); + DEBUG(5,("mode: %x\n", mode)); + + break; + + case SMB_FILE_STANDARD_INFORMATION: + case SMB_QUERY_FILE_STANDARD_INFO: + data_size = 24; + /* Fake up allocation size. */ + SOFF_T(pdata,0,allocation_size); + SOFF_T(pdata,8,size); + SIVAL(pdata,16,sbuf.st_nlink); + SCVAL(pdata,20,0); + SCVAL(pdata,21,(mode&aDIR)?1:0); + break; + + case SMB_FILE_EA_INFORMATION: + case SMB_QUERY_FILE_EA_INFO: + data_size = 4; + break; + + /* Get the 8.3 name - used if NT SMB was negotiated. */ + case SMB_QUERY_FILE_ALT_NAME_INFO: + { + pstring short_name; + + pstrcpy(short_name,base_name); + /* Mangle if not already 8.3 */ + if(!mangle_is_8_3(short_name, True)) { + if(!mangle_map(short_name,True,True,SNUM(conn))) + *short_name = '\0'; + } + len = srvstr_push(outbuf, pdata+4, short_name, -1, STR_TERMINATE|STR_UPPER); + data_size = 4 + len; + SIVAL(pdata,0,len); + break; + } + + case SMB_QUERY_FILE_NAME_INFO: + /* + this must be *exactly* right for ACLs on mapped drives to work + */ + len = srvstr_push(outbuf, pdata+4, dos_fname, -1, STR_UNICODE); + data_size = 4 + len; + SIVAL(pdata,0,len); + break; + + case SMB_FILE_END_OF_FILE_INFORMATION: + case SMB_QUERY_FILE_END_OF_FILEINFO: + data_size = 8; + SOFF_T(pdata,0,size); + break; + + case SMB_FILE_ALLOCATION_INFORMATION: + case SMB_QUERY_FILE_ALLOCATION_INFO: + data_size = 8; + SOFF_T(pdata,0,allocation_size); + break; + + case SMB_QUERY_FILE_ALL_INFO: + put_long_date(pdata,c_time); + put_long_date(pdata+8,sbuf.st_atime); + put_long_date(pdata+16,sbuf.st_mtime); /* write time */ + put_long_date(pdata+24,sbuf.st_mtime); /* change time */ + SIVAL(pdata,32,mode); + pdata += 40; + SOFF_T(pdata,0,allocation_size); + SOFF_T(pdata,8,size); + SIVAL(pdata,16,sbuf.st_nlink); + SCVAL(pdata,20,delete_pending); + SCVAL(pdata,21,(mode&aDIR)?1:0); + pdata += 24; + SINO_T(pdata,0,(SMB_INO_T)sbuf.st_ino); + pdata += 8; /* index number */ + pdata += 4; /* EA info */ + if (mode & aRONLY) + SIVAL(pdata,0,0xA9); + else + SIVAL(pdata,0,0xd01BF); + pdata += 4; + SOFF_T(pdata,0,pos); /* current offset */ + pdata += 8; + SIVAL(pdata,0,mode); /* is this the right sort of mode info? */ + pdata += 4; + pdata += 4; /* alignment */ + len = srvstr_push(outbuf, pdata+4, dos_fname, -1, STR_TERMINATE); + SIVAL(pdata,0,len); + pdata += 4 + len; + data_size = PTR_DIFF(pdata,(*ppdata)); + break; + + case SMB_FILE_INTERNAL_INFORMATION: + /* This should be an index number - looks like dev/ino to me :-) */ + SIVAL(pdata,0,sbuf.st_dev); + SIVAL(pdata,4,sbuf.st_ino); + data_size = 8; + break; + + case SMB_FILE_ACCESS_INFORMATION: + SIVAL(pdata,0,0x12019F); /* ??? */ + data_size = 4; + break; + + case SMB_FILE_NAME_INFORMATION: + /* Pathname with leading '\'. */ + { + size_t byte_len; + byte_len = dos_PutUniCode(pdata+4,dos_fname,max_data_bytes,False); + SIVAL(pdata,0,byte_len); + data_size = 4 + byte_len; + break; + } + + case SMB_FILE_DISPOSITION_INFORMATION: + data_size = 1; + SCVAL(pdata,0,delete_pending); + break; + + case SMB_FILE_POSITION_INFORMATION: + data_size = 8; + SOFF_T(pdata,0,pos); + break; + + case SMB_FILE_MODE_INFORMATION: + SIVAL(pdata,0,mode); + data_size = 4; + break; + + case SMB_FILE_ALIGNMENT_INFORMATION: + SIVAL(pdata,0,0); /* No alignment needed. */ + data_size = 4; + break; - 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)); + /* Not yet finished... JRA */ + case 1018: + { + put_long_date(pdata,c_time); + put_long_date(pdata+8,sbuf.st_atime); + put_long_date(pdata+16,sbuf.st_mtime); /* write time */ + put_long_date(pdata+24,sbuf.st_mtime); /* change time */ + SIVAL(pdata,32,mode); + SIVAL(pdata,36,0); /* ??? */ + SIVAL(pdata,40,0x20); /* ??? */ + SIVAL(pdata,44,0); /* ??? */ + SOFF_T(pdata,48,size); + SIVAL(pdata,56,0x1); /* ??? */ + SIVAL(pdata,60,0); /* ??? */ + SIVAL(pdata,64,0); /* ??? */ + SIVAL(pdata,68,length); /* Following string length in bytes. */ + dos_PutUniCode(pdata+72,,False); + break; + } #endif - } - bzero(pdata,data_size); + case SMB_FILE_ALTERNATE_NAME_INFORMATION: + /* Last component of pathname. */ + { + size_t byte_len = dos_PutUniCode(pdata+4,fname,max_data_bytes,False); + SIVAL(pdata,0,byte_len); + data_size = 4 + byte_len; + break; + } + + case SMB_FILE_STREAM_INFORMATION: + if (mode & aDIR) { + data_size = 0; + } else { + size_t byte_len = dos_PutUniCode(pdata+24,"::$DATA", 0xE, False); + SIVAL(pdata,0,0); /* ??? */ + SIVAL(pdata,4,byte_len); /* Byte length of unicode string ::$DATA */ + SOFF_T(pdata,8,size); + SIVAL(pdata,16,allocation_size); + SIVAL(pdata,20,0); /* ??? */ + data_size = 24 + byte_len; + } + break; + + case SMB_FILE_COMPRESSION_INFORMATION: + SOFF_T(pdata,0,size); + SIVAL(pdata,8,0); /* ??? */ + SIVAL(pdata,12,0); /* ??? */ + data_size = 16; + break; + + case SMB_FILE_NETWORK_OPEN_INFORMATION: + put_long_date(pdata,c_time); + put_long_date(pdata+8,sbuf.st_atime); + put_long_date(pdata+16,sbuf.st_mtime); /* write time */ + put_long_date(pdata+24,sbuf.st_mtime); /* change time */ + SIVAL(pdata,32,allocation_size); + SOFF_T(pdata,40,size); + SIVAL(pdata,48,mode); + SIVAL(pdata,52,0); /* ??? */ + data_size = 56; + break; + + case SMB_FILE_ATTRIBUTE_TAG_INFORMATION: + SIVAL(pdata,0,mode); + SIVAL(pdata,4,0); + data_size = 8; + break; - 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; +#if 0 + /* NT4 server just returns "invalid query" to this - if we try to answer + it then NTws gets a BSOD! (tridge) */ + case SMB_QUERY_FILE_STREAM_INFO: + SIVAL(pdata,0,pos); + SIVAL(pdata,4,(uint32)size); + SIVAL(pdata,12,(uint32)allocation_size); + len = srvstr_push(outbuf, pdata+24, fname, -1, STR_TERMINATE); + SIVAL(pdata,20,len); + data_size = 24 + len; + break; +#endif - 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; + default: + return ERROR_DOS(ERRDOS,ERRunknownlevel); + } - case 4: - data_size = 4; - SIVAL(pdata,0,data_size); - break; + send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, data_size); - case 6: - return(ERROR(ERRDOS,ERRbadfunc)); /* os/2 needs this */ + return(-1); +} - 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; +/**************************************************************************** + Deal with the internal needs of setting the delete on close flag. Note that + as the tdb locking is recursive, it is safe to call this from within + open_file_shared. JRA. +****************************************************************************/ - 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; +NTSTATUS set_delete_on_close_internal(files_struct *fsp, BOOL delete_on_close) +{ + /* + * Only allow delete on close for writable shares. + */ + + if (delete_on_close && !CAN_WRITE(fsp->conn)) { + DEBUG(10,("set_delete_on_close_internal: file %s delete on close flag set but write access denied on share.\n", + fsp->fsp_name )); + return NT_STATUS_ACCESS_DENIED; + } + /* + * Only allow delete on close for files/directories opened with delete intent. + */ + + if (delete_on_close && !(fsp->desired_access & DELETE_ACCESS)) { + DEBUG(10,("set_delete_on_close_internal: file %s delete on close flag set but delete access denied.\n", + fsp->fsp_name )); + return NT_STATUS_ACCESS_DENIED; + } - case SMB_QUERY_FILE_EA_INFO: - data_size = 4; - break; + if(fsp->is_directory) { + fsp->directory_delete_on_close = delete_on_close; + DEBUG(10, ("set_delete_on_close_internal: %s delete on close flag for fnum = %d, directory %s\n", + delete_on_close ? "Added" : "Removed", fsp->fnum, fsp->fsp_name )); + } else { - 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; + files_struct *iterate_fsp; - 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; + /* + * Modify the share mode entry for all files open + * on this device and inode to tell other smbds we have + * changed the delete on close flag. This will be noticed + * in the close code, the last closer will delete the file + * if flag is set. + */ - 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)); - } + DEBUG(10,("set_delete_on_close_internal: %s delete on close flag for fnum = %d, file %s\n", + delete_on_close ? "Adding" : "Removing", fsp->fnum, fsp->fsp_name )); - send_trans2_replies( outbuf, bufsize, params, 2, *ppdata, data_size); + if (lock_share_entry_fsp(fsp) == False) + return NT_STATUS_ACCESS_DENIED; - return(-1); + if (!modify_delete_flag(fsp->dev, fsp->inode, delete_on_close)) { + DEBUG(0,("set_delete_on_close_internal: failed to change delete on close flag for file %s\n", + fsp->fsp_name )); + unlock_share_entry_fsp(fsp); + return NT_STATUS_ACCESS_DENIED; + } + + /* + * Release the lock. + */ + + unlock_share_entry_fsp(fsp); + + /* + * Go through all files we have open on the same device and + * inode (hanging off the same hash bucket) and set the DELETE_ON_CLOSE_FLAG. + * Other smbd's that have this file open will look in the share_mode on close. + * take care of this (rare) case in close_file(). See the comment there. + * NB. JRA. We don't really need to do this anymore - all should be taken + * care of in the share_mode changes in the tdb. + */ + + for(iterate_fsp = file_find_di_first(fsp->dev, fsp->inode); + iterate_fsp; iterate_fsp = file_find_di_next(iterate_fsp)) + fsp->delete_on_close = delete_on_close; + + /* + * Set the delete on close flag in the fsp. + */ + fsp->delete_on_close = delete_on_close; + + DEBUG(10, ("set_delete_on_close_internal: %s delete on close flag for fnum = %d, file %s\n", + delete_on_close ? "Added" : "Removed", fsp->fnum, fsp->fsp_name )); + + } + + return NT_STATUS_OK; } /**************************************************************************** - reply to a TRANS2_SETFILEINFO (set file info by fileid) + 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, + +static int call_trans2setfilepathinfo(connection_struct *conn, + char *inbuf, char *outbuf, int length, + int bufsize, 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)); - } - } + char *params = *pparams; + char *pdata = *ppdata; + uint16 tran_call = SVAL(inbuf, smb_setup0); + uint16 info_level; + int mode=0; + SMB_OFF_T size=0; + struct utimbuf tvs; + SMB_STRUCT_STAT sbuf; + pstring fname; + int fd = -1; + BOOL bad_path = False; + files_struct *fsp = NULL; + + if (tran_call == TRANSACT2_SETFILEINFO) { + fsp = file_fsp(params,0); + info_level = SVAL(params,2); + + if(fsp && (fsp->is_directory || fsp->fd == -1)) { + /* + * This is actually a SETFILEINFO on a directory + * handle (returned from an NT SMB). NT5.0 seems + * to do this call. JRA. + */ + pstrcpy(fname, fsp->fsp_name); + unix_convert(fname,conn,0,&bad_path,&sbuf); + if (!check_name(fname,conn) || (!VALID_STAT(sbuf))) { + DEBUG(3,("fileinfo of %s failed (%s)\n",fname,strerror(errno))); + if((errno == ENOENT) && bad_path) { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } + return(UNIXERROR(ERRDOS,ERRbadpath)); + } + } else if (fsp && fsp->print_file) { + /* + * Doing a DELETE_ON_CLOSE should cancel a print job. + */ + if ((info_level == SMB_SET_FILE_DISPOSITION_INFO) && CVAL(pdata,0)) { + fsp->share_mode = FILE_DELETE_ON_CLOSE; + + DEBUG(3,("call_trans2setfilepathinfo: Cancelling print job (%s)\n", fsp->fsp_name )); + + SSVAL(params,0,0); + send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0); + return(-1); + } + } else { + /* + * Original code - this is an open file. + */ + CHECK_FSP(fsp,conn); + + pstrcpy(fname, fsp->fsp_name); + fd = fsp->fd; + + if (vfs_fstat(fsp,fd,&sbuf) != 0) { + DEBUG(3,("fstat of fnum %d failed (%s)\n",fsp->fnum, strerror(errno))); + return(UNIXERROR(ERRDOS,ERRbadfid)); + } + } + } else { + /* set path info */ + info_level = SVAL(params,0); + srvstr_pull(inbuf, fname, ¶ms[6], sizeof(fname), -1, STR_TERMINATE); + unix_convert(fname,conn,0,&bad_path,&sbuf); + if(!check_name(fname, conn)) { + if((errno == ENOENT) && bad_path) { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } + return(UNIXERROR(ERRDOS,ERRbadpath)); + } + + if(!VALID_STAT(sbuf)) { + DEBUG(3,("stat of %s failed (%s)\n", fname, strerror(errno))); + if((errno == ENOENT) && bad_path) { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } + return(UNIXERROR(ERRDOS,ERRbadpath)); + } + } - DEBUG(3,("call_trans2setfilepathinfo(%d) %s info_level=%d totdata=%d\n", - tran_call,fname,info_level,total_data)); + if (!CAN_WRITE(conn)) + return ERROR_DOS(ERRSRV,ERRaccess); - /* Realloc the parameter and data sizes */ - params = *pparams = Realloc(*pparams,2); SSVAL(params,0,0); - if(params == NULL) - return(ERROR(ERRDOS,ERRnomem)); + DEBUG(3,("call_trans2setfilepathinfo(%d) %s info_level=%d totdata=%d\n", + tran_call,fname,info_level,total_data)); - size = st.st_size; - tvs.modtime = st.st_mtime; - tvs.actime = st.st_atime; - mode = dos_mode(cnum,fname,&st); + /* Realloc the parameter and data sizes */ + params = Realloc(*pparams,2); + if(params == NULL) + return ERROR_DOS(ERRDOS,ERRnomem); + *pparams = params; - 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); + SSVAL(params,0,0); - send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0); - - return(-1); - } + size = sbuf.st_size; + tvs.modtime = sbuf.st_mtime; + tvs.actime = sbuf.st_atime; + mode = dos_mode(conn,fname,&sbuf); - 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; + if (total_data > 4 && IVAL(pdata,0) == total_data) { + /* uggh, EAs for OS2 */ + DEBUG(4,("Rejecting EA request with total_data=%d\n",total_data)); + return ERROR_DOS(ERRDOS,ERReasnotsupported); + } - 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; + switch (info_level) { + case SMB_INFO_STANDARD: + case SMB_INFO_QUERY_EA_SIZE: + { + /* access time */ + tvs.actime = make_unix_date2(pdata+l1_fdateLastAccess); + + /* write time */ + tvs.modtime = make_unix_date2(pdata+l1_fdateLastWrite); + + mode = SVAL(pdata,l1_attrFile); + size = IVAL(pdata,l1_cbFile); + break; + } + + /* XXXX um, i don't think this is right. + it's also not in the cifs6.txt spec. + */ + case SMB_INFO_QUERY_EAS_FROM_LIST: + tvs.actime = make_unix_date2(pdata+8); + tvs.modtime = make_unix_date2(pdata+12); + size = IVAL(pdata,16); + mode = IVAL(pdata,24); + break; + + /* XXXX nor this. not in cifs6.txt, either. */ + case SMB_INFO_QUERY_ALL_EAS: + 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: + case SMB_FILE_BASIC_INFORMATION: + { + /* Patch to do this correctly from Paul Eggert <eggert@twinsun.com>. */ + time_t write_time; + time_t changed_time; + + /* Ignore create time at offset pdata. */ + + /* access time */ + tvs.actime = interpret_long_date(pdata+8); + + write_time = interpret_long_date(pdata+16); + changed_time = interpret_long_date(pdata+24); + + tvs.modtime = MIN(write_time, changed_time); + + /* Prefer a defined time to an undefined one. */ + if (tvs.modtime == (time_t)0 || tvs.modtime == (time_t)-1) + tvs.modtime = (write_time == (time_t)0 || write_time == (time_t)-1 + ? changed_time : write_time); + + /* attributes */ + mode = IVAL(pdata,32); + break; + } + + case SMB_FILE_ALLOCATION_INFORMATION: + case SMB_SET_FILE_ALLOCATION_INFO: + { + int ret = -1; + SMB_OFF_T allocation_size = IVAL(pdata,0); +#ifdef LARGE_SMB_OFF_T + allocation_size |= (((SMB_OFF_T)IVAL(pdata,4)) << 32); +#else /* LARGE_SMB_OFF_T */ + if (IVAL(pdata,4) != 0) /* more than 32 bits? */ + return ERROR_DOS(ERRDOS,ERRunknownlevel); +#endif /* LARGE_SMB_OFF_T */ + DEBUG(10,("call_trans2setfilepathinfo: Set file allocation info for file %s to %.0f\n", + fname, (double)allocation_size )); + + if(allocation_size != sbuf.st_size) { + SMB_STRUCT_STAT new_sbuf; + + DEBUG(10,("call_trans2setfilepathinfo: file %s : setting new allocation size to %.0f\n", + fname, (double)allocation_size )); + + if (fd == -1) { + files_struct *new_fsp = NULL; + int access_mode = 0; + int action = 0; + + if(global_oplock_break) { + /* Queue this file modify as we are the process of an oplock break. */ + + DEBUG(2,("call_trans2setfilepathinfo: queueing message due to being ")); + DEBUGADD(2,( "in oplock break state.\n")); + + push_oplock_pending_smb_message(inbuf, length); + return -1; + } + + new_fsp = open_file_shared1(conn, fname, &sbuf,FILE_WRITE_DATA, + SET_OPEN_MODE(DOS_OPEN_RDWR), + (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), + 0, 0, &access_mode, &action); + + if (new_fsp == NULL) + return(UNIXERROR(ERRDOS,ERRbadpath)); + ret = vfs_allocate_file_space(new_fsp, allocation_size); + if (vfs_fstat(new_fsp,new_fsp->fd,&new_sbuf) != 0) { + DEBUG(3,("fstat of fnum %d failed (%s)\n",new_fsp->fnum, strerror(errno))); + ret = -1; + } + close_file(new_fsp,True); + } else { + ret = vfs_allocate_file_space(fsp, allocation_size); + if (vfs_fstat(fsp,fd,&new_sbuf) != 0) { + DEBUG(3,("fstat of fnum %d failed (%s)\n",fsp->fnum, strerror(errno))); + ret = -1; + } + } + if (ret == -1) + return ERROR_NT(NT_STATUS_DISK_FULL); + + /* Allocate can trucate size... */ + size = new_sbuf.st_size; + } + + break; + } + + case SMB_FILE_END_OF_FILE_INFORMATION: + case SMB_SET_FILE_END_OF_FILE_INFO: + { + size = IVAL(pdata,0); +#ifdef LARGE_SMB_OFF_T + size |= (((SMB_OFF_T)IVAL(pdata,4)) << 32); +#else /* LARGE_SMB_OFF_T */ + if (IVAL(pdata,4) != 0) /* more than 32 bits? */ + return ERROR_DOS(ERRDOS,ERRunknownlevel); +#endif /* LARGE_SMB_OFF_T */ + DEBUG(10,("call_trans2setfilepathinfo: Set end of file info for file %s to %.0f\n", fname, (double)size )); + break; + } + + case SMB_FILE_DISPOSITION_INFORMATION: + case SMB_SET_FILE_DISPOSITION_INFO: /* Set delete on close for open file. */ + { + BOOL delete_on_close = (CVAL(pdata,0) ? True : False); + NTSTATUS status; + + if (tran_call != TRANSACT2_SETFILEINFO) + return ERROR_DOS(ERRDOS,ERRunknownlevel); + + if (fsp == NULL) + return(UNIXERROR(ERRDOS,ERRbadfid)); + + status = set_delete_on_close_internal(fsp, delete_on_close); + + if (NT_STATUS_V(status) != NT_STATUS_V(NT_STATUS_OK)) + return ERROR_NT(status); - 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; + 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; + default: + return ERROR_DOS(ERRDOS,ERRunknownlevel); + } - 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; + /* get some defaults (no modifications) if any info is zero or -1. */ + if (tvs.actime == (time_t)0 || tvs.actime == (time_t)-1) + tvs.actime = sbuf.st_atime; + + if (tvs.modtime == (time_t)0 || tvs.modtime == (time_t)-1) + tvs.modtime = sbuf.st_mtime; + + DEBUG(6,("actime: %s " , ctime(&tvs.actime))); + DEBUG(6,("modtime: %s ", ctime(&tvs.modtime))); + DEBUG(6,("size: %.0f ", (double)size)); + DEBUG(6,("mode: %x\n" , mode)); + + if (S_ISDIR(sbuf.st_mode)) + mode |= aDIR; + else + mode &= ~aDIR; + + if(!((info_level == SMB_SET_FILE_END_OF_FILE_INFO) || + (info_level == SMB_SET_FILE_ALLOCATION_INFO) || + (info_level == SMB_FILE_ALLOCATION_INFORMATION) || + (info_level == SMB_FILE_END_OF_FILE_INFORMATION))) { + + /* + * Only do this test if we are not explicitly + * changing the size of a file. + */ + if (!size) + size = sbuf.st_size; + } - 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; + /* + * Try and set the times, size and mode of this file - + * if they are different from the current values + */ + if (sbuf.st_mtime != tvs.modtime || sbuf.st_atime != tvs.actime) { + if(fsp != NULL) { + /* + * This was a setfileinfo on an open file. + * NT does this a lot. It's actually pointless + * setting the time here, as it will be overwritten + * on the next write, so we save the request + * away and will set it on file code. JRA. + */ + + if (tvs.modtime != (time_t)0 && tvs.modtime != (time_t)-1) { + DEBUG(10,("call_trans2setfilepathinfo: setting pending modtime to %s\n", ctime(&tvs.modtime) )); + fsp->pending_modtime = tvs.modtime; + } + + } else { + + DEBUG(10,("call_trans2setfilepathinfo: setting utimes to modified values.\n")); + + if(file_utime(conn, fname, &tvs)!=0) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + } - case SMB_SET_FILE_DISPOSITION_INFO: /* not supported yet */ - case SMB_SET_FILE_ALLOCATION_INFO: /* not supported yet */ - default: - return(ERROR(ERRDOS,ERRunknownlevel)); - } + /* check the mode isn't different, before changing it */ + if ((mode != 0) && (mode != dos_mode(conn, fname, &sbuf))) { + DEBUG(10,("call_trans2setfilepathinfo: file %s : setting dos mode %x\n", fname, mode )); - if (!tvs.actime) tvs.actime = st.st_atime; - if (!tvs.modtime) tvs.modtime = st.st_mtime; - if (!size) size = st.st_size; + if(file_chmod(conn, fname, mode, NULL)) { + DEBUG(2,("chmod of %s failed (%s)\n", fname, strerror(errno))); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + } - /* 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); - } - } + if(size != sbuf.st_size) { - SSVAL(params,0,0); + int ret; - send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0); + DEBUG(10,("call_trans2setfilepathinfo: file %s : setting new size to %.0f\n", + fname, (double)size )); + + if (fd == -1) { + files_struct *new_fsp = NULL; + int access_mode = 0; + int action = 0; + + if(global_oplock_break) { + /* Queue this file modify as we are the process of an oplock break. */ + + DEBUG(2,("call_trans2setfilepathinfo: queueing message due to being ")); + DEBUGADD(2,( "in oplock break state.\n")); + + push_oplock_pending_smb_message(inbuf, length); + return -1; + } + + new_fsp = open_file_shared(conn, fname, &sbuf, + SET_OPEN_MODE(DOS_OPEN_RDWR), + (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), + 0, 0, &access_mode, &action); + + if (new_fsp == NULL) + return(UNIXERROR(ERRDOS,ERRbadpath)); + ret = vfs_set_filelen(new_fsp, size); + close_file(new_fsp,True); + } else { + ret = vfs_set_filelen(fsp, size); + } + + if (ret == -1) + return (UNIXERROR(ERRHRD,ERRdiskfull)); + } + + SSVAL(params,0,0); + + send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0); - return(-1); + return(-1); } /**************************************************************************** - reply to a TRANS2_MKDIR (make directory with extended attributes). + 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) + +static int call_trans2mkdir(connection_struct *conn, + char *inbuf, char *outbuf, int length, int bufsize, + char **pparams, char **ppdata) { char *params = *pparams; pstring directory; int ret = -1; + SMB_STRUCT_STAT sbuf; + BOOL bad_path = False; - if (!CAN_WRITE(cnum)) - return(ERROR(ERRSRV,ERRaccess)); + if (!CAN_WRITE(conn)) + return ERROR_DOS(ERRSRV,ERRaccess); - strcpy(directory, ¶ms[4]); + srvstr_pull(inbuf, directory, ¶ms[4], sizeof(directory), -1, STR_TERMINATE); 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)); + unix_convert(directory,conn,0,&bad_path,&sbuf); + if (check_name(directory,conn)) + ret = vfs_mkdir(conn,directory,unix_mode(conn,aDIR,directory)); if(ret < 0) { DEBUG(5,("call_trans2mkdir error (%s)\n", strerror(errno))); + if((errno == ENOENT) && bad_path) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } return(UNIXERROR(ERRDOS,ERRnoaccess)); } /* Realloc the parameter and data sizes */ - params = *pparams = Realloc(*pparams,2); - if(params == NULL) - return(ERROR(ERRDOS,ERRnomem)); + params = Realloc(*pparams,2); + if(params == NULL) { + return ERROR_DOS(ERRDOS,ERRnomem); + } + *pparams = params; SSVAL(params,0,0); @@ -1373,11 +2388,14 @@ static int call_trans2mkdir(char *inbuf, char *outbuf, int length, int bufsize, } /**************************************************************************** - reply to a TRANS2_FINDNOTIFYFIRST (start monitoring a directory for changes) - We don't actually do this - we just send a null response. + 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 int call_trans2findnotifyfirst(connection_struct *conn, + char *inbuf, char *outbuf, + int length, int bufsize, + char **pparams, char **ppdata) { static uint16 fnf_handle = 257; char *params = *pparams; @@ -1391,13 +2409,15 @@ static int call_trans2findnotifyfirst(char *inbuf, char *outbuf, int length, int case 2: break; default: - return(ERROR(ERRDOS,ERRunknownlevel)); + return ERROR_DOS(ERRDOS,ERRunknownlevel); } /* Realloc the parameter and data sizes */ - params = *pparams = Realloc(*pparams,6); - if(params == NULL) - return(ERROR(ERRDOS,ERRnomem)); + params = Realloc(*pparams,6); + if(params == NULL) { + return ERROR_DOS(ERRDOS,ERRnomem); + } + *pparams = params; SSVAL(params,0,fnf_handle); SSVAL(params,2,0); /* No changes */ @@ -1414,20 +2434,25 @@ static int call_trans2findnotifyfirst(char *inbuf, char *outbuf, int length, int } /**************************************************************************** - reply to a TRANS2_FINDNOTIFYNEXT (continue monitoring a directory for - changes). Currently this does nothing. + 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) + +static int call_trans2findnotifynext(connection_struct *conn, + char *inbuf, char *outbuf, + int length, int bufsize, + 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)); + params = Realloc(*pparams,4); + if(params == NULL) { + return ERROR_DOS(ERRDOS,ERRnomem); + } + *pparams = params; SSVAL(params,0,0); /* No changes */ SSVAL(params,2,0); /* No EA errors */ @@ -1438,209 +2463,388 @@ static int call_trans2findnotifynext(char *inbuf, char *outbuf, int length, int } /**************************************************************************** - reply to a SMBfindclose (stop trans2 directory search) + Reply to a TRANS2_GET_DFS_REFERRAL - Shirish Kalele <kalele@veritas.com>. ****************************************************************************/ -int reply_findclose(char *inbuf,char *outbuf,int length,int bufsize) + +static int call_trans2getdfsreferral(connection_struct *conn, char* inbuf, + char* outbuf, int length, int bufsize, + char** pparams, char** ppdata) { - int cnum; - int outsize = 0; - uint16 dptr_num=SVAL(inbuf,smb_vwv0); + char *params = *pparams; + pstring pathname; + int reply_size = 0; + int max_referral_level = SVAL(params,0); - cnum = SVAL(inbuf,smb_tid); - DEBUG(3,("reply_findclose, cnum = %d, dptr_num = %d\n", cnum, dptr_num)); + DEBUG(10,("call_trans2getdfsreferral\n")); - dptr_close(dptr_num); + if(!lp_host_msdfs()) + return ERROR_DOS(ERRDOS,ERRbadfunc); - outsize = set_message(outbuf,0,0,True); + srvstr_pull(inbuf, pathname, ¶ms[2], sizeof(pathname), -1, STR_TERMINATE); - DEBUG(3,("%s SMBfindclose cnum=%d, dptr_num = %d\n",timestring(),cnum,dptr_num)); + if((reply_size = setup_dfs_referral(pathname,max_referral_level,ppdata)) < 0) + return ERROR_DOS(ERRDOS,ERRbadfile); + + SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2) | FLAGS2_DFS_PATHNAMES); + send_trans2_replies(outbuf,bufsize,0,0,*ppdata,reply_size); - return(outsize); + return(-1); } +#define LMCAT_SPL 0x53 +#define LMFUNC_GETJOBID 0x60 + /**************************************************************************** - reply to a SMBfindnclose (stop FINDNOTIFYFIRST directory search) + Reply to a TRANS2_IOCTL - used for OS/2 printing. ****************************************************************************/ -int reply_findnclose(char *inbuf,char *outbuf,int length,int bufsize) + +static int call_trans2ioctl(connection_struct *conn, char* inbuf, + char* outbuf, int length, int bufsize, + char** pparams, char** ppdata) { - int cnum; - int outsize = 0; - int dptr_num= -1; + char *pdata = *ppdata; + files_struct *fsp = file_fsp(inbuf,smb_vwv15); - cnum = SVAL(inbuf,smb_tid); - dptr_num = SVAL(inbuf,smb_vwv0); + if ((SVAL(inbuf,(smb_setup+4)) == LMCAT_SPL) && + (SVAL(inbuf,(smb_setup+6)) == LMFUNC_GETJOBID)) { + pdata = Realloc(*ppdata, 32); + if(pdata == NULL) { + return ERROR_DOS(ERRDOS,ERRnomem); + } + *ppdata = pdata; - DEBUG(3,("reply_findnclose, cnum = %d, dptr_num = %d\n", cnum, dptr_num)); + /* NOTE - THIS IS ASCII ONLY AT THE MOMENT - NOT SURE IF OS/2 + CAN ACCEPT THIS IN UNICODE. JRA. */ - /* We never give out valid handles for a - findnotifyfirst - so any dptr_num is ok here. - Just ignore it. */ + SSVAL(pdata,0,fsp->print_jobid); /* Job number */ + srvstr_push( outbuf, pdata + 2, global_myname, 15, STR_ASCII|STR_TERMINATE); /* Our NetBIOS name */ + srvstr_push( outbuf, pdata+18, lp_servicename(SNUM(conn)), 13, STR_ASCII|STR_TERMINATE); /* Service name */ + send_trans2_replies(outbuf,bufsize,*pparams,0,*ppdata,32); + return(-1); + } else { + DEBUG(2,("Unknown TRANS2_IOCTL\n")); + return ERROR_DOS(ERRSRV,ERRerror); + } +} - outsize = set_message(outbuf,0,0,True); +/**************************************************************************** + Reply to a SMBfindclose (stop trans2 directory search). +****************************************************************************/ + +int reply_findclose(connection_struct *conn, + char *inbuf,char *outbuf,int length,int bufsize) +{ + int outsize = 0; + int dptr_num=SVALS(inbuf,smb_vwv0); + START_PROFILE(SMBfindclose); + + DEBUG(3,("reply_findclose, dptr_num = %d\n", dptr_num)); + + dptr_close(&dptr_num); + + outsize = set_message(outbuf,0,0,True); - DEBUG(3,("%s SMB_findnclose cnum=%d, dptr_num = %d\n",timestring(),cnum,dptr_num)); + DEBUG(3,("SMBfindclose dptr_num = %d\n", dptr_num)); - return(outsize); + END_PROFILE(SMBfindclose); + return(outsize); } +/**************************************************************************** + Reply to a SMBfindnclose (stop FINDNOTIFYFIRST directory search). +****************************************************************************/ + +int reply_findnclose(connection_struct *conn, + char *inbuf,char *outbuf,int length,int bufsize) +{ + int outsize = 0; + int dptr_num= -1; + START_PROFILE(SMBfindnclose); + + dptr_num = SVAL(inbuf,smb_vwv0); + + DEBUG(3,("reply_findnclose, dptr_num = %d\n", 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,("SMB_findnclose dptr_num = %d\n", dptr_num)); + + END_PROFILE(SMBfindnclose); + return(outsize); +} /**************************************************************************** - reply to a SMBtranss2 - just ignore it! + Reply to a SMBtranss2 - just ignore it! ****************************************************************************/ -int reply_transs2(char *inbuf,char *outbuf,int length,int bufsize) + +int reply_transs2(connection_struct *conn, + char *inbuf,char *outbuf,int length,int bufsize) { - DEBUG(4,("Ignoring transs2 of length %d\n",length)); - return(-1); + START_PROFILE(SMBtranss2); + DEBUG(4,("Ignoring transs2 of length %d\n",length)); + END_PROFILE(SMBtranss2); + return(-1); } /**************************************************************************** - reply to a SMBtrans2 + Reply to a SMBtrans2. ****************************************************************************/ -int reply_trans2(char *inbuf,char *outbuf,int length,int bufsize) + +int reply_trans2(connection_struct *conn, + 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); + int outsize = 0; + 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); + 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); + 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; + START_PROFILE(SMBtrans2); + + if(global_oplock_break && (tran_call == TRANSACT2_OPEN)) { + /* Queue this open message as we are the process of an + * oplock break. */ + + DEBUG(2,("reply_trans2: queueing message trans2open due to being ")); + DEBUGADD(2,( "in oplock break state.\n")); + + push_oplock_pending_smb_message(inbuf, length); + END_PROFILE(SMBtrans2); + return -1; + } + + if (IS_IPC(conn) && (tran_call != TRANSACT2_OPEN) + && (tran_call != TRANSACT2_GET_DFS_REFERRAL)) { + END_PROFILE(SMBtrans2); + return ERROR_DOS(ERRSRV,ERRaccess); + } - /* 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)); - } + 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) { + /* + * Need to have rc=0 for ioctl to get job id for OS/2. + * Network printing will fail if function is not successful. + * Similar function in reply.c will be used if protocol + * is LANMAN1.0 instead of LM1.2X002. + * Until DosPrintSetJobInfo with PRJINFO3 is supported, + * outbuf doesn't have to be set(only job id is used). + */ + if ( (suwcnt == 4) && (tran_call == TRANSACT2_IOCTL) && + (SVAL(inbuf,(smb_setup+4)) == LMCAT_SPL) && + (SVAL(inbuf,(smb_setup+6)) == LMFUNC_GETJOBID)) { + DEBUG(2,("Got Trans2 DevIOctl jobid\n")); + } else { + DEBUG(2,("Invalid smb_sucnt in trans2 call(%d)\n",suwcnt)); + DEBUG(2,("Transaction is %d\n",tran_call)); + END_PROFILE(SMBtrans2); + return ERROR_DOS(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); + /* 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 ((total_params && !params) || (total_data && !data)) { + DEBUG(2,("Out of memory in reply_trans2\n")); + SAFE_FREE(params); + SAFE_FREE(data); + END_PROFILE(SMBtrans2); + return ERROR_DOS(ERRDOS,ERRnomem); + } - 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)); - } + /* 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); + + if (num_params > total_params || num_data > total_data) + exit_server("invalid params in reply_trans2"); + + if(params) + memcpy( params, smb_base(inbuf) + SVAL(inbuf, smb_psoff), num_params); + if(data) + 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); + if (!send_smb(smbd_server_fd(),outbuf)) + exit_server("reply_trans2: send_smb failed."); + + while (num_data_sofar < total_data || + num_params_sofar < total_params) { + BOOL ret; + + ret = receive_next_smb(inbuf,bufsize,SMB_SECONDARY_WAIT); + + if ((ret && + (CVAL(inbuf, smb_com) != SMBtranss2)) || !ret) { + outsize = set_message(outbuf,0,0,True); + if(ret) + DEBUG(0,("reply_trans2: Invalid secondary trans2 packet\n")); + else + DEBUG(0,("reply_trans2: %s in getting secondary trans2 response.\n", + (smb_read_error == READ_ERROR) ? "error" : "timeout" )); + SAFE_FREE(params); + SAFE_FREE(data); + END_PROFILE(SMBtrans2); + return ERROR_DOS(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); + /* 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)); + if (num_params_sofar > total_params || num_data_sofar > total_data) + exit_server("data overflow in trans2"); + + 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) { + SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2) | 0x40); /* IS_LONG_NAME */ } - } - - 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. */ + /* Now we must call the relevant TRANS2 function */ + switch(tran_call) { + case TRANSACT2_OPEN: + START_PROFILE_NESTED(Trans2_open); + outsize = call_trans2open(conn, + inbuf, outbuf, bufsize, + ¶ms, &data); + END_PROFILE_NESTED(Trans2_open); + break; + + case TRANSACT2_FINDFIRST: + START_PROFILE_NESTED(Trans2_findfirst); + outsize = call_trans2findfirst(conn, inbuf, outbuf, + bufsize, ¶ms, &data); + END_PROFILE_NESTED(Trans2_findfirst); + break; + + case TRANSACT2_FINDNEXT: + START_PROFILE_NESTED(Trans2_findnext); + outsize = call_trans2findnext(conn, inbuf, outbuf, + length, bufsize, + ¶ms, &data); + END_PROFILE_NESTED(Trans2_findnext); + break; + + case TRANSACT2_QFSINFO: + START_PROFILE_NESTED(Trans2_qfsinfo); + outsize = call_trans2qfsinfo(conn, inbuf, outbuf, + length, bufsize, ¶ms, + &data); + END_PROFILE_NESTED(Trans2_qfsinfo); + break; + + case TRANSACT2_SETFSINFO: + START_PROFILE_NESTED(Trans2_setfsinfo); + outsize = call_trans2setfsinfo(conn, inbuf, outbuf, + length, bufsize, + ¶ms, &data); + END_PROFILE_NESTED(Trans2_setfsinfo); + break; + + case TRANSACT2_QPATHINFO: + case TRANSACT2_QFILEINFO: + START_PROFILE_NESTED(Trans2_qpathinfo); + outsize = call_trans2qfilepathinfo(conn, inbuf, outbuf, + length, bufsize, + ¶ms, &data, total_data); + END_PROFILE_NESTED(Trans2_qpathinfo); + break; + case TRANSACT2_SETPATHINFO: + case TRANSACT2_SETFILEINFO: + START_PROFILE_NESTED(Trans2_setpathinfo); + outsize = call_trans2setfilepathinfo(conn, inbuf, outbuf, + length, bufsize, + ¶ms, &data, + total_data); + END_PROFILE_NESTED(Trans2_setpathinfo); + break; + + case TRANSACT2_FINDNOTIFYFIRST: + START_PROFILE_NESTED(Trans2_findnotifyfirst); + outsize = call_trans2findnotifyfirst(conn, inbuf, outbuf, + length, bufsize, + ¶ms, &data); + END_PROFILE_NESTED(Trans2_findnotifyfirst); + break; + + case TRANSACT2_FINDNOTIFYNEXT: + START_PROFILE_NESTED(Trans2_findnotifynext); + outsize = call_trans2findnotifynext(conn, inbuf, outbuf, + length, bufsize, + ¶ms, &data); + END_PROFILE_NESTED(Trans2_findnotifynext); + break; + case TRANSACT2_MKDIR: + START_PROFILE_NESTED(Trans2_mkdir); + outsize = call_trans2mkdir(conn, inbuf, outbuf, length, + bufsize, ¶ms, &data); + END_PROFILE_NESTED(Trans2_mkdir); + break; + + case TRANSACT2_GET_DFS_REFERRAL: + START_PROFILE_NESTED(Trans2_get_dfs_referral); + outsize = call_trans2getdfsreferral(conn,inbuf,outbuf,length, + bufsize, ¶ms, &data); + END_PROFILE_NESTED(Trans2_get_dfs_referral); + break; + case TRANSACT2_IOCTL: + START_PROFILE_NESTED(Trans2_ioctl); + outsize = call_trans2ioctl(conn,inbuf,outbuf,length, + bufsize,¶ms,&data); + END_PROFILE_NESTED(Trans2_ioctl); + break; + default: + /* Error in request */ + DEBUG(2,("Unknown request %d in trans2 call\n", tran_call)); + SAFE_FREE(params); + SAFE_FREE(data); + END_PROFILE(SMBtrans2); + return ERROR_DOS(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. + */ + + SAFE_FREE(params); + SAFE_FREE(data); + END_PROFILE(SMBtrans2); + return outsize; /* If a correct response was needed the + call_trans2xxx calls have already sent + it. If outsize != -1 then it is returning */ } diff --git a/source3/smbd/uid.c b/source3/smbd/uid.c new file mode 100644 index 0000000000..864d3d6c66 --- /dev/null +++ b/source3/smbd/uid.c @@ -0,0 +1,709 @@ +/* + Unix SMB/CIFS implementation. + uid/user handling + Copyright (C) Andrew Tridgell 1992-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + 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" + +/* what user is current? */ +extern struct current_user current_user; + +/**************************************************************************** + Become the guest user without changing the security context stack. +****************************************************************************/ + +BOOL change_to_guest(void) +{ + static struct passwd *pass=NULL; + + if (!pass) { + /* Don't need to free() this as its stored in a static */ + pass = getpwnam_alloc(lp_guestaccount()); + if (!pass) + return(False); + } + +#ifdef AIX + /* MWW: From AIX FAQ patch to WU-ftpd: call initgroups before + setting IDs */ + initgroups(pass->pw_name, pass->pw_gid); +#endif + + set_sec_ctx(pass->pw_uid, pass->pw_gid, 0, NULL, NULL); + + current_user.conn = NULL; + current_user.vuid = UID_FIELD_INVALID; + + return True; +} + +/******************************************************************* + Check if a username is OK. +********************************************************************/ + +static BOOL check_user_ok(connection_struct *conn, user_struct *vuser,int snum) +{ + int i; + for (i=0;i<conn->uid_cache.entries;i++) + if (conn->uid_cache.list[i] == vuser->uid) + return(True); + + if (!user_ok(vuser->user.unix_name,snum)) + return(False); + + i = conn->uid_cache.entries % UID_CACHE_SIZE; + conn->uid_cache.list[i] = vuser->uid; + + if (conn->uid_cache.entries < UID_CACHE_SIZE) + conn->uid_cache.entries++; + + return(True); +} + +/**************************************************************************** + Become the user of a connection number without changing the security context + stack, but modify the currnet_user entries. +****************************************************************************/ + +BOOL change_to_user(connection_struct *conn, uint16 vuid) +{ + user_struct *vuser = get_valid_user_struct(vuid); + int snum; + gid_t gid; + uid_t uid; + char group_c; + BOOL must_free_token = False; + NT_USER_TOKEN *token = NULL; + + if (!conn) { + DEBUG(2,("change_to_user: Connection not open\n")); + return(False); + } + + /* + * We need a separate check in security=share mode due to vuid + * always being UID_FIELD_INVALID. If we don't do this then + * in share mode security we are *always* changing uid's between + * SMB's - this hurts performance - Badly. + */ + + if((lp_security() == SEC_SHARE) && (current_user.conn == conn) && + (current_user.uid == conn->uid)) { + DEBUG(4,("change_to_user: Skipping user change - already user\n")); + return(True); + } else if ((current_user.conn == conn) && + (vuser != 0) && (current_user.vuid == vuid) && + (current_user.uid == vuser->uid)) { + DEBUG(4,("change_to_user: Skipping user change - already user\n")); + return(True); + } + + snum = SNUM(conn); + + if((vuser != NULL) && !check_user_ok(conn, vuser, snum)) + return False; + + if (conn->force_user || + conn->admin_user || + (lp_security() == SEC_SHARE)) { + uid = conn->uid; + gid = conn->gid; + current_user.groups = conn->groups; + current_user.ngroups = conn->ngroups; + token = conn->nt_user_token; + } else { + if (!vuser) { + DEBUG(2,("change_to_user: Invalid vuid used %d\n",vuid)); + return(False); + } + uid = vuser->uid; + gid = vuser->gid; + current_user.ngroups = vuser->n_groups; + current_user.groups = vuser->groups; + token = vuser->nt_user_token; + } + + /* + * See if we should force group for this service. + * If so this overrides any group set in the force + * user code. + */ + + if((group_c = *lp_force_group(snum))) { + BOOL is_guest = False; + + if(group_c == '+') { + + /* + * Only force group if the user is a member of + * the service group. Check the group memberships for + * this user (we already have this) to + * see if we should force the group. + */ + + int i; + for (i = 0; i < current_user.ngroups; i++) { + if (current_user.groups[i] == conn->gid) { + gid = conn->gid; + break; + } + } + } else { + gid = conn->gid; + } + + /* + * We've changed the group list in the token - we must + * re-create it. + */ + + if (vuser && vuser->guest) + is_guest = True; + + token = create_nt_token(uid, gid, current_user.ngroups, current_user.groups, is_guest, NULL); + must_free_token = True; + } + + set_sec_ctx(uid, gid, current_user.ngroups, current_user.groups, token); + + /* + * Free the new token (as set_sec_ctx copies it). + */ + + if (must_free_token) + delete_nt_token(&token); + + current_user.conn = conn; + current_user.vuid = vuid; + + DEBUG(5,("change_to_user uid=(%d,%d) gid=(%d,%d)\n", + (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid())); + + return(True); +} + +/**************************************************************************** + Go back to being root without changing the security context stack, + but modify the current_user entries. +****************************************************************************/ + +BOOL change_to_root_user(void) +{ + set_root_sec_ctx(); + + DEBUG(5,("change_to_root_user: now uid=(%d,%d) gid=(%d,%d)\n", + (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid())); + + current_user.conn = NULL; + current_user.vuid = UID_FIELD_INVALID; + + return(True); +} + +/**************************************************************************** + Become the user of an authenticated connected named pipe. + When this is called we are currently running as the connection + user. Doesn't modify current_user. +****************************************************************************/ + +BOOL become_authenticated_pipe_user(pipes_struct *p) +{ + if (!push_sec_ctx()) + return False; + + set_sec_ctx(p->pipe_user.uid, p->pipe_user.gid, + p->pipe_user.ngroups, p->pipe_user.groups, p->pipe_user.nt_user_token); + + return True; +} + +/**************************************************************************** + Unbecome the user of an authenticated connected named pipe. + When this is called we are running as the authenticated pipe + user and need to go back to being the connection user. Doesn't modify + current_user. +****************************************************************************/ + +BOOL unbecome_authenticated_pipe_user(void) +{ + return pop_sec_ctx(); +} + +/**************************************************************************** + Utility functions used by become_xxx/unbecome_xxx. +****************************************************************************/ + +struct conn_ctx { + connection_struct *conn; + uint16 vuid; +}; + +/* A stack of current_user connection contexts. */ + +static struct conn_ctx conn_ctx_stack[MAX_SEC_CTX_DEPTH]; +static int conn_ctx_stack_ndx; + +static void push_conn_ctx(void) +{ + struct conn_ctx *ctx_p; + + /* Check we don't overflow our stack */ + + if (conn_ctx_stack_ndx == MAX_SEC_CTX_DEPTH) { + DEBUG(0, ("Connection context stack overflow!\n")); + smb_panic("Connection context stack overflow!\n"); + } + + /* Store previous user context */ + ctx_p = &conn_ctx_stack[conn_ctx_stack_ndx]; + + ctx_p->conn = current_user.conn; + ctx_p->vuid = current_user.vuid; + + DEBUG(3, ("push_conn_ctx(%u) : conn_ctx_stack_ndx = %d\n", + (unsigned int)ctx_p->vuid, conn_ctx_stack_ndx )); + + conn_ctx_stack_ndx++; +} + +static void pop_conn_ctx(void) +{ + struct conn_ctx *ctx_p; + + /* Check for stack underflow. */ + + if (conn_ctx_stack_ndx == 0) { + DEBUG(0, ("Connection context stack underflow!\n")); + smb_panic("Connection context stack underflow!\n"); + } + + conn_ctx_stack_ndx--; + ctx_p = &conn_ctx_stack[conn_ctx_stack_ndx]; + + current_user.conn = ctx_p->conn; + current_user.vuid = ctx_p->vuid; + + ctx_p->conn = NULL; + ctx_p->vuid = UID_FIELD_INVALID; +} + +void init_conn_ctx(void) +{ + int i; + + /* Initialise connection context stack */ + for (i = 0; i < MAX_SEC_CTX_DEPTH; i++) { + conn_ctx_stack[i].conn = NULL; + conn_ctx_stack[i].vuid = UID_FIELD_INVALID; + } +} + +/**************************************************************************** + Temporarily become a root user. Must match with unbecome_root(). Saves and + restores the connection context. +****************************************************************************/ + +void become_root(void) +{ + push_sec_ctx(); + push_conn_ctx(); + set_root_sec_ctx(); +} + +/* Unbecome the root user */ + +void unbecome_root(void) +{ + pop_sec_ctx(); + pop_conn_ctx(); +} + +/**************************************************************************** + Push the current security context then force a change via change_to_user(). + Saves and restores the connection context. +****************************************************************************/ + +BOOL become_user(connection_struct *conn, uint16 vuid) +{ + if (!push_sec_ctx()) + return False; + + push_conn_ctx(); + + if (!change_to_user(conn, vuid)) { + pop_sec_ctx(); + pop_conn_ctx(); + return False; + } + + return True; +} + +BOOL unbecome_user(void) +{ + pop_sec_ctx(); + pop_conn_ctx(); + return True; +} + +/***************************************************************** + Convert the suplimentary SIDs returned in a netlogon into UNIX + group gid_t's. Add to the total group array. +*****************************************************************/ + +void add_supplementary_nt_login_groups(int *n_groups, gid_t **pp_groups, NT_USER_TOKEN **pptok) +{ + int total_groups; + int current_n_groups = *n_groups; + gid_t *final_groups = NULL; + size_t i; + NT_USER_TOKEN *ptok = *pptok; + NT_USER_TOKEN *new_tok = NULL; + + if (!ptok || (ptok->num_sids == 0)) + return; + + new_tok = dup_nt_token(ptok); + if (!new_tok) { + DEBUG(0,("add_supplementary_nt_login_groups: Failed to malloc new token\n")); + return; + } + /* Leave the allocated space but empty the number of SIDs. */ + new_tok->num_sids = 0; + + total_groups = current_n_groups + ptok->num_sids; + + final_groups = (gid_t *)malloc(total_groups * sizeof(gid_t)); + if (!final_groups) { + DEBUG(0,("add_supplementary_nt_login_groups: Failed to malloc new groups.\n")); + delete_nt_token(&new_tok); + return; + } + + memcpy(final_groups, *pp_groups, current_n_groups * sizeof(gid_t)); + for (i = 0; i < ptok->num_sids; i++) { + enum SID_NAME_USE sid_type; + gid_t new_grp; + + if (sid_to_gid(&ptok->user_sids[i], &new_grp, &sid_type)) { + /* + * Don't add the gid_t if it is already in the current group + * list. Some UNIXen don't like the same group more than once. + */ + int j; + + for (j = 0; j < current_n_groups; j++) + if (final_groups[j] == new_grp) + break; + + if ( j == current_n_groups) { + /* Group not already present. */ + final_groups[current_n_groups++] = new_grp; + } + } else { + /* SID didn't map. Copy to the new token to be saved. */ + sid_copy(&new_tok->user_sids[new_tok->num_sids++], &ptok->user_sids[i]); + } + } + + SAFE_FREE(*pp_groups); + *pp_groups = final_groups; + *n_groups = current_n_groups; + + /* Replace the old token with the truncated one. */ + delete_nt_token(&ptok); + *pptok = new_tok; +} + +/***************************************************************** + *THE CANONICAL* convert name to SID function. + Tries local lookup first - for local domains - then uses winbind. +*****************************************************************/ + +BOOL lookup_name(const char *domain, const char *name, DOM_SID *psid, enum SID_NAME_USE *name_type) +{ + extern pstring global_myname; + extern fstring global_myworkgroup; + fstring sid; + BOOL ret = False; + + *name_type = SID_NAME_UNKNOWN; + + /* If we are looking up a domain user, make sure it is + for the local machine only */ + + switch (lp_server_role()) { + case ROLE_DOMAIN_PDC: + case ROLE_DOMAIN_BDC: + if (strequal(domain, global_myworkgroup)) { + ret = local_lookup_name(name, psid, name_type); + } + /* No break is deliberate here. JRA. */ + default: + if (ret) { + } else if (strequal(global_myname, domain)) { + ret = local_lookup_name(name, psid, name_type); + } else { + DEBUG(5, ("lookup_name: domain %s is not local\n", domain)); + } + } + + if (ret) { + DEBUG(10, + ("lookup_name: (local) [%s]\\[%s] -> SID %s (type %u)\n", + domain, name, sid_to_string(sid,psid), + (unsigned int)*name_type )); + return True; + } else if (winbind_lookup_name(domain, name, psid, name_type) || (*name_type != SID_NAME_USER) ) { + + DEBUG(10,("lookup_name (winbindd): [%s]\\[%s] -> SID %s (type %u)\n", + domain, name, sid_to_string(sid, psid), + (unsigned int)*name_type)); + return True; + } + + DEBUG(10, ("lookup_name: winbind and local lookups for [%s]\\[%s] failed\n", domain, name)); + + return False; +} + +/***************************************************************** + *THE CANONICAL* convert SID to name function. + Tries local lookup first - for local sids, then tries winbind. +*****************************************************************/ + +BOOL lookup_sid(DOM_SID *sid, fstring dom_name, fstring name, enum SID_NAME_USE *name_type) +{ + if (!name_type) + return False; + + *name_type = SID_NAME_UNKNOWN; + + /* Check if this is our own sid. This should perhaps be done by + winbind? For the moment handle it here. */ + + if (sid->num_auths == 5) { + DOM_SID tmp_sid; + uint32 rid; + + sid_copy(&tmp_sid, sid); + sid_split_rid(&tmp_sid, &rid); + + if (sid_equal(&global_sam_sid, &tmp_sid)) { + + return map_domain_sid_to_name(&tmp_sid, dom_name) && + local_lookup_sid(sid, name, name_type); + } + } + + if (!winbind_lookup_sid(sid, dom_name, name, name_type)) { + fstring sid_str; + DOM_SID tmp_sid; + uint32 rid; + + DEBUG(10,("lookup_sid: winbind lookup for SID %s failed - trying local.\n", sid_to_string(sid_str, sid) )); + + sid_copy(&tmp_sid, sid); + sid_split_rid(&tmp_sid, &rid); + return map_domain_sid_to_name(&tmp_sid, dom_name) && + lookup_known_rid(&tmp_sid, rid, name, name_type); + } + return True; +} + +/***************************************************************** + *THE CANONICAL* convert uid_t to SID function. + Tries winbind first - then uses local lookup. + Returns SID pointer. +*****************************************************************/ + +DOM_SID *uid_to_sid(DOM_SID *psid, uid_t uid) +{ + uid_t low, high; + fstring sid; + + if (lp_winbind_uid(&low, &high) && uid >= low && uid <= high) { + if (winbind_uid_to_sid(psid, uid)) { + + DEBUG(10,("uid_to_sid: winbindd %u -> %s\n", + (unsigned int)uid, sid_to_string(sid, psid))); + + return psid; + } + } + + local_uid_to_sid(psid, uid); + + DEBUG(10,("uid_to_sid: local %u -> %s\n", (unsigned int)uid, sid_to_string(sid, psid))); + + return psid; +} + +/***************************************************************** + *THE CANONICAL* convert gid_t to SID function. + Tries winbind first - then uses local lookup. + Returns SID pointer. +*****************************************************************/ + +DOM_SID *gid_to_sid(DOM_SID *psid, gid_t gid) +{ + gid_t low, high; + fstring sid; + + if (lp_winbind_gid(&low, &high) && gid >= low && gid <= high) { + if (winbind_gid_to_sid(psid, gid)) { + + DEBUG(10,("gid_to_sid: winbindd %u -> %s\n", + (unsigned int)gid, sid_to_string(sid, psid))); + + return psid; + } + } + + local_gid_to_sid(psid, gid); + + DEBUG(10,("gid_to_sid: local %u -> %s\n", (unsigned int)gid, sid_to_string(sid, psid))); + + return psid; +} + +/***************************************************************** + *THE CANONICAL* convert SID to uid function. + Tries winbind first - then uses local lookup. + Returns True if this name is a user sid and the conversion + was done correctly, False if not. sidtype is set by this function. +*****************************************************************/ + +BOOL sid_to_uid(DOM_SID *psid, uid_t *puid, enum SID_NAME_USE *sidtype) +{ + fstring sid_str; + + /* if we know its local then don't try winbindd */ + if (sid_compare_domain(&global_sam_sid, psid) == 0) { + return local_sid_to_uid(puid, psid, sidtype); + } + +/* (tridge) I commented out the slab of code below in order to support foreign SIDs + Do we really need to validate the type of SID we have in this case? +*/ +#if 0 + fstring dom_name, name; + enum SID_NAME_USE name_type; + + *sidtype = SID_NAME_UNKNOWN; + /* + * First we must look up the name and decide if this is a user sid. + */ + + if ( (!winbind_lookup_sid(psid, dom_name, name, &name_type)) || (name_type != SID_NAME_USER) ) { + DEBUG(10,("sid_to_uid: winbind lookup for sid %s failed - trying local.\n", + sid_to_string(sid_str, psid) )); + + return local_sid_to_uid(puid, psid, sidtype); + } + + /* + * Ensure this is a user sid. + */ + + if (name_type != SID_NAME_USER) { + DEBUG(10,("sid_to_uid: winbind lookup succeeded but SID is not a uid (%u)\n", + (unsigned int)name_type )); + return False; + } +#endif + *sidtype = SID_NAME_USER; + + /* + * Get the uid for this SID. + */ + + if (!winbind_sid_to_uid(puid, psid)) { + DEBUG(10,("sid_to_uid: winbind lookup for sid %s failed.\n", + sid_to_string(sid_str, psid) )); + return local_sid_to_uid(puid, psid, sidtype); + } + + DEBUG(10,("sid_to_uid: winbindd %s -> %u\n", + sid_to_string(sid_str, psid), + (unsigned int)*puid )); + + return True; +} + +/***************************************************************** + *THE CANONICAL* convert SID to gid function. + Tries winbind first - then uses local lookup. + Returns True if this name is a user sid and the conversion + was done correctly, False if not. +*****************************************************************/ + +BOOL sid_to_gid(DOM_SID *psid, gid_t *pgid, enum SID_NAME_USE *sidtype) +{ + fstring dom_name, name, sid_str; + enum SID_NAME_USE name_type; + + *sidtype = SID_NAME_UNKNOWN; + + /* + * First we must look up the name and decide if this is a group sid. + */ + + if (!winbind_lookup_sid(psid, dom_name, name, &name_type)) { + DEBUG(10,("sid_to_gid: winbind lookup for sid %s failed - trying local.\n", + sid_to_string(sid_str, psid) )); + + if (!local_sid_to_gid(pgid, psid, sidtype)) { + /* this was probably a foreign sid - assume its a group rid + and continue */ + name_type = SID_NAME_DOM_GRP; + } else { + return True; + } + } + + /* + * Ensure this is a group sid. + */ + + if ((name_type != SID_NAME_DOM_GRP) && (name_type != SID_NAME_ALIAS) && (name_type != SID_NAME_WKN_GRP)) { + DEBUG(10,("sid_to_gid: winbind lookup succeeded but SID is not a known group (%u)\n", + (unsigned int)name_type )); + + return local_sid_to_gid(pgid, psid, sidtype); + } + + *sidtype = name_type; + + /* + * Get the gid for this SID. + */ + + if (!winbind_sid_to_gid(pgid, psid)) { + DEBUG(10,("sid_to_gid: winbind lookup for sid %s failed.\n", + sid_to_string(sid_str, psid) )); + return False; + } + + DEBUG(10,("sid_to_gid: winbindd %s -> %u\n", + sid_to_string(sid_str, psid), + (unsigned int)*pgid )); + + return True; +} + diff --git a/source3/smbd/utmp.c b/source3/smbd/utmp.c new file mode 100644 index 0000000000..6b7b0f3ad1 --- /dev/null +++ b/source3/smbd/utmp.c @@ -0,0 +1,606 @@ +/* + Unix SMB/CIFS implementation. + utmp routines + Copyright (C) T.D.Lee@durham.ac.uk 1999 + Heavily modified by Andrew Bartlett and Tridge, April 2001 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +#ifdef WITH_UTMP + +/**************************************************************************** +Reflect connection status in utmp/wtmp files. + T.D.Lee@durham.ac.uk September 1999 + + With grateful thanks since then to many who have helped port it to + different operating systems. The variety of OS quirks thereby + uncovered is amazing... + +Hints for porting: + o Always attempt to use programmatic interface (pututline() etc.) + Indeed, at present only programmatic use is supported. + o The only currently supported programmatic interface to "wtmp{,x}" + is through "updwtmp*()" routines. + o The "x" (utmpx/wtmpx; HAVE_UTMPX_H) seems preferable. + o The HAVE_* items should identify supported features. + o If at all possible, avoid "if defined(MY-OS)" constructions. + +OS observations and status: + Almost every OS seems to have its own quirks. + + Solaris 2.x: + Tested on 2.6 and 2.7; should be OK on other flavours. + AIX: + Apparently has utmpx.h but doesn't implement. + OSF: + Has utmpx.h, but (e.g.) no "getutmpx()". (Is this like AIX ?) + Redhat 6: + utmpx.h seems not to set default filenames. non-x better. + IRIX 6.5: + Not tested. Appears to have "x". + HP-UX 9.x: + Not tested. Appears to lack "x". + HP-UX 10.x: + Not tested. + "updwtmp*()" routines seem absent, so no current wtmp* support. + Has "ut_addr": probably trivial to implement (although remember + that IPv6 is coming...). + + FreeBSD: + No "putut*()" type of interface. + No "ut_type" and associated defines. + Write files directly. Alternatively use its login(3)/logout(3). + SunOS 4: + Not tested. Resembles FreeBSD, but no login()/logout(). + +lastlog: + Should "lastlog" files, if any, be updated? + BSD systems (SunOS 4, FreeBSD): + o Prominent mention on man pages. + System-V (e.g. Solaris 2): + o No mention on man pages, even under "man -k". + o Has a "/var/adm/lastlog" file, but pututxline() etc. seem + not to touch it. + o Despite downplaying (above), nevertheless has <lastlog.h>. + So perhaps UN*X "lastlog" facility is intended for tty/terminal only? + +Notes: + Each connection requires a small number (starting at 0, working up) + to represent the line (unum). This must be unique within and across + all smbd processes. + + The 4 byte 'ut_id' component is vital to distinguish connections, + of which there could be several hundered or even thousand. + Entries seem to be printable characters, with optional NULL pads. + + We need to be distinct from other entries in utmp/wtmp. + + Observed things: therefore avoid them. Add to this list please. + From Solaris 2.x (because that's what I have): + 'sN' : run-levels; N: [0-9] + 'co' : console + 'CC' : arbitrary things; C: [a-z] + 'rXNN' : rlogin; N: [0-9]; X: [0-9a-z] + 'tXNN' : rlogin; N: [0-9]; X: [0-9a-z] + '/NNN' : Solaris CDE + 'ftpZ' : ftp (Z is the number 255, aka 0377, aka 0xff) + Mostly a record uses the same 'ut_id' in both "utmp" and "wtmp", + but differences have been seen. + + Arbitrarily I have chosen to use a distinctive 'SM' for the + first two bytes. + + The remaining two encode the "unum" (see above). + + For "utmp consolidate" the suggestion was made to encode the pid into + those remaining two bytes (16 bits). But recent UNIX (e.g Solaris 8) + is migrating to pids > 16 bits, so we ought not to do this. + +****************************************************************************/ + +#include <utmp.h> + +#ifdef HAVE_UTMPX_H +#include <utmpx.h> +#endif + +/* BSD systems: some may need lastlog.h (SunOS 4), some may not (FreeBSD) */ +/* Some System-V systems (e.g. Solaris 2) declare this too. */ +#ifdef HAVE_LASTLOG_H +#include <lastlog.h> +#endif + +/**************************************************************************** + Obtain/release a small number (0 upwards) unique within and across smbds. +****************************************************************************/ +/* + * Need a "small" number to represent this connection, unique within this + * smbd and across all smbds. + * + * claim: + * Start at 0, hunt up for free, unique number "unum" by attempting to + * store it as a key in a tdb database: + * key: unum data: pid+conn + * Also store its inverse, ready for yield function: + * key: pid+conn data: unum + * + * yield: + * Find key: pid+conn; data is unum; delete record + * Find key: unum ; delete record. + * + * Comment: + * The claim algorithm (a "for" loop attempting to store numbers in a tdb + * database) will be increasingly inefficient with larger numbers of + * connections. Is it possible to write a suitable primitive within tdb? + * + * However, by also storing the inverse key/data pair, we at least make + * the yield algorithm efficient. + */ + +/**************************************************************************** + Default paths to various {u,w}tmp{,x} files. +****************************************************************************/ + +#ifdef HAVE_UTMPX_H + +static const char *ux_pathname = +# if defined (UTMPX_FILE) + UTMPX_FILE ; +# elif defined (_UTMPX_FILE) + _UTMPX_FILE ; +# elif defined (_PATH_UTMPX) + _PATH_UTMPX ; +# else + "" ; +# endif + +static const char *wx_pathname = +# if defined (WTMPX_FILE) + WTMPX_FILE ; +# elif defined (_WTMPX_FILE) + _WTMPX_FILE ; +# elif defined (_PATH_WTMPX) + _PATH_WTMPX ; +# else + "" ; +# endif + +#endif /* HAVE_UTMPX_H */ + +static const char *ut_pathname = +# if defined (UTMP_FILE) + UTMP_FILE ; +# elif defined (_UTMP_FILE) + _UTMP_FILE ; +# elif defined (_PATH_UTMP) + _PATH_UTMP ; +# else + "" ; +# endif + +static const char *wt_pathname = +# if defined (WTMP_FILE) + WTMP_FILE ; +# elif defined (_WTMP_FILE) + _WTMP_FILE ; +# elif defined (_PATH_WTMP) + _PATH_WTMP ; +# else + "" ; +# endif + +/* BSD-like systems might want "lastlog" support. */ +/* *** Not yet implemented */ +#ifndef HAVE_PUTUTLINE /* see "pututline_my()" */ +static const char *ll_pathname = +# if defined (_PATH_LASTLOG) /* what other names (if any?) */ + _PATH_LASTLOG ; +# else + "" ; +# endif /* _PATH_LASTLOG */ +#endif /* HAVE_PUTUTLINE */ + +/* + * Get name of {u,w}tmp{,x} file. + * return: fname contains filename + * Possibly empty if this code not yet ported to this system. + * + * utmp{,x}: try "utmp dir", then default (a define) + * wtmp{,x}: try "wtmp dir", then "utmp dir", then default (a define) + */ +static void uw_pathname(pstring fname, const char *uw_name, const char *uw_default) +{ + pstring dirname; + + pstrcpy(dirname, ""); + + /* For w-files, first look for explicit "wtmp dir" */ + if (uw_name[0] == 'w') { + pstrcpy(dirname,lp_wtmpdir()); + trim_string(dirname,"","/"); + } + + /* For u-files and non-explicit w-dir, look for "utmp dir" */ + if (dirname == 0 || strlen(dirname) == 0) { + pstrcpy(dirname,lp_utmpdir()); + trim_string(dirname,"","/"); + } + + /* If explicit directory above, use it */ + if (dirname != 0 && strlen(dirname) != 0) { + pstrcpy(fname, dirname); + pstrcat(fname, "/"); + pstrcat(fname, uw_name); + return; + } + + /* No explicit directory: attempt to use default paths */ + if (strlen(uw_default) == 0) { + /* No explicit setting, no known default. + * Has it yet been ported to this OS? + */ + DEBUG(2,("uw_pathname: unable to determine pathname\n")); + } + pstrcpy(fname, uw_default); +} + +#ifndef HAVE_PUTUTLINE + +/**************************************************************************** + Update utmp file directly. No subroutine interface: probably a BSD system. +****************************************************************************/ + +static void pututline_my(pstring uname, struct utmp *u, BOOL claim) +{ + DEBUG(1,("pututline_my: not yet implemented\n")); + /* BSD implementor: may want to consider (or not) adjusting "lastlog" */ +} +#endif /* HAVE_PUTUTLINE */ + +#ifndef HAVE_UPDWTMP + +/**************************************************************************** + Update wtmp file directly. No subroutine interface: probably a BSD system. + Credit: Michail Vidiassov <master@iaas.msu.ru> +****************************************************************************/ + +static void updwtmp_my(pstring wname, struct utmp *u, BOOL claim) +{ + int fd; + struct stat buf; + + if (! claim) { + /* + * BSD-like systems: + * may use empty ut_name to distinguish a logout record. + * + * May need "if defined(SUNOS4)" etc. around some of these, + * but try to avoid if possible. + * + * SunOS 4: + * man page indicates ut_name and ut_host both NULL + * FreeBSD 4.0: + * man page appears not to specify (hints non-NULL) + * A correspondent suggest at least ut_name should be NULL + */ + memset((char *)&u->ut_name, '\0', sizeof(u->ut_name)); + memset((char *)&u->ut_host, '\0', sizeof(u->ut_host)); + } + /* Stolen from logwtmp function in libutil. + * May be more locking/blocking is needed? + */ + if ((fd = open(wname, O_WRONLY|O_APPEND, 0)) < 0) + return; + if (fstat(fd, &buf) == 0) { + if (write(fd, (char *)u, sizeof(struct utmp)) != sizeof(struct utmp)) + (void) ftruncate(fd, buf.st_size); + } + (void) close(fd); +} +#endif /* HAVE_UPDWTMP */ + +/**************************************************************************** + Update via utmp/wtmp (not utmpx/wtmpx). +****************************************************************************/ + +static void utmp_nox_update(struct utmp *u, BOOL claim) +{ + pstring uname, wname; +#if defined(PUTUTLINE_RETURNS_UTMP) + struct utmp *urc; +#endif /* PUTUTLINE_RETURNS_UTMP */ + + uw_pathname(uname, "utmp", ut_pathname); + DEBUG(2,("utmp_nox_update: uname:%s\n", uname)); + +#ifdef HAVE_PUTUTLINE + if (strlen(uname) != 0) { + utmpname(uname); + } + +# if defined(PUTUTLINE_RETURNS_UTMP) + setutent(); + urc = pututline(u); + endutent(); + if (urc == NULL) { + DEBUG(2,("utmp_nox_update: pututline() failed\n")); + return; + } +# else /* PUTUTLINE_RETURNS_UTMP */ + setutent(); + pututline(u); + endutent(); +# endif /* PUTUTLINE_RETURNS_UTMP */ + +#else /* HAVE_PUTUTLINE */ + if (strlen(uname) != 0) { + pututline_my(uname, u, claim); + } +#endif /* HAVE_PUTUTLINE */ + + uw_pathname(wname, "wtmp", wt_pathname); + DEBUG(2,("utmp_nox_update: wname:%s\n", wname)); + if (strlen(wname) != 0) { +#ifdef HAVE_UPDWTMP + updwtmp(wname, u); + /* + * updwtmp() and the newer updwtmpx() may be unsymmetrical. + * At least one OS, Solaris 2.x declares the former in the + * "utmpx" (latter) file and context. + * In the Solaris case this is irrelevant: it has both and + * we always prefer the "x" case, so doesn't come here. + * But are there other systems, with no "x", which lack + * updwtmp() perhaps? + */ +#else + updwtmp_my(wname, u, claim); +#endif /* HAVE_UPDWTMP */ + } +} + +/**************************************************************************** + Copy a string in the utmp structure. +****************************************************************************/ + +static void utmp_strcpy(char *dest, const char *src, size_t n) +{ + size_t len = 0; + + memset(dest, '\0', n); + if (src) + len = strlen(src); + if (len >= n) { + memcpy(dest, src, n); + } else { + if (len) + memcpy(dest, src, len); + } +} + +/**************************************************************************** + Update via utmpx/wtmpx (preferred) or via utmp/wtmp. +****************************************************************************/ + +static void sys_utmp_update(struct utmp *u, const char *hostname, BOOL claim) +{ +#if !defined(HAVE_UTMPX_H) + /* No utmpx stuff. Drop to non-x stuff */ + utmp_nox_update(u, claim); +#elif !defined(HAVE_PUTUTXLINE) + /* Odd. Have utmpx.h but no "pututxline()". Drop to non-x stuff */ + DEBUG(1,("utmp_update: have utmpx.h but no pututxline() function\n")); + utmp_nox_update(u, claim); +#elif !defined(HAVE_GETUTMPX) + /* Odd. Have utmpx.h but no "getutmpx()". Drop to non-x stuff */ + DEBUG(1,("utmp_update: have utmpx.h but no getutmpx() function\n")); + utmp_nox_update(u, claim); +#else + pstring uname, wname; + struct utmpx ux, *uxrc; + + getutmpx(u, &ux); + +#if defined(HAVE_UX_UT_SYSLEN) + if (hostname) + ux.ut_syslen = strlen(hostname) + 1; /* include end NULL */ + else + ux.ut_syslen = 0; +#endif + utmp_strcpy(ux.ut_host, hostname, sizeof(ux.ut_host)); + + uw_pathname(uname, "utmpx", ux_pathname); + uw_pathname(wname, "wtmpx", wx_pathname); + DEBUG(2,("utmp_update: uname:%s wname:%s\n", uname, wname)); + /* + * Check for either uname or wname being empty. + * Some systems, such as Redhat 6, have a "utmpx.h" which doesn't + * define default filenames. + * Also, our local installation has not provided an override. + * Drop to non-x method. (E.g. RH6 has good defaults in "utmp.h".) + */ + if ((strlen(uname) == 0) || (strlen(wname) == 0)) { + utmp_nox_update(u, claim); + } else { + utmpxname(uname); + setutxent(); + uxrc = pututxline(&ux); + endutxent(); + if (uxrc == NULL) { + DEBUG(2,("utmp_update: pututxline() failed\n")); + return; + } + updwtmpx(wname, &ux); + } +#endif /* HAVE_UTMPX_H */ +} + +#if defined(HAVE_UT_UT_ID) +/**************************************************************************** + Encode the unique connection number into "ut_id". +****************************************************************************/ + +static int ut_id_encode(int i, char *fourbyte) +{ + int nbase; + char *ut_id_encstr = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + fourbyte[0] = 'S'; + fourbyte[1] = 'M'; + +/* + * Encode remaining 2 bytes from 'i'. + * 'ut_id_encstr' is the character set on which modulo arithmetic is done. + * Example: digits would produce the base-10 numbers from '001'. + */ + nbase = strlen(ut_id_encstr); + + fourbyte[3] = ut_id_encstr[i % nbase]; + i /= nbase; + fourbyte[2] = ut_id_encstr[i % nbase]; + i /= nbase; + + return(i); /* 0: good; else overflow */ +} +#endif /* defined(HAVE_UT_UT_ID) */ + + +/* + fill a system utmp structure given all the info we can gather +*/ +static BOOL sys_utmp_fill(struct utmp *u, + const char *username, const char *hostname, + const char *id_str, int id_num) +{ + struct timeval timeval; + + /* + * ut_name, ut_user: + * Several (all?) systems seems to define one as the other. + * It is easier and clearer simply to let the following take its course, + * rather than to try to detect and optimise. + */ +#if defined(HAVE_UT_UT_USER) + utmp_strcpy(u->ut_user, username, sizeof(u->ut_user)); +#elif defined(HAVE_UT_UT_NAME) + utmp_strcpy(u->ut_name, username, sizeof(u->ut_name)); +#endif + + /* + * ut_line: + * If size limit proves troublesome, then perhaps use "ut_id_encode()". + * + * Temporary variable "line_tmp" avoids trouble: + * o with unwanted trailing NULL if ut_line full; + * o with overflow if ut_line would be more than full. + */ + if (strlen(id_str) > sizeof(u->ut_line)) { + DEBUG(1,("id_str [%s] is too long for %d char utmp field\n", + id_str, sizeof(u->ut_line))); + return False; + } + utmp_strcpy(u->ut_line, id_str, sizeof(u->ut_line)); + +#if defined(HAVE_UT_UT_PID) + u->ut_pid = sys_getpid(); +#endif + +/* + * ut_time, ut_tv: + * Some have one, some the other. Many have both, but defined (aliased). + * It is easier and clearer simply to let the following take its course. + * But note that we do the more precise ut_tv as the final assignment. + */ +#if defined(HAVE_UT_UT_TIME) + gettimeofday(&timeval, NULL); + u->ut_time = timeval.tv_sec; +#elif defined(HAVE_UT_UT_TV) + gettimeofday(&timeval, NULL); + u->ut_tv = timeval; +#else +#error "with-utmp must have UT_TIME or UT_TV" +#endif + +#if defined(HAVE_UT_UT_HOST) + utmp_strcpy(u->ut_host, hostname, sizeof(u->ut_host)); +#endif + +#if defined(HAVE_UT_UT_ADDR) + /* + * "(unsigned long) ut_addr" apparently exists on at least HP-UX 10.20. + * Volunteer to implement, please ... + */ +#endif + +#if defined(HAVE_UT_UT_ID) + if (ut_id_encode(id_num, u->ut_id) != 0) { + DEBUG(1,("utmp_fill: cannot encode id %d\n", id_num)); + return False; + } +#endif + + return True; +} + +/**************************************************************************** + Close a connection. +****************************************************************************/ + +void sys_utmp_yield(const char *username, const char *hostname, + const char *id_str, int id_num) +{ + struct utmp u; + + ZERO_STRUCT(u); + +#if defined(HAVE_UT_UT_EXIT) + u.ut_exit.e_termination = 0; + u.ut_exit.e_exit = 0; +#endif + +#if defined(HAVE_UT_UT_TYPE) + u.ut_type = DEAD_PROCESS; +#endif + + if (!sys_utmp_fill(&u, username, hostname, id_str, id_num)) return; + + sys_utmp_update(&u, NULL, False); +} + +/**************************************************************************** + Claim a entry in whatever utmp system the OS uses. +****************************************************************************/ + +void sys_utmp_claim(const char *username, const char *hostname, + const char *id_str, int id_num) +{ + struct utmp u; + + ZERO_STRUCT(u); + +#if defined(HAVE_UT_UT_TYPE) + u.ut_type = USER_PROCESS; +#endif + + if (!sys_utmp_fill(&u, username, hostname, id_str, id_num)) return; + + sys_utmp_update(&u, hostname, True); +} + +#else /* WITH_UTMP */ + void dummy_utmp(void) {} +#endif diff --git a/source3/smbd/vfs-wrap.c b/source3/smbd/vfs-wrap.c new file mode 100644 index 0000000000..fadc435a2f --- /dev/null +++ b/source3/smbd/vfs-wrap.c @@ -0,0 +1,726 @@ +/* + Unix SMB/CIFS implementation. + Wrap disk only vfs functions to sidestep dodgy compilers. + Copyright (C) Tim Potter 1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + 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" + +/* Check for NULL pointer parameters in vfswrap_* functions */ + +/* We don't want to have NULL function pointers lying around. Someone + is sure to try and execute them. These stubs are used to prevent + this possibility. */ + +int vfswrap_dummy_connect(connection_struct *conn, const char *service, const char *user) +{ + return 0; /* Return >= 0 for success */ +} + +void vfswrap_dummy_disconnect(connection_struct *conn) +{ +} + +/* Disk operations */ + +SMB_BIG_UINT vfswrap_disk_free(connection_struct *conn, const char *path, BOOL small_query, SMB_BIG_UINT *bsize, + SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize) +{ + SMB_BIG_UINT result; + + result = sys_disk_free(path, small_query, bsize, dfree, dsize); + return result; +} + +/* Directory operations */ + +DIR *vfswrap_opendir(connection_struct *conn, const char *fname) +{ + DIR *result; + + START_PROFILE(syscall_opendir); + result = opendir(fname); + END_PROFILE(syscall_opendir); + return result; +} + +struct dirent *vfswrap_readdir(connection_struct *conn, DIR *dirp) +{ + struct dirent *result; + + START_PROFILE(syscall_readdir); + result = readdir(dirp); + END_PROFILE(syscall_readdir); + return result; +} + +int vfswrap_mkdir(connection_struct *conn, const char *path, mode_t mode) +{ + int result; + BOOL has_dacl = False; + + START_PROFILE(syscall_mkdir); + + if (lp_inherit_acls(SNUM(conn)) && (has_dacl = directory_has_default_acl(conn, parent_dirname(path)))) + mode = 0777; + + result = mkdir(path, mode); + + if (result == 0 && !has_dacl) { + /* + * We need to do this as the default behavior of POSIX ACLs + * is to set the mask to be the requested group permission + * bits, not the group permission bits to be the requested + * group permission bits. This is not what we want, as it will + * mess up any inherited ACL bits that were set. JRA. + */ + int saved_errno = errno; /* We may get ENOSYS */ + if (conn->vfs_ops.chmod_acl != NULL) { + if ((conn->vfs_ops.chmod_acl(conn, path, mode) == -1) && (errno == ENOSYS)) + errno = saved_errno; + } + } + + END_PROFILE(syscall_mkdir); + return result; +} + +int vfswrap_rmdir(connection_struct *conn, const char *path) +{ + int result; + + START_PROFILE(syscall_rmdir); + result = rmdir(path); + END_PROFILE(syscall_rmdir); + return result; +} + +int vfswrap_closedir(connection_struct *conn, DIR *dirp) +{ + int result; + + START_PROFILE(syscall_closedir); + result = closedir(dirp); + END_PROFILE(syscall_closedir); + return result; +} + +/* File operations */ + +int vfswrap_open(connection_struct *conn, const char *fname, int flags, mode_t mode) +{ + int result; + + START_PROFILE(syscall_open); + result = sys_open(fname, flags, mode); + END_PROFILE(syscall_open); + return result; +} + +int vfswrap_close(files_struct *fsp, int fd) +{ + int result; + + START_PROFILE(syscall_close); + + result = close(fd); + END_PROFILE(syscall_close); + return result; +} + +ssize_t vfswrap_read(files_struct *fsp, int fd, void *data, size_t n) +{ + ssize_t result; + + START_PROFILE_BYTES(syscall_read, n); + result = read(fd, data, n); + END_PROFILE(syscall_read); + return result; +} + +ssize_t vfswrap_write(files_struct *fsp, int fd, const void *data, size_t n) +{ + ssize_t result; + + START_PROFILE_BYTES(syscall_write, n); + result = write(fd, data, n); + END_PROFILE(syscall_write); + return result; +} + +SMB_OFF_T vfswrap_lseek(files_struct *fsp, int filedes, SMB_OFF_T offset, int whence) +{ + SMB_OFF_T result = 0; + + START_PROFILE(syscall_lseek); + + /* Cope with 'stat' file opens. */ + if (filedes != -1) + result = sys_lseek(filedes, offset, whence); + + /* + * We want to maintain the fiction that we can seek + * on a fifo for file system purposes. This allows + * people to set up UNIX fifo's that feed data to Windows + * applications. JRA. + */ + + if((result == -1) && (errno == ESPIPE)) { + result = 0; + errno = 0; + } + + END_PROFILE(syscall_lseek); + return result; +} + +int vfswrap_rename(connection_struct *conn, const char *old, const char *new) +{ + int result; + + START_PROFILE(syscall_rename); + result = rename(old, new); + END_PROFILE(syscall_rename); + return result; +} + +int vfswrap_fsync(files_struct *fsp, int fd) +{ +#ifdef HAVE_FSYNC + int result; + + START_PROFILE(syscall_fsync); + + result = fsync(fd); + END_PROFILE(syscall_fsync); + return result; +#else + return 0; +#endif +} + +int vfswrap_stat(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *sbuf) +{ + int result; + + START_PROFILE(syscall_stat); + result = sys_stat(fname, sbuf); + END_PROFILE(syscall_stat); + return result; +} + +int vfswrap_fstat(files_struct *fsp, int fd, SMB_STRUCT_STAT *sbuf) +{ + int result; + + START_PROFILE(syscall_fstat); + result = sys_fstat(fd, sbuf); + END_PROFILE(syscall_fstat); + return result; +} + +int vfswrap_lstat(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf) +{ + int result; + + START_PROFILE(syscall_lstat); + result = sys_lstat(path, sbuf); + END_PROFILE(syscall_lstat); + return result; +} + +int vfswrap_unlink(connection_struct *conn, const char *path) +{ + int result; + + START_PROFILE(syscall_unlink); + result = unlink(path); + END_PROFILE(syscall_unlink); + return result; +} + +int vfswrap_chmod(connection_struct *conn, const char *path, mode_t mode) +{ + int result; + + START_PROFILE(syscall_chmod); + + /* + * We need to do this due to the fact that the default POSIX ACL + * chmod modifies the ACL *mask* for the group owner, not the + * group owner bits directly. JRA. + */ + + + if (conn->vfs_ops.chmod_acl != NULL) { + int saved_errno = errno; /* We might get ENOSYS */ + if ((result = conn->vfs_ops.chmod_acl(conn, path, mode)) == 0) { + END_PROFILE(syscall_chmod); + return result; + } + /* Error - return the old errno. */ + errno = saved_errno; + } + + result = chmod(path, mode); + END_PROFILE(syscall_chmod); + return result; +} + +int vfswrap_fchmod(files_struct *fsp, int fd, mode_t mode) +{ + int result; + struct vfs_ops *vfs_ops = &fsp->conn->vfs_ops; + + START_PROFILE(syscall_fchmod); + + /* + * We need to do this due to the fact that the default POSIX ACL + * chmod modifies the ACL *mask* for the group owner, not the + * group owner bits directly. JRA. + */ + + if (vfs_ops->fchmod_acl != NULL) { + int saved_errno = errno; /* We might get ENOSYS */ + if ((result = vfs_ops->fchmod_acl(fsp, fd, mode)) == 0) { + END_PROFILE(syscall_chmod); + return result; + } + /* Error - return the old errno. */ + errno = saved_errno; + } + + result = fchmod(fd, mode); + END_PROFILE(syscall_fchmod); + return result; +} + +int vfswrap_chown(connection_struct *conn, const char *path, uid_t uid, gid_t gid) +{ + int result; + + START_PROFILE(syscall_chown); + result = sys_chown(path, uid, gid); + END_PROFILE(syscall_chown); + return result; +} + +int vfswrap_fchown(files_struct *fsp, int fd, uid_t uid, gid_t gid) +{ + int result; + + START_PROFILE(syscall_fchown); + + result = fchown(fd, uid, gid); + END_PROFILE(syscall_fchown); + return result; +} + +int vfswrap_chdir(connection_struct *conn, const char *path) +{ + int result; + + START_PROFILE(syscall_chdir); + result = chdir(path); + END_PROFILE(syscall_chdir); + return result; +} + +char *vfswrap_getwd(connection_struct *conn, char *path) +{ + char *result; + + START_PROFILE(syscall_getwd); + result = sys_getwd(path); + END_PROFILE(syscall_getwd); + return result; +} + +int vfswrap_utime(connection_struct *conn, const char *path, struct utimbuf *times) +{ + int result; + + START_PROFILE(syscall_utime); + result = utime(path, times); + END_PROFILE(syscall_utime); + return result; +} + +/********************************************************************* + A version of ftruncate that will write the space on disk if strict + allocate is set. +**********************************************************************/ + +static int strict_allocate_ftruncate(files_struct *fsp, int fd, SMB_OFF_T len) +{ + struct vfs_ops *vfs_ops = &fsp->conn->vfs_ops; + SMB_STRUCT_STAT st; + SMB_OFF_T currpos = vfs_ops->lseek(fsp, fd, 0, SEEK_CUR); + unsigned char zero_space[4096]; + SMB_OFF_T space_to_write; + + if (currpos == -1) + return -1; + + if (vfs_ops->fstat(fsp, fd, &st) == -1) + return -1; + + space_to_write = len - st.st_size; + +#ifdef S_ISFIFO + if (S_ISFIFO(st.st_mode)) + return 0; +#endif + + if (st.st_size == len) + return 0; + + /* Shrink - just ftruncate. */ + if (st.st_size > len) + return sys_ftruncate(fd, len); + + /* Write out the real space on disk. */ + if (vfs_ops->lseek(fsp, fd, st.st_size, SEEK_SET) != st.st_size) + return -1; + + space_to_write = len - st.st_size; + + memset(zero_space, '\0', sizeof(zero_space)); + while ( space_to_write > 0) { + SMB_OFF_T retlen; + SMB_OFF_T current_len_to_write = MIN(sizeof(zero_space),space_to_write); + + retlen = vfs_ops->write(fsp,fsp->fd,(char *)zero_space,current_len_to_write); + if (retlen <= 0) + return -1; + + space_to_write -= retlen; + } + + /* Seek to where we were */ + if (vfs_ops->lseek(fsp, fd, currpos, SEEK_SET) != currpos) + return -1; + + return 0; +} + +int vfswrap_ftruncate(files_struct *fsp, int fd, SMB_OFF_T len) +{ + int result = -1; + struct vfs_ops *vfs_ops = &fsp->conn->vfs_ops; + SMB_STRUCT_STAT st; + char c = 0; + SMB_OFF_T currpos; + + START_PROFILE(syscall_ftruncate); + + if (lp_strict_allocate(SNUM(fsp->conn))) { + result = strict_allocate_ftruncate(fsp, fd, len); + END_PROFILE(syscall_ftruncate); + return result; + } + + /* we used to just check HAVE_FTRUNCATE_EXTEND and only use + sys_ftruncate if the system supports it. Then I discovered that + you can have some filesystems that support ftruncate + expansion and some that don't! On Linux fat can't do + ftruncate extend but ext2 can. */ + + result = sys_ftruncate(fd, len); + if (result == 0) + goto done; + + /* According to W. R. Stevens advanced UNIX prog. Pure 4.3 BSD cannot + extend a file with ftruncate. Provide alternate implementation + for this */ + currpos = vfs_ops->lseek(fsp, fd, 0, SEEK_CUR); + if (currpos == -1) { + goto done; + } + + /* Do an fstat to see if the file is longer than the requested + size in which case the ftruncate above should have + succeeded or shorter, in which case seek to len - 1 and + write 1 byte of zero */ + if (vfs_ops->fstat(fsp, fd, &st) == -1) { + goto done; + } + +#ifdef S_ISFIFO + if (S_ISFIFO(st.st_mode)) { + result = 0; + goto done; + } +#endif + + if (st.st_size == len) { + result = 0; + goto done; + } + + if (st.st_size > len) { + /* the sys_ftruncate should have worked */ + goto done; + } + + if (vfs_ops->lseek(fsp, fd, len-1, SEEK_SET) != len -1) + goto done; + + if (vfs_ops->write(fsp, fd, &c, 1)!=1) + goto done; + + /* Seek to where we were */ + if (vfs_ops->lseek(fsp, fd, currpos, SEEK_SET) != currpos) + goto done; + result = 0; + + done: + + END_PROFILE(syscall_ftruncate); + return result; +} + +BOOL vfswrap_lock(files_struct *fsp, int fd, int op, SMB_OFF_T offset, SMB_OFF_T count, int type) +{ + BOOL result; + + START_PROFILE(syscall_fcntl_lock); + + result = fcntl_lock(fd, op, offset, count,type); + END_PROFILE(syscall_fcntl_lock); + return result; +} + +int vfswrap_symlink(connection_struct *conn, const char *oldpath, const char *newpath) +{ + int result; + + START_PROFILE(syscall_symlink); + result = sys_symlink(oldpath, newpath); + END_PROFILE(syscall_symlink); + return result; +} + +int vfswrap_readlink(connection_struct *conn, const char *path, char *buf, size_t bufsiz) +{ + int result; + + START_PROFILE(syscall_readlink); + result = sys_readlink(path, buf, bufsiz); + END_PROFILE(syscall_readlink); + return result; +} + +int vfswrap_link(connection_struct *conn, const char *oldpath, const char *newpath) +{ + int result; + + START_PROFILE(syscall_link); + result = sys_link(oldpath, newpath); + END_PROFILE(syscall_link); + return result; +} + +int vfswrap_mknod(connection_struct *conn, const char *pathname, mode_t mode, SMB_DEV_T dev) +{ + int result; + + START_PROFILE(syscall_mknod); + result = sys_mknod(pathname, mode, dev); + END_PROFILE(syscall_mknod); + return result; +} + +char *vfswrap_realpath(connection_struct *conn, const char *path, char *resolved_path) +{ + char *result; + + START_PROFILE(syscall_realpath); + result = sys_realpath(path, resolved_path); + END_PROFILE(syscall_realpath); + return result; +} + +size_t vfswrap_fget_nt_acl(files_struct *fsp, int fd, SEC_DESC **ppdesc) +{ + size_t result; + + START_PROFILE(fget_nt_acl); + result = get_nt_acl(fsp, ppdesc); + END_PROFILE(fget_nt_acl); + return result; +} + +size_t vfswrap_get_nt_acl(files_struct *fsp, const char *name, SEC_DESC **ppdesc) +{ + size_t result; + + START_PROFILE(get_nt_acl); + result = get_nt_acl(fsp, ppdesc); + END_PROFILE(get_nt_acl); + return result; +} + +BOOL vfswrap_fset_nt_acl(files_struct *fsp, int fd, uint32 security_info_sent, SEC_DESC *psd) +{ + BOOL result; + + START_PROFILE(fset_nt_acl); + result = set_nt_acl(fsp, security_info_sent, psd); + END_PROFILE(fset_nt_acl); + return result; +} + +BOOL vfswrap_set_nt_acl(files_struct *fsp, const char *name, uint32 security_info_sent, SEC_DESC *psd) +{ + BOOL result; + + START_PROFILE(set_nt_acl); + result = set_nt_acl(fsp, security_info_sent, psd); + END_PROFILE(set_nt_acl); + return result; +} + +int vfswrap_chmod_acl(connection_struct *conn, const char *name, mode_t mode) +{ + int result; + + START_PROFILE(chmod_acl); + result = chmod_acl(conn, name, mode); + END_PROFILE(chmod_acl); + return result; +} + +int vfswrap_fchmod_acl(files_struct *fsp, int fd, mode_t mode) +{ + int result; + + START_PROFILE(fchmod_acl); + result = fchmod_acl(fsp, fd, mode); + END_PROFILE(fchmod_acl); + return result; +} + +int vfswrap_sys_acl_get_entry(struct connection_struct *conn, SMB_ACL_T theacl, int entry_id, SMB_ACL_ENTRY_T *entry_p) +{ + return sys_acl_get_entry(theacl, entry_id, entry_p); +} + +int vfswrap_sys_acl_get_tag_type(struct connection_struct *conn, SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p) +{ + return sys_acl_get_tag_type(entry_d, tag_type_p); +} + +int vfswrap_sys_acl_get_permset(struct connection_struct *conn, SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p) +{ + return sys_acl_get_permset(entry_d, permset_p); +} + +void * vfswrap_sys_acl_get_qualifier(struct connection_struct *conn, SMB_ACL_ENTRY_T entry_d) +{ + return sys_acl_get_qualifier(entry_d); +} + +SMB_ACL_T vfswrap_sys_acl_get_file(struct connection_struct *conn, const char *path_p, SMB_ACL_TYPE_T type) +{ + return sys_acl_get_file(path_p, type); +} + +SMB_ACL_T vfswrap_sys_acl_get_fd(struct files_struct *fsp, int fd) +{ + return sys_acl_get_fd(fd); +} + +int vfswrap_sys_acl_clear_perms(struct connection_struct *conn, SMB_ACL_PERMSET_T permset) +{ + return sys_acl_clear_perms(permset); +} + +int vfswrap_sys_acl_add_perm(struct connection_struct *conn, SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm) +{ + return sys_acl_add_perm(permset, perm); +} + +char * vfswrap_sys_acl_to_text(struct connection_struct *conn, SMB_ACL_T theacl, ssize_t *plen) +{ + return sys_acl_to_text(theacl, plen); +} + +SMB_ACL_T vfswrap_sys_acl_init(struct connection_struct *conn, int count) +{ + return sys_acl_init(count); +} + +int vfswrap_sys_acl_create_entry(struct connection_struct *conn, SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry) +{ + return sys_acl_create_entry(pacl, pentry); +} + +int vfswrap_sys_acl_set_tag_type(struct connection_struct *conn, SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype) +{ + return sys_acl_set_tag_type(entry, tagtype); +} + +int vfswrap_sys_acl_set_qualifier(struct connection_struct *conn, SMB_ACL_ENTRY_T entry, void *qual) +{ + return sys_acl_set_qualifier(entry, qual); +} + +int vfswrap_sys_acl_set_permset(struct connection_struct *conn, SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset) +{ + return sys_acl_set_permset(entry, permset); +} + +int vfswrap_sys_acl_valid(struct connection_struct *conn, SMB_ACL_T theacl ) +{ + return sys_acl_valid(theacl ); +} + +int vfswrap_sys_acl_set_file(struct connection_struct *conn, const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl) +{ + return sys_acl_set_file(name, acltype, theacl); +} + +int vfswrap_sys_acl_set_fd(struct files_struct *fsp, int fd, SMB_ACL_T theacl) +{ + return sys_acl_set_fd(fd, theacl); +} + +int vfswrap_sys_acl_delete_def_file(struct connection_struct *conn, const char *path) +{ + return sys_acl_delete_def_file(path); +} + +int vfswrap_sys_acl_get_perm(struct connection_struct *conn, SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm) +{ + return sys_acl_get_perm(permset, perm); +} + +int vfswrap_sys_acl_free_text(struct connection_struct *conn, char *text) +{ + return sys_acl_free_text(text); +} + +int vfswrap_sys_acl_free_acl(struct connection_struct *conn, SMB_ACL_T posix_acl) +{ + return sys_acl_free_acl(posix_acl); +} + +int vfswrap_sys_acl_free_qualifier(struct connection_struct *conn, void *qualifier, SMB_ACL_TAG_T tagtype) +{ + return sys_acl_free_qualifier(qualifier, tagtype); +} diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c new file mode 100644 index 0000000000..4e2e353ed8 --- /dev/null +++ b/source3/smbd/vfs.c @@ -0,0 +1,802 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + VFS initialisation and support functions + Copyright (C) Tim Potter 1999 + + 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" + +/* Some structures to help us initialise the vfs operations table */ + +struct vfs_syminfo { + char *name; + void *fptr; +}; + +/* Default vfs hooks. WARNING: The order of these initialisers is + very important. They must be in the same order as defined in + vfs.h. Change at your own peril. */ + +struct vfs_ops default_vfs_ops = { + + /* Disk operations */ + + vfswrap_dummy_connect, + vfswrap_dummy_disconnect, + vfswrap_disk_free, + + /* Directory operations */ + + vfswrap_opendir, + vfswrap_readdir, + vfswrap_mkdir, + vfswrap_rmdir, + vfswrap_closedir, + + /* File operations */ + + vfswrap_open, + vfswrap_close, + vfswrap_read, + vfswrap_write, + vfswrap_lseek, + vfswrap_rename, + vfswrap_fsync, + vfswrap_stat, + vfswrap_fstat, + vfswrap_lstat, + vfswrap_unlink, + vfswrap_chmod, + vfswrap_fchmod, + vfswrap_chown, + vfswrap_fchown, + vfswrap_chdir, + vfswrap_getwd, + vfswrap_utime, + vfswrap_ftruncate, + vfswrap_lock, + vfswrap_symlink, + vfswrap_readlink, + vfswrap_link, + vfswrap_mknod, + vfswrap_realpath, + + vfswrap_fget_nt_acl, + vfswrap_get_nt_acl, + vfswrap_fset_nt_acl, + vfswrap_set_nt_acl, + + /* POSIX ACL operations. */ +#if defined(HAVE_NO_ACLS) + NULL, + NULL, +#else + vfswrap_chmod_acl, + vfswrap_fchmod_acl, +#endif + vfswrap_sys_acl_get_entry, + vfswrap_sys_acl_get_tag_type, + vfswrap_sys_acl_get_permset, + vfswrap_sys_acl_get_qualifier, + vfswrap_sys_acl_get_file, + vfswrap_sys_acl_get_fd, + vfswrap_sys_acl_clear_perms, + vfswrap_sys_acl_add_perm, + vfswrap_sys_acl_to_text, + vfswrap_sys_acl_init, + vfswrap_sys_acl_create_entry, + vfswrap_sys_acl_set_tag_type, + vfswrap_sys_acl_set_qualifier, + vfswrap_sys_acl_set_permset, + vfswrap_sys_acl_valid, + vfswrap_sys_acl_set_file, + vfswrap_sys_acl_set_fd, + vfswrap_sys_acl_delete_def_file, + vfswrap_sys_acl_get_perm, + vfswrap_sys_acl_free_text, + vfswrap_sys_acl_free_acl, + vfswrap_sys_acl_free_qualifier +}; + +/**************************************************************************** + initialise default vfs hooks +****************************************************************************/ + +static BOOL vfs_init_default(connection_struct *conn) +{ + DEBUG(3, ("Initialising default vfs hooks\n")); + + memcpy(&conn->vfs_ops, &default_vfs_ops, sizeof(struct vfs_ops)); + return True; +} + +/**************************************************************************** + initialise custom vfs hooks +****************************************************************************/ + +static BOOL vfs_init_custom(connection_struct *conn) +{ + int vfs_version = -1; + struct vfs_ops *ops, *(*init_fptr)(int *, struct vfs_ops *); + + DEBUG(3, ("Initialising custom vfs hooks from %s\n", lp_vfsobj(SNUM(conn)))); + + /* Open object file */ + + if ((conn->dl_handle = sys_dlopen(lp_vfsobj(SNUM(conn)), RTLD_NOW | RTLD_GLOBAL)) == NULL) { + DEBUG(0, ("Error opening %s: %s\n", lp_vfsobj(SNUM(conn)), dlerror())); + return False; + } + + /* Get handle on vfs_init() symbol */ + + init_fptr = (struct vfs_ops *(*)(int *, struct vfs_ops *))sys_dlsym(conn->dl_handle, "vfs_init"); + + if (init_fptr == NULL) { + DEBUG(0, ("No vfs_init() symbol found in %s\n", lp_vfsobj(SNUM(conn)))); + return False; + } + + /* Initialise vfs_ops structure */ + + conn->vfs_ops = default_vfs_ops; + + if ((ops = init_fptr(&vfs_version, &default_vfs_ops)) == NULL) { + DEBUG(0, ("vfs_init function from %s failed\n", lp_vfsobj(SNUM(conn)))); + return False; + } + + if (vfs_version != SMB_VFS_INTERFACE_VERSION) { + DEBUG(0, ("vfs_init returned wrong interface version info (was %d, should be %d)\n", + vfs_version, SMB_VFS_INTERFACE_VERSION )); + return False; + } + + if (ops != &conn->vfs_ops) { + memcpy(&conn->vfs_ops, ops, sizeof(struct vfs_ops)); + } + + return True; +} + +/***************************************************************** + Generic VFS init. +******************************************************************/ + +BOOL smbd_vfs_init(connection_struct *conn) +{ + if (*lp_vfsobj(SNUM(conn))) { + + /* Loadable object file */ + + if (!vfs_init_custom(conn)) { + DEBUG(0, ("smbd_vfs_init: vfs_init_custom failed\n")); + return False; + } + + return True; + } + + /* Normal share - initialise with disk access functions */ + + return vfs_init_default(conn); +} + +/******************************************************************* + Check if directory exists. +********************************************************************/ + +BOOL vfs_directory_exist(connection_struct *conn, const char *dname, SMB_STRUCT_STAT *st) +{ + SMB_STRUCT_STAT st2; + BOOL ret; + + if (!st) + st = &st2; + + if (vfs_stat(conn,dname,st) != 0) + return(False); + + ret = S_ISDIR(st->st_mode); + if(!ret) + errno = ENOTDIR; + + return ret; +} + +/******************************************************************* + vfs getwd wrapper +********************************************************************/ +char *vfs_getwd(connection_struct *conn, char *path) +{ + return conn->vfs_ops.getwd(conn,path); +} + +/******************************************************************* + vfs mkdir wrapper +********************************************************************/ + +int vfs_mkdir(connection_struct *conn, const char *name, mode_t mode) +{ + int ret; + SMB_STRUCT_STAT sbuf; + + if(!(ret=conn->vfs_ops.mkdir(conn,name,mode))) { + /* + * Check if high bits should have been set, + * then (if bits are missing): add them. + * Consider bits automagically set by UNIX, i.e. SGID bit from parent dir. + */ + if(mode & ~(S_IRWXU|S_IRWXG|S_IRWXO) && + !vfs_stat(conn,name,&sbuf) && (mode & ~sbuf.st_mode)) + vfs_chmod(conn,name,sbuf.st_mode | (mode & ~sbuf.st_mode)); + } + return ret; +} + +/******************************************************************* + Check if an object exists in the vfs. +********************************************************************/ + +BOOL vfs_object_exist(connection_struct *conn,const char *fname,SMB_STRUCT_STAT *sbuf) +{ + SMB_STRUCT_STAT st; + + if (!sbuf) + sbuf = &st; + + ZERO_STRUCTP(sbuf); + + if (vfs_stat(conn,fname,sbuf) == -1) + return(False); + return True; +} + +/******************************************************************* + Check if a file exists in the vfs. +********************************************************************/ + +BOOL vfs_file_exist(connection_struct *conn, const char *fname,SMB_STRUCT_STAT *sbuf) +{ + SMB_STRUCT_STAT st; + + if (!sbuf) + sbuf = &st; + + ZERO_STRUCTP(sbuf); + + if (vfs_stat(conn,fname,sbuf) == -1) + return False; + return(S_ISREG(sbuf->st_mode)); +} + +/**************************************************************************** + Read data from fsp on the vfs. (note: EINTR re-read differs from vfs_write_data) +****************************************************************************/ + +ssize_t vfs_read_data(files_struct *fsp, char *buf, size_t byte_count) +{ + size_t total=0; + + while (total < byte_count) + { + ssize_t ret = fsp->conn->vfs_ops.read(fsp, fsp->fd, buf + total, + byte_count - total); + + if (ret == 0) return total; + if (ret == -1) { + if (errno == EINTR) + continue; + else + return -1; + } + total += ret; + } + return (ssize_t)total; +} + +/**************************************************************************** + Write data to a fd on the vfs. +****************************************************************************/ + +ssize_t vfs_write_data(files_struct *fsp,const char *buffer,size_t N) +{ + size_t total=0; + ssize_t ret; + + while (total < N) { + ret = fsp->conn->vfs_ops.write(fsp,fsp->fd,buffer + total,N - total); + + if (ret == -1) + return -1; + if (ret == 0) + return total; + + total += ret; + } + return (ssize_t)total; +} + +/**************************************************************************** + An allocate file space call using the vfs interface. + Allocates space for a file from a filedescriptor. + Returns 0 on success, -1 on failure. +****************************************************************************/ + +int vfs_allocate_file_space(files_struct *fsp, SMB_OFF_T len) +{ + int ret; + SMB_STRUCT_STAT st; + connection_struct *conn = fsp->conn; + struct vfs_ops *vfs_ops = &conn->vfs_ops; + SMB_OFF_T space_avail; + SMB_BIG_UINT bsize,dfree,dsize; + + release_level_2_oplocks_on_change(fsp); + + /* + * Actually try and commit the space on disk.... + */ + + DEBUG(10,("vfs_allocate_file_space: file %s, len %.0f\n", fsp->fsp_name, (double)len )); + + ret = vfs_fstat(fsp,fsp->fd,&st); + if (ret == -1) + return ret; + + if (len == st.st_size) + return 0; + + if (len < st.st_size) { + /* Shrink - use ftruncate. */ + + DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current size %.0f\n", + fsp->fsp_name, (double)st.st_size )); + + flush_write_cache(fsp, SIZECHANGE_FLUSH); + if ((ret = vfs_ops->ftruncate(fsp, fsp->fd, len)) != -1) { + set_filelen_write_cache(fsp, len); + } + return ret; + } + + /* Grow - we need to test if we have enough space. */ + + if (!lp_strict_allocate(SNUM(fsp->conn))) + return 0; + + len -= st.st_size; + len /= 1024; /* Len is now number of 1k blocks needed. */ + space_avail = (SMB_OFF_T)conn->vfs_ops.disk_free(conn,fsp->fsp_name,False,&bsize,&dfree,&dsize); + + DEBUG(10,("vfs_allocate_file_space: file %s, grow. Current size %.0f, needed blocks = %lu, space avail = %lu\n", + fsp->fsp_name, (double)st.st_size, (unsigned long)len, (unsigned long)space_avail )); + + if (len > space_avail) { + errno = ENOSPC; + return -1; + } + + return 0; +} + +/**************************************************************************** + A vfs set_filelen call. + set the length of a file from a filedescriptor. + Returns 0 on success, -1 on failure. +****************************************************************************/ + +int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len) +{ + int ret; + + release_level_2_oplocks_on_change(fsp); + DEBUG(10,("vfs_set_filelen: ftruncate %s to len %.0f\n", fsp->fsp_name, (double)len)); + flush_write_cache(fsp, SIZECHANGE_FLUSH); + if ((ret = fsp->conn->vfs_ops.ftruncate(fsp, fsp->fd, len)) != -1) + set_filelen_write_cache(fsp, len); + + return ret; +} + +/**************************************************************************** + Transfer some data (n bytes) between two file_struct's. +****************************************************************************/ + +static files_struct *in_fsp; +static files_struct *out_fsp; + +static ssize_t read_fn(int fd, void *buf, size_t len) +{ + return in_fsp->conn->vfs_ops.read(in_fsp, fd, buf, len); +} + +static ssize_t write_fn(int fd, const void *buf, size_t len) +{ + return out_fsp->conn->vfs_ops.write(out_fsp, fd, buf, len); +} + +SMB_OFF_T vfs_transfer_file(files_struct *in, files_struct *out, SMB_OFF_T n) +{ + in_fsp = in; + out_fsp = out; + + return transfer_file_internal(in_fsp->fd, out_fsp->fd, n, read_fn, write_fn); +} + +/******************************************************************* + A vfs_readdir wrapper which just returns the file name. +********************************************************************/ + +char *vfs_readdirname(connection_struct *conn, void *p) +{ + struct dirent *ptr; + char *dname; + + if (!p) + return(NULL); + + ptr = (struct dirent *)conn->vfs_ops.readdir(conn,p); + if (!ptr) + return(NULL); + + dname = ptr->d_name; + +#ifdef NEXT2 + if (telldir(p) < 0) + return(NULL); +#endif + +#ifdef HAVE_BROKEN_READDIR + /* using /usr/ucb/cc is BAD */ + dname = dname - 2; +#endif + + return(dname); +} + +/* VFS options not quite working yet */ + +#if 0 + +/*************************************************************************** + handle the interpretation of the vfs option parameter + *************************************************************************/ +static BOOL handle_vfs_option(char *pszParmValue, char **ptr) +{ + struct vfs_options *new_option, **options = (struct vfs_options **)ptr; + int i; + + /* Create new vfs option */ + + new_option = (struct vfs_options *)malloc(sizeof(*new_option)); + if (new_option == NULL) { + return False; + } + + ZERO_STRUCTP(new_option); + + /* Get name and value */ + + new_option->name = strtok(pszParmValue, "="); + + if (new_option->name == NULL) { + return False; + } + + while(isspace(*new_option->name)) { + new_option->name++; + } + + for (i = strlen(new_option->name); i > 0; i--) { + if (!isspace(new_option->name[i - 1])) break; + } + + new_option->name[i] = '\0'; + new_option->name = strdup(new_option->name); + + new_option->value = strtok(NULL, "="); + + if (new_option->value != NULL) { + + while(isspace(*new_option->value)) { + new_option->value++; + } + + for (i = strlen(new_option->value); i > 0; i--) { + if (!isspace(new_option->value[i - 1])) break; + } + + new_option->value[i] = '\0'; + new_option->value = strdup(new_option->value); + } + + /* Add to list */ + + DLIST_ADD(*options, new_option); + + return True; +} + +#endif + + +/******************************************************************* + A wrapper for vfs_chdir(). +********************************************************************/ + +int vfs_ChDir(connection_struct *conn, char *path) +{ + int res; + static pstring LastDir=""; + + if (strcsequal(path,".")) + return(0); + + if (*path == '/' && strcsequal(LastDir,path)) + return(0); + + DEBUG(3,("vfs_ChDir to %s\n",path)); + + res = vfs_chdir(conn,path); + if (!res) + pstrcpy(LastDir,path); + return(res); +} + +/* number of list structures for a caching GetWd function. */ +#define MAX_GETWDCACHE (50) + +struct { + SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */ + SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */ + char *dos_path; /* The pathname in DOS format. */ + BOOL valid; +} ino_list[MAX_GETWDCACHE]; + +extern BOOL use_getwd_cache; + +/**************************************************************************** + Prompte a ptr (to make it recently used) +****************************************************************************/ + +static void array_promote(char *array,int elsize,int element) +{ + char *p; + if (element == 0) + return; + + p = (char *)malloc(elsize); + + if (!p) { + DEBUG(5,("array_promote: malloc fail\n")); + return; + } + + memcpy(p,array + element * elsize, elsize); + memmove(array + elsize,array,elsize*element); + memcpy(array,p,elsize); + SAFE_FREE(p); +} + +/******************************************************************* + Return the absolute current directory path - given a UNIX pathname. + Note that this path is returned in DOS format, not UNIX + format. Note this can be called with conn == NULL. +********************************************************************/ + +char *vfs_GetWd(connection_struct *conn, char *path) +{ + pstring s; + static BOOL getwd_cache_init = False; + SMB_STRUCT_STAT st, st2; + int i; + + *s = 0; + + if (!use_getwd_cache) + return(vfs_getwd(conn,path)); + + /* init the cache */ + if (!getwd_cache_init) { + getwd_cache_init = True; + for (i=0;i<MAX_GETWDCACHE;i++) { + string_set(&ino_list[i].dos_path,""); + ino_list[i].valid = False; + } + } + + /* Get the inode of the current directory, if this doesn't work we're + in trouble :-) */ + + if (vfs_stat(conn, ".",&st) == -1) { + DEBUG(0,("Very strange, couldn't stat \".\" path=%s\n", path)); + return(vfs_getwd(conn,path)); + } + + + for (i=0; i<MAX_GETWDCACHE; i++) { + if (ino_list[i].valid) { + + /* If we have found an entry with a matching inode and dev number + then find the inode number for the directory in the cached string. + If this agrees with that returned by the stat for the current + directory then all is o.k. (but make sure it is a directory all + the same...) */ + + if (st.st_ino == ino_list[i].inode && st.st_dev == ino_list[i].dev) { + if (vfs_stat(conn,ino_list[i].dos_path,&st2) == 0) { + if (st.st_ino == st2.st_ino && st.st_dev == st2.st_dev && + (st2.st_mode & S_IFMT) == S_IFDIR) { + pstrcpy (path, ino_list[i].dos_path); + + /* promote it for future use */ + array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i); + return (path); + } else { + /* If the inode is different then something's changed, + scrub the entry and start from scratch. */ + ino_list[i].valid = False; + } + } + } + } + } + + /* We don't have the information to hand so rely on traditional methods. + The very slow getcwd, which spawns a process on some systems, or the + not quite so bad getwd. */ + + if (!vfs_getwd(conn,s)) { + DEBUG(0,("vfs_GetWd: vfs_getwd call failed, errno %s\n",strerror(errno))); + return (NULL); + } + + pstrcpy(path,s); + + DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",s,(double)st.st_ino,(double)st.st_dev)); + + /* add it to the cache */ + i = MAX_GETWDCACHE - 1; + string_set(&ino_list[i].dos_path,s); + ino_list[i].dev = st.st_dev; + ino_list[i].inode = st.st_ino; + ino_list[i].valid = True; + + /* put it at the top of the list */ + array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i); + + return (path); +} + +/******************************************************************* + Reduce a file name, removing .. elements and checking that + it is below dir in the heirachy. This uses vfs_GetWd() and so must be run + on the system that has the referenced file system. + Widelinks are allowed if widelinks is true. +********************************************************************/ + +BOOL reduce_name(connection_struct *conn, char *s,char *dir,BOOL widelinks) +{ +#ifndef REDUCE_PATHS + return True; +#else + pstring dir2; + pstring wd; + pstring base_name; + pstring newname; + char *p=NULL; + BOOL relative = (*s != '/'); + + *dir2 = *wd = *base_name = *newname = 0; + + if (widelinks) { + unix_clean_name(s); + /* can't have a leading .. */ + if (strncmp(s,"..",2) == 0 && (s[2]==0 || s[2]=='/')) { + DEBUG(3,("Illegal file name? (%s)\n",s)); + return(False); + } + + if (strlen(s) == 0) + pstrcpy(s,"./"); + + return(True); + } + + DEBUG(3,("reduce_name [%s] [%s]\n",s,dir)); + + /* remove any double slashes */ + all_string_sub(s,"//","/",0); + + pstrcpy(base_name,s); + p = strrchr_m(base_name,'/'); + + if (!p) + return(True); + + if (!vfs_GetWd(conn,wd)) { + DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s,dir)); + return(False); + } + + if (vfs_ChDir(conn,dir) != 0) { + DEBUG(0,("couldn't vfs_ChDir to %s\n",dir)); + return(False); + } + + if (!vfs_GetWd(conn,dir2)) { + DEBUG(0,("couldn't vfs_GetWd for %s\n",dir)); + vfs_ChDir(conn,wd); + return(False); + } + + if (p && (p != base_name)) { + *p = 0; + if (strcmp(p+1,".")==0) + p[1]=0; + if (strcmp(p+1,"..")==0) + *p = '/'; + } + + if (vfs_ChDir(conn,base_name) != 0) { + vfs_ChDir(conn,wd); + DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s,dir,base_name)); + return(False); + } + + if (!vfs_GetWd(conn,newname)) { + vfs_ChDir(conn,wd); + DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s,dir2)); + return(False); + } + + if (p && (p != base_name)) { + pstrcat(newname,"/"); + pstrcat(newname,p+1); + } + + { + size_t l = strlen(dir2); + if (dir2[l-1] == '/') + l--; + + if (strncmp(newname,dir2,l) != 0) { + vfs_ChDir(conn,wd); + DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,(int)l)); + return(False); + } + + if (relative) { + if (newname[l] == '/') + pstrcpy(s,newname + l + 1); + else + pstrcpy(s,newname+l); + } else + pstrcpy(s,newname); + } + + vfs_ChDir(conn,wd); + + if (strlen(s) == 0) + pstrcpy(s,"./"); + + DEBUG(3,("reduced to %s\n",s)); + return(True); +#endif +} diff --git a/source3/smbd/vt_mode.c b/source3/smbd/vt_mode.c deleted file mode 100644 index 83b62a38ac..0000000000 --- a/source3/smbd/vt_mode.c +++ /dev/null @@ -1,496 +0,0 @@ -/* vt_mode.c */ -/* -support vtp-sessions - -written by Christian A. Lademann <cal@zls.com> -*/ - -/* -02.05.95:cal:ported to samba-1.9.13 -*/ - -#define __vt_mode_c__ - - -/* #include <stdio.h> */ -/* #include <fcntl.h> */ -/* #include <sys/types.h> */ -/* #include <unistd.h> */ -/* #include <signal.h> */ -/* #include <errno.h> */ -/* #include <ctype.h> */ -/* #include <utmp.h> */ -/* #include <sys/param.h> */ -/* #include <sys/ioctl.h> */ -/* #include <stdlib.h> */ -/* #include <string.h> */ - -#include "includes.h" -#include "vt_mode.h" -#include <utmp.h> - -#ifdef SCO - extern char *strdup(); -#endif - -extern int Client; - -#ifdef LINUX -# define HAS_VTY -#endif - -#ifdef SCO -# define HAS_PTY -# define HAS_VTY - -# include <sys/tty.h> -#endif - -extern int DEBUGLEVEL; -extern char *InBuffer, *OutBuffer; -extern int done_become_user; - -char master_name [64], slave_name [64]; -int master, slave, i, o, e; - -int ms_type = MS_NONE, - ms_poll = 0; - - -/* -VT_Check: test incoming packet for "vtp" or "iVT1\0" -*/ -int VT_Check(buffer) -char *buffer; -{ - DEBUG(3,("Checking packet: <%10s...>\n", buffer+4)); - if((strncmp(buffer+4, "vtp", 3) == 0 && smb_len(buffer) == 3) || (strncmp(buffer+4, "iVT1\0", 5) == 0 && smb_len(buffer) == 5)) - return(1); - else - return(0); -} - - -/* -VT_Start_utmp: prepare /etc/utmp for /bin/login -*/ -VT_Start_utmp() -{ - struct utmp u, *v; - char *tt; - - - setutent(); - - strcpy(u.ut_line, VT_Line); - - if((v = getutline(&u)) == NULL) { - if(strncmp(VT_Line, "tty", 3) == 0) - tt = VT_Line + 3; - else if(strlen(VT_Line) > 4) - tt = VT_Line + strlen(VT_Line) - 4; - else - tt = VT_Line; - - strcpy(u.ut_id, tt); - u.ut_time = time((time_t*)0); - } - - strcpy(u.ut_user, "LOGIN"); - strcpy(u.ut_line, VT_Line); - u.ut_pid = getpid(); - u.ut_type = LOGIN_PROCESS; - pututline(&u); - - endutent(); - - return(0); -} - - -/* -VT_Stop_utmp: prepare /etc/utmp for other processes -*/ -VT_Stop_utmp() -{ - struct utmp u, *v; - - - if(VT_Line != NULL) { - setutent(); - - strcpy(u.ut_line, VT_Line); - - if((v = getutline(&u)) != NULL) { - strcpy(v->ut_user, ""); - v->ut_type = DEAD_PROCESS; - v->ut_time = time((time_t*)0); - pututline(v); - } - - endutent(); - } - - return(0); -} - - -/* -VT_AtExit: Things to do when the program exits -*/ -void VT_AtExit() -{ - if(VT_ChildPID > 0) { - kill(VT_ChildPID, SIGHUP); - (void)wait(NULL); - } - - VT_Stop_utmp(); -} - - -/* -VT_SigCLD: signalhandler for SIGCLD: set flag if child-process died -*/ -void VT_SigCLD(sig) -int sig; -{ - if(wait(NULL) == VT_ChildPID) - VT_ChildDied = True; - else - signal(SIGCLD, VT_SigCLD); -} - - -/* -VT_SigEXIT: signalhandler for signals that cause the process to exit -*/ -void VT_SigEXIT(sig) -int sig; -{ - VT_AtExit(); - - exit(1); -} - - -/* -VT_Start: initialize vt-specific data, alloc pty, spawn shell and send ACK -*/ -int VT_Start() -{ - char OutBuf [64], *X, *Y; - - - ms_type = MS_NONE; - master = slave = -1; - -#ifdef HAS_VTY -#ifdef LINUX -# define MASTER_TMPL "/dev/pty " -# define SLAVE_TMPL "/dev/tty " -# define LETTER1 "pqrs" -# define POS1 8 -# define LETTER2 "0123456789abcdef" -# define POS2 9 -#endif - -#ifdef SCO -# define MASTER_TMPL "/dev/ptyp_ " -# define SLAVE_TMPL "/dev/ttyp_ " -# define LETTER1 "0123456" -# define POS1 10 -# define LETTER2 "0123456789abcdef" -# define POS2 11 -#endif - - if(ms_poll == MS_VTY || ms_poll == 0) { - strcpy(master_name, MASTER_TMPL); - strcpy(slave_name, SLAVE_TMPL); - - for(X = LETTER1; *X && master < 0; X++) - for(Y = LETTER2; *Y && master < 0; Y++) { - master_name [POS1] = *X; - master_name [POS2] = *Y; - if((master = open(master_name, O_RDWR)) >= 0) { - slave_name [POS1] = *X; - slave_name [POS2] = *Y; - if((slave = open(slave_name, O_RDWR)) < 0) - close(master); - } - } - - if(master >= 0 && slave >= 0) - ms_type = MS_VTY; - } - -# undef MASTER_TMPL -# undef SLAVE_TMPL -# undef LETTER1 -# undef LETTER2 -# undef POS1 -# undef POS2 -#endif - - -#ifdef HAS_PTY -#ifdef SCO -# define MASTER_TMPL "/dev/ptyp%d" -# define SLAVE_TMPL "/dev/ttyp%d" -# define MIN_I 0 -# define MAX_I 63 -#endif - - if(ms_poll == MS_PTY || ms_poll == 0) { - int i; - - for(i = MIN_I; i <= MAX_I && master < 0; i++) { - sprintf(master_name, MASTER_TMPL, i); - if((master = open(master_name, O_RDWR)) >= 0) { - sprintf(slave_name, SLAVE_TMPL, i); - if((slave = open(slave_name, O_RDWR)) < 0) - close(master); - } - } - - if(master >= 0 && slave >= 0) - ms_type = MS_PTY; - } - -# undef MASTER_TMPL -# undef SLAVE_TMPL -# undef MIN_I -# undef MAX_I -#endif - - - if(! ms_type) - return(-1); - - VT_Line = strdup(strrchr(slave_name, '/') + 1); - - switch((VT_ChildPID = fork())) { - case -1: - return(-1); - break; - - case 0: -#ifdef SCO - setsid(); -#endif - close(0); - close(1); - close(2); - - i = open(slave_name, O_RDWR); - o = open(slave_name, O_RDWR); - e = open(slave_name, O_RDWR); - -#ifdef LINUX - setsid(); - if (ioctl(slave, TIOCSCTTY, (char *)NULL) == -1) - exit(1); -#endif -#ifdef SCO - tcsetpgrp(0, getpid()); -#endif - - VT_Start_utmp(); - - system("stty sane"); - execlp("/bin/login", "login", "-c", (char*)0); - exit(1); - break; - - default: - VT_Mode = True; - VT_Status = VT_OPEN; - VT_ChildDied = False; - VT_Fd = master; - - signal(SIGCLD, VT_SigCLD); - - signal(SIGHUP, VT_SigEXIT); - signal(SIGTERM, VT_SigEXIT); - signal(SIGINT, VT_SigEXIT); - signal(SIGQUIT, VT_SigEXIT); - - memset(OutBuf, 0, sizeof(OutBuf)); - OutBuf [4] = 0x06; - _smb_setlen(OutBuf, 1); - - send_smb(Client,OutBuf); - - return(0); - break; - } -} - - -/* -VT_Output: transport data from socket to pty -*/ -int VT_Output(Buffer) -char *Buffer; -{ - int i, len, nb; - - - if(VT_Status != VT_OPEN) - return(-1); - - len = smb_len(Buffer); - - nb = write(VT_Fd, Buffer + 4, len); - - return((nb == len) ? 0 : -1); -} - - -/* -VT_Input: transport data from pty to socket -*/ -int VT_Input(Buffer, Size) -char *Buffer; -int Size; -{ - int len; - - - if(VT_Status != VT_OPEN) - return(-1); - - memset(Buffer, 0, Size); - len = read(VT_Fd, Buffer + 4, MIN(VT_MAXREAD, Size)); - - _smb_setlen(Buffer, len); - - return(len + 4); -} - - -/* -VT_Process: main loop while in vt-mode -*/ -void VT_Process() -{ - static int trans_num = 0; - extern int Client; - int nread; - - - VT_Start(); - - atexit(VT_AtExit); - - while (True) { - int32 len; - int msg_type; - int msg_flags; - int counter; - int last_keepalive=0; - struct fd_set si; - struct timeval to, *top; - int n, ret, t; - - - errno = 0; - t = SMBD_SELECT_LOOP*1000; - - - FD_ZERO(&si); - FD_SET(Client, &si); - - FD_SET(VT_Fd, &si); - - if(t >= 0) { - to.tv_sec = t / 1000; - to.tv_usec = t - (to.tv_sec * 1000); - - top = &to; - } else - top = NULL; - - if(VT_ChildDied) - goto leave_VT_Process; - - n = select(MAX(VT_Fd, Client) + 1, &si, NULL, NULL, top); - - if(VT_ChildDied) - goto leave_VT_Process; - - if(n == 0) { - int i; - time_t t; - BOOL allidle = True; - extern int keepalive; - - counter += SMBD_SELECT_LOOP; - - t = time(NULL); - - if (keepalive && (counter-last_keepalive)>keepalive) { - if (!send_keepalive(Client)) - goto leave_VT_Process; - last_keepalive = counter; - } - } else if(n > 0) { - counter = 0; - - if(FD_ISSET(VT_Fd, &si)) { - /* got input from vt */ - nread = VT_Input(OutBuffer, MIN(BUFFER_SIZE,lp_maxxmit())); - - if(nread > 0) - send_smb(Client,OutBuffer); - } - - if(FD_ISSET(Client, &si)) { - /* got input from socket */ - - if(receive_smb(Client,InBuffer, 0)) { - msg_type = CVAL(InBuffer,0); - msg_flags = CVAL(InBuffer,1); - - len = smb_len(InBuffer); - - DEBUG(6,("got message type 0x%x of len 0x%x\n",msg_type,len)); - - nread = len + 4; - - DEBUG(3,("%s Transaction %d of length %d\n",timestring(),trans_num,nread)); - - if(msg_type == 0) - VT_Output(InBuffer); - else { - nread = construct_reply(InBuffer,OutBuffer,nread,MIN(BUFFER_SIZE,lp_maxxmit())); - - if(nread > 0) { - if (nread != smb_len(OutBuffer) + 4) { - DEBUG(0,("ERROR: Invalid message response size! %d %d\n", - nread, - smb_len(OutBuffer))); - } else - send_smb(Client,OutBuffer); - } - } - } else - if(errno == EBADF) - goto leave_VT_Process; - } - } - - trans_num++; - } - - leave_VT_Process: -/* - if(VT_ChildPID > 0) - kill(VT_ChildPID, SIGHUP); - - VT_Stop_utmp(VT_Line); - return; -*/ - close_sockets(); - exit(0); -} |