summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/include/local.h5
-rw-r--r--source3/include/proto.h7
-rw-r--r--source3/include/smb.h41
-rw-r--r--source3/lib/util.c211
-rw-r--r--source3/smbd/dir.c6
-rw-r--r--source3/smbd/password.c45
-rw-r--r--source3/smbd/reply.c11
-rw-r--r--source3/smbd/server.c456
8 files changed, 637 insertions, 145 deletions
diff --git a/source3/include/local.h b/source3/include/local.h
index 9548bf74b6..e7eff2a300 100644
--- a/source3/include/local.h
+++ b/source3/include/local.h
@@ -159,4 +159,9 @@
/* the directory to sit in when idle */
/* #define IDLE_DIR "/" */
+/* Timout (in seconds) to wait for an oplock breal
+ message to return. */
+
+#define OPLOCK_BREAK_TIMEOUT 120
+
#endif
diff --git a/source3/include/proto.h b/source3/include/proto.h
index 8903437d00..7a1ccc626f 100644
--- a/source3/include/proto.h
+++ b/source3/include/proto.h
@@ -719,6 +719,9 @@ int find_service(char *service);
int cached_error_packet(char *inbuf,char *outbuf,int fnum,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);
+BOOL request_oplock_break(min_share_mode_entry *share_entry,
+ uint32 dev, uint32 inode);
BOOL snum_used(int snum);
BOOL reload_services(BOOL test);
int setup_groups(char *user, int uid, int gid, int *p_ngroups,
@@ -925,7 +928,9 @@ int read_data(int fd,char *buffer,int N);
int write_data(int fd,char *buffer,int N);
int transfer_file(int infd,int outfd,int n,char *header,int headlen,int align);
int read_smb_length(int fd,char *inbuf,int timeout);
-BOOL receive_smb(int fd,char *buffer,int timeout);
+BOOL receive_smb(int fd,char *buffer, int timeout);
+BOOL receive_local_message(int fd, char *buffer, int buffer_len, int timeout);
+BOOL push_local_message(char *buf, int msg_len);
BOOL receive_message_or_smb(int smbfd, int oplock_fd,
char *buffer, int buffer_len,
int timeout, BOOL *got_smb);
diff --git a/source3/include/smb.h b/source3/include/smb.h
index 07614194f7..c8de001fda 100644
--- a/source3/include/smb.h
+++ b/source3/include/smb.h
@@ -1014,6 +1014,17 @@ extern int unix_ERR_code;
#define EXTENDED_OPLOCK_REQUEST(inbuf) (((SVAL(inbuf,smb_vwv2)|(1<<1))>>1) | \
((SVAL(inbuf,smb_vwv2)|(1<<2))>>1))
+/* Lock types. */
+#define LOCKING_ANDX_SHARED_LOCK 0x1
+#define LOCKING_ANDX_OPLOCK_RELEASE 0x2
+#define LOCKING_ANDX_CHANGE_LOCKTYPE 0x4
+#define LOCKING_ANDX_CANCEL_LOCK 0x8
+#define LOCKING_ANDX_LARGE_FILES 0x10
+
+/* Oplock levels */
+#define OPLOCKLEVEL_NONE 0
+#define OPLOCKLEVEL_II 1
+
/*
* Bits we test with.
*/
@@ -1023,4 +1034,34 @@ extern int unix_ERR_code;
#define CORE_OPLOCK_GRANTED (1<<5)
#define EXTENDED_OPLOCK_GRANTED (1<<15)
+/*
+ * Loopback command offsets.
+ */
+
+#define UDP_CMD_LEN_OFFSET 0
+#define UDP_CMD_PORT_OFFSET 4
+#define UDP_CMD_HEADER_LEN 6
+
+#define UDP_MESSAGE_CMD_OFFSET 0
+
+/*
+ * Oplock break command code to send over the udp socket.
+ *
+ * Form of this is :
+ *
+ * 0 2 6 10
+ * +----+--------+--------+--------+
+ * | cmd| pid | dev | inode |
+ * +----+--------+--------+--------+
+ */
+
+#define OPLOCK_BREAK_CMD 0x1
+#define OPLOCK_BREAK_PID_OFFSET 2
+#define OPLOCK_BREAK_DEV_OFFSET 6
+#define OPLOCK_BREAK_INODE_OFFSET 10
+#define OPLOCK_BREAK_MSG_LEN 14
+
+
+#define CMD_REPLY 0x8000
+
/* _SMB_H */
diff --git a/source3/lib/util.c b/source3/lib/util.c
index 05dd619813..056e7e18db 100644
--- a/source3/lib/util.c
+++ b/source3/lib/util.c
@@ -2270,10 +2270,11 @@ int read_smb_length(int fd,char *inbuf,int timeout)
/****************************************************************************
- read an smb from a fd.
+ read an smb from a fd. Note that the buffer *MUST* be of size
+ BUFFER_SIZE+SAFETY_MARGIN.
The timeout is in milli seconds
****************************************************************************/
-BOOL receive_smb(int fd,char *buffer,int timeout)
+BOOL receive_smb(int fd,char *buffer, int timeout)
{
int len,ret;
@@ -2302,8 +2303,133 @@ BOOL receive_smb(int fd,char *buffer,int timeout)
#ifdef USE_OPLOCKS
/****************************************************************************
+ read a message from a udp fd.
+The timeout is in milli seconds
+****************************************************************************/
+BOOL receive_local_message(int fd, char *buffer, int buffer_len, int timeout)
+{
+ struct sockaddr_in from;
+ int fromlen = sizeof(from);
+ int32 msg_len = 0;
+
+ if(timeout != 0)
+ {
+ struct timeval to;
+ fd_set fds;
+ int selrtn;
+
+ FD_ZERO(&fds);
+ FD_SET(fd,&fds);
+
+ to.tv_sec = timeout / 1000;
+ to.tv_usec = (timeout % 1000) * 1000;
+
+ selrtn = sys_select(&fds,&to);
+
+ /* 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;
+ }
+ }
+
+ /*
+ * Read a loopback udp message.
+ */
+ msg_len = recvfrom(fd, &buffer[UDP_CMD_HEADER_LEN],
+ buffer_len - UDP_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 - UDP_CMD_HEADER_LEN))
+ {
+ DEBUG(0,("receive_local_message: invalid msg_len (%d) max can be %d\n",
+ msg_len,
+ buffer_len - UDP_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 %x should be 127.0.0.1\n", from.sin_addr.s_addr));
+ return False;
+ }
+
+ /* Setup the message header */
+ SIVAL(buffer,UDP_CMD_LEN_OFFSET,msg_len);
+ SSVAL(buffer,UDP_CMD_PORT_OFFSET,ntohs(from.sin_port));
+
+ return True;
+}
+
+/****************************************************************************
+ structure to hold a linked list of local udp messages.
+ for processing.
+****************************************************************************/
+
+typedef struct _udp_message_list {
+ struct _udp_message_list *msg_next;
+ char *msg_buf;
+ int msg_len;
+} udp_message_list;
+
+static udp_message_list *udp_msg_head = NULL;
+
+/****************************************************************************
+ Function to push a linked list of local udp messages ready
+ for processing.
+****************************************************************************/
+BOOL push_local_message(char *buf, int msg_len)
+{
+ udp_message_list *msg = (udp_message_list *)malloc(sizeof(udp_message_list));
+
+ if(msg == NULL)
+ {
+ DEBUG(0,("push_local_message: malloc fail (1)\n"));
+ return False;
+ }
+
+ msg->msg_buf = (char *)malloc(msg_len);
+ if(msg->msg_buf == NULL)
+ {
+ DEBUG(0,("push_local_message: malloc fail (2)\n"));
+ free((char *)msg);
+ return False;
+ }
+
+ memcpy(msg->msg_buf, buf, msg_len);
+ msg->msg_len = msg_len;
+
+ msg->msg_next = udp_msg_head;
+ udp_msg_head = msg;
+
+ return True;
+}
+
+/****************************************************************************
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) return this first.
+
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
@@ -2322,7 +2448,24 @@ BOOL receive_message_or_smb(int smbfd, int oplock_fd,
struct timeval to;
*got_smb = False;
-
+
+ /*
+ * Check to see if we already have a message on the udp queue.
+ * If so - copy and return it.
+ */
+
+ if(udp_msg_head)
+ {
+ udp_message_list *msg = udp_msg_head;
+ memcpy(buffer, msg->msg_buf, MIN(buffer_len, msg->msg_len));
+ udp_msg_head = msg->msg_next;
+
+ /* Free the message we just copied. */
+ free((char *)msg->msg_buf);
+ free((char *)msg);
+ return True;
+ }
+
FD_ZERO(&fds);
FD_SET(smbfd,&fds);
FD_SET(oplock_fd,&fds);
@@ -2352,34 +2495,8 @@ BOOL receive_message_or_smb(int smbfd, int oplock_fd,
}
else
{
- /*
- * Read a udp message.
- */
- struct sockaddr_in from;
- int fromlen = sizeof(from);
- int32 msg_len = 0;
- uint16 port = 0;
-
- msg_len = recvfrom(oplock_fd, &buffer[6+sizeof(struct in_addr)],
- buffer_len - (6 + sizeof(struct in_addr)), 0,
- (struct sockaddr *)&from, &fromlen);
-
- if(msg_len < 0)
- {
- DEBUG(0,("Invalid loopback packet ! (%s).\n",strerror(errno)));
- return False;
- }
-
- port = ntohs(from.sin_port);
-
- /* Setup the message header */
- SIVAL(buffer,0,msg_len);
- SSVAL(buffer,4,port);
- memcpy(&buffer[6],(char *)&from.sin_addr,sizeof(struct in_addr));
-
+ return receive_local_message(oplock_fd, buffer, buffer_len, 0);
}
-
- return True;
}
#endif /* USE_OPLOCKS */
@@ -3713,10 +3830,10 @@ char *readdirname(void *p)
return(dname);
}
-/*
- * Utility function used to decide if the last component
- * of a path matches a (possibly wildcarded) entry in a namelist.
- */
+/*******************************************************************
+ Utility function used to decide if the last component
+ of a path matches a (possibly wildcarded) entry in a namelist.
+********************************************************************/
BOOL is_in_path(char *name, name_compare_entry *namelist)
{
@@ -3763,19 +3880,19 @@ BOOL is_in_path(char *name, name_compare_entry *namelist)
return False;
}
-/*
- * Strip a '/' separated list into an array of
- * name_compare_enties structures suitable for
- * passing to is_in_path(). We do this for
- * speed so we can pre-parse all the names in the list
- * and don't do it for each call to is_in_path().
- * namelist is modified here and is assumed to be
- * a copy owned by the caller.
- * We also check if the entry contains a wildcard to
- * remove a potentially expensive call to mask_match
- * if possible.
- */
-
+/*******************************************************************
+ Strip a '/' separated list into an array of
+ name_compare_enties structures suitable for
+ passing to is_in_path(). We do this for
+ speed so we can pre-parse all the names in the list
+ and don't do it for each call to is_in_path().
+ namelist is modified here and is assumed to be
+ a copy owned by the caller.
+ We also check if the entry contains a wildcard to
+ remove a potentially expensive call to mask_match
+ if possible.
+********************************************************************/
+
void set_namearray(name_compare_entry **ppname_array, char *namelist)
{
char *name_end;
diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c
index 567bc14424..316b58818f 100644
--- a/source3/smbd/dir.c
+++ b/source3/smbd/dir.c
@@ -470,12 +470,12 @@ BOOL get_dir_entry(int cnum,char *mask,int dirtype,char *fname,int *size,int *mo
if (isrootdir && (strequal(filename,"..") || strequal(filename,".")))
continue;
- strcpy(fname,filename);
+ pstrcpy(fname,filename);
*path = 0;
- strcpy(path,Connections[cnum].dirpath);
+ pstrcpy(path,Connections[cnum].dirpath);
if(needslash)
strcat(path,"/");
- strcpy(pathreal,path);
+ pstrcpy(pathreal,path);
strcat(path,fname);
strcat(pathreal,dname);
if (sys_stat(pathreal,&sbuf) != 0)
diff --git a/source3/smbd/password.c b/source3/smbd/password.c
index 35f73eab2d..f4d94791cf 100644
--- a/source3/smbd/password.c
+++ b/source3/smbd/password.c
@@ -1504,13 +1504,14 @@ BOOL check_hosts_equiv(char *user)
int password_client = -1;
static fstring pserver;
+static char *secserver_inbuf = NULL;
/****************************************************************************
attempted support for server level security
****************************************************************************/
BOOL server_cryptkey(char *buf)
{
- pstring inbuf,outbuf;
+ pstring outbuf;
fstring pass_protocol;
extern fstring remote_machine;
char *p;
@@ -1519,6 +1520,14 @@ BOOL server_cryptkey(char *buf)
struct in_addr dest_ip;
int port = SMB_PORT;
BOOL ret;
+
+ if(secserver_inbuf == NULL) {
+ secserver_inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+ if(secserver_inbuf == NULL) {
+ DEBUG(0,("server_cryptkey: malloc fail for input buffer.\n"));
+ return False;
+ }
+ }
if (password_client >= 0)
close(password_client);
@@ -1530,7 +1539,7 @@ BOOL server_cryptkey(char *buf)
strcpy(pass_protocol,"NT LM 0.12");
}
- bzero(inbuf,sizeof(inbuf));
+ bzero(secserver_inbuf,BUFFER_SIZE + SAFETY_MARGIN);
bzero(outbuf,sizeof(outbuf));
for (p=strtok(lp_passwordserver(),LIST_SEP); p ; p = strtok(NULL,LIST_SEP)) {
@@ -1596,8 +1605,8 @@ BOOL server_cryptkey(char *buf)
send_smb(password_client,outbuf);
- if (!receive_smb(password_client,inbuf,5000) ||
- CVAL(inbuf,0) != 0x82) {
+ if (!receive_smb(password_client,secserver_inbuf,5000) ||
+ CVAL(secserver_inbuf,0) != 0x82) {
DEBUG(1,("%s rejected the session\n",pserver));
close(password_client); password_client = -1;
return(False);
@@ -1618,21 +1627,21 @@ BOOL server_cryptkey(char *buf)
SSVAL(outbuf,smb_flg2,0x1);
send_smb(password_client,outbuf);
- ret = receive_smb(password_client,inbuf,5000);
+ ret = receive_smb(password_client,secserver_inbuf,5000);
- if (!ret || CVAL(inbuf,smb_rcls) || SVAL(inbuf,smb_vwv0)) {
+ if (!ret || CVAL(secserver_inbuf,smb_rcls) || SVAL(secserver_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)) {
+ if (!(CVAL(secserver_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);
+ memcpy(buf,secserver_inbuf,smb_len(secserver_inbuf)+4);
DEBUG(3,("password server OK\n"));
@@ -1644,15 +1653,23 @@ attempted support for server level security
****************************************************************************/
BOOL server_validate(char *buf)
{
- pstring inbuf,outbuf;
+ pstring outbuf;
BOOL ret;
+ if(secserver_inbuf == NULL) {
+ secserver_inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+ if(secserver_inbuf == NULL) {
+ DEBUG(0,("server_validate: malloc fail for input buffer.\n"));
+ return False;
+ }
+ }
+
if (password_client < 0) {
DEBUG(1,("%s not connected\n",pserver));
return(False);
}
- bzero(inbuf,sizeof(inbuf));
+ bzero(secserver_inbuf,BUFFER_SIZE + SAFETY_MARGIN);
memcpy(outbuf,buf,sizeof(outbuf));
/* send a session setup command */
@@ -1662,18 +1679,18 @@ BOOL server_validate(char *buf)
set_message(outbuf,smb_numwords(outbuf),smb_buflen(outbuf),False);
- SCVAL(inbuf,smb_rcls,1);
+ SCVAL(secserver_inbuf,smb_rcls,1);
send_smb(password_client,outbuf);
- ret = receive_smb(password_client,inbuf,5000);
+ ret = receive_smb(password_client,secserver_inbuf,5000);
- if (!ret || CVAL(inbuf,smb_rcls) != 0) {
+ if (!ret || CVAL(secserver_inbuf,smb_rcls) != 0) {
DEBUG(1,("password server %s rejected the password\n",pserver));
return(False);
}
/* if logged in as guest then reject */
- if ((SVAL(inbuf,smb_vwv2) & 1) != 0) {
+ if ((SVAL(secserver_inbuf,smb_vwv2) & 1) != 0) {
DEBUG(1,("password server %s gave us guest only\n",pserver));
return(False);
}
diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c
index c1c42be801..8987e7c0c2 100644
--- a/source3/smbd/reply.c
+++ b/source3/smbd/reply.c
@@ -42,6 +42,7 @@ extern BOOL short_case_preserve;
extern pstring sesssetup_user;
extern fstring myworkgroup;
extern int Client;
+extern int global_oplock_break;
/* this macro should always be used to extract an fnum (smb_fid) from
a packet to ensure chaining works correctly */
@@ -388,7 +389,9 @@ int reply_sesssetup_and_X(char *inbuf,char *outbuf,int length,int bufsize)
if (Protocol < PROTOCOL_NT1) {
smb_apasslen = SVAL(inbuf,smb_vwv7);
if (smb_apasslen > MAX_PASSWORD_LENGTH)
+ {
overflow_attack(smb_apasslen);
+ }
memcpy(smb_apasswd,smb_buf(inbuf),smb_apasslen);
pstrcpy(user,smb_buf(inbuf)+smb_apasslen);
@@ -1163,7 +1166,7 @@ int reply_open(char *inbuf,char *outbuf)
SSVAL(outbuf,smb_vwv6,rmode);
if (oplock_request && lp_fake_oplocks(SNUM(cnum))) {
- fsp->granted_oplock = True;
+ CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED;
}
if(fsp->granted_oplock)
@@ -1250,7 +1253,7 @@ int reply_open_and_X(char *inbuf,char *outbuf,int length,int bufsize)
}
if (oplock_request && lp_fake_oplocks(SNUM(cnum))) {
- fsp->granted_oplock = True;
+ smb_action |= EXTENDED_OPLOCK_GRANTED;
}
if(fsp->granted_oplock)
@@ -1377,7 +1380,7 @@ int reply_mknew(char *inbuf,char *outbuf)
SSVAL(outbuf,smb_vwv0,fnum);
if (oplock_request && lp_fake_oplocks(SNUM(cnum))) {
- fsp->granted_oplock = True;
+ CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED;
}
if(fsp->granted_oplock)
@@ -1453,7 +1456,7 @@ int reply_ctemp(char *inbuf,char *outbuf)
strcpy(smb_buf(outbuf) + 1,fname2);
if (oplock_request && lp_fake_oplocks(SNUM(cnum))) {
- fsp->granted_oplock = True;
+ CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED;
}
if(fsp->granted_oplock)
diff --git a/source3/smbd/server.c b/source3/smbd/server.c
index d2ad803c9c..708a2c272b 100644
--- a/source3/smbd/server.c
+++ b/source3/smbd/server.c
@@ -89,9 +89,11 @@ static int num_connections_open = 0;
int oplock_sock = -1;
uint16 oplock_port = 0;
/* Current number of oplocks we have outstanding. */
-uint32 oplocks_open = 0;
+uint32 global_oplocks_open = 0;
#endif /* USE_OPLOCKS */
+BOOL global_oplock_break = False;
+
extern fstring remote_machine;
pstring OriginalDir;
@@ -1355,17 +1357,17 @@ void close_file(int fnum)
fs_p->open = False;
Connections[cnum].num_files_open--;
if(fs_p->wbmpx_ptr)
- {
- free((char *)fs_p->wbmpx_ptr);
- fs_p->wbmpx_ptr = NULL;
- }
+ {
+ free((char *)fs_p->wbmpx_ptr);
+ fs_p->wbmpx_ptr = NULL;
+ }
#if USE_MMAP
if(fs_p->mmap_ptr)
- {
- munmap(fs_p->mmap_ptr,fs_p->mmap_size);
- fs_p->mmap_ptr = NULL;
- }
+ {
+ munmap(fs_p->mmap_ptr,fs_p->mmap_size);
+ fs_p->mmap_ptr = NULL;
+ }
#endif
if (lp_share_modes(SNUM(cnum)))
@@ -1668,12 +1670,15 @@ void open_file_shared(int fnum,int cnum,char *fname,int share_mode,int ofun,
do
{
+
broke_oplock = False;
for(i = 0; i < num_shares; i++)
{
+ min_share_mode_entry *share_entry = &old_shares[i];
+
/* someone else has a share lock on it, check to see
if we can too */
- if(check_share_mode(&old_shares[i], deny_mode, fname, fcbopen, &flags) == False)
+ if(check_share_mode(share_entry, deny_mode, fname, fcbopen, &flags) == False)
{
free((char *)old_shares);
unlock_share_entry(cnum, dev, inode, token);
@@ -1688,23 +1693,31 @@ void open_file_shared(int fnum,int cnum,char *fname,int share_mode,int ofun,
* has an oplock on this file. If so we must break it before
* continuing.
*/
- if(old_shares[i].op_type != 0)
+ if(share_entry->op_type & (EXCLUSIVE_OPLOCK|BATCH_OPLOCK))
{
+
+ DEBUG(5,("open file shared: breaking oplock (%x) on file %s, \
+dev = %x, inode = %x\n", share_entry->op_type, fname, dev, inode));
+
/* Oplock break.... */
unlock_share_entry(cnum, dev, inode, token);
-#if 0 /* Work in progress..... */
- if(break_oplock())
+ if(request_oplock_break(share_entry, dev, inode) == False)
{
free((char *)old_shares);
- /* Error condition here... */
+ DEBUG(0,("open file shared: FAILED when breaking oplock (%x) on file %s, \
+dev = %x, inode = %x\n", old_shares[i].op_type, fname, dev, inode));
+ errno = EACCES;
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadshare;
+ return;
}
lock_share_entry(cnum, dev, inode, &token);
broke_oplock = True;
break;
-#endif
}
#endif /* USE_OPLOCKS */
- }
+ } /* end for */
+
if(broke_oplock)
{
free((char *)old_shares);
@@ -2312,6 +2325,52 @@ static BOOL open_sockets(BOOL is_daemon,int port)
return True;
}
+/****************************************************************************
+ process an smb from the client - split out from the process() code so
+ it can be used by the oplock break code.
+****************************************************************************/
+
+static void process_smb(char *inbuf, char *outbuf)
+{
+ extern int Client;
+ static int trans_num = 0;
+
+ int msg_type = CVAL(inbuf,0);
+ int32 len = smb_len(outbuf);
+ int nread = len + 4;
+
+ DEBUG(6,("got message type 0x%x of len 0x%x\n",msg_type,len));
+ DEBUG(3,("%s Transaction %d of length %d\n",timestring(),trans_num,nread));
+
+#ifdef WITH_VTP
+ if(trans_num == 1 && VT_Check(inbuf))
+ {
+ VT_Process();
+ return;
+ }
+#endif
+
+ if (msg_type == 0)
+ show_msg(inbuf);
+
+ 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++;
+}
+
#ifdef USE_OPLOCKS
/****************************************************************************
open the oplock IPC socket communication
@@ -2355,33 +2414,324 @@ static BOOL process_local_message(int oplock_sock, char *buffer, int buf_size)
{
int32 msg_len;
int16 from_port;
- struct in_addr from_addr;
char *msg_start;
- msg_len = IVAL(buffer,0);
- from_port = SVAL(buffer,4);
- memcpy((char *)&from_addr, &buffer[6], sizeof(struct in_addr));
+ 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 sockaddr_in toaddr;
+
+ DEBUG(5,("process_local_message: oplock break request from \
+pid %d, dev %d, inode %d\n", remotepid, 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) == 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( 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",
+ remotepid, strerror(errno)));
+ return False;
+ }
+ }
+ break;
+ 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)
+{
+ extern int Client;
+ static char *inbuf = NULL;
+ static char *outbuf = NULL;
+ files_struct *fsp = NULL;
+ int fnum;
+
+ if(inbuf == NULL)
+ {
+ inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+ if(inbuf == NULL) {
+ DEBUG(0,("oplock_break: malloc fail for input buffer.\n"));
+ return False;
+ }
+ outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+ if(outbuf == NULL) {
+ DEBUG(0,("oplock_break: malloc fail for output buffer.\n"));
+ free(inbuf);
+ inbuf = NULL;
+ return False;
+ }
+ }
+
+ /* We need to search the file open table for the
+ entry containing this dev and inode, and ensure
+ we have an oplock on it. */
+ for( fnum = 0; fnum < MAX_OPEN_FILES; fnum++)
+ {
+ if(OPEN_FNUM(fnum))
+ {
+ fsp = &Files[fnum];
+ if((fsp->fd_ptr->dev == dev) && (fsp->fd_ptr->inode == inode))
+ break;
+ }
+ }
+
+ if(fsp == NULL)
+ {
+ /* The file could have been closed in the meantime - return success. */
+ DEBUG(3,("oplock_break: cannot find open file with dev = %x, inode = %x (fnum = %d) \
+allowing break to succeed.\n", dev, inode, fnum));
+ return True;
+ }
+
+ /* Ensure we have an oplock on the file */
+
+ /* Question - can a client asynchronously break an oplock ? Would it
+ ever do so ? If so this test is invalid for external smbd oplock
+ breaks and we should return True in these cases (JRA).
+ */
+
+ if(!fsp->granted_oplock)
+ {
+ DEBUG(0,("oplock_break: file %s (fnum = %d, dev = %x, inode = %x) has no oplock.\n",
+ fsp->name, fnum, dev, inode));
+ 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.
+ */
+
+ /* Prepare the SMBlockingX message. */
+ bzero(outbuf,smb_size);
+ set_message(outbuf,8,0,True);
+
+ SCVAL(outbuf,smb_com,SMBlockingX);
+ SSVAL(outbuf,smb_tid,fsp->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,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);
+
+ global_oplock_break = True;
+
+ /* Process incoming messages. */
+ while(global_oplock_break && OPEN_FNUM(fnum))
+ {
+ if(receive_smb(Client,inbuf,OPLOCK_BREAK_TIMEOUT * 1000) == False)
+ {
+ if (smb_read_error == READ_EOF)
+ {
+ DEBUG(3,("oplock_break: end of file from client\n"));
+ return False;
+ }
+
+ if (smb_read_error == READ_ERROR)
+ {
+ DEBUG(3,("oplock_break: receive_smb error (%s)\n",
+ strerror(errno)));
+ return False;
+ }
+ }
+ process_smb(inbuf, outbuf);
+ }
+
+ 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.
+****************************************************************************/
- msg_start = &buffer[6 + sizeof(struct in_addr)];
+BOOL request_oplock_break(min_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();
+
+ 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 = %x, port = %d \
+should be %d\n", pid, share_entry->op_port, oplock_port));
+ return False;
+ }
+ /* Call oplock break direct. */
+ return oplock_break(dev, inode);
+ }
+
+ /* We need to send a OPLOCK_BREAK_CMD message to the
+ port in the share mode entry. */
- /* Validate message length. */
- if(msg_len > (buf_size - (6 + sizeof(struct in_addr))))
+ 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);
+
+ /* 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;
+
+ DEBUG(3,("request_oplock_break: sending a oplock break message to pid %d on port %d \
+for dev = %x, inode = %x\n", share_entry->pid, share_entry->op_port, dev, inode));
+
+ if(sendto(oplock_sock,op_break_msg,OPLOCK_BREAK_MSG_LEN,0,
+ (struct sockaddr *)&addr_out,sizeof(addr_out)) < 0)
{
- DEBUG(0,("process_local_message: invalid msg_len (%d) max can be %d\n",
- msg_len, buf_size - (6 + sizeof(struct in_addr))));
+ DEBUG(0,("request_oplock_break: failed when sending a oplock break message \
+to pid %d on port %d for dev = %x, inode = %x. Error was %s\n",
+ share_entry->pid, share_entry->op_port, dev, inode,
+ strerror(errno)));
return False;
}
- /* Validate message from address (must be localhost). */
- if(from_addr.s_addr != htonl(INADDR_LOOPBACK))
+ /*
+ * 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 seconds.
+ * While we get messages that aren't ours, loop.
+ */
+
+ while(1)
{
- DEBUG(0,("process_local_message: invalid 'from' address \
-(was %x should be 127.0.0.1\n", from_addr.s_addr));
- return False;
+ char op_break_reply[UDP_CMD_HEADER_LEN+OPLOCK_BREAK_MSG_LEN];
+ int32 reply_msg_len;
+ int16 reply_from_port;
+ char *reply_msg_start;
+
+ if(receive_local_message(oplock_sock, op_break_reply, sizeof(op_break_reply),
+ OPLOCK_BREAK_TIMEOUT * 1000) == False)
+ {
+ if(smb_read_error == READ_TIMEOUT)
+ DEBUG(0,("request_oplock_break: no response received to oplock break request to \
+pid %d on port %d for dev = %x, inode = %x\n", share_entry->pid,
+ share_entry->op_port, dev, inode));
+ else
+ DEBUG(0,("request_oplock_break: error in response received to oplock break request to \
+pid %d on port %d for dev = %x, inode = %x. Error was (%s).\n", share_entry->pid,
+ share_entry->op_port, dev, inode, strerror(errno)));
+ return False;
+ }
+
+ /*
+ * If the response we got was not an answer to our message, but
+ * was a completely different request, push it onto the pending
+ * udp message stack so that we can deal with it in the main loop.
+ * It may be another oplock break request to us.
+ */
+
+ /*
+ * Local note from JRA. There exists the possibility of a denial
+ * of service attack here by allowing non-root processes running
+ * on a local machine sending many of these pending messages to
+ * a smbd port. Currently I'm not sure how to restrict the messages
+ * I will queue (although I could add a limit to the queue) to
+ * those received by root processes only. There should be a
+ * way to make this bulletproof....
+ */
+
+ 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. Ignoring\n"));
+ continue;
+ }
+
+ if(((SVAL(reply_msg_start,UDP_MESSAGE_CMD_OFFSET) & CMD_REPLY) == 0) ||
+ (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))
+ {
+ DEBUG(3,("request_oplock_break: received other message whilst awaiting \
+oplock break response from pid %d on port %d for dev = %x, inode = %x.\n",
+ share_entry->pid, share_entry->op_port, dev, inode));
+ if(push_local_message(op_break_reply, sizeof(op_break_reply)) == False)
+ return False;
+ }
+
+ break;
}
+ DEBUG(3,("request_oplock_break: broke oplock.\n"));
+
return True;
}
+
#endif /* USE_OPLOCKS */
/****************************************************************************
@@ -4057,52 +4407,6 @@ int construct_reply(char *inbuf,char *outbuf,int size,int bufsize)
}
/****************************************************************************
- process an smb from the client - split out from the process() code so
- it can be used by the oplock break code.
-****************************************************************************/
-
-static void process_smb(char *inbuf, char *outbuf)
-{
- extern int Client;
- static int trans_num = 0;
-
- int msg_type = CVAL(inbuf,0);
- int32 len = smb_len(outbuf);
- int nread = len + 4;
-
- DEBUG(6,("got message type 0x%x of len 0x%x\n",msg_type,len));
- DEBUG(3,("%s Transaction %d of length %d\n",timestring(),trans_num,nread));
-
-#ifdef WITH_VTP
- if(trans_num == 1 && VT_Check(inbuf))
- {
- VT_Process();
- return;
- }
-#endif
-
- if (msg_type == 0)
- show_msg(inbuf);
-
- 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++;
-}
-
-/****************************************************************************
process commands from the client
****************************************************************************/
static void process(void)