Commit message: In chain_reply, copy the subrequests' error to the main request
CVE: None
Vulnerability description: None

Project: samba
File: process.c

void remove_deferred_open_smb_message(uint16 mid)
{
	struct pending_message_list *pml;

	for (pml = deferred_open_queue; pml; pml = pml->next) {
		if (mid == SVAL(pml->buf.data,smb_mid)) {
			DEBUG(10,("remove_sharing_violation_open_smb_message: "
				  "deleting mid %u len %u\n",
				  (unsigned int)mid,
				  (unsigned int)pml->buf.length ));
			DLIST_REMOVE(deferred_open_queue, pml);
			TALLOC_FREE(pml);
			return;
		}
	}
}

Project: samba
File: process.c

static void smbd_deferred_open_timer(struct event_context *ev,
				     struct timed_event *te,
				     struct timeval _tval,
				     void *private_data)
{
	struct pending_message_list *msg = talloc_get_type(private_data,
					   struct pending_message_list);
	TALLOC_CTX *mem_ctx = talloc_tos();
	uint8_t *inbuf;

	inbuf = (uint8_t *)talloc_memdup(mem_ctx, msg->buf.data,
					 msg->buf.length);
	if (inbuf == NULL) {
		exit_server("smbd_deferred_open_timer: talloc failed\n");
		return;
	}

	/* We leave this message on the queue so the open code can
	   know this is a retry. */
	DEBUG(5,("smbd_deferred_open_timer: trigger mid %u.\n",
		(unsigned int)SVAL(msg->buf.data,smb_mid)));

	process_smb(smbd_server_conn, inbuf,
		    msg->buf.length, 0,
		    msg->seqnum, msg->encrypted, &msg->pcd);
}

Project: samba
File: process.c

void smbd_process(void)
{
	TALLOC_CTX *frame = talloc_stackframe();
	char remaddr[INET6_ADDRSTRLEN];

	if (lp_maxprotocol() == PROTOCOL_SMB2 &&
	    lp_security() != SEC_SHARE) {
		smbd_server_conn->allow_smb2 = true;
	}

	/* Ensure child is set to blocking mode */
	set_blocking(smbd_server_fd(),True);

	set_socket_options(smbd_server_fd(),"SO_KEEPALIVE");
	set_socket_options(smbd_server_fd(), lp_socket_options());

	/* this is needed so that we get decent entries
	   in smbstatus for port 445 connects */
	set_remote_machine_name(get_peer_addr(smbd_server_fd(),
					      remaddr,
					      sizeof(remaddr)),
					      false);
	reload_services(true);

	/*
	 * Before the first packet, check the global hosts allow/ hosts deny
	 * parameters before doing any parsing of packets passed to us by the
	 * client. This prevents attacks on our parsing code from hosts not in
	 * the hosts allow list.
	 */

	if (!check_access(smbd_server_fd(), lp_hostsallow(-1),
			  lp_hostsdeny(-1))) {
		char addr[INET6_ADDRSTRLEN];

		/*
		 * send a negative session response "not listening on calling
		 * name"
		 */
		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)srv_send_smb(smbd_server_fd(),(char *)buf, false,
				   0, false, NULL);
		exit_server_cleanly("connection denied");
	}

	static_init_rpc;

	init_modules();

	smb_perfcount_init();

	if (!init_account_policy()) {
		exit_server("Could not open account policy tdb.\n");
	}

	if (*lp_rootdir()) {
		if (chroot(lp_rootdir()) != 0) {
			DEBUG(0,("Failed to change root to %s\n", lp_rootdir()));
			exit_server("Failed to chroot()");
		}
		if (chdir("/") == -1) {
			DEBUG(0,("Failed to chdir to / on chroot to %s\n", lp_rootdir()));
			exit_server("Failed to chroot()");
		}
		DEBUG(0,("Changed root to %s\n", lp_rootdir()));
	}

	if (!srv_init_signing(smbd_server_conn)) {
		exit_server("Failed to init smb_signing");
	}

	/* Setup oplocks */
	if (!init_oplocks(smbd_messaging_context()))
		exit_server("Failed to init oplocks");

	/* Setup aio signal handler. */
	initialize_async_io_handler();

	/* register our message handlers */
	messaging_register(smbd_messaging_context(), NULL,
			   MSG_SMB_FORCE_TDIS, msg_force_tdis);
	messaging_register(smbd_messaging_context(), NULL,
			   MSG_SMB_RELEASE_IP, msg_release_ip);
	messaging_register(smbd_messaging_context(), NULL,
			   MSG_SMB_CLOSE_FILE, msg_close_file);

	/*
	 * Use the default MSG_DEBUG handler to avoid rebroadcasting
	 * MSGs to all child processes
	 */
	messaging_deregister(smbd_messaging_context(),
			     MSG_DEBUG, NULL);
	messaging_register(smbd_messaging_context(), NULL,
			   MSG_DEBUG, debug_message);

	if ((lp_keepalive() != 0)
	    && !(event_add_idle(smbd_event_context(), NULL,
				timeval_set(lp_keepalive(), 0),
				"keepalive", keepalive_fn,
				NULL))) {
		DEBUG(0, ("Could not add keepalive event\n"));
		exit(1);
	}

	if (!(event_add_idle(smbd_event_context(), NULL,
			     timeval_set(IDLE_CLOSED_TIMEOUT, 0),
			     "deadtime", deadtime_fn, NULL))) {
		DEBUG(0, ("Could not add deadtime event\n"));
		exit(1);
	}

	if (!(event_add_idle(smbd_event_context(), NULL,
			     timeval_set(SMBD_SELECT_TIMEOUT, 0),
			     "housekeeping", housekeeping_fn, NULL))) {
		DEBUG(0, ("Could not add housekeeping event\n"));
		exit(1);
	}

