1
0
mirror of https://github.com/samba-team/samba.git synced 2025-03-26 18:50:30 +03:00

this completes the splitup of server.c.

the splitup was done with an axe, not a scalpel, so there are some
rough edges. I mostly wanted to get the general form right with fine
tuning of what goes where to come later. Still, this is better than
what we had before where server.c was a general repository for
anything that didn't fit elsewhere.
(This used to be commit a6d194886a4a5f7507fa37289ff96c1be56f14a6)
This commit is contained in:
Andrew Tridgell 1998-08-17 13:11:34 +00:00
parent 0922615405
commit c3effa8b59
10 changed files with 3574 additions and 3349 deletions

View File

@ -126,7 +126,8 @@ SMBD_OBJ1 = smbd/server.o smbd/files.o smbd/chgpasswd.o smbd/connection.o \
smbd/groupname.o smbd/ipc.o smbd/mangle.o smbd/negprot.o \
smbd/message.o smbd/nttrans.o smbd/pipes.o smbd/predict.o \
smbd/quotas.o smbd/reply.o smbd/ssl.o smbd/trans2.o smbd/uid.o \
smbd/dosmode.o smbd/filename.o
smbd/dosmode.o smbd/filename.o smbd/open.o smbd/close.o \
smbd/process.o smbd/oplock.o smbd/service.o smbd/error.o
PRINTING_OBJ = printing/pcap.o printing/print_svid.o printing/printing.o

View File

