summaryrefslogtreecommitdiff
path: root/source3/smbd/process.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/smbd/process.c')
-rw-r--r--source3/smbd/process.c219
1 files changed, 155 insertions, 64 deletions
diff --git a/source3/smbd/process.c b/source3/smbd/process.c
index ffc9e106f6..4672510d8d 100644
--- a/source3/smbd/process.c
+++ b/source3/smbd/process.c
@@ -20,10 +20,8 @@
#include "includes.h"
-extern struct auth_context *negprot_global_auth_context;
extern int smb_echo_count;
-const int total_buffer_size = (BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN);
static enum smb_read_errors smb_read_error = SMB_READ_OK;
/*
@@ -38,7 +36,6 @@ int max_send = BUFFER_SIZE;
*/
int max_recv = BUFFER_SIZE;
-extern int last_message;
SIG_ATOMIC_T reload_after_sighup = 0;
SIG_ATOMIC_T got_sig_term = 0;
extern bool global_machine_password_needs_changing;
@@ -51,6 +48,73 @@ enum smb_read_errors *get_srv_read_error(void)
return &smb_read_error;
}
+/****************************************************************************
+ Send an smb to a fd.
+****************************************************************************/
+
+bool srv_send_smb(int fd, char *buffer, bool do_encrypt)
+{
+ size_t len;
+ size_t nwritten=0;
+ ssize_t ret;
+ char *buf_out = buffer;
+
+ /* Sign the outgoing packet if required. */
+ srv_calculate_sign_mac(buf_out);
+
+ if (do_encrypt) {
+ NTSTATUS status = srv_encrypt_buffer(buffer, &buf_out);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("send_smb: SMB encryption failed "
+ "on outgoing packet! Error %s\n",
+ nt_errstr(status) ));
+ return false;
+ }
+ }
+
+ len = smb_len(buf_out) + 4;
+
+ while (nwritten < len) {
+ ret = write_data(fd,buf_out+nwritten,len - nwritten);
+ if (ret <= 0) {
+ DEBUG(0,("Error writing %d bytes to client. %d. (%s)\n",
+ (int)len,(int)ret, strerror(errno) ));
+ srv_free_enc_buffer(buf_out);
+ return false;
+ }
+ nwritten += ret;
+ }
+
+ srv_free_enc_buffer(buf_out);
+ return true;
+}
+
+/*******************************************************************
+ Setup the word count and byte count for a smb message.
+********************************************************************/
+
+int srv_set_message(char *buf,
+ int num_words,
+ int num_bytes,
+ bool zero)
+{
+ if (zero && (num_words || num_bytes)) {
+ memset(buf + smb_size,'\0',num_words*2 + num_bytes);
+ }
+ SCVAL(buf,smb_wct,num_words);
+ SSVAL(buf,smb_vwv + num_words*SIZEOFWORD,num_bytes);
+ smb_setlen(buf,(smb_size + num_words*2 + num_bytes - 4));
+ return (smb_size + num_words*2 + num_bytes);
+}
+
+static bool valid_smb_header(const uint8_t *inbuf)
+{
+ if (is_encrypted_packet(inbuf)) {
+ return true;
+ }
+ return (strncmp(smb_base(inbuf),"\377SMB",4) == 0);
+}
+
/* Socket functions for smbd packet processing. */
static bool valid_packet_size(size_t len)
@@ -90,16 +154,8 @@ static ssize_t read_packet_remainder(int fd,
return len;
}
- if (timeout > 0) {
- ret = read_socket_with_timeout(fd,
- buffer,
- len,
- len,
- timeout,
- get_srv_read_error());
- } else {
- ret = read_data(fd, buffer, len, get_srv_read_error());
- }
+ ret = read_socket_with_timeout(fd, buffer, len, len, timeout,
+ get_srv_read_error());
if (ret != len) {
cond_set_smb_read_error(get_srv_read_error(),
@@ -126,7 +182,7 @@ static ssize_t read_packet_remainder(int fd,
(2*14) + /* word count (including bcc) */ \
1 /* pad byte */)
-ssize_t receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx,
+static ssize_t receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx,
const char lenbuf[4],
int fd,
char **buffer,
@@ -141,19 +197,10 @@ ssize_t receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx,
memcpy(writeX_header, lenbuf, sizeof(lenbuf));
- if (timeout > 0) {
- ret = read_socket_with_timeout(fd,
- writeX_header + 4,
- STANDARD_WRITE_AND_X_HEADER_SIZE,
- STANDARD_WRITE_AND_X_HEADER_SIZE,
- timeout,
- get_srv_read_error());
- } else {
- ret = read_data(fd,
- writeX_header+4,
- STANDARD_WRITE_AND_X_HEADER_SIZE,
- get_srv_read_error());
- }
+ ret = read_socket_with_timeout(fd, writeX_header + 4,
+ STANDARD_WRITE_AND_X_HEADER_SIZE,
+ STANDARD_WRITE_AND_X_HEADER_SIZE,
+ timeout, get_srv_read_error());
if (ret != STANDARD_WRITE_AND_X_HEADER_SIZE) {
cond_set_smb_read_error(get_srv_read_error(),
@@ -166,7 +213,7 @@ ssize_t receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx,
* valid writeX call.
*/
- if (is_valid_writeX_buffer(writeX_header)) {
+ if (is_valid_writeX_buffer((uint8_t *)writeX_header)) {
/*
* If the data offset is beyond what
* we've read, drain the extra bytes.
@@ -274,7 +321,7 @@ static ssize_t receive_smb_raw_talloc(TALLOC_CTX *mem_ctx,
return -1;
}
- if (CVAL(lenbuf,0) != SMBkeepalive &&
+ if (CVAL(lenbuf,0) == 0 &&
min_recv_size &&
smb_len_large(lenbuf) > min_recv_size && /* Could be a UNIX large writeX. */
!srv_is_signing_active()) {
@@ -314,17 +361,36 @@ static ssize_t receive_smb_raw_talloc(TALLOC_CTX *mem_ctx,
return len + 4;
}
-ssize_t receive_smb_talloc(TALLOC_CTX *mem_ctx, int fd, char **buffer,
- unsigned int timeout, size_t *p_unread)
+static ssize_t receive_smb_talloc(TALLOC_CTX *mem_ctx,
+ int fd,
+ char **buffer,
+ unsigned int timeout,
+ size_t *p_unread,
+ bool *p_encrypted)
{
ssize_t len;
+ *p_encrypted = false;
+
len = receive_smb_raw_talloc(mem_ctx, fd, buffer, timeout, p_unread);
if (len < 0) {
return -1;
}
+ if (is_encrypted_packet((uint8_t *)*buffer)) {
+ NTSTATUS status = srv_decrypt_buffer(*buffer);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("receive_smb_talloc: SMB decryption failed on "
+ "incoming packet! Error %s\n",
+ nt_errstr(status) ));
+ cond_set_smb_read_error(get_srv_read_error(),
+ SMB_READ_BAD_DECRYPT);
+ return -1;
+ }
+ *p_encrypted = true;
+ }
+
/* Check the incoming SMB signature. */
if (!srv_check_sign_mac(*buffer, true)) {
DEBUG(0, ("receive_smb: SMB Signature verification failed on "
@@ -342,7 +408,8 @@ ssize_t receive_smb_talloc(TALLOC_CTX *mem_ctx, int fd, char **buffer,
void init_smb_request(struct smb_request *req,
const uint8 *inbuf,
- size_t unread_bytes)
+ size_t unread_bytes,
+ bool encrypted)
{
size_t req_size = smb_len(inbuf) + 4;
/* Ensure we have at least smb_size bytes. */
@@ -358,6 +425,8 @@ void init_smb_request(struct smb_request *req,
req->tid = SVAL(inbuf, smb_tid);
req->wct = CVAL(inbuf, smb_wct);
req->unread_bytes = unread_bytes;
+ req->encrypted = encrypted;
+ req->conn = conn_find(req->tid);
/* Ensure we have at least wct words and 2 bytes of bcc. */
if (smb_size + req->wct*2 > req_size) {
@@ -415,6 +484,7 @@ static bool push_queued_message(struct smb_request *req,
msg->request_time = request_time;
msg->end_time = end_time;
+ msg->encrypted = req->encrypted;
if (private_data) {
msg->private_data = data_blob_talloc(msg, private_data,
@@ -690,7 +760,8 @@ static bool receive_message_or_smb(TALLOC_CTX *mem_ctx,
char **buffer,
size_t *buffer_len,
int timeout,
- size_t *p_unread)
+ size_t *p_unread,
+ bool *p_encrypted)
{
fd_set r_fds, w_fds;
int selrtn;
@@ -757,6 +828,7 @@ static bool receive_message_or_smb(TALLOC_CTX *mem_ctx,
return False;
}
*buffer_len = msg->buf.length;
+ *p_encrypted = msg->encrypted;
/* We leave this message on the queue so the open code can
know this is a retry. */
@@ -873,7 +945,8 @@ static bool receive_message_or_smb(TALLOC_CTX *mem_ctx,
goto again;
}
- len = receive_smb_talloc(mem_ctx, smbd_server_fd(), buffer, 0, p_unread);
+ len = receive_smb_talloc(mem_ctx, smbd_server_fd(),
+ buffer, 0, p_unread, p_encrypted);
if (len == -1) {
return False;
@@ -953,7 +1026,7 @@ force write permissions on print services.
*/
static const struct smb_message_struct {
const char *name;
- void (*fn_new)(connection_struct *conn, struct smb_request *req);
+ void (*fn_new)(struct smb_request *req);
int flags;
} smb_messages[256] = {
@@ -1240,7 +1313,7 @@ void reply_outbuf(struct smb_request *req, uint8 num_words, uint32 num_bytes)
}
construct_reply_common((char *)req->inbuf, (char *)req->outbuf);
- set_message((char *)req->outbuf, num_words, num_bytes, False);
+ srv_set_message((char *)req->outbuf, num_words, num_bytes, false);
/*
* Zero out the word area, the caller has to take care of the bcc area
* himself
@@ -1298,21 +1371,19 @@ static void smb_dump(const char *name, int type, const char *data, ssize_t len)
find.
****************************************************************************/
-static void switch_message(uint8 type, struct smb_request *req, int size)
+static connection_struct *switch_message(uint8 type, struct smb_request *req, int size)
{
int flags;
uint16 session_tag;
- connection_struct *conn;
+ connection_struct *conn = NULL;
static uint16 last_session_tag = UID_FIELD_INVALID;
errno = 0;
- last_message = type;
-
/* Make sure this is an SMB packet. smb_size contains NetBIOS header
* so subtract 4 from it. */
- if ((strncmp(smb_base(req->inbuf),"\377SMB",4) != 0)
+ if (!valid_smb_header(req->inbuf)
|| (size < (smb_size - 4))) {
DEBUG(2,("Non-SMB packet of length %d. Terminating server\n",
smb_len(req->inbuf)));
@@ -1323,7 +1394,7 @@ static void switch_message(uint8 type, struct smb_request *req, int size)
DEBUG(0,("Unknown message type %d!\n",type));
smb_dump("Unknown", 1, (char *)req->inbuf, size);
reply_unknown_new(req, type);
- return;
+ return NULL;
}
flags = smb_messages[type].flags;
@@ -1331,7 +1402,7 @@ static void switch_message(uint8 type, struct smb_request *req, int size)
/* In share mode security we must ignore the vuid. */
session_tag = (lp_security() == SEC_SHARE)
? UID_FIELD_INVALID : req->vuid;
- conn = conn_find(req->tid);
+ conn = req->conn;
DEBUG(3,("switch message %s (pid %d) conn 0x%lx\n", smb_fn_name(type),
(int)sys_getpid(), (unsigned long)conn));
@@ -1376,12 +1447,12 @@ static void switch_message(uint8 type, struct smb_request *req, int size)
} else {
reply_doserror(req, ERRSRV, ERRinvnid);
}
- return;
+ return NULL;
}
if (!change_to_user(conn,session_tag)) {
reply_nterror(req, NT_STATUS_DOS(ERRSRV, ERRbaduid));
- return;
+ return conn;
}
/* All NEED_WRITE and CAN_IPC flags must also have AS_USER. */
@@ -1389,13 +1460,13 @@ static void switch_message(uint8 type, struct smb_request *req, int size)
/* Does it need write permission? */
if ((flags & NEED_WRITE) && !CAN_WRITE(conn)) {
reply_nterror(req, NT_STATUS_MEDIA_WRITE_PROTECTED);
- return;
+ return conn;
}
/* IPC services are limited */
if (IS_IPC(conn) && !(flags & CAN_IPC)) {
reply_doserror(req, ERRSRV,ERRaccess);
- return;
+ return conn;
}
} else {
/* This call needs to be run as root */
@@ -1404,11 +1475,24 @@ static void switch_message(uint8 type, struct smb_request *req, int size)
/* load service specific parameters */
if (conn) {
+ if (req->encrypted) {
+ conn->encrypted_tid = true;
+ /* encrypted required from now on. */
+ conn->encrypt_level = Required;
+ } else if (ENCRYPTION_REQUIRED(conn)) {
+ uint8 com = CVAL(req->inbuf,smb_com);
+ if (com != SMBtrans2 && com != SMBtranss2) {
+ exit_server_cleanly("encryption required "
+ "on connection");
+ return conn;
+ }
+ }
+
if (!set_current_service(conn,SVAL(req->inbuf,smb_flg),
(flags & (AS_USER|DO_CHDIR)
?True:False))) {
reply_doserror(req, ERRSRV, ERRaccess);
- return;
+ return conn;
}
conn->num_smb_operations++;
}
@@ -1419,19 +1503,21 @@ static void switch_message(uint8 type, struct smb_request *req, int size)
!check_access(smbd_server_fd(), lp_hostsallow(-1),
lp_hostsdeny(-1)))) {
reply_doserror(req, ERRSRV, ERRaccess);
- return;
+ return conn;
}
- smb_messages[type].fn_new(conn, req);
+ smb_messages[type].fn_new(req);
+ return req->conn;
}
/****************************************************************************
Construct a reply to the incoming packet.
****************************************************************************/
-static void construct_reply(char *inbuf, int size, size_t unread_bytes)
+static void construct_reply(char *inbuf, int size, size_t unread_bytes, bool encrypted)
{
uint8 type = CVAL(inbuf,smb_com);
+ connection_struct *conn;
struct smb_request *req;
chain_size = 0;
@@ -1441,9 +1527,9 @@ static void construct_reply(char *inbuf, int size, size_t unread_bytes)
if (!(req = talloc(talloc_tos(), struct smb_request))) {
smb_panic("could not allocate smb_request");
}
- init_smb_request(req, (uint8 *)inbuf, unread_bytes);
+ init_smb_request(req, (uint8 *)inbuf, unread_bytes, encrypted);
- switch_message(type, req, size);
+ conn = switch_message(type, req, size);
if (req->unread_bytes) {
/* writeX failed. drain socket. */
@@ -1462,8 +1548,10 @@ static void construct_reply(char *inbuf, int size, size_t unread_bytes)
show_msg((char *)req->outbuf);
}
- if (!send_smb(smbd_server_fd(), (char *)req->outbuf)) {
- exit_server_cleanly("construct_reply: send_smb failed.");
+ if (!srv_send_smb(smbd_server_fd(),
+ (char *)req->outbuf,
+ IS_CONN_ENCRYPTED(conn)||req->encrypted)) {
+ exit_server_cleanly("construct_reply: srv_send_smb failed.");
}
TALLOC_FREE(req);
@@ -1475,7 +1563,7 @@ static void construct_reply(char *inbuf, int size, size_t unread_bytes)
Process an smb from the client
****************************************************************************/
-static void process_smb(char *inbuf, size_t nread, size_t unread_bytes)
+static void process_smb(char *inbuf, size_t nread, size_t unread_bytes, bool encrypted)
{
static int trans_num;
int msg_type = CVAL(inbuf,0);
@@ -1496,7 +1584,7 @@ static void process_smb(char *inbuf, size_t nread, size_t unread_bytes)
static unsigned char buf[5] = {0x83, 0, 0, 1, 0x81};
DEBUG( 1, ( "Connection denied from %s\n",
client_addr(get_client_fd(),addr,sizeof(addr)) ) );
- (void)send_smb(smbd_server_fd(),(char *)buf);
+ (void)srv_send_smb(smbd_server_fd(),(char *)buf,false);
exit_server_cleanly("connection denied");
}
}
@@ -1517,7 +1605,7 @@ static void process_smb(char *inbuf, size_t nread, size_t unread_bytes)
show_msg(inbuf);
- construct_reply(inbuf,nread,unread_bytes);
+ construct_reply(inbuf,nread,unread_bytes,encrypted);
trans_num++;
}
@@ -1554,7 +1642,7 @@ void remove_from_common_flags2(uint32 v)
void construct_reply_common(const char *inbuf, char *outbuf)
{
- set_message(outbuf,0,0,False);
+ srv_set_message(outbuf,0,0,false);
SCVAL(outbuf,smb_com,CVAL(inbuf,smb_com));
SIVAL(outbuf,smb_rcls,0);
@@ -1677,7 +1765,7 @@ void chain_reply(struct smb_request *req)
if (!(req2 = talloc(talloc_tos(), struct smb_request))) {
smb_panic("could not allocate smb_request");
}
- init_smb_request(req2, (uint8 *)inbuf2,0);
+ init_smb_request(req2, (uint8 *)inbuf2,0, req->encrypted);
/* process the request */
switch_message(smb_com2, req2, new_size);
@@ -1963,7 +2051,8 @@ void smbd_process(void)
int num_echos;
char *inbuf;
size_t inbuf_len;
- TALLOC_CTX *frame = talloc_stackframe();
+ bool encrypted = false;
+ TALLOC_CTX *frame = talloc_stackframe_pool(8192);
errno = 0;
@@ -1977,8 +2066,10 @@ void smbd_process(void)
run_events(smbd_event_context(), 0, NULL, NULL);
- while (!receive_message_or_smb(NULL, &inbuf, &inbuf_len,
- select_timeout, &unread_bytes)) {
+ while (!receive_message_or_smb(talloc_tos(), &inbuf, &inbuf_len,
+ select_timeout,
+ &unread_bytes,
+ &encrypted)) {
if(!timeout_processing(&select_timeout,
&last_timeout_processing_time))
return;
@@ -1997,7 +2088,7 @@ void smbd_process(void)
*/
num_echos = smb_echo_count;
- process_smb(inbuf, inbuf_len, unread_bytes);
+ process_smb(inbuf, inbuf_len, unread_bytes, encrypted);
TALLOC_FREE(inbuf);