#ifdef CLUSTER_SUPPORT

	if (lp_clustering()) {
		/*
		 * We need to tell ctdb about our client's TCP
		 * connection, so that for failover ctdbd can send
		 * tickle acks, triggering a reconnection by the
		 * client.
		 */

		struct sockaddr_storage srv, clnt;

		if (client_get_tcp_info(&srv, &clnt) == 0) {

			NTSTATUS status;

			status = ctdbd_register_ips(
				messaging_ctdbd_connection(),
				&srv, &clnt, release_ip, NULL);

			if (!NT_STATUS_IS_OK(status)) {
				DEBUG(0, ("ctdbd_register_ips failed: %s\n",
					  nt_errstr(status)));
			}
		} else
		{
			DEBUG(0,("Unable to get tcp info for "
				 "CTDB_CONTROL_TCP_CLIENT: %s\n",
				 strerror(errno)));
		}
	}

#endif

	smbd_server_conn->nbt.got_session = false;

	smbd_server_conn->smb1.negprot.max_recv = MIN(lp_maxxmit(),BUFFER_SIZE);

	smbd_server_conn->smb1.sessions.done_sesssetup = false;
	smbd_server_conn->smb1.sessions.max_send = BUFFER_SIZE;
	smbd_server_conn->smb1.sessions.last_session_tag = UID_FIELD_INVALID;
	/* users from session setup */
	smbd_server_conn->smb1.sessions.session_userlist = NULL;
	/* workgroup from session setup. */
	smbd_server_conn->smb1.sessions.session_workgroup = NULL;
	/* this holds info on user ids that are already validated for this VC */
	smbd_server_conn->smb1.sessions.validated_users = NULL;
	smbd_server_conn->smb1.sessions.next_vuid = VUID_OFFSET;
	smbd_server_conn->smb1.sessions.num_validated_vuids = 0;
#ifdef HAVE_NETGROUP
	smbd_server_conn->smb1.sessions.my_yp_domain = NULL;
#endif

	conn_init(smbd_server_conn);

	smbd_server_conn->smb1.fde = event_add_fd(smbd_event_context(),
						  smbd_server_conn,
						  smbd_server_fd(),
						  EVENT_FD_READ,
						  smbd_server_connection_handler,
						  smbd_server_conn);
	if (!smbd_server_conn->smb1.fde) {
		exit_server("failed to create smbd_server_connection fde");
	}

	TALLOC_FREE(frame);

	while (True) {
		NTSTATUS status;

		frame = talloc_stackframe_pool(8192);

		errno = 0;

		status = smbd_server_connection_loop_once(smbd_server_conn);
		if (!NT_STATUS_EQUAL(status, NT_STATUS_RETRY) &&
		    !NT_STATUS_IS_OK(status)) {
			DEBUG(3, ("smbd_server_connection_loop_once failed: %s,"
				  " exiting\n", nt_errstr(status)));
			break;
		}

		TALLOC_FREE(frame);
	}

	exit_server_cleanly(NULL);
}

Project: samba
File: process.c