@ -1912,6 +1912,11 @@ BOOL check_oem_password(char *user, unsigned char *data,
int new_passwd_size);
BOOL change_oem_password(struct smb_passwd *smbpw, char *new_passwd, BOOL override);
/*The following definitions come from smbd/close.c */
void close_file(files_struct *fsp, BOOL normal_close);
void close_directory(files_struct *fsp);
/*The following definitions come from smbd/conn.c */
void conn_init(void);
@ -1965,6 +1970,14 @@ void DirCacheFlush(int snum);
mode_t unix_mode(connection_struct *conn,int dosmode);
int dos_mode(connection_struct *conn,char *path,struct stat *sbuf);
int dos_chmod(connection_struct *conn,char *fname,int dosmode,struct stat *st);
int file_utime(connection_struct *conn, char *fname, struct utimbuf *times);
BOOL set_filetime(connection_struct *conn, char *fname, time_t mtime);
/*The following definitions come from smbd/error.c */
int cached_error_packet(char *inbuf,char *outbuf,files_struct *fsp,int line);
int unix_error_packet(char *inbuf,char *outbuf,int def_class,uint32 def_code,int line);
int error_packet(char *inbuf,char *outbuf,int error_class,uint32 error_code,int line);
/*The following definitions come from smbd/fileio.c */
@ -2046,6 +2059,27 @@ void process_pending_change_notify_queue(time_t t);
int reply_nttrans(connection_struct *conn,
char *inbuf,char *outbuf,int length,int bufsize);
/*The following definitions come from smbd/open.c */
void fd_add_to_uid_cache(file_fd_struct *fd_ptr, uid_t u);
int fd_attempt_close(file_fd_struct *fd_ptr);
void open_file_shared(files_struct *fsp,connection_struct *conn,char *fname,int share_mode,int ofun,
int mode,int oplock_request, int *Access,int *action);
int open_directory(files_struct *fsp,connection_struct *conn,
char *fname, int smb_ofun, int unixmode, int *action);
BOOL check_file_sharing(connection_struct *conn,char *fname, BOOL rename_op);
int check_share_mode( share_mode_entry *share, int deny_mode, char *fname,
BOOL fcbopen, int *flags);
/*The following definitions come from smbd/oplock.c */
BOOL open_oplock_ipc(void);
BOOL process_local_message(int sock, char *buffer, int buf_size);
BOOL oplock_break(uint32 dev, uint32 inode, struct timeval *tval);
BOOL request_oplock_break(share_mode_entry *share_entry,
uint32 dev, uint32 inode);
BOOL attempt_close_oplocked_file(files_struct *fsp);
/*The following definitions come from smbd/password.c */
void generate_next_challenge(char *challenge);
@ -2088,6 +2122,16 @@ int read_predict(int fd,int offset,char *buf,char **ptr,int num);
void do_read_prediction(void);
void invalidate_read_prediction(int fd);
/*The following definitions come from smbd/process.c */
BOOL receive_next_smb(int smbfd, int oplockfd, char *inbuf, int bufsize, int timeout);
void process_smb(char *inbuf, char *outbuf);
char *smb_fn_name(int type);
int chain_reply(char *inbuf,char *outbuf,int size,int bufsize);
void construct_reply_common(char *inbuf,char *outbuf);
int construct_reply(char *inbuf,char *outbuf,int size,int bufsize);
void smbd_process(void);
/*The following definitions come from smbd/quotas.c */
BOOL disk_quotas(char *path, int *bsize, int *dfree, int *dsize);
@ -2168,36 +2212,15 @@ int reply_getattrE(connection_struct *conn, char *inbuf,char *outbuf, int dum_si
void *dflt_sig(void);
void killkids(void);
int file_utime(connection_struct *conn, char *fname, struct utimbuf *times);
BOOL set_filetime(connection_struct *conn, char *fname, time_t mtime);
void fd_add_to_uid_cache(file_fd_struct *fd_ptr, uid_t u);
void close_file(files_struct *fsp, BOOL normal_close);
void close_directory(files_struct *fsp);
int open_directory(files_struct *fsp,connection_struct *conn,
char *fname, int smb_ofun, int unixmode, int *action);
BOOL check_file_sharing(connection_struct *conn,char *fname, BOOL rename_op);
int check_share_mode( share_mode_entry *share, int deny_mode, char *fname,
BOOL fcbopen, int *flags);
void open_file_shared(files_struct *fsp,connection_struct *conn,char *fname,int share_mode,int ofun,
int mode,int oplock_request, int *Access,int *action);
BOOL reload_services(BOOL test);
void exit_server(char *reason);
/*The following definitions come from smbd/service.c */
BOOL become_service(connection_struct *conn,BOOL do_chdir);
int find_service(char *service);
int cached_error_packet(char *inbuf,char *outbuf,files_struct *fsp,int line);
int unix_error_packet(char *inbuf,char *outbuf,int def_class,uint32 def_code,int line);
int error_packet(char *inbuf,char *outbuf,int error_class,uint32 error_code,int line);
BOOL oplock_break(uint32 dev, uint32 inode, struct timeval *tval);
BOOL request_oplock_break(share_mode_entry *share_entry,
uint32 dev, uint32 inode);
BOOL receive_next_smb(int smbfd, int oplockfd, char *inbuf, int bufsize, int timeout);
BOOL reload_services(BOOL test);
connection_struct *make_connection(char *service,char *user,char *password, int pwlen, char *dev,uint16 vuid, int *ecode);
BOOL attempt_close_oplocked_file(files_struct *fsp);
void close_cnum(connection_struct *conn, uint16 vuid);
void exit_server(char *reason);
char *smb_fn_name(int type);
int chain_reply(char *inbuf,char *outbuf,int size,int bufsize);
void construct_reply_common(char *inbuf,char *outbuf);
int construct_reply(char *inbuf,char *outbuf,int size,int bufsize);
/*The following definitions come from smbd/ssl.c */

170
source3/smbd/close.c Normal file
View File

@ -0,0 +1,170 @@
/*
Unix SMB/Netbios implementation.
Version 1.9.
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"
extern int DEBUGLEVEL;
extern int32 global_oplocks_open;
/****************************************************************************
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(fsp->fsp_name,'/')))
p = fsp->fsp_name;
else
p++;
if (!strequal(lp_magicscript(SNUM(conn)),p))
return;
}
{
int ret;
pstring magic_output;
pstring fname;
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,magic_output,False);
DEBUG(3,("Invoking magic command %s gave %d\n",fname,ret));
unlink(fname);
}
}
/****************************************************************************
Common code to close a file or a directory.
****************************************************************************/
static void close_filestruct(files_struct *fsp)
{
connection_struct *conn = fsp->conn;
fsp->open = False;
fsp->is_directory = False;
conn->num_files_open--;
if(fsp->wbmpx_ptr) {
free((char *)fsp->wbmpx_ptr);
fsp->wbmpx_ptr = NULL;
}
#if WITH_MMAP
if(fsp->mmap_ptr) {
munmap(fsp->mmap_ptr,fsp->mmap_size);
fsp->mmap_ptr = NULL;
}
#endif
}
/****************************************************************************
Close a file - possibly invalidating the read prediction.
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.
****************************************************************************/
void close_file(files_struct *fsp, BOOL normal_close)
{
uint32 dev = fsp->fd_ptr->dev;
uint32 inode = fsp->fd_ptr->inode;
int token;
connection_struct *conn = fsp->conn;
close_filestruct(fsp);
#if USE_READ_PREDICTION
invalidate_read_prediction(fsp->fd_ptr->fd);
#endif
if (lp_share_modes(SNUM(conn))) {
lock_share_entry(conn, dev, inode, &token);
del_share_mode(token, fsp);
}
fd_attempt_close(fsp->fd_ptr);
if (lp_share_modes(SNUM(conn)))
unlock_share_entry(conn, dev, inode, token);
/* NT uses smbclose to start a print - weird */
if (normal_close && fsp->print_file)
print_file(conn, fsp);
/* check for magic scripts */
if (normal_close) {
check_magic(fsp,conn);
}
if(fsp->granted_oplock == True)
global_oplocks_open--;
fsp->sent_oplock_break = False;
DEBUG(2,("%s closed file %s (numopen=%d)\n",
conn->user,fsp->fsp_name,
conn->num_files_open));
if (fsp->fsp_name) {
string_free(&fsp->fsp_name);
}
file_free(fsp);
}
/****************************************************************************
Close a directory opened by an NT SMB call.
****************************************************************************/
void close_directory(files_struct *fsp)
{
/* TODO - walk the list of pending
change notify requests and free
any pertaining to this fsp. */
remove_pending_change_notify_requests_by_fid(fsp);
/*
* Do the code common to files and directories.
*/
close_filestruct(fsp);
if (fsp->fsp_name)
string_free(&fsp->fsp_name);
file_free(fsp);
}

View File

@ -200,3 +200,71 @@ int dos_chmod(connection_struct *conn,char *fname,int dosmode,struct stat *st)
return(sys_chmod(fname,unixmode));
}
/*******************************************************************
Wrapper around sys_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;
struct stat sb;
int ret = -1;
errno = 0;
if(sys_utime(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(sys_stat(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(False);
ret = sys_utime(fname, times);
unbecome_root(False);
}
}
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, &times)) {
DEBUG(4,("set_filetime(%s) failed: %s\n",fname,strerror(errno)));
}
return(True);
}

142
source3/smbd/error.c Normal file
View File

@ -0,0 +1,142 @@
/*
Unix SMB/Netbios implementation.
Version 1.9.
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"
extern int DEBUGLEVEL;
/****************************************************************************
create an error packet from a cached error.
****************************************************************************/
int cached_error_packet(char *inbuf,char *outbuf,files_struct *fsp,int line)
{
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(inbuf,outbuf,eclass,err,line);
}
struct
{
int unixerror;
int smbclass;
int smbcode;
} unix_smb_errmap[] =
{
{EPERM,ERRDOS,ERRnoaccess},
{EACCES,ERRDOS,ERRnoaccess},
{ENOENT,ERRDOS,ERRbadfile},
{ENOTDIR,ERRDOS,ERRbadpath},
{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 != SMB_SUCCESS)
{
eclass = unix_ERR_class;
ecode = unix_ERR_code;
unix_ERR_class = SMB_SUCCESS;
unix_ERR_code = 0;
}
else
{
while (unix_smb_errmap[i].smbclass != 0)
{
if (unix_smb_errmap[i].unixerror == errno)
{
eclass = unix_smb_errmap[i].smbclass;
ecode = unix_smb_errmap[i].smbcode;
break;
}
i++;
}
}
return(error_packet(inbuf,outbuf,eclass,ecode,line));
}
/****************************************************************************
create an error packet. Normally called using the ERROR() macro
****************************************************************************/
int error_packet(char *inbuf,char *outbuf,int error_class,uint32 error_code,int line)
{
int outsize = set_message(outbuf,0,0,True);
int cmd = CVAL(inbuf,smb_com);
int flgs2 = SVAL(outbuf,smb_flg2);
if ((flgs2 & FLAGS2_32_BIT_ERROR_CODES) == FLAGS2_32_BIT_ERROR_CODES)
{
SIVAL(outbuf,smb_rcls,error_code);
DEBUG( 3, ( "32 bit error packet at line %d cmd=%d (%s) eclass=%08x [%s]\n",
line, cmd, smb_fn_name(cmd), error_code, smb_errstr(outbuf) ) );
}
else
{
CVAL(outbuf,smb_rcls) = error_class;
SSVAL(outbuf,smb_err,error_code);
DEBUG( 3, ( "error packet at line %d cmd=%d (%s) eclass=%d ecode=%d\n",
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);
}

1150
source3/smbd/open.c Normal file

File diff suppressed because it is too large Load Diff

639
source3/smbd/oplock.c Normal file
View File

@ -0,0 +1,639 @@
/*
Unix SMB/Netbios implementation.
Version 1.9.
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"
extern int DEBUGLEVEL;
extern int oplock_sock;
extern uint16 oplock_port;
extern int32 global_oplocks_open;
extern int32 global_oplocks_open;
extern int global_oplock_break;
extern int smb_read_error;
/****************************************************************************
open the oplock IPC socket communication
****************************************************************************/
BOOL open_oplock_ipc(void)
{
struct sockaddr_in sock_name;
int 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));
if (oplock_sock == -1)
{
DEBUG(0,("open_oplock_ipc: Failed to get local UDP socket for \
address %x. Error was %s\n", htonl(INADDR_LOOPBACK), strerror(errno)));
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;
oplock_port = 0;
return False;
}
oplock_port = ntohs(sock_name.sin_port);
DEBUG(3,("open_oplock ipc: pid = %d, oplock_port = %u\n",
(int)getpid(), oplock_port));
return True;
}
/****************************************************************************
process an oplock break message.
****************************************************************************/
BOOL process_local_message(int sock, char *buffer, int buf_size)
{
int32 msg_len;
uint16 from_port;
char *msg_start;
msg_len = IVAL(buffer,UDP_CMD_LEN_OFFSET);
from_port = SVAL(buffer,UDP_CMD_PORT_OFFSET);
msg_start = &buffer[UDP_CMD_HEADER_LEN];
DEBUG(5,("process_local_message: Got a message of length %d from port (%d)\n",
msg_len, from_port));
/* Switch on message command - currently OPLOCK_BREAK_CMD is the
only valid request. */
switch(SVAL(msg_start,UDP_MESSAGE_CMD_OFFSET))
{
case 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", msg_len, OPLOCK_BREAK_MSG_LEN));
return False;
}
{
uint32 remotepid = IVAL(msg_start,OPLOCK_BREAK_PID_OFFSET);
uint32 dev = IVAL(msg_start,OPLOCK_BREAK_DEV_OFFSET);
uint32 inode = IVAL(msg_start, OPLOCK_BREAK_INODE_OFFSET);
struct timeval tval;
struct sockaddr_in toaddr;
tval.tv_sec = IVAL(msg_start, OPLOCK_BREAK_SEC_OFFSET);
tval.tv_usec = IVAL(msg_start, OPLOCK_BREAK_USEC_OFFSET);
DEBUG(5,("process_local_message: oplock break request from \
pid %d, port %d, dev = %x, inode = %x\n", remotepid, from_port, dev, inode));
/*
* 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 return success in this case.
*/
if(global_oplocks_open != 0)
{
if(oplock_break(dev, inode, &tval) == False)
{
DEBUG(0,("process_local_message: oplock break failed - \
not returning udp message.\n"));
return False;
}
}
else
{
DEBUG(3,("process_local_message: oplock break requested with no outstanding \
oplocks. Returning success.\n"));
}
/* Send the message back after OR'ing in the 'REPLY' bit. */
SSVAL(msg_start,UDP_MESSAGE_CMD_OFFSET,OPLOCK_BREAK_CMD | CMD_REPLY);
bzero((char *)&toaddr,sizeof(toaddr));
toaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
toaddr.sin_port = htons(from_port);
toaddr.sin_family = AF_INET;
if(sendto( 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",
remotepid, strerror(errno)));
return False;
}
DEBUG(5,("process_local_message: oplock break reply sent to \
pid %d, port %d, for file dev = %x, inode = %x\n", remotepid,
from_port, dev, inode));
}
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", msg_len, OPLOCK_BREAK_MSG_LEN));
return False;
}
{
uint32 remotepid = IVAL(msg_start,OPLOCK_BREAK_PID_OFFSET);
uint32 dev = IVAL(msg_start,OPLOCK_BREAK_DEV_OFFSET);
uint32 inode = IVAL(msg_start, OPLOCK_BREAK_INODE_OFFSET);
DEBUG(0,("process_local_message: unsolicited oplock break reply from \
pid %d, port %d, dev = %x, inode = %x\n", remotepid, from_port, dev, inode));
}
return False;
default:
DEBUG(0,("process_local_message: unknown UDP message command code (%x) - ignoring.\n",
(unsigned int)SVAL(msg_start,0)));
return False;
}
return True;
}
/****************************************************************************
Process an oplock break directly.
****************************************************************************/
BOOL oplock_break(uint32 dev, uint32 inode, struct timeval *tval)
{
extern struct current_user current_user;
extern int Client;
char *inbuf = NULL;
char *outbuf = NULL;
files_struct *fsp = NULL;
time_t start_time;
BOOL shutdown_server = False;
connection_struct *saved_conn;
int saved_vuid;
pstring saved_dir;
if( DEBUGLVL( 3 ) )
{
dbgtext( "oplock_break: called for dev = %x, inode = %x.\n", dev, inode );
dbgtext( "Current global_oplocks_open = %d\n", global_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_dit(dev, inode, tval);
if(fsp == NULL)
{
/* The file could have been closed in the meantime - return success. */
if( DEBUGLVL( 0 ) )
{
dbgtext( "oplock_break: cannot find open file with " );
dbgtext( "dev = %x, inode = %x ", dev, inode);
dbgtext( "allowing break to succeed.\n" );
}
return True;
}
/* 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->granted_oplock)
{
if( DEBUGLVL( 0 ) )
{
dbgtext( "oplock_break: file %s ", fsp->fsp_name );
dbgtext( "(dev = %x, inode = %x) has no oplock.\n", dev, inode );
dbgtext( "Allowing break to succeed regardless.\n" );
}
return True;
}
/* 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 = %x)\n", dev, inode );
}
/* 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;
}
/* 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 + SAFETY_MARGIN))==NULL)
{
DEBUG(0,("oplock_break: malloc fail for input buffer.\n"));
return False;
}
if((outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN))==NULL)
{
DEBUG(0,("oplock_break: malloc fail for output buffer.\n"));
free(inbuf);
inbuf = NULL;
return False;
}
/* Prepare the SMBlockingX message. */
bzero(outbuf,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);
/* Change this when we have level II oplocks. */
SCVAL(outbuf,smb_vwv3+1,OPLOCKLEVEL_NONE);
send_smb(Client, outbuf);
/* Remember we just sent an oplock break on this file. */
fsp->sent_oplock_break = True;
/* 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_conn = fsp->conn;
saved_vuid = current_user.vuid;
GetWd(saved_dir);
unbecome_user();
while(OPEN_FSP(fsp) && fsp->granted_oplock)
{
if(receive_smb(Client,inbuf,OPLOCK_BREAK_TIMEOUT * 1000) == False)
{
/*
* Die if we got an error.
*/
if (smb_read_error == READ_EOF)
DEBUG( 0, ( "oplock_break: end of file from client\n" ) );
if (smb_read_error == READ_ERROR)
DEBUG( 0, ("oplock_break: receive_smb error (%s)\n", strerror(errno)) );
if (smb_read_error == READ_TIMEOUT)
DEBUG( 0, ( "oplock_break: receive_smb timed out after %d seconds.\n",
OPLOCK_BREAK_TIMEOUT ) );
DEBUGADD( 0, ( "oplock_break failed for file %s ", fsp->fsp_name ) );
DEBUGADD( 0, ( "(dev = %x, inode = %x).\n", dev, inode));
shutdown_server = True;
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 = %x).\n", dev, inode );
}
shutdown_server = True;
break;
}
}
/*
* Go back to being the user who requested the oplock
* break.
*/
if(!become_user(saved_conn, saved_vuid))
{
DEBUG( 0, ( "oplock_break: unable to re-become user!" ) );
DEBUGADD( 0, ( "Shutting down server\n" ) );
close_sockets();
close(oplock_sock);
exit_server("unable to re-become user");
}
/* Including the directory. */
ChDir(saved_dir);
/* Free the buffers we've been using to recurse. */
free(inbuf);
free(outbuf);
/* We need this in case a readraw crossed on the wire. */
if(global_oplock_break)
global_oplock_break = False;
/*
* If the client did not respond we must die.
*/
if(shutdown_server)
{
DEBUG( 0, ( "oplock_break: client failure in break - " ) );
DEBUGADD( 0, ( "shutting down this smbd.\n" ) );
close_sockets();
close(oplock_sock);
exit_server("oplock break failure");
}
if(OPEN_FSP(fsp))
{
/* The lockingX reply will have removed the oplock flag
from the sharemode. */
/* Paranoia.... */
fsp->granted_oplock = False;
fsp->sent_oplock_break = False;
global_oplocks_open--;
}
/* Santity check - remove this later. JRA */
if(global_oplocks_open < 0)
{
DEBUG(0,("oplock_break: global_oplocks_open < 0 (%d). PANIC ERROR\n",
global_oplocks_open));
exit_server("oplock_break: global_oplocks_open < 0");
}
if( DEBUGLVL( 3 ) )
{
dbgtext( "oplock_break: returning success for " );
dbgtext( "dev = %x, inode = %x.\n", dev, inode );
dbgtext( "Current global_oplocks_open = %d\n", global_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,
uint32 dev, uint32 inode)
{
char op_break_msg[OPLOCK_BREAK_MSG_LEN];
struct sockaddr_in addr_out;
int pid = getpid();
time_t start_time;
int time_left;
if(pid == share_entry->pid)
{
/* We are breaking our own oplock, make sure it's us. */
if(share_entry->op_port != oplock_port)
{
DEBUG(0,("request_oplock_break: corrupt share mode entry - pid = %d, port = %d \
should be %d\n", pid, share_entry->op_port, oplock_port));
return False;
}
DEBUG(5,("request_oplock_break: breaking our own oplock\n"));
/* Call oplock break direct. */
return oplock_break(dev, inode, &share_entry->time);
}
/* We need to send a OPLOCK_BREAK_CMD message to the
port in the share mode entry. */
SSVAL(op_break_msg,UDP_MESSAGE_CMD_OFFSET,OPLOCK_BREAK_CMD);
SIVAL(op_break_msg,OPLOCK_BREAK_PID_OFFSET,pid);
SIVAL(op_break_msg,OPLOCK_BREAK_DEV_OFFSET,dev);
SIVAL(op_break_msg,OPLOCK_BREAK_INODE_OFFSET,inode);
SIVAL(op_break_msg,OPLOCK_BREAK_SEC_OFFSET,(uint32)share_entry->time.tv_sec);
SIVAL(op_break_msg,OPLOCK_BREAK_USEC_OFFSET,(uint32)share_entry->time.tv_usec);
/* set the address and port */
bzero((char *)&addr_out,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 ", share_entry->pid, share_entry->op_port );
dbgtext( "for dev = %x, inode = %x\n", dev, inode );
}
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 ", share_entry->pid );
dbgtext( "on port %d ", share_entry->op_port );
dbgtext( "for dev = %x, inode = %x.\n", dev, inode );
dbgtext( "Error was %s\n", strerror(errno) );
}
return False;
}
/*
* 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[UDP_CMD_HEADER_LEN+OPLOCK_BREAK_MSG_LEN];
int32 reply_msg_len;
uint16 reply_from_port;
char *reply_msg_start;
if(receive_local_message(oplock_sock, 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 ", share_entry->pid );
dbgtext( "on port %d ", share_entry->op_port );
dbgtext( "for dev = %x, inode = %x\n", dev, inode );
}
/*
* 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 ", share_entry->pid );
dbgtext( "on port %d ", share_entry->op_port );
dbgtext( "for dev = %x, inode = %x.\n", dev, inode );
dbgtext( "Error was (%s).\n", strerror(errno) );
}
return False;
}
reply_msg_len = IVAL(op_break_reply,UDP_CMD_LEN_OFFSET);
reply_from_port = SVAL(op_break_reply,UDP_CMD_PORT_OFFSET);
reply_msg_start = &op_break_reply[UDP_CMD_HEADER_LEN];
if(reply_msg_len != OPLOCK_BREAK_MSG_LEN)
{
/* Ignore it. */
DEBUG( 0, ( "request_oplock_break: invalid message length received." ) );
DEBUGADD( 0, ( " Ignoring.\n" ) );
continue;
}
/*
* Test to see if this is the reply we are awaiting.
*/
if((SVAL(reply_msg_start,UDP_MESSAGE_CMD_OFFSET) & CMD_REPLY) &&
(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 - probably a break request.
* 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(oplock_sock, 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 (fsp->open && fsp->granted_oplock && !fsp->sent_oplock_break) {
/* Try and break the oplock. */
file_fd_struct *fd_ptr = fsp->fd_ptr;
if(oplock_break( fd_ptr->dev, fd_ptr->inode, &fsp->open_time)) {
if(!fsp->open) /* Did the oplock break close the file ? */
return True;
}
}
return False;
}

810
source3/smbd/process.c Normal file
View File

@ -0,0 +1,810 @@
/*
Unix SMB/Netbios implementation.
Version 1.9.
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"
extern int DEBUGLEVEL;
extern int last_message;
extern int global_oplock_break;
extern pstring sesssetup_user;
extern char *last_inbuf;
extern time_t smb_last_time;
extern char *InBuffer;
extern char *OutBuffer;
extern int oplock_sock;
extern int smb_read_error;
extern BOOL reload_after_sighup;
extern BOOL global_machine_pasword_needs_changing;
extern fstring global_myworkgroup;
extern pstring global_myname;
extern int max_send;
/****************************************************************************
Get the next SMB packet, doing the local message processing automatically.
****************************************************************************/
BOOL receive_next_smb(int smbfd, int oplockfd, char *inbuf, int bufsize, int timeout)
{
BOOL got_smb = False;
BOOL ret;
do
{
ret = receive_message_or_smb(smbfd,oplockfd,inbuf,bufsize,
timeout,&got_smb);
if(ret && !got_smb)
{
/* Deal with oplock break requests from other smbd's. */
process_local_message(oplock_sock, inbuf, bufsize);
continue;
}
if(ret && (CVAL(inbuf,0) == 0x85))
{
/* Keepalive packet. */
got_smb = False;
}
}
while(ret && !got_smb);
return ret;
}
/****************************************************************************
process an smb from the client - split out from the process() code so
it can be used by the oplock break code.
****************************************************************************/
void process_smb(char *inbuf, char *outbuf)
{
extern int Client;
#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;
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 (!check_access(Client, lp_hostsallow(-1), lp_hostsdeny(-1))) {
/* send a negative session response "not listining on calling
name" */
static unsigned char buf[5] = {0x83, 0, 0, 1, 0x81};
DEBUG( 1, ( "Connection denied from %s\n",
client_addr(Client) ) );
send_smb(Client,(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(Client, msg_type);
if(sslConnected < 0){ /* an error occured */
exit_server("SSL negotiation failed");
}else if(sslConnected){
trans_num++;
return;
}
}
#endif /* WITH_SSL */
#ifdef WITH_VTP
if(trans_num == 1 && VT_Check(inbuf))
{
VT_Process();
return;
}
#endif
if (msg_type == 0)
show_msg(inbuf);
else if(msg_type == 0x85)
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
send_smb(Client,outbuf);
}
trans_num++;
}
/*
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!
*/
struct smb_message_struct
{
int code;
char *name;
int (*fn)(connection_struct *conn, char *, char *, int, int);
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}, /* ulogoff doesn't give a valid TID */
{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 | QUEUE_IN_OPLOCK },
/* 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 | QUEUE_IN_OPLOCK},
{SMBread,"SMBread",reply_read,AS_USER},
{SMBwrite,"SMBwrite",reply_write,AS_USER},
{SMBclose,"SMBclose",reply_close,AS_USER | CAN_IPC},
{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 | QUEUE_IN_OPLOCK},
/* 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 | QUEUE_IN_OPLOCK },
{SMBsplopen,"SMBsplopen",reply_printopen,AS_USER | QUEUE_IN_OPLOCK },
{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 | QUEUE_IN_OPLOCK },
{SMBmove,"SMBmove",NULL,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK },
{SMBopenX,"SMBopenX",reply_open_and_X,AS_USER | CAN_IPC | QUEUE_IN_OPLOCK },
{SMBreadX,"SMBreadX",reply_read_and_X,AS_USER | CAN_IPC },
{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},
/* NT PROTOCOL FOLLOWS */
{SMBntcreateX, "SMBntcreateX", reply_ntcreate_and_X, AS_USER | CAN_IPC | QUEUE_IN_OPLOCK },
{SMBnttrans, "SMBnttrans", reply_nttrans, AS_USER | CAN_IPC },
{SMBnttranss, "SMBnttranss", reply_nttranss, AS_USER | CAN_IPC },
{SMBntcancel, "SMBntcancel", reply_ntcancel, 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;
extern int Client;
#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(global_oplock_break && (smb_messages[match].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;
}
if (smb_messages[match].fn)
{
int flags = smb_messages[match].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));
/* Ensure this value is replaced in the incoming packet. */
SSVAL(inbuf,smb_uid,session_tag);
/*
* Ensure the correct username is in sesssetup_user.
* 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)
pstrcpy( sesssetup_user, vuser->requested_name);
}
/* 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(conn,session_tag)) {
if (flags & AS_GUEST)
flags &= ~AS_USER;
else
return(ERROR(ERRSRV,ERRinvnid));
}
/* 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(ERRSRV,ERRaccess));
/* ipc services are limited */
if (IS_IPC(conn) && (flags & AS_USER) && !(flags & CAN_IPC)) {
return(ERROR(ERRSRV,ERRaccess));
}
/* load service specific parameters */
if (conn &&
!become_service(conn,(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(Client, lp_hostsallow(-1), lp_hostsdeny(-1)))) {
return(ERROR(ERRSRV,ERRaccess));
}
last_inbuf = inbuf;
outsize = smb_messages[match].fn(conn, inbuf,outbuf,size,bufsize);
}
else
{
outsize = reply_unknown(inbuf,outbuf);
}
}
#if PROFILING
GetTimeOfDay(&msg_end_time);
if (!(smb_messages[match].flags & TIME_INIT))
{
smb_messages[match].time = 0;
smb_messages[match].flags |= TIME_INIT;
}
{
unsigned long this_time =
(msg_end_time.tv_sec - msg_start_time.tv_sec)*1e6 +
(msg_end_time.tv_usec - msg_start_time.tv_usec);
smb_messages[match].time += this_time;
total_time += this_time;
}
DEBUG(2,("TIME %s %d usecs %g pct\n",
smb_fn_name(type),smb_messages[match].time,
(100.0*smb_messages[match].time) / total_time));
#endif
return(outsize);
}
/****************************************************************************
construct a chained reply and add it to the already made reply
**************************************************************************/
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];
extern int chain_size;
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) {
CVAL(outbuf,smb_vwv0) = 0xFF;
return outsize;
}
if (chain_size == 0) {
/* this is the first part of the chain */
orig_inbuf = inbuf;
orig_outbuf = outbuf;
}
/* we need to tell the client where the next part of the reply will be */
SSVAL(outbuf,smb_vwv1,smb_offset(outbuf+outsize,outbuf));
CVAL(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 */
CVAL(inbuf2,smb_com) = smb_com2;
/* create the out buffer */
bzero(outbuf2,smb_size);
set_message(outbuf2,0,0,True);
CVAL(outbuf2,smb_com) = CVAL(inbuf2,smb_com);
memcpy(outbuf2+4,inbuf2+4,4);
CVAL(outbuf2,smb_rcls) = SMB_SUCCESS;
CVAL(outbuf2,smb_reh) = 0;
CVAL(outbuf2,smb_flg) = 0x80 | (CVAL(inbuf2,smb_flg) & 0x8); /* bit 7 set
means a reply */
SSVAL(outbuf2,smb_flg2,1); /* say we support long filenames */
SSVAL(outbuf2,smb_err,SMB_SUCCESS);
SSVAL(outbuf2,smb_tid,SVAL(inbuf2,smb_tid));
SSVAL(outbuf2,smb_pid,SVAL(inbuf2,smb_pid));
SSVAL(outbuf2,smb_uid,SVAL(inbuf2,smb_uid));
SSVAL(outbuf2,smb_mid,SVAL(inbuf2,smb_mid));
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);
CVAL(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;
}
/****************************************************************************
Helper function for contruct_reply.
****************************************************************************/
void construct_reply_common(char *inbuf,char *outbuf)
{
bzero(outbuf,smb_size);
CVAL(outbuf,smb_com) = CVAL(inbuf,smb_com);
set_message(outbuf,0,0,True);
memcpy(outbuf+4,inbuf+4,4);
CVAL(outbuf,smb_rcls) = SMB_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,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 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);
extern int chain_size;
smb_last_time = time(NULL);
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);
}
/****************************************************************************
process commands from the client
****************************************************************************/
void smbd_process(void)
{
extern int Client;
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,NMB_PORT,SOCK_DGRAM);
}
#endif
/* re-initialise the timezone */
TimeInit();
while (True)
{
int deadtime = lp_deadtime()*60;
int counter;
int last_keepalive=0;
int service_load_counter = 0;
BOOL got_smb = False;
if (deadtime <= 0)
deadtime = DEFAULT_SMBD_TIMEOUT;
#if USE_READ_PREDICTION
if (lp_readprediction())
do_read_prediction();
#endif
errno = 0;
for (counter=SMBD_SELECT_LOOP;
!receive_message_or_smb(Client,oplock_sock,
InBuffer,BUFFER_SIZE,SMBD_SELECT_LOOP*1000,&got_smb);
counter += SMBD_SELECT_LOOP)
{
time_t t;
BOOL allidle = True;
extern int keepalive;
if (counter > 365 * 3600) /* big number of seconds. */
{
counter = 0;
service_load_counter = 0;
}
if (smb_read_error == READ_EOF)
{
DEBUG(3,("end of file from client\n"));
return;
}
if (smb_read_error == READ_ERROR)
{
DEBUG(3,("receive_smb error (%s) exiting\n",
strerror(errno)));
return;
}
t = time(NULL);
/* become root again if waiting */
unbecome_user();
/* check for smb.conf reload */
if (counter >= service_load_counter + SMBD_RELOAD_CHECK)
{
service_load_counter = counter;
/* reload services, if files have changed. */
reload_services(True);
}
/*
* If reload_after_sighup == True then we got a SIGHUP
* and are being asked to reload. Fix from <branko.cibej@hermes.si>
*/
if (reload_after_sighup)
{
DEBUG(0,("Reloading services after SIGHUP\n"));
reload_services(False);
reload_after_sighup = False;
}
/* automatic timeout if all connections are closed */
if (conn_num_open()==0 && counter >= IDLE_CLOSED_TIMEOUT)
{
DEBUG( 2, ( "Closing idle connection\n" ) );
return;
}
if (keepalive && (counter-last_keepalive)>keepalive)
{
struct cli_state *cli = server_client();
if (!send_keepalive(Client)) {
DEBUG( 2, ( "Keepalive failed - exiting.\n" ) );
return;
}
/* also send a keepalive to the password server if its still
connected */
if (cli && cli->initialised)
send_keepalive(cli->fd);
last_keepalive = counter;
}
/* check for connection timeouts */
allidle = conn_idle_all(t, deadtime);
if (allidle && conn_num_open()>0) {
DEBUG(2,("Closing idle connection 2.\n"));
return;
}
if(global_machine_pasword_needs_changing)
{
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(!trust_password_lock( global_myworkgroup, global_myname, True)) {
DEBUG(0,("process: unable to open the machine account password file for \
machine %s in domain %s.\n", global_myname, global_myworkgroup ));
continue;
}
if(!get_trust_account_password( 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 ));
trust_password_unlock();
continue;
}
/*
* Make sure someone else hasn't already done this.
*/
if(t < lct + lp_machine_password_timeout()) {
trust_password_unlock();
global_machine_pasword_needs_changing = False;
continue;
}
pstrcpy(remote_machine_list, lp_passwordserver());
change_trust_account_password( global_myworkgroup, remote_machine_list);
trust_password_unlock();
global_machine_pasword_needs_changing = False;
}
/*
* Check to see if we have any change notifies
* outstanding on the queue.
*/
process_pending_change_notify_queue(t);
}
if(got_smb)
process_smb(InBuffer, OutBuffer);
else
process_local_message(oplock_sock, InBuffer, BUFFER_SIZE);
}
}

