diff options
-rw-r--r-- | source3/include/proto.h | 2 | ||||
-rw-r--r-- | source3/lib/util.c | 53 | ||||
-rw-r--r-- | source3/printing/printing.c | 7 | ||||
-rw-r--r-- | source3/smbd/server.c | 106 |
4 files changed, 110 insertions, 58 deletions
diff --git a/source3/include/proto.h b/source3/include/proto.h index d6027e3716..3d478b9246 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -1443,7 +1443,7 @@ int read_smb_length(int fd,char *inbuf,int timeout); BOOL receive_smb(int fd,char *buffer, int timeout); BOOL client_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 push_smb_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/lib/util.c b/source3/lib/util.c index 1aa88c0708..12d7adcb56 100644 --- a/source3/lib/util.c +++ b/source3/lib/util.c @@ -2485,29 +2485,31 @@ BOOL receive_local_message(int fd, char *buffer, int buffer_len, int timeout) } /**************************************************************************** - structure to hold a linked list of local udp messages. + structure to hold a linked list of local messages. for processing. ****************************************************************************/ -typedef struct _udp_message_list { - struct _udp_message_list *msg_next; +typedef struct _message_list { + struct _message_list *msg_next; char *msg_buf; int msg_len; -} udp_message_list; +} pending_message_list; -static udp_message_list *udp_msg_head = NULL; +static pending_message_list *smb_msg_head = NULL; /**************************************************************************** - Function to push a linked list of local udp messages ready + Function to push a linked list of local messages ready for processing. ****************************************************************************/ -BOOL push_local_message(char *buf, int msg_len) + +static BOOL push_local_message(pending_message_list **pml, char *buf, int msg_len) { - udp_message_list *msg = (udp_message_list *)malloc(sizeof(udp_message_list)); + pending_message_list *msg = (pending_message_list *) + malloc(sizeof(pending_message_list)); if(msg == NULL) { - DEBUG(0,("push_local_message: malloc fail (1)\n")); + DEBUG(0,("push_message: malloc fail (1)\n")); return False; } @@ -2522,19 +2524,33 @@ BOOL push_local_message(char *buf, int msg_len) memcpy(msg->msg_buf, buf, msg_len); msg->msg_len = msg_len; - msg->msg_next = udp_msg_head; - udp_msg_head = msg; + msg->msg_next = *pml; + *pml = msg; return True; } /**************************************************************************** + Function to push a linked list of local smb messages ready + for processing. +****************************************************************************/ + +BOOL push_smb_message(char *buf, int msg_len) +{ + return push_local_message(&smb_msg_head, buf, msg_len); +} + +/**************************************************************************** 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 a pending smb message has been pushed onto the + queue (this can only happen during oplock break + processing) return this next. + If the first smbfd is ready then read an smb from it. if the second (loopback UDP) fd is ready then read a message from it and setup the buffer header to identify the length @@ -2555,19 +2571,22 @@ BOOL receive_message_or_smb(int smbfd, int oplock_fd, *got_smb = False; /* - * Check to see if we already have a message on the udp queue. + * Check to see if we already have a message on the smb queue. * If so - copy and return it. */ - - if(udp_msg_head) + + if(smb_msg_head) { - udp_message_list *msg = udp_msg_head; + pending_message_list *msg = smb_msg_head; memcpy(buffer, msg->msg_buf, MIN(buffer_len, msg->msg_len)); - udp_msg_head = msg->msg_next; - + smb_msg_head = msg->msg_next; + /* Free the message we just copied. */ free((char *)msg->msg_buf); free((char *)msg); + *got_smb = True; + + DEBUG(5,("receive_message_or_smb: returning queued smb message.\n")); return True; } diff --git a/source3/printing/printing.c b/source3/printing/printing.c index 76b962606b..629b3ad496 100644 --- a/source3/printing/printing.c +++ b/source3/printing/printing.c @@ -436,7 +436,12 @@ A long spool-path will just waste significant chars of the file name. buf->job = atoi(tok[LPRNG_JOBTOK]); buf->size = atoi(tok[LPRNG_TOTALTOK]); - buf->status = strequal(tok[LPRNG_RANKTOK],"active")?LPQ_PRINTING:LPQ_QUEUED; + if (strequal(tok[LPRNG_RANKTOK],"active")) + buf->status = LPQ_PRINTING; + else if (strequal(tok[LPRNG_RANKTOK],"hold")) + buf->status = LPQ_PAUSED; + else + buf->status = LPQ_QUEUED; /* buf->time = time(NULL); */ buf->time = LPRng_time(tok,LPRNG_TIMETOK); DEBUG(3,("Time reported for job %d is %s", buf->job, ctime(&buf->time))); diff --git a/source3/smbd/server.c b/source3/smbd/server.c index 751039070f..b5408a2903 100644 --- a/source3/smbd/server.c +++ b/source3/smbd/server.c @@ -2963,6 +2963,16 @@ inode = %x).\n", timestring(), fsp->name, fnum, 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); /* @@ -3036,6 +3046,8 @@ BOOL request_oplock_break(share_mode_entry *share_entry, 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) { @@ -3089,7 +3101,10 @@ to pid %d on port %d for dev = %x, inode = %x. Error was %s\n", * While we get messages that aren't ours, loop. */ - while(1) + 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; @@ -3097,7 +3112,7 @@ to pid %d on port %d for dev = %x, inode = %x. Error was %s\n", char *reply_msg_start; if(receive_local_message(oplock_sock, op_break_reply, sizeof(op_break_reply), - (OPLOCK_BREAK_TIMEOUT+OPLOCK_BREAK_TIMEOUT_FUDGEFACTOR) * 1000) == False) + time_left ? time_left * 1000 : 1) == False) { if(smb_read_error == READ_TIMEOUT) { @@ -3120,23 +3135,6 @@ pid %d on port %d for dev = %x, inode = %x. Error was (%s).\n", timestring, shar 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); @@ -3150,21 +3148,37 @@ pid %d on port %d for dev = %x, inode = %x. Error was (%s).\n", timestring, shar continue; } - if(((SVAL(reply_msg_start,UDP_MESSAGE_CMD_OFFSET) & CMD_REPLY) == 0) || - (reply_from_port != share_entry->op_port) || + /* + * 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)) + OPLOCK_BREAK_MSG_LEN - OPLOCK_BREAK_PID_OFFSET) == 0)) { - DEBUG(3,("%s request_oplock_break: received other message whilst awaiting \ -oplock break response from pid %d on port %d for dev = %x, inode = %x.\n", - timestring(), share_entry->pid, share_entry->op_port, dev, inode)); - if(push_local_message(op_break_reply, sizeof(op_break_reply)) == False) - return False; - continue; + /* + * 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. + */ - break; + process_local_message(oplock_sock, op_break_reply, sizeof(op_break_reply)); + } + + time_left -= (time(NULL) - start_time); } DEBUG(3,("%s request_oplock_break: broke oplock.\n", timestring())); @@ -4522,7 +4536,7 @@ force write permissions on print services. #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 @@ -4556,20 +4570,20 @@ struct smb_message_struct {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}, + {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}, + {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}, + {SMBmv,"SMBmv",reply_mv,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK}, /* this is a Pathworks specific call, allowing the changing of the root path */ @@ -4577,8 +4591,8 @@ struct smb_message_struct {SMBlseek,"SMBlseek",reply_lseek,AS_USER}, {SMBflush,"SMBflush",reply_flush,AS_USER}, - {SMBctemp,"SMBctemp",reply_ctemp,AS_USER}, - {SMBsplopen,"SMBsplopen",reply_printopen,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|AS_GUEST}, {SMBsplwr,"SMBsplwr",reply_printwrite,AS_USER}, @@ -4605,10 +4619,10 @@ struct smb_message_struct {SMBtrans,"SMBtrans",reply_trans,AS_USER | CAN_IPC}, {SMBtranss,"SMBtranss",NULL,AS_USER | CAN_IPC}, {SMBioctls,"SMBioctls",NULL,AS_USER}, - {SMBcopy,"SMBcopy",reply_copy,AS_USER | NEED_WRITE}, - {SMBmove,"SMBmove",NULL,AS_USER | NEED_WRITE}, + {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}, + {SMBopenX,"SMBopenX",reply_open_and_X,AS_USER | CAN_IPC | QUEUE_IN_OPLOCK }, {SMBreadX,"SMBreadX",reply_read_and_X,AS_USER}, {SMBwriteX,"SMBwriteX",reply_write_and_X,AS_USER}, {SMBlockingX,"SMBlockingX",reply_lockingX,AS_USER}, @@ -4620,7 +4634,7 @@ struct smb_message_struct /* LANMAN2.0 PROTOCOL FOLLOWS */ {SMBfindnclose, "SMBfindnclose", reply_findnclose, AS_USER}, {SMBfindclose, "SMBfindclose", reply_findclose,AS_USER}, - {SMBtrans2, "SMBtrans2", reply_trans2, AS_USER}, + {SMBtrans2, "SMBtrans2", reply_trans2, AS_USER | QUEUE_IN_OPLOCK }, {SMBtranss2, "SMBtranss2", reply_transs2, AS_USER}, /* messaging routines */ @@ -4702,6 +4716,20 @@ static int switch_message(int type,char *inbuf,char *outbuf,int size,int bufsize 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,("%s: switch_message: queueing message due to being in oplock break state.\n", + timestring() )); + + push_smb_message( inbuf, size); + return -1; + } + if (smb_messages[match].fn) { int cnum = SVAL(inbuf,smb_tid); |