static connection_struct *switch_message(uint8 type, struct smb_request *req, int size)
{
	int flags;
	uint16 session_tag;
	connection_struct *conn = NULL;
	struct smbd_server_connection *sconn = smbd_server_conn;

	errno = 0;

	/* Make sure this is an SMB packet. smb_size contains NetBIOS header
	 * so subtract 4 from it. */
	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)));
		exit_server_cleanly("Non-SMB packet");
	}

	if (smb_messages[type].fn == NULL) {
		DEBUG(0,("Unknown message type %d!\n",type));
		smb_dump("Unknown", 1, (char *)req->inbuf, size);
		reply_unknown_new(req, type);
		return NULL;
	}

	flags = smb_messages[type].flags;

	/* In share mode security we must ignore the vuid. */
	session_tag = (lp_security() == SEC_SHARE)
		? UID_FIELD_INVALID : req->vuid;
	conn = req->conn;

	DEBUG(3,("switch message %s (pid %d) conn 0x%lx\n", smb_fn_name(type),
		 (int)sys_getpid(), (unsigned long)conn));

	smb_dump(smb_fn_name(type), 1, (char *)req->inbuf, size);

	/* Ensure this value is replaced in the incoming packet. */
	SSVAL(req->inbuf,smb_uid,session_tag);

	/*
	 * Ensure the correct username is in current_user_info.  This is a
	 * really ugly bugfix for problems with multiple session_setup_and_X's
	 * being done and allowing %U and %G substitutions to work correctly.
	 * There is a reason this code is done here, don't move it unless you
	 * know what you're doing... :-).
	 * JRA.
	 */

	if (session_tag != sconn->smb1.sessions.last_session_tag) {
		user_struct *vuser = NULL;

		sconn->smb1.sessions.last_session_tag = session_tag;
		if(session_tag != UID_FIELD_INVALID) {
			vuser = get_valid_user_struct(sconn, session_tag);
			if (vuser) {
				set_current_user_info(
					vuser->server_info->sanitized_username,
					vuser->server_info->unix_name,
					pdb_get_domain(vuser->server_info
						       ->sam_account));
			}
		}
	}

	/* Does this call need to be run as the connected user? */
	if (flags & AS_USER) {

		/* Does this call need a valid tree connection? */
		if (!conn) {
			/*
			 * Amazingly, the error code depends on the command
			 * (from Samba4).
			 */
			if (type == SMBntcreateX) {
				reply_nterror(req, NT_STATUS_INVALID_HANDLE);
			} else {
				reply_doserror(req, ERRSRV, ERRinvnid);
			}
			return NULL;
		}

		if (!change_to_user(conn,session_tag)) {
			reply_nterror(req, NT_STATUS_DOS(ERRSRV, ERRbaduid));
			remove_deferred_open_smb_message(req->mid);
			return conn;
		}

		/* All NEED_WRITE and CAN_IPC flags must also have AS_USER. */

		/* Does it need write permission? */
		if ((flags & NEED_WRITE) && !CAN_WRITE(conn)) {
			reply_nterror(req, NT_STATUS_MEDIA_WRITE_PROTECTED);
			return conn;
		}

		/* IPC services are limited */
		if (IS_IPC(conn) && !(flags & CAN_IPC)) {
			reply_doserror(req, ERRSRV,ERRaccess);
			return conn;
		}
	} else {
		/* This call needs to be run as root */
		change_to_root_user();
	}

	/* 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)) {
			if (req->cmd != SMBtrans2 && req->cmd != 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 conn;
		}
		conn->num_smb_operations++;
	}

	/* does this protocol need to be run as guest? */
	if ((flags & AS_GUEST)
	    && (!change_to_guest() ||
		!check_access(smbd_server_fd(), lp_hostsallow(-1),
			      lp_hostsdeny(-1)))) {
		reply_doserror(req, ERRSRV, ERRaccess);
		return conn;
	}

	smb_messages[type].fn(req);
	return req->conn;
}

Project: samba
File: process.c

static void release_ip(const char *ip, void *priv)
{
	char addr[INET6_ADDRSTRLEN];
	char *p = addr;

	client_socket_addr(get_client_fd(),addr,sizeof(addr));

	if (strncmp("::ffff:", addr, 7) == 0) {
		p = addr + 7;
	}

	if ((strcmp(p, ip) == 0) || ((p != addr) && strcmp(addr, ip) == 0)) {
		/* we can't afford to do a clean exit - that involves
		   database writes, which would potentially mean we
		   are still running after the failover has finished -
		   we have to get rid of this process ID straight
		   away */
		DEBUG(0,("Got release IP message for our IP %s - exiting immediately\n",
			ip));
		/* note we must exit with non-zero status so the unclean handler gets
		   called in the parent, so that the brl database is tickled */
		_exit(1);
	}
}

Project: samba
File: process.c