File diff suppressed because it is too large Load Diff

542
source3/smbd/service.c Normal file
View File

@ -0,0 +1,542 @@
/*
Unix SMB/Netbios implementation.
Version 1.9.
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 int DEBUGLEVEL;
extern time_t 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 pstring sesssetup_user;
extern fstring remote_machine;
/****************************************************************************
load parameters specific to a connection/service
****************************************************************************/
BOOL become_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;
snum = SNUM(conn);
if (do_chdir &&
ChDir(conn->connectpath) != 0 &&
ChDir(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);
}
/****************************************************************************
find a service entry
****************************************************************************/
int find_service(char *service)
{
int iService;
string_sub(service,"\\","/");
iService = lp_servicenumber(service);
/* now handle the special case of a home directory */
if (iService < 0)
{
char *phome_dir = get_home_dir(service);
if(!phome_dir)
{
/*
* Try mapping the servicename, it may
* be a Windows to unix mapped user name.
*/
if(map_username(service))
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 *pdefservice = lp_defaultservice();
if (pdefservice && *pdefservice && !strequal(pdefservice,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)
{
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);
}
/****************************************************************************
make a connection to a service
****************************************************************************/
connection_struct *make_connection(char *service,char *user,char *password, int pwlen, char *dev,uint16 vuid, int *ecode)
{
int snum;
struct passwd *pass = NULL;
BOOL guest = False;
BOOL force = False;
extern int Client;
connection_struct *conn;
strlower(service);
snum = find_service(service);
if (snum < 0) {
extern int Client;
if (strequal(service,"IPC$")) {
DEBUG(3,("refusing IPC connection\n"));
*ecode = ERRnoipc;
return NULL;
}
DEBUG(0,("%s (%s) couldn't find service %s\n",
remote_machine, client_addr(Client), service));
*ecode = ERRinvnetname;
return NULL;
}
if (strequal(service,HOMES_NAME)) {
if (*user && Get_Pwnam(user,True))
return(make_connection(user,user,password,
pwlen,dev,vuid,ecode));
if(lp_security() != SEC_SHARE) {
if (validated_username(vuid)) {
pstrcpy(user,validated_username(vuid));
return(make_connection(user,user,password,pwlen,dev,vuid,ecode));
}
} else {
/* Security = share. Try with sesssetup_user
* as the username. */
if(*sesssetup_user) {
pstrcpy(user,sesssetup_user);
return(make_connection(user,user,password,pwlen,dev,vuid,ecode));
}
}
}
if (!lp_snum_ok(snum) ||
!check_access(Client,
lp_hostsallow(snum), lp_hostsdeny(snum))) {
*ecode = ERRaccess;
return NULL;
}
/* you can only connect to the IPC$ service as an ipc device */
if (strequal(service,"IPC$"))
pstrcpy(dev,"IPC");
if (*dev == '?' || !*dev) {
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"));
*ecode = ERRinvdevice;
return NULL;
}
/* 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, ( "Invalid username/password for %s\n", service ) );
*ecode = ERRbadpw;
return NULL;
}
conn = conn_new();
if (!conn) {
DEBUG(0,("Couldn't find free connection.\n"));
*ecode = ERRnoresource;
conn_free(conn);
return NULL;
}
/* find out some info about the user */
pass = Get_Pwnam(user,True);
if (pass == NULL) {
DEBUG(0,( "Couldn't find account %s\n",user));
*ecode = ERRbaduid;
conn_free(conn);
return NULL;
}
conn->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))
conn->read_only = True;
StrnCpy(list,lp_writelist(snum),sizeof(pstring)-1);
string_sub(list,"%S",service);
if (user_in_list(user,list))
conn->read_only = False;
}
/* 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(user,lp_admin_users(snum))
#if 0
&& !conn->read_only
#endif
) {
conn->admin_user = True;
DEBUG(0,("%s logged in as admin user (root privileges)\n",user));
} else {
conn->admin_user = False;
}
conn->force_user = force;
conn->vuid = vuid;
conn->uid = pass->pw_uid;
conn->gid = pass->pw_gid;
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);
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);
#ifdef HAVE_GETGRNAM
if (*lp_force_group(snum)) {
struct group *gptr;
pstring gname;
StrnCpy(gname,lp_force_group(snum),sizeof(pstring)-1);
/* default service may be a group name */
string_sub(gname,"%S",service);
gptr = (struct group *)getgrnam(gname);
if (gptr) {
conn->gid = gptr->gr_gid;
DEBUG(3,("Forced group %s\n",gname));
} else {
DEBUG(1,("Couldn't find group %s\n",gname));
}
}
#endif
if (*lp_force_user(snum)) {
struct passwd *pass2;
fstring fuser;
fstrcpy(fuser,lp_force_user(snum));
pass2 = (struct passwd *)Get_Pwnam(fuser,True);
if (pass2) {
conn->uid = pass2->pw_uid;
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));
}
}
{
pstring s;
pstrcpy(s,lp_pathname(snum));
standard_sub(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;
if (!IS_IPC(conn)) {
/* Find all the groups this uid is in and
store them. Used by become_user() */
setup_groups(conn->user,conn->uid,conn->gid,
&conn->ngroups,&conn->groups);
/* 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"));
*ecode = ERRnoresource;
conn_free(conn);
return NULL;
}
if (lp_status(SNUM(conn)))
claim_connection(conn,"STATUS.",
MAXSTATUS,False);
} /* IS_IPC */
/* execute any "root preexec = " line */
if (*lp_rootpreexec(SNUM(conn))) {
pstring cmd;
pstrcpy(cmd,lp_rootpreexec(SNUM(conn)));
standard_sub(conn,cmd);
DEBUG(5,("cmd=%s\n",cmd));
smbrun(cmd,NULL,False);
}
if (!become_user(conn, conn->vuid)) {
DEBUG(0,("Can't become connected user!\n"));
if (!IS_IPC(conn)) {
yield_connection(conn,
lp_servicename(SNUM(conn)),
lp_max_connections(SNUM(conn)));
if (lp_status(SNUM(conn))) {
yield_connection(conn,"STATUS.",MAXSTATUS);
}
}
conn_free(conn);
*ecode = ERRbadpw;
return NULL;
}
if (ChDir(conn->connectpath) != 0) {
DEBUG(0,("Can't change directory to %s (%s)\n",
conn->connectpath,strerror(errno)));
unbecome_user();
if (!IS_IPC(conn)) {
yield_connection(conn,
lp_servicename(SNUM(conn)),
lp_max_connections(SNUM(conn)));
if (lp_status(SNUM(conn)))
yield_connection(conn,"STATUS.",MAXSTATUS);
}
conn_free(conn);
*ecode = ERRinvnetname;
return NULL;
}
string_set(&conn->origpath,conn->connectpath);
#if SOFTLINK_OPTIMISATION
/* resolve any soft links early */
{
pstring s;
pstrcpy(s,conn->connectpath);
GetWd(s);
string_set(&conn->connectpath,s);
ChDir(conn->connectpath);
}
#endif
add_session_user(user);
/* execute any "preexec = " line */
if (*lp_preexec(SNUM(conn))) {
pstring cmd;
pstrcpy(cmd,lp_preexec(SNUM(conn)));
standard_sub(conn,cmd);
smbrun(cmd,NULL,False);
}
/* we've finished with the sensitive stuff */
unbecome_user();
/* 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)));
}
if( DEBUGLVL( IS_IPC(conn) ? 3 : 1 ) ) {
extern int Client;
dbgtext( "%s (%s) ", remote_machine, client_addr(Client) );
dbgtext( "connect to service %s ", lp_servicename(SNUM(conn)) );
dbgtext( "as user %s ", user );
dbgtext( "(uid=%d, gid=%d) ", conn->uid, conn->gid );
dbgtext( "(pid %d)\n", (int)getpid() );
}
return(conn);
}
/****************************************************************************
close a cnum
****************************************************************************/
void close_cnum(connection_struct *conn, uint16 vuid)
{
extern int Client;
DirCacheFlush(SNUM(conn));
unbecome_user();
DEBUG(IS_IPC(conn)?3:1, ("%s (%s) closed connection to service %s\n",
remote_machine,client_addr(Client),
lp_servicename(SNUM(conn))));
yield_connection(conn,
lp_servicename(SNUM(conn)),
lp_max_connections(SNUM(conn)));
if (lp_status(SNUM(conn)))
yield_connection(conn,"STATUS.",MAXSTATUS);
file_close_conn(conn);
dptr_closecnum(conn);
/* execute any "postexec = " line */
if (*lp_postexec(SNUM(conn)) &&
become_user(conn, vuid)) {
pstring cmd;
pstrcpy(cmd,lp_postexec(SNUM(conn)));
standard_sub(conn,cmd);
smbrun(cmd,NULL,False);
unbecome_user();
}
unbecome_user();
/* execute any "root postexec = " line */
if (*lp_rootpostexec(SNUM(conn))) {
pstring cmd;
pstrcpy(cmd,lp_rootpostexec(SNUM(conn)));
standard_sub(conn,cmd);
smbrun(cmd,NULL,False);
}
conn_free(conn);
}