static NTSTATUS receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx,
						    const char lenbuf[4],
						    int fd, char **buffer,
						    unsigned int timeout,
						    size_t *p_unread,
						    size_t *len_ret)
{
	/* Size of a WRITEX call (+4 byte len). */
	char writeX_header[4 + STANDARD_WRITE_AND_X_HEADER_SIZE];
	ssize_t len = smb_len_large(lenbuf); /* Could be a UNIX large writeX. */
	ssize_t toread;
	NTSTATUS status;

	memcpy(writeX_header, lenbuf, 4);

	status = read_socket_with_timeout(
		fd, writeX_header + 4,
		STANDARD_WRITE_AND_X_HEADER_SIZE,
		STANDARD_WRITE_AND_X_HEADER_SIZE,
		timeout, NULL);

	if (!NT_STATUS_IS_OK(status)) {
		return status;
	}

	/*
	 * Ok - now try and see if this is a possible
	 * valid writeX call.
	 */

	if (is_valid_writeX_buffer((uint8_t *)writeX_header)) {
		/*
		 * If the data offset is beyond what
		 * we've read, drain the extra bytes.
		 */
		uint16_t doff = SVAL(writeX_header,smb_vwv11);
		ssize_t newlen;

		if (doff > STANDARD_WRITE_AND_X_HEADER_SIZE) {
			size_t drain = doff - STANDARD_WRITE_AND_X_HEADER_SIZE;
			if (drain_socket(smbd_server_fd(), drain) != drain) {
	                        smb_panic("receive_smb_raw_talloc_partial_read:"
					" failed to drain pending bytes");
	                }
		} else {
			doff = STANDARD_WRITE_AND_X_HEADER_SIZE;
		}

		/* Spoof down the length and null out the bcc. */
		set_message_bcc(writeX_header, 0);
		newlen = smb_len(writeX_header);

		/* Copy the header we've written. */

		*buffer = (char *)TALLOC_MEMDUP(mem_ctx,
				writeX_header,
				sizeof(writeX_header));

		if (*buffer == NULL) {
			DEBUG(0, ("Could not allocate inbuf of length %d\n",
				  (int)sizeof(writeX_header)));
			return NT_STATUS_NO_MEMORY;
		}

		/* Work out the remaining bytes. */
		*p_unread = len - STANDARD_WRITE_AND_X_HEADER_SIZE;
		*len_ret = newlen + 4;
		return NT_STATUS_OK;
	}

	if (!valid_packet_size(len)) {
		return NT_STATUS_INVALID_PARAMETER;
	}

	/*
	 * Not a valid writeX call. Just do the standard
	 * talloc and return.
	 */

	*buffer = TALLOC_ARRAY(mem_ctx, char, len+4);

	if (*buffer == NULL) {
		DEBUG(0, ("Could not allocate inbuf of length %d\n",
			  (int)len+4));
		return NT_STATUS_NO_MEMORY;
	}

	/* Copy in what we already read. */
	memcpy(*buffer,
		writeX_header,
		4 + STANDARD_WRITE_AND_X_HEADER_SIZE);
	toread = len - STANDARD_WRITE_AND_X_HEADER_SIZE;

	if(toread > 0) {
		status = read_packet_remainder(
			fd, (*buffer) + 4 + STANDARD_WRITE_AND_X_HEADER_SIZE,
			timeout, toread);

		if (!NT_STATUS_IS_OK(status)) {
			DEBUG(10, ("receive_smb_raw_talloc_partial_read: %s\n",
				   nt_errstr(status)));
			return status;
		}
	}

	*len_ret = len + 4;
	return NT_STATUS_OK;
}

Project: samba
File: process.c

static void construct_reply_common(struct smb_request *req, const char *inbuf,
				   char *outbuf)
{
	srv_set_message(outbuf,0,0,false);
	
	SCVAL(outbuf, smb_com, req->cmd);
	SIVAL(outbuf,smb_rcls,0);
	SCVAL(outbuf,smb_flg, FLAG_REPLY | (CVAL(inbuf,smb_flg) & FLAG_CASELESS_PATHNAMES)); 
	SSVAL(outbuf,smb_flg2,
		(SVAL(inbuf,smb_flg2) & FLAGS2_UNICODE_STRINGS) |
		common_flags2);
	memset(outbuf+smb_pidhigh,'\0',(smb_tid-smb_pidhigh));

	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));
}

Project: samba
File: process.c

void check_reload(time_t t)
{
	time_t printcap_cache_time = (time_t)lp_printcap_cache_time();

	if(last_smb_conf_reload_time == 0) {
		last_smb_conf_reload_time = t;
		/* Our printing subsystem might not be ready at smbd start up.
		   Then no printer is available till the first printers check
		   is performed.  A lower initial interval circumvents this. */
		if ( printcap_cache_time > 60 )
			last_printer_reload_time = t - printcap_cache_time + 60;
		else
			last_printer_reload_time = t;
	}

	if (mypid != getpid()) { /* First time or fork happened meanwhile */
		/* randomize over 60 second the printcap reload to avoid all
		 * process hitting cupsd at the same time */
		int time_range = 60;

		last_printer_reload_time += random() % time_range;
		mypid = getpid();
	}

	if (t >= last_smb_conf_reload_time+SMBD_RELOAD_CHECK) {
		reload_services(True);
		last_smb_conf_reload_time = t;
	}

	/* 'printcap cache time = 0' disable the feature */
	
	if ( printcap_cache_time != 0 )
	{ 
		/* see if it's time to reload or if the clock has been set back */
		
		if ( (t >= last_printer_reload_time+printcap_cache_time) 
			|| (t-last_printer_reload_time  < 0) ) 
		{
			DEBUG( 3,( "Printcap cache time expired.\n"));
			reload_printers();
			last_printer_reload_time = t;
		}
	}
}

Project: samba
File: process.c

static bool push_queued_message(struct smb_request *req,
				struct timeval request_time,
				struct timeval end_time,
				char *private_data, size_t private_len)
{
	int msg_len = smb_len(req->inbuf) + 4;
	struct pending_message_list *msg;

	msg = TALLOC_ZERO_P(NULL, struct pending_message_list);

	if(msg == NULL) {
		DEBUG(0,("push_message: malloc fail (1)\n"));
		return False;
	}

	msg->buf = data_blob_talloc(msg, req->inbuf, msg_len);
	if(msg->buf.data == NULL) {
		DEBUG(0,("push_message: malloc fail (2)\n"));
		TALLOC_FREE(msg);
		return False;
	}

	msg->request_time = request_time;
	msg->seqnum = req->seqnum;
	msg->encrypted = req->encrypted;
	SMB_PERFCOUNT_DEFER_OP(&req->pcd, &msg->pcd);

	if (private_data) {
		msg->private_data = data_blob_talloc(msg, private_data,
						     private_len);
		if (msg->private_data.data == NULL) {
			DEBUG(0,("push_message: malloc fail (3)\n"));
			TALLOC_FREE(msg);
			return False;
		}
	}

	msg->te = event_add_timed(smbd_event_context(),
				  msg,
				  end_time,
				  smbd_deferred_open_timer,
				  msg);
	if (!msg->te) {
		DEBUG(0,("push_message: event_add_timed failed\n"));
		TALLOC_FREE(msg);
		return false;
	}

	DLIST_ADD_END(deferred_open_queue, msg, struct pending_message_list *);

	DEBUG(10,("push_message: pushed message length %u on "
		  "deferred_open_queue\n", (unsigned int)msg_len));

	return True;
}

Project: samba
File: process.c

static void msg_release_ip(struct messaging_context *msg_ctx, void *private_data,
			   uint32_t msg_type, struct server_id server_id, DATA_BLOB *data)
{
	release_ip((char *)data->data, NULL);
}

Project: samba
File: process.c

static NTSTATUS read_packet_remainder(int fd, char *buffer,
				      unsigned int timeout, ssize_t len)
{
	if (len <= 0) {
		return NT_STATUS_OK;
	}

	return read_socket_with_timeout(fd, buffer, len, len, timeout, NULL);
}

Project: samba
File: process.c

static void construct_reply(char *inbuf, int size, size_t unread_bytes,
			    uint32_t seqnum, bool encrypted,
			    struct smb_perfcount_data *deferred_pcd)
{
	connection_struct *conn;
	struct smb_request *req;

	if (!(req = talloc(talloc_tos(), struct smb_request))) {
		smb_panic("could not allocate smb_request");
	}

	init_smb_request(req, (uint8 *)inbuf, unread_bytes, encrypted);
	req->inbuf  = (uint8_t *)talloc_move(req, &inbuf);
	req->seqnum = seqnum;

	/* we popped this message off the queue - keep original perf data */
	if (deferred_pcd)
		req->pcd = *deferred_pcd;
	else {
		SMB_PERFCOUNT_START(&req->pcd);
		SMB_PERFCOUNT_SET_OP(&req->pcd, req->cmd);
		SMB_PERFCOUNT_SET_MSGLEN_IN(&req->pcd, size);
	}

	conn = switch_message(req->cmd, req, size);

	if (req->unread_bytes) {
		/* writeX failed. drain socket. */
		if (drain_socket(smbd_server_fd(), req->unread_bytes) !=
				req->unread_bytes) {
			smb_panic("failed to drain pending bytes");
		}
		req->unread_bytes = 0;
	}

	if (req->outbuf == NULL) {
		return;
	}

	if (CVAL(req->outbuf,0) == 0) {
		show_msg((char *)req->outbuf);
	}

	if (!srv_send_smb(smbd_server_fd(),
			(char *)req->outbuf,
			true, req->seqnum+1,
			IS_CONN_ENCRYPTED(conn)||req->encrypted,
			&req->pcd)) {
		exit_server_cleanly("construct_reply: srv_send_smb failed.");
	}

	TALLOC_FREE(req);

	return;
}

Project: samba
File: process.c

bool open_was_deferred(uint16 mid)
{
	struct pending_message_list *pml;

	for (pml = deferred_open_queue; pml; pml = pml->next) {
		if (SVAL(pml->buf.data,smb_mid) == mid) {
			return True;
		}
	}
	return False;
}

Project: samba
File: process.c

static int client_get_tcp_info(struct sockaddr_storage *server,
			       struct sockaddr_storage *client)
{
	socklen_t length;
	if (server_fd == -1) {
		return -1;
	}
	length = sizeof(*server);
	if (getsockname(server_fd, (struct sockaddr *)server, &length) != 0) {
		return -1;
	}
	length = sizeof(*client);
	if (getpeername(server_fd, (struct sockaddr *)client, &length) != 0) {
		return -1;
	}
	return 0;
}

Project: samba
File: process.c

void schedule_deferred_open_smb_message(uint16 mid)
{
	struct pending_message_list *pml;
	int i = 0;

	for (pml = deferred_open_queue; pml; pml = pml->next) {
		uint16 msg_mid = SVAL(pml->buf.data,smb_mid);

		DEBUG(10,("schedule_deferred_open_smb_message: [%d] msg_mid = %u\n", i++,
			(unsigned int)msg_mid ));

		if (mid == msg_mid) {
			struct timed_event *te;

			DEBUG(10,("schedule_deferred_open_smb_message: scheduling mid %u\n",
				mid ));

			te = event_add_timed(smbd_event_context(),
					     pml,
					     timeval_zero(),
					     smbd_deferred_open_timer,
					     pml);
			if (!te) {
				DEBUG(10,("schedule_deferred_open_smb_message: "
					  "event_add_timed() failed, skipping mid %u\n",
					  mid ));
			}

			TALLOC_FREE(pml->te);
			pml->te = te;
			DLIST_PROMOTE(deferred_open_queue, pml);
			return;
		}
	}

	DEBUG(10,("schedule_deferred_open_smb_message: failed to find message mid %u\n",
		mid ));
}

Project: samba
File: process.c

void init_smb_request(struct smb_request *req,
			const uint8 *inbuf,
			size_t unread_bytes,
			bool encrypted)
{
	struct smbd_server_connection *sconn = smbd_server_conn;
	size_t req_size = smb_len(inbuf) + 4;
	/* Ensure we have at least smb_size bytes. */
	if (req_size < smb_size) {
		DEBUG(0,("init_smb_request: invalid request size %u\n",
			(unsigned int)req_size ));
		exit_server_cleanly("Invalid SMB request");
	}
	req->cmd    = CVAL(inbuf, smb_com);
	req->flags2 = SVAL(inbuf, smb_flg2);
	req->smbpid = SVAL(inbuf, smb_pid);
	req->mid    = SVAL(inbuf, smb_mid);
	req->seqnum = 0;
	req->vuid   = SVAL(inbuf, smb_uid);
	req->tid    = SVAL(inbuf, smb_tid);
	req->wct    = CVAL(inbuf, smb_wct);
	req->vwv    = (uint16_t *)(inbuf+smb_vwv);
	req->buflen = smb_buflen(inbuf);
	req->buf    = (const uint8_t *)smb_buf(inbuf);
	req->unread_bytes = unread_bytes;
	req->encrypted = encrypted;
	req->conn = conn_find(sconn,req->tid);
	req->chain_fsp = NULL;
	req->chain_outbuf = NULL;
	smb_init_perfcount_data(&req->pcd);

	/* Ensure we have at least wct words and 2 bytes of bcc. */
	if (smb_size + req->wct*2 > req_size) {
		DEBUG(0,("init_smb_request: invalid wct number %u (size %u)\n",
			(unsigned int)req->wct,
			(unsigned int)req_size));
		exit_server_cleanly("Invalid SMB request");
	}
	/* Ensure bcc is correct. */
	if (((uint8 *)smb_buf(inbuf)) + req->buflen > inbuf + req_size) {
		DEBUG(0,("init_smb_request: invalid bcc number %u "
			"(wct = %u, size %u)\n",
			(unsigned int)req->buflen,
			(unsigned int)req->wct,
			(unsigned int)req_size));
		exit_server_cleanly("Invalid SMB request");
	}

	req->outbuf = NULL;
}

Project: samba
File: process.c

static void smbd_server_connection_write_handler(struct smbd_server_connection *conn)
{
	/* TODO: make write nonblocking */
}

Project: samba
File: process.c

static void smbd_server_connection_read_handler(struct smbd_server_connection *conn)
{
	uint8_t *inbuf = NULL;
	size_t inbuf_len = 0;
	size_t unread_bytes = 0;
	bool encrypted = false;
	TALLOC_CTX *mem_ctx = talloc_tos();
	NTSTATUS status;
	uint32_t seqnum;

	/* TODO: make this completely nonblocking */

	status = receive_smb_talloc(mem_ctx, smbd_server_fd(),
				    (char **)(void *)&inbuf,
				    0, /* timeout */
				    &unread_bytes,
				    &encrypted,
				    &inbuf_len, &seqnum);
	if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
		goto process;
	}
	if (NT_STATUS_IS_ERR(status)) {
		exit_server_cleanly("failed to receive smb request");
	}
	if (!NT_STATUS_IS_OK(status)) {
		return;
	}

process:
	process_smb(conn, inbuf, inbuf_len, unread_bytes,
		    seqnum, encrypted, NULL);
}

Project: samba
File: process.c

static void smbd_server_connection_handler(struct event_context *ev,
					   struct fd_event *fde,
					   uint16_t flags,
					   void *private_data)
{
	struct smbd_server_connection *conn = talloc_get_type(private_data,
					      struct smbd_server_connection);

	if (flags & EVENT_FD_WRITE) {
		smbd_server_connection_write_handler(conn);
	} else if (flags & EVENT_FD_READ) {
		smbd_server_connection_read_handler(conn);
	}
}

Project: samba
File: process.c

void chain_reply(struct smb_request *req)
{
	size_t smblen = smb_len(req->inbuf);
	size_t already_used, length_needed;
	uint8_t chain_cmd;
	uint32_t chain_offset;	/* uint32_t to avoid overflow */

	uint8_t wct;
	uint16_t *vwv;
	uint16_t buflen;
	uint8_t *buf;

	if (IVAL(req->outbuf, smb_rcls) != 0) {
		fixup_chain_error_packet(req);
	}

	/*
	 * Any of the AndX requests and replies have at least a wct of
	 * 2. vwv[0] is the next command, vwv[1] is the offset from the
	 * beginning of the SMB header to the next wct field.
	 *
	 * None of the AndX requests put anything valuable in vwv[0] and [1],
	 * so we can overwrite it here to form the chain.
	 */

	if ((req->wct < 2) || (CVAL(req->outbuf, smb_wct) < 2)) {
		goto error;
	}

	/*
	 * Here we assume that this is the end of the chain. For that we need
	 * to set "next command" to 0xff and the offset to 0. If we later find
	 * more commands in the chain, this will be overwritten again.
	 */

	SCVAL(req->outbuf, smb_vwv0, 0xff);
	SCVAL(req->outbuf, smb_vwv0+1, 0);
	SSVAL(req->outbuf, smb_vwv1, 0);

	if (req->chain_outbuf == NULL) {
		/*
		 * In req->chain_outbuf we collect all the replies. Start the
		 * chain by copying in the first reply.
		 *
		 * We do the realloc because later on we depend on
		 * talloc_get_size to determine the length of
		 * chain_outbuf. The reply_xxx routines might have
		 * over-allocated (reply_pipe_read_and_X used to be such an
		 * example).
		 */
		req->chain_outbuf = TALLOC_REALLOC_ARRAY(
			req, req->outbuf, uint8_t, smb_len(req->outbuf) + 4);
		if (req->chain_outbuf == NULL) {
			goto error;
		}
		req->outbuf = NULL;
	} else {
		/*
		 * Update smb headers where subsequent chained commands
		 * may have updated them.
		 */
		SCVAL(req->chain_outbuf, smb_tid, CVAL(req->outbuf, smb_tid));
		SCVAL(req->chain_outbuf, smb_uid, CVAL(req->outbuf, smb_uid));

		if (!smb_splice_chain(&req->chain_outbuf,
				      CVAL(req->outbuf, smb_com),
				      CVAL(req->outbuf, smb_wct),
				      (uint16_t *)(req->outbuf + smb_vwv),
				      0, smb_buflen(req->outbuf),
				      (uint8_t *)smb_buf(req->outbuf))) {
			goto error;
		}
		TALLOC_FREE(req->outbuf);
	}

	/*
	 * We use the old request's vwv field to grab the next chained command
	 * and offset into the chained fields.
	 */

	chain_cmd = CVAL(req->vwv+0, 0);
	chain_offset = SVAL(req->vwv+1, 0);

	if (chain_cmd == 0xff) {
		/*
		 * End of chain, no more requests from the client. So ship the
		 * replies.
		 */
		smb_setlen((char *)(req->chain_outbuf),
			   talloc_get_size(req->chain_outbuf) - 4);

		if (!srv_send_smb(smbd_server_fd(), (char *)req->chain_outbuf,
				  true, req->seqnum+1,
				  IS_CONN_ENCRYPTED(req->conn)
				  ||req->encrypted,
				  &req->pcd)) {
			exit_server_cleanly("chain_reply: srv_send_smb "
					    "failed.");
		}
		TALLOC_FREE(req);

		return;
	}

	/* add a new perfcounter for this element of chain */
	SMB_PERFCOUNT_ADD(&req->pcd);
	SMB_PERFCOUNT_SET_OP(&req->pcd, chain_cmd);
	SMB_PERFCOUNT_SET_MSGLEN_IN(&req->pcd, smblen);

	/*
	 * Check if the client tries to fool us. The request so far uses the
	 * space to the end of the byte buffer in the request just
	 * processed. The chain_offset can't point into that area. If that was
	 * the case, we could end up with an endless processing of the chain,
	 * we would always handle the same request.
	 */

	already_used = PTR_DIFF(req->buf+req->buflen, smb_base(req->inbuf));
	if (chain_offset < already_used) {
		goto error;
	}

	/*
	 * Next check: Make sure the chain offset does not point beyond the
	 * overall smb request length.
	 */

	length_needed = chain_offset+1;	/* wct */
	if (length_needed > smblen) {
		goto error;
	}

	/*
	 * Now comes the pointer magic. Goal here is to set up req->vwv and
	 * req->buf correctly again to be able to call the subsequent
	 * switch_message(). The chain offset (the former vwv[1]) points at
	 * the new wct field.
	 */

	wct = CVAL(smb_base(req->inbuf), chain_offset);

	/*
	 * Next consistency check: Make the new vwv array fits in the overall
	 * smb request.
	 */

	length_needed += (wct+1)*sizeof(uint16_t); /* vwv+buflen */
	if (length_needed > smblen) {
		goto error;
	}
	vwv = (uint16_t *)(smb_base(req->inbuf) + chain_offset + 1);

	/*
	 * Now grab the new byte buffer....
	 */

	buflen = SVAL(vwv+wct, 0);

	/*
	 * .. and check that it fits.
	 */

	length_needed += buflen;
	if (length_needed > smblen) {
		goto error;
	}
	buf = (uint8_t *)(vwv+wct+1);

	req->cmd = chain_cmd;
	req->wct = wct;
	req->vwv = vwv;
	req->buflen = buflen;
	req->buf = buf;

	switch_message(chain_cmd, req, smblen);

	if (req->outbuf == NULL) {
		/*
		 * This happens if the chained command has suspended itself or
		 * if it has called srv_send_smb() itself.
		 */
		return;
	}

	/*
	 * We end up here if the chained command was not itself chained or
	 * suspended, but for example a close() command. We now need to splice
	 * the chained commands' outbuf into the already built up chain_outbuf
	 * and ship the result.
	 */
	goto done;

 error:
	/*
	 * We end up here if there's any error in the chain syntax. Report a
	 * DOS error, just like Windows does.
	 */
	reply_nterror(req, NT_STATUS_DOS(ERRSRV, ERRerror));
	fixup_chain_error_packet(req);

 done:
	/*
	 * This scary statement intends to set the
	 * FLAGS2_32_BIT_ERROR_CODES flg2 field in req->chain_outbuf
	 * to the value req->outbuf carries
	 */
	SSVAL(req->chain_outbuf, smb_flg2,
	      (SVAL(req->chain_outbuf, smb_flg2) & ~FLAGS2_32_BIT_ERROR_CODES)
	      | (SVAL(req->outbuf, smb_flg2) & FLAGS2_32_BIT_ERROR_CODES));

	/*
	 * Transfer the error codes from the subrequest to the main one
	 */
	SSVAL(req->chain_outbuf, smb_rcls, SVAL(req->outbuf, smb_rcls));
	SSVAL(req->chain_outbuf, smb_err, SVAL(req->outbuf, smb_err));

	if (!smb_splice_chain(&req->chain_outbuf,
			      CVAL(req->outbuf, smb_com),
			      CVAL(req->outbuf, smb_wct),
			      (uint16_t *)(req->outbuf + smb_vwv),
			      0, smb_buflen(req->outbuf),
			      (uint8_t *)smb_buf(req->outbuf))) {
		exit_server_cleanly("chain_reply: smb_splice_chain failed\n");
	}
	TALLOC_FREE(req->outbuf);

	smb_setlen((char *)(req->chain_outbuf),
		   talloc_get_size(req->chain_outbuf) - 4);

	show_msg((char *)(req->chain_outbuf));

	if (!srv_send_smb(smbd_server_fd(), (char *)req->chain_outbuf,
			  true, req->seqnum+1,
			  IS_CONN_ENCRYPTED(req->conn)||req->encrypted,
			  &req->pcd)) {
		exit_server_cleanly("construct_reply: srv_send_smb failed.");
	}
	TALLOC_FREE(req);
}