Commit message: CVE-2021-44142: smbd: add Netatalk xattr used by vfs_fruit to the list of private Samba xattrs

This is an internal xattr that should not be user visible.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=14914

Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
CVE: None
CWE: CWE-787
Vulnerability description: None

Project: samba
File: trans2.c

NTSTATUS set_ea(connection_struct *conn, files_struct *fsp,
		struct ea_list *ea_list)
{
	NTSTATUS status;
	bool posix_pathnames = false;

	if (!lp_ea_support(SNUM(conn))) {
		return NT_STATUS_EAS_NOT_SUPPORTED;
	}

	if (fsp == NULL) {
		return NT_STATUS_INVALID_HANDLE;
	}

	posix_pathnames = (fsp->fsp_name->flags & SMB_FILENAME_POSIX_PATH);

	status = refuse_symlink_fsp(fsp);
	if (!NT_STATUS_IS_OK(status)) {
		return status;
	}

	status = check_access_fsp(fsp, FILE_WRITE_EA);
	if (!NT_STATUS_IS_OK(status)) {
		return status;
	}

	/* Setting EAs on streams isn't supported. */
	if (is_ntfs_stream_smb_fname(fsp->fsp_name)) {
		return NT_STATUS_INVALID_PARAMETER;
	}

	/*
	 * Filter out invalid Windows EA names - before
	 * we set *any* of them.
	 */

	if (!posix_pathnames && ea_list_has_invalid_name(ea_list)) {
		return STATUS_INVALID_EA_NAME;
	}

	for (;ea_list; ea_list = ea_list->next) {
		int ret;
		fstring unix_ea_name;

		fstrcpy(unix_ea_name, "user."); /* All EA's must start with user. */
		fstrcat(unix_ea_name, ea_list->ea.name);

		canonicalize_ea_name(fsp, unix_ea_name);

		DEBUG(10,("set_ea: ea_name %s ealen = %u\n", unix_ea_name, (unsigned int)ea_list->ea.value.length));

		if (samba_private_attr_name(unix_ea_name)) {
			DEBUG(10,("set_ea: ea name %s is a private Samba name.\n", unix_ea_name));
			return NT_STATUS_ACCESS_DENIED;
		}

		if (ea_list->ea.value.length == 0) {
			/* Remove the attribute. */
			DBG_DEBUG("deleting ea name %s on "
				  "file %s by file descriptor.\n",
				  unix_ea_name, fsp_str_dbg(fsp));
			ret = SMB_VFS_FREMOVEXATTR(fsp, unix_ea_name);
#ifdef ENOATTR
			/* Removing a non existent attribute always succeeds. */
			if (ret == -1 && errno == ENOATTR) {
				DEBUG(10,("set_ea: deleting ea name %s didn't exist - succeeding by default.\n",
						unix_ea_name));
				ret = 0;
			}
#endif
		} else {
			DEBUG(10,("set_ea: setting ea name %s on file "
				  "%s by file descriptor.\n",
				  unix_ea_name, fsp_str_dbg(fsp)));
			ret = SMB_VFS_FSETXATTR(fsp, unix_ea_name,
						ea_list->ea.value.data, ea_list->ea.value.length, 0);
		}

		if (ret == -1) {
#ifdef ENOTSUP
			if (errno == ENOTSUP) {
				return NT_STATUS_EAS_NOT_SUPPORTED;
			}
#endif
			return map_nt_error_from_unix(errno);
		}

	}
	return NT_STATUS_OK;
}

Project: samba
File: trans2.c

static NTSTATUS smb_set_file_unix_hlink(connection_struct *conn,
					struct smb_request *req,
					const char *pdata, int total_data,
					struct smb_filename *smb_fname_new)
{
	char *oldname = NULL;
	struct smb_filename *smb_fname_old = NULL;
	uint32_t ucf_flags = ucf_flags_from_smb_request(req);
	TALLOC_CTX *ctx = talloc_tos();
	NTSTATUS status = NT_STATUS_OK;

	/* Set a hard link. */
	if (total_data == 0) {
		return NT_STATUS_INVALID_PARAMETER;
	}

	if (req->posix_pathnames) {
		srvstr_get_path_posix(ctx,
			pdata,
			req->flags2,
			&oldname,
			pdata,
			total_data,
			STR_TERMINATE,
			&status);
	} else {
		srvstr_get_path(ctx,
			pdata,
			req->flags2,
			&oldname,
			pdata,
			total_data,
			STR_TERMINATE,
			&status);
	}
	if (!NT_STATUS_IS_OK(status)) {
		return status;
	}

	DEBUG(10,("smb_set_file_unix_hlink: SMB_SET_FILE_UNIX_LINK doing hard link %s -> %s\n",
		smb_fname_str_dbg(smb_fname_new), oldname));

	status = filename_convert(ctx,
				conn,
				oldname,
				ucf_flags,
				0,
				&smb_fname_old);
	if (!NT_STATUS_IS_OK(status)) {
		return status;
	}

	return hardlink_internals(ctx, conn, req, false,
			smb_fname_old, smb_fname_new);
}

Project: samba
File: trans2.c

void send_trans2_replies(connection_struct *conn,
			struct smb_request *req,
			NTSTATUS status,
			 const char *params,
			 int paramsize,
			 const char *pdata,
			 int datasize,
			 int max_data_bytes)
{
	/* As we are using a protocol > LANMAN1 then the max_send
	 variable must have been set in the sessetupX call.
	 This takes precedence over the max_xmit field in the
	 global struct. These different max_xmit variables should
	 be merged as this is now too confusing */

	int data_to_send = datasize;
	int params_to_send = paramsize;
	int useable_space;
	const char *pp = params;
	const char *pd = pdata;
	int params_sent_thistime, data_sent_thistime, total_sent_thistime;
	int alignment_offset = 1; /* JRA. This used to be 3. Set to 1 to make netmon parse ok. */
	int data_alignment_offset = 0;
	bool overflow = False;
	struct smbXsrv_connection *xconn = req->xconn;
	int max_send = xconn->smb1.sessions.max_send;

	/* Modify the data_to_send and datasize and set the error if
	   we're trying to send more than max_data_bytes. We still send
	   the part of the packet(s) that fit. Strange, but needed
	   for OS/2. */

	if (max_data_bytes > 0 && datasize > max_data_bytes) {
		DEBUG(5,("send_trans2_replies: max_data_bytes %d exceeded by data %d\n",
			max_data_bytes, datasize ));
		datasize = data_to_send = max_data_bytes;
		overflow = True;
	}

	/* If there genuinely are no parameters or data to send just send the empty packet */

	if(params_to_send == 0 && data_to_send == 0) {
		reply_outbuf(req, 10, 0);
		if (NT_STATUS_V(status)) {
			uint8_t eclass;
			uint32_t ecode;
			ntstatus_to_dos(status, &eclass, &ecode);
			error_packet_set((char *)req->outbuf,
					eclass, ecode, status,
					__LINE__,__FILE__);
		}
		show_msg((char *)req->outbuf);
		if (!srv_send_smb(xconn,
				(char *)req->outbuf,
				true, req->seqnum+1,
				IS_CONN_ENCRYPTED(conn),
				&req->pcd)) {
			exit_server_cleanly("send_trans2_replies: srv_send_smb failed.");
		}
		TALLOC_FREE(req->outbuf);
		return;
	}

	/* When sending params and data ensure that both are nicely aligned */
	/* Only do this alignment when there is also data to send - else
		can cause NT redirector problems. */

	if (((params_to_send % 4) != 0) && (data_to_send != 0))
		data_alignment_offset = 4 - (params_to_send % 4);

	/* Space is bufsize minus Netbios over TCP header minus SMB header */
	/* The alignment_offset is to align the param bytes on an even byte
		boundary. NT 4.0 Beta needs this to work correctly. */

	useable_space = max_send - (smb_size
				    + 2 * 10 /* wct */
				    + alignment_offset
				    + data_alignment_offset);

	if (useable_space < 0) {
		DEBUG(0, ("send_trans2_replies failed sanity useable_space "
			  "= %d!!!", useable_space));
		exit_server_cleanly("send_trans2_replies: Not enough space");
	}

	while (params_to_send || data_to_send) {
		/* Calculate whether we will totally or partially fill this packet */

		total_sent_thistime = params_to_send + data_to_send;

		/* We can never send more than useable_space */
		/*
		 * Note that 'useable_space' does not include the alignment offsets,
		 * but we must include the alignment offsets in the calculation of
		 * the length of the data we send over the wire, as the alignment offsets
		 * are sent here. Fix from Marc_Jacobsen@hp.com.
		 */

		total_sent_thistime = MIN(total_sent_thistime, useable_space);

		reply_outbuf(req, 10, total_sent_thistime + alignment_offset
			     + data_alignment_offset);

		/* Set total params and data to be sent */
		SSVAL(req->outbuf,smb_tprcnt,paramsize);
		SSVAL(req->outbuf,smb_tdrcnt,datasize);

		/* Calculate how many parameters and data we can fit into
		 * this packet. Parameters get precedence
		 */

		params_sent_thistime = MIN(params_to_send,useable_space);
		data_sent_thistime = useable_space - params_sent_thistime;
		data_sent_thistime = MIN(data_sent_thistime,data_to_send);

		SSVAL(req->outbuf,smb_prcnt, params_sent_thistime);

		/* smb_proff is the offset from the start of the SMB header to the
			parameter bytes, however the first 4 bytes of outbuf are
			the Netbios over TCP header. Thus use smb_base() to subtract
			them from the calculation */

		SSVAL(req->outbuf,smb_proff,
		      ((smb_buf(req->outbuf)+alignment_offset)
		       - smb_base(req->outbuf)));

		if(params_sent_thistime == 0)
			SSVAL(req->outbuf,smb_prdisp,0);
		else
			/* Absolute displacement of param bytes sent in this packet */
			SSVAL(req->outbuf,smb_prdisp,pp - params);

		SSVAL(req->outbuf,smb_drcnt, data_sent_thistime);
		if(data_sent_thistime == 0) {
			SSVAL(req->outbuf,smb_droff,0);
			SSVAL(req->outbuf,smb_drdisp, 0);
		} else {
			/* The offset of the data bytes is the offset of the
				parameter bytes plus the number of parameters being sent this time */
			SSVAL(req->outbuf, smb_droff,
			      ((smb_buf(req->outbuf)+alignment_offset)
			       - smb_base(req->outbuf))
			      + params_sent_thistime + data_alignment_offset);
			SSVAL(req->outbuf,smb_drdisp, pd - pdata);
		}

		/* Initialize the padding for alignment */

		if (alignment_offset != 0) {
			memset(smb_buf(req->outbuf), 0, alignment_offset);
		}

		/* Copy the param bytes into the packet */

		if(params_sent_thistime) {
			memcpy((smb_buf(req->outbuf)+alignment_offset), pp,
			       params_sent_thistime);
		}

		/* Copy in the data bytes */
		if(data_sent_thistime) {
			if (data_alignment_offset != 0) {
				memset((smb_buf(req->outbuf)+alignment_offset+
					params_sent_thistime), 0,
				       data_alignment_offset);
			}
			memcpy(smb_buf(req->outbuf)+alignment_offset
			       +params_sent_thistime+data_alignment_offset,
			       pd,data_sent_thistime);
		}

		DEBUG(9,("t2_rep: params_sent_thistime = %d, data_sent_thistime = %d, useable_space = %d\n",
			params_sent_thistime, data_sent_thistime, useable_space));
		DEBUG(9,("t2_rep: params_to_send = %d, data_to_send = %d, paramsize = %d, datasize = %d\n",
			params_to_send, data_to_send, paramsize, datasize));

		if (overflow) {
			error_packet_set((char *)req->outbuf,
					 ERRDOS,ERRbufferoverflow,
					 STATUS_BUFFER_OVERFLOW,
					 __LINE__,__FILE__);
		} else if (NT_STATUS_V(status)) {
			uint8_t eclass;
			uint32_t ecode;
			ntstatus_to_dos(status, &eclass, &ecode);
			error_packet_set((char *)req->outbuf,
					eclass, ecode, status,
					__LINE__,__FILE__);
		}

		/* Send the packet */
		show_msg((char *)req->outbuf);
		if (!srv_send_smb(xconn,
				(char *)req->outbuf,
				true, req->seqnum+1,
				IS_CONN_ENCRYPTED(conn),
				&req->pcd))
			exit_server_cleanly("send_trans2_replies: srv_send_smb failed.");

		TALLOC_FREE(req->outbuf);

		pp += params_sent_thistime;
		pd += data_sent_thistime;

		params_to_send -= params_sent_thistime;
		data_to_send -= data_sent_thistime;

		/* Sanity check */
		if(params_to_send < 0 || data_to_send < 0) {
			DEBUG(0,("send_trans2_replies failed sanity check pts = %d, dts = %d\n!!!",
				params_to_send, data_to_send));
			return;
		}
	}

	return;
}

Project: samba
File: trans2.c

static NTSTATUS smbd_do_posix_setfilepathinfo(struct connection_struct *conn,
					      struct smb_request *req,
					      TALLOC_CTX *mem_ctx,
					      uint16_t info_level,
					      struct smb_filename *smb_fname,
					      files_struct *fsp,
					      char **ppdata,
					      int total_data,
					      int *ret_data_size)
{
	char *pdata = *ppdata;
	NTSTATUS status = NT_STATUS_OK;
	int data_return_size = 0;

	*ret_data_size = 0;

	if (!CAN_WRITE(conn)) {
		/* Allow POSIX opens. The open path will deny
		 * any non-readonly opens. */
		if (info_level != SMB_POSIX_PATH_OPEN) {
			return NT_STATUS_DOS(ERRSRV, ERRaccess);
		}
	}

	DBG_DEBUG("file=%s (%s) info_level=%d totdata=%d\n",
		  smb_fname_str_dbg(smb_fname),
		  fsp_fnum_dbg(fsp),
		  info_level,
		  total_data);

	switch (info_level) {
		case SMB_SET_FILE_UNIX_BASIC:
		{
			status = smb_set_file_unix_basic(conn, req,
							pdata,
							total_data,
							fsp,
							smb_fname);
			break;
		}

		case SMB_SET_FILE_UNIX_INFO2:
		{
			status = smb_set_file_unix_info2(conn, req,
							pdata,
							total_data,
							fsp,
							smb_fname);
			break;
		}

		case SMB_SET_FILE_UNIX_LINK:
		{
			if (smb_fname == NULL) {
				/* We must have a pathname for this. */
				return NT_STATUS_INVALID_LEVEL;
			}
			status = smb_set_file_unix_link(conn, req, pdata,
							total_data, smb_fname);
			break;
		}

		case SMB_SET_FILE_UNIX_HLINK:
		{
			if (smb_fname == NULL) {
				/* We must have a pathname for this. */
				return NT_STATUS_INVALID_LEVEL;
			}
			status = smb_set_file_unix_hlink(conn, req,
							 pdata,	total_data,
							 smb_fname);
			break;
		}

#if defined(HAVE_POSIX_ACLS)
		case SMB_SET_POSIX_ACL:
		{
			status = smb_set_posix_acl(conn,
						req,
						pdata,
						total_data,
						fsp,
						smb_fname);
			break;
		}
#endif

		case SMB_SET_POSIX_LOCK:
		{
			if (fsp == NULL) {
				return NT_STATUS_INVALID_LEVEL;
			}
			status = smb_set_posix_lock(conn, req,
						    pdata, total_data, fsp);
			break;
		}

		case SMB_POSIX_PATH_OPEN:
		{
			if (smb_fname == NULL) {
				/* We must have a pathname for this. */
				return NT_STATUS_INVALID_LEVEL;
			}

			status = smb_posix_open(conn, req,
						ppdata,
						total_data,
						smb_fname,
						&data_return_size);
			break;
		}

		case SMB_POSIX_PATH_UNLINK:
		{
			if (smb_fname == NULL) {
				/* We must have a pathname for this. */
				return NT_STATUS_INVALID_LEVEL;
			}

			status = smb_posix_unlink(conn, req,
						pdata,
						total_data,
						smb_fname);
			break;
		}

		default:
			return NT_STATUS_INVALID_LEVEL;
	}

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

	*ret_data_size = data_return_size;
	return NT_STATUS_OK;
}

Project: samba
File: trans2.c

NTSTATUS get_ea_value_fsp(TALLOC_CTX *mem_ctx,
			  files_struct *fsp,
			  const char *ea_name,
			  struct ea_struct *pea)
{
	/* Get the value of this xattr. Max size is 64k. */
	size_t attr_size = 256;
	char *val = NULL;
	ssize_t sizeret;
	size_t max_xattr_size = 0;

	if (fsp == NULL) {
		return NT_STATUS_INVALID_HANDLE;
	}

	max_xattr_size = lp_smbd_max_xattr_size(SNUM(fsp->conn));

 again:

	val = talloc_realloc(mem_ctx, val, char, attr_size);
	if (!val) {
		return NT_STATUS_NO_MEMORY;
	}

	sizeret = SMB_VFS_FGETXATTR(fsp, ea_name, val, attr_size);
	if (sizeret == -1 && errno == ERANGE && attr_size < max_xattr_size) {
		attr_size = max_xattr_size;
		goto again;
	}

	if (sizeret == -1) {
		return map_nt_error_from_unix(errno);
	}

	DEBUG(10,("get_ea_value: EA %s is of length %u\n", ea_name, (unsigned int)sizeret));
	dump_data(10, (uint8_t *)val, sizeret);

	pea->flags = 0;
	if (strnequal(ea_name, "user.", 5)) {
		pea->name = talloc_strdup(mem_ctx, &ea_name[5]);
	} else {
		pea->name = talloc_strdup(mem_ctx, ea_name);
	}
	if (pea->name == NULL) {
		TALLOC_FREE(val);
		return NT_STATUS_NO_MEMORY;
	}
	pea->value.data = (unsigned char *)val;
	pea->value.length = (size_t)sizeret;
	return NT_STATUS_OK;
}

Project: samba
File: trans2.c

static struct ea_list *ea_list_union(struct ea_list *name_list, struct ea_list *file_list, size_t *total_ea_len)
{
	struct ea_list *nlistp, *flistp;

	for (nlistp = name_list; nlistp; nlistp = nlistp->next) {
		for (flistp = file_list; flistp; flistp = flistp->next) {
			if (strequal(nlistp->ea.name, flistp->ea.name)) {
				break;
			}
		}

		if (flistp) {
			/* Copy the data from this entry. */
			nlistp->ea.flags = flistp->ea.flags;
			nlistp->ea.value = flistp->ea.value;
		} else {
			/* Null entry. */
			nlistp->ea.flags = 0;
			ZERO_STRUCT(nlistp->ea.value);
		}
	}

	*total_ea_len = ea_list_size(name_list);
	return name_list;
}

Project: samba
File: trans2.c

static NTSTATUS get_posix_fsp(connection_struct *conn,
			struct smb_request *req,
			struct smb_filename *smb_fname,
			uint32_t access_mask,
			files_struct **ret_fsp)
{
	NTSTATUS status;
	uint32_t create_disposition = FILE_OPEN;
	uint32_t share_access = FILE_SHARE_READ|
				FILE_SHARE_WRITE|
				FILE_SHARE_DELETE;
	struct smb2_create_blobs *posx = NULL;

	/*
	 * Only FILE_FLAG_POSIX_SEMANTICS matters on existing files,
	 * but set reasonable defaults.
	 */
	uint32_t file_attributes = 0664;
	uint32_t oplock = NO_OPLOCK;
	uint32_t create_options = FILE_NON_DIRECTORY_FILE;

	/* File or directory must exist. */
	if (!VALID_STAT(smb_fname->st)) {
		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
	}
	/* Cannot be a symlink. */
	if (S_ISLNK(smb_fname->st.st_ex_mode)) {
		return NT_STATUS_ACCESS_DENIED;
	}
	/* Set options correctly for directory open. */
	if (S_ISDIR(smb_fname->st.st_ex_mode)) {
		/*
		 * Only FILE_FLAG_POSIX_SEMANTICS matters on existing
		 * directories, but set reasonable defaults.
		 */
		file_attributes = 0775;
		create_options = FILE_DIRECTORY_FILE;
	}

	status = make_smb2_posix_create_ctx(
		talloc_tos(), &posx, file_attributes);
	if (!NT_STATUS_IS_OK(status)) {
		DBG_WARNING("make_smb2_posix_create_ctx failed: %s\n",
			    nt_errstr(status));
		goto done;
	}

	status = SMB_VFS_CREATE_FILE(
		conn,           /* conn */
		req,            /* req */
		smb_fname,      /* fname */
		access_mask,    /* access_mask */
		share_access,   /* share_access */
		create_disposition,/* create_disposition*/
		create_options, /* create_options */
		file_attributes,/* file_attributes */
		oplock,         /* oplock_request */
		NULL,           /* lease */
		0,              /* allocation_size */
		0,              /* private_flags */
		NULL,           /* sd */
		NULL,           /* ea_list */
		ret_fsp,	/* result */
		NULL,           /* pinfo */
		posx,           /* in_context */
		NULL);          /* out_context */

done:
	TALLOC_FREE(posx);
	return status;
}

Project: samba
File: trans2.c

static void call_trans2findnext(connection_struct *conn,
				struct smb_request *req,
				char **pparams, int total_params,
				char **ppdata, int total_data,
				unsigned int max_data_bytes)
{
	/* We must be careful here that we don't return more than the
		allowed number of data bytes. If this means returning fewer than
		maxentries then so be it. We assume that the redirector has
		enough room for the fixed number of parameter bytes it has
		requested. */
	char *params = *pparams;
	char *pdata = *ppdata;
	char *data_end;
	int dptr_num;
	int maxentries;
	uint16_t info_level;
	uint32_t resume_key;
	uint16_t findnext_flags;
	bool close_after_request;
	bool close_if_end;
	bool requires_resume_key;
	bool continue_bit;
	char *resume_name = NULL;
	const char *mask = NULL;
	const char *directory = NULL;
	char *p = NULL;
	uint16_t dirtype;
	int numentries = 0;
	int i, last_entry_off=0;
	bool finished = False;
	bool dont_descend = False;
	bool out_of_space = False;
	int space_remaining;
	struct ea_list *ea_list = NULL;
	NTSTATUS ntstatus = NT_STATUS_OK;
	bool ask_sharemode = lp_smbd_search_ask_sharemode(SNUM(conn));
	TALLOC_CTX *ctx = talloc_tos();
	struct smbd_server_connection *sconn = req->sconn;
	bool backup_priv = false; 
	bool as_root = false;
	files_struct *fsp = NULL;
	const struct loadparm_substitution *lp_sub =
		loadparm_s3_global_substitution();

	if (total_params < 13) {
		reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
		return;
	}

	dptr_num = SVAL(params,0);
	maxentries = SVAL(params,2);
	info_level = SVAL(params,4);
	resume_key = IVAL(params,6);
	findnext_flags = SVAL(params,10);
	close_after_request = (findnext_flags & FLAG_TRANS2_FIND_CLOSE);
	close_if_end = (findnext_flags & FLAG_TRANS2_FIND_CLOSE_IF_END);
	requires_resume_key = (findnext_flags & FLAG_TRANS2_FIND_REQUIRE_RESUME);
	continue_bit = (findnext_flags & FLAG_TRANS2_FIND_CONTINUE);

	if (!continue_bit) {
		/* We only need resume_name if continue_bit is zero. */
		if (req->posix_pathnames) {
			srvstr_get_path_posix(ctx,
				params,
				req->flags2,
				&resume_name,
				params+12,
				total_params - 12,
				STR_TERMINATE,
				&ntstatus);
		} else {
			srvstr_get_path(ctx,
				params,
				req->flags2,
				&resume_name,
				params+12,
				total_params - 12,
				STR_TERMINATE,
				&ntstatus);
		}
		if (!NT_STATUS_IS_OK(ntstatus)) {
			/* Win9x or OS/2 can send a resume name of ".." or ".". This will cause the parser to
			   complain (it thinks we're asking for the directory above the shared
			   path or an invalid name). Catch this as the resume name is only compared, never used in
			   a file access. JRA. */
			srvstr_pull_talloc(ctx, params, req->flags2,
				&resume_name, params+12,
				total_params - 12,
				STR_TERMINATE);

			if (!resume_name || !(ISDOT(resume_name) || ISDOTDOT(resume_name))) {
				reply_nterror(req, ntstatus);
				return;
			}
		}
	}

	DEBUG(3,("call_trans2findnext: dirhandle = %d, max_data_bytes = %d, maxentries = %d, \
close_after_request=%d, close_if_end = %d requires_resume_key = %d \
resume_key = %d resume name = %s continue=%d level = %d\n",
		dptr_num, max_data_bytes, maxentries, close_after_request, close_if_end, 
		requires_resume_key, resume_key,
		resume_name ? resume_name : "(NULL)", continue_bit, info_level));

	if (!maxentries) {
		/* W2K3 seems to treat zero as 1. */
		maxentries = 1;
	}

	switch (info_level) {
		case SMB_FIND_INFO_STANDARD:
		case SMB_FIND_EA_SIZE:
		case SMB_FIND_EA_LIST:
		case SMB_FIND_FILE_DIRECTORY_INFO:
		case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
		case SMB_FIND_FILE_NAMES_INFO:
		case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
		case SMB_FIND_ID_FULL_DIRECTORY_INFO:
		case SMB_FIND_ID_BOTH_DIRECTORY_INFO:
			break;
		case SMB_FIND_FILE_UNIX:
		case SMB_FIND_FILE_UNIX_INFO2:
			/* Always use filesystem for UNIX mtime query. */
			ask_sharemode = false;
			if (!lp_unix_extensions()) {
				reply_nterror(req, NT_STATUS_INVALID_LEVEL);
				return;
			}
			break;
		default:
			reply_nterror(req, NT_STATUS_INVALID_LEVEL);
			return;
	}

	if (info_level == SMB_FIND_EA_LIST) {
		uint32_t ea_size;

		if (total_data < 4) {
			reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
			return;
		}

		ea_size = IVAL(pdata,0);
		if (ea_size != total_data) {
			DEBUG(4,("call_trans2findnext: Rejecting EA request with incorrect \
total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pdata,0) ));
			reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
			return;
		}

		if (!lp_ea_support(SNUM(conn))) {
			reply_nterror(req, NT_STATUS_EAS_NOT_SUPPORTED);
			return;
		}

		/* Pull out the list of names. */
		ea_list = read_ea_name_list(ctx, pdata + 4, ea_size - 4);
		if (!ea_list) {
			reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
			return;
		}
	}

	if (max_data_bytes + DIR_ENTRY_SAFETY_MARGIN < max_data_bytes) {
		reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
		return;
	}

	*ppdata = (char *)SMB_REALLOC(
		*ppdata, max_data_bytes + DIR_ENTRY_SAFETY_MARGIN);
	if(*ppdata == NULL) {
		reply_nterror(req, NT_STATUS_NO_MEMORY);
		return;
	}

	pdata = *ppdata;
	data_end = pdata + max_data_bytes + DIR_ENTRY_SAFETY_MARGIN - 1;

	/*
	 * squash valgrind "writev(vector[...]) points to uninitialised byte(s)"
	 * error.
	 */
	memset(pdata + total_data, 0, (max_data_bytes + DIR_ENTRY_SAFETY_MARGIN) - total_data);
	/* Realloc the params space */
	*pparams = (char *)SMB_REALLOC(*pparams, 6*SIZEOFWORD);
	if(*pparams == NULL ) {
		reply_nterror(req, NT_STATUS_NO_MEMORY);
		return;
	}

	params = *pparams;

	/* Check that the dptr is valid */
	fsp = dptr_fetch_lanman2_fsp(sconn, dptr_num);
	if (fsp == NULL) {
		reply_nterror(req, STATUS_NO_MORE_FILES);
		return;
	}

	directory = dptr_path(sconn, dptr_num);

	/* Get the wildcard mask from the dptr */
	if((mask = dptr_wcard(sconn, dptr_num))== NULL) {
		DEBUG(2,("dptr_num %d has no wildcard\n", dptr_num));
		reply_nterror(req, STATUS_NO_MORE_FILES);
		return;
	}

	/* Get the attr mask from the dptr */
	dirtype = dptr_attr(sconn, dptr_num);

	backup_priv = dptr_get_priv(fsp->dptr);

	DEBUG(3,("dptr_num is %d, mask = %s, attr = %x, dirptr=(0x%lX,%ld) "
		"backup_priv = %d\n",
		dptr_num, mask, dirtype,
		(long)fsp->dptr,
		dptr_TellDir(fsp->dptr),
		(int)backup_priv));

	/* We don't need to check for VOL here as this is returned by
		a different TRANS2 call. */

	DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n",
		 directory,lp_dont_descend(ctx, lp_sub, SNUM(conn))));
	if (in_list(directory,lp_dont_descend(ctx, lp_sub, SNUM(conn)),conn->case_sensitive))
		dont_descend = True;

	p = pdata;
	space_remaining = max_data_bytes;
	out_of_space = False;

	if (backup_priv) {
		become_root();
		as_root = true;
	}

	/*
	 * Seek to the correct position. We no longer use the resume key but
	 * depend on the last file name instead.
	 */

	if(!continue_bit && resume_name && *resume_name) {
		SMB_STRUCT_STAT st;

		long current_pos = 0;
		/*
		 * Remember, name_to_8_3 is called by
		 * get_lanman2_dir_entry(), so the resume name
		 * could be mangled. Ensure we check the unmangled name.
		 */

		if (mangle_is_mangled(resume_name, conn->params)) {
			char *new_resume_name = NULL;
			mangle_lookup_name_from_8_3(ctx,
						resume_name,
						&new_resume_name,
						conn->params);
			if (new_resume_name) {
				resume_name = new_resume_name;
			}
		}

		/*
		 * Fix for NT redirector problem triggered by resume key indexes
		 * changing between directory scans. We now return a resume key of 0
		 * and instead look for the filename to continue from (also given
		 * to us by NT/95/smbfs/smbclient). If no other scans have been done between the
		 * findfirst/findnext (as is usual) then the directory pointer
		 * should already be at the correct place.
		 */

		finished = !dptr_SearchDir(fsp->dptr, resume_name, &current_pos, &st);
	} /* end if resume_name && !continue_bit */

	for (i=0;(i<(int)maxentries) && !finished && !out_of_space ;i++) {
		bool got_exact_match = False;

		/* this is a heuristic to avoid seeking the fsp->dptr except when
			absolutely necessary. It allows for a filename of about 40 chars */
		if (space_remaining < DIRLEN_GUESS && numentries > 0) {
			out_of_space = True;
			finished = False;
		} else {
			ntstatus = get_lanman2_dir_entry(ctx,
						conn,
						fsp->dptr,
						req->flags2,
						mask,dirtype,info_level,
						requires_resume_key,dont_descend,
						ask_sharemode,
						&p,pdata,data_end,
						space_remaining,
						&got_exact_match,
						&last_entry_off, ea_list);
			if (NT_STATUS_EQUAL(ntstatus,
					NT_STATUS_ILLEGAL_CHARACTER)) {
				/*
				 * Bad character conversion on name. Ignore this
				 * entry.
				 */
				continue;
			}
			if (NT_STATUS_EQUAL(ntstatus, STATUS_MORE_ENTRIES)) {
				out_of_space = true;
			} else {
				finished = !NT_STATUS_IS_OK(ntstatus);
			}
		}

		if (!finished && !out_of_space)
			numentries++;

		/*
		 * As an optimisation if we know we aren't looking
		 * for a wildcard name (ie. the name matches the wildcard exactly)
		 * then we can finish on any (first) match.
		 * This speeds up large directory searches. JRA.
		 */

		if(got_exact_match)
			finished = True;

		space_remaining = max_data_bytes - PTR_DIFF(p,pdata);
	}

	DEBUG( 3, ( "%s mask=%s directory=%s dirtype=%d numentries=%d\n",
		smb_fn_name(req->cmd),
		mask, directory, dirtype, numentries ) );

	/* Check if we can close the fsp->dptr */
	if(close_after_request || (finished && close_if_end)) {
		DEBUG(5,("call_trans2findnext: closing dptr_num = %d\n", dptr_num));
		dptr_num = -1;
		close_file(NULL, fsp, NORMAL_CLOSE);
		fsp = NULL;
	}

	if (as_root) {
		unbecome_root();
	}

	/* Set up the return parameter block */
	SSVAL(params,0,numentries);
	SSVAL(params,2,finished);
	SSVAL(params,4,0); /* Never an EA error */
	SSVAL(params,6,last_entry_off);

	send_trans2_replies(conn, req, NT_STATUS_OK, params, 8, pdata, PTR_DIFF(p,pdata),
			    max_data_bytes);

	return;
}

Project: samba
File: trans2.c

NTSTATUS smb_set_file_time(connection_struct *conn,
			   files_struct *fsp,
			   struct smb_filename *smb_fname,
			   struct smb_file_time *ft,
			   bool setting_write_time)
{
	struct files_struct *set_fsp = NULL;
	struct timeval_buf tbuf[4];
	uint32_t action =
		FILE_NOTIFY_CHANGE_LAST_ACCESS
		|FILE_NOTIFY_CHANGE_LAST_WRITE
		|FILE_NOTIFY_CHANGE_CREATION;
	int ret;

	if (!VALID_STAT(smb_fname->st)) {
		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
	}

	if (fsp == NULL) {
		/* A symlink */
		return NT_STATUS_OK;
	}

	set_fsp = fsp->base_fsp == NULL ? fsp : fsp->base_fsp;

	/* get some defaults (no modifications) if any info is zero or -1. */
	if (is_omit_timespec(&ft->create_time)) {
		action &= ~FILE_NOTIFY_CHANGE_CREATION;
	}

	if (is_omit_timespec(&ft->atime)) {
		action &= ~FILE_NOTIFY_CHANGE_LAST_ACCESS;
	}

	if (is_omit_timespec(&ft->mtime)) {
		action &= ~FILE_NOTIFY_CHANGE_LAST_WRITE;
	}

	if (!setting_write_time) {
		/* ft->mtime comes from change time, not write time. */
		action &= ~FILE_NOTIFY_CHANGE_LAST_WRITE;
	}

	/* Ensure the resolution is the correct for
	 * what we can store on this filesystem. */

	round_timespec(conn->ts_res, &ft->create_time);
	round_timespec(conn->ts_res, &ft->ctime);
	round_timespec(conn->ts_res, &ft->atime);
	round_timespec(conn->ts_res, &ft->mtime);

	DBG_DEBUG("smb_set_filetime: actime: %s\n ",
		  timespec_string_buf(&ft->atime, true, &tbuf[0]));
	DBG_DEBUG("smb_set_filetime: modtime: %s\n ",
		  timespec_string_buf(&ft->mtime, true, &tbuf[1]));
	DBG_DEBUG("smb_set_filetime: ctime: %s\n ",
		  timespec_string_buf(&ft->ctime, true, &tbuf[2]));
	DBG_DEBUG("smb_set_file_time: createtime: %s\n ",
		  timespec_string_buf(&ft->create_time, true, &tbuf[3]));

	if (setting_write_time) {
		/*
		 * This was a Windows setfileinfo on an open file.
		 * NT does this a lot. We also need to 
		 * set the time here, as it can be read by 
		 * FindFirst/FindNext and with the patch for bug #2045
		 * in smbd/fileio.c it ensures that this timestamp is
		 * kept sticky even after a write. We save the request
		 * away and will set it on file close and after a write. JRA.
		 */

		DBG_DEBUG("setting pending modtime to %s\n",
			  timespec_string_buf(&ft->mtime, true, &tbuf[0]));

		if (set_fsp != NULL) {
			set_sticky_write_time_fsp(set_fsp, ft->mtime);
		} else {
			set_sticky_write_time_path(
				vfs_file_id_from_sbuf(conn, &smb_fname->st),
				ft->mtime);
		}
	}

	DEBUG(10,("smb_set_file_time: setting utimes to modified values.\n"));

	ret = file_ntimes(conn, set_fsp, ft);
	if (ret != 0) {
		return map_nt_error_from_unix(errno);
	}

	notify_fname(conn, NOTIFY_ACTION_MODIFIED, action,
		     smb_fname->base_name);
	return NT_STATUS_OK;
}

Project: samba
File: trans2.c

void reply_trans2(struct smb_request *req)
{
	connection_struct *conn = req->conn;
	unsigned int dsoff;
	unsigned int dscnt;
	unsigned int psoff;
	unsigned int pscnt;
	unsigned int tran_call;
	struct trans_state *state;
	NTSTATUS result;

	START_PROFILE(SMBtrans2);

	if (req->wct < 14) {
		reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
		END_PROFILE(SMBtrans2);
		return;
	}

	dsoff = SVAL(req->vwv+12, 0);
	dscnt = SVAL(req->vwv+11, 0);
	psoff = SVAL(req->vwv+10, 0);
	pscnt = SVAL(req->vwv+9, 0);
	tran_call = SVAL(req->vwv+14, 0);

	result = allow_new_trans(conn->pending_trans, req->mid);
	if (!NT_STATUS_IS_OK(result)) {
		DEBUG(2, ("Got invalid trans2 request: %s\n",
			  nt_errstr(result)));
		reply_nterror(req, result);
		END_PROFILE(SMBtrans2);
		return;
	}

	if (IS_IPC(conn)) {
		switch (tran_call) {
		/* List the allowed trans2 calls on IPC$ */
		case TRANSACT2_OPEN:
		case TRANSACT2_GET_DFS_REFERRAL:
		case TRANSACT2_QFILEINFO:
		case TRANSACT2_QFSINFO:
		case TRANSACT2_SETFSINFO:
			break;
		default:
			reply_nterror(req, NT_STATUS_ACCESS_DENIED);
			END_PROFILE(SMBtrans2);
			return;
		}
	}

	if ((state = talloc(conn, struct trans_state)) == NULL) {
		DEBUG(0, ("talloc failed\n"));
		reply_nterror(req, NT_STATUS_NO_MEMORY);
		END_PROFILE(SMBtrans2);
		return;
	}

	state->cmd = SMBtrans2;

	state->mid = req->mid;
	state->vuid = req->vuid;
	state->setup_count = SVAL(req->vwv+13, 0);
	state->setup = NULL;
	state->total_param = SVAL(req->vwv+0, 0);
	state->param = NULL;
	state->total_data =  SVAL(req->vwv+1, 0);
	state->data = NULL;
	state->max_param_return = SVAL(req->vwv+2, 0);
	state->max_data_return  = SVAL(req->vwv+3, 0);
	state->max_setup_return = SVAL(req->vwv+4, 0);
	state->close_on_completion = BITSETW(req->vwv+5, 0);
	state->one_way = BITSETW(req->vwv+5, 1);

	state->call = tran_call;

	/* All trans2 messages we handle have smb_sucnt == 1 - ensure this
	   is so as a sanity check */
	if (state->setup_count != 1) {
		/*
		 * Need to have rc=0 for ioctl to get job id for OS/2.
		 *  Network printing will fail if function is not successful.
		 *  Similar function in reply.c will be used if protocol
		 *  is LANMAN1.0 instead of LM1.2X002.
		 *  Until DosPrintSetJobInfo with PRJINFO3 is supported,
		 *  outbuf doesn't have to be set(only job id is used).
		 */
		if ( (state->setup_count == 4)
		     && (tran_call == TRANSACT2_IOCTL)
		     && (SVAL(req->vwv+16, 0) == LMCAT_SPL)
		     &&	(SVAL(req->vwv+17, 0) == LMFUNC_GETJOBID)) {
			DEBUG(2,("Got Trans2 DevIOctl jobid\n"));
		} else {
			DEBUG(2,("Invalid smb_sucnt in trans2 call(%u)\n",state->setup_count));
			DEBUG(2,("Transaction is %d\n",tran_call));
			TALLOC_FREE(state);
			reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
			END_PROFILE(SMBtrans2);
			return;
		}
	}

	if ((dscnt > state->total_data) || (pscnt > state->total_param))
		goto bad_param;

	if (state->total_data) {

		if (trans_oob(state->total_data, 0, dscnt)
		    || trans_oob(smb_len(req->inbuf), dsoff, dscnt)) {
			goto bad_param;
		}

		/* Can't use talloc here, the core routines do realloc on the
		 * params and data. */
		state->data = (char *)SMB_MALLOC(state->total_data);
		if (state->data == NULL) {
			DEBUG(0,("reply_trans2: data malloc fail for %u "
				 "bytes !\n", (unsigned int)state->total_data));
			TALLOC_FREE(state);
			reply_nterror(req, NT_STATUS_NO_MEMORY);
			END_PROFILE(SMBtrans2);
			return;
		}

		memcpy(state->data,smb_base(req->inbuf)+dsoff,dscnt);
	}

	if (state->total_param) {

		if (trans_oob(state->total_param, 0, pscnt)
		    || trans_oob(smb_len(req->inbuf), psoff, pscnt)) {
			goto bad_param;
		}

		/* Can't use talloc here, the core routines do realloc on the
		 * params and data. */
		state->param = (char *)SMB_MALLOC(state->total_param);
		if (state->param == NULL) {
			DEBUG(0,("reply_trans: param malloc fail for %u "
				 "bytes !\n", (unsigned int)state->total_param));
			SAFE_FREE(state->data);
			TALLOC_FREE(state);
			reply_nterror(req, NT_STATUS_NO_MEMORY);
			END_PROFILE(SMBtrans2);
			return;
		} 

		memcpy(state->param,smb_base(req->inbuf)+psoff,pscnt);
	}

	state->received_data  = dscnt;
	state->received_param = pscnt;

	if ((state->received_param == state->total_param) &&
	    (state->received_data == state->total_data)) {

		handle_trans2(conn, req, state);

		SAFE_FREE(state->data);
		SAFE_FREE(state->param);
		TALLOC_FREE(state);
		END_PROFILE(SMBtrans2);
		return;
	}

	DLIST_ADD(conn->pending_trans, state);

	/* We need to send an interim response then receive the rest
	   of the parameter/data bytes */
	reply_outbuf(req, 0, 0);
	show_msg((char *)req->outbuf);
	END_PROFILE(SMBtrans2);
	return;

  bad_param:

	DEBUG(0,("reply_trans2: invalid trans parameters\n"));
	SAFE_FREE(state->data);
	SAFE_FREE(state->param);
	TALLOC_FREE(state);
	END_PROFILE(SMBtrans2);
	reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
}

Project: samba
File: trans2.c

NTSTATUS smbd_dirptr_lanman2_entry(TALLOC_CTX *ctx,
			       connection_struct *conn,
			       struct dptr_struct *dirptr,
			       uint16_t flags2,
			       const char *path_mask,
			       uint32_t dirtype,
			       int info_level,
			       int requires_resume_key,
			       bool dont_descend,
			       bool ask_sharemode,
			       bool get_dosmode,
			       uint8_t align,
			       bool do_pad,
			       char **ppdata,
			       char *base_data,
			       char *end_data,
			       int space_remaining,
			       struct smb_filename **_smb_fname,
			       bool *got_exact_match,
			       int *_last_entry_off,
			       struct ea_list *name_list,
			       struct file_id *file_id)
{
	const char *p;
	const char *mask = NULL;
	long prev_dirpos = 0;
	uint32_t mode = 0;
	char *fname = NULL;
	struct smb_filename *smb_fname = NULL;
	struct smbd_dirptr_lanman2_state state;
	bool ok;
	uint64_t last_entry_off = 0;
	NTSTATUS status;
	enum mangled_names_options mangled_names;
	bool marshall_with_83_names;

	mangled_names = lp_mangled_names(conn->params);

	ZERO_STRUCT(state);
	state.conn = conn;
	state.info_level = info_level;
	if (mangled_names != MANGLED_NAMES_NO) {
		state.check_mangled_names = true;
	}
	state.has_wild = dptr_has_wild(dirptr);
	state.got_exact_match = false;

	*got_exact_match = false;

	p = strrchr_m(path_mask,'/');
	if(p != NULL) {
		if(p[1] == '\0') {
			mask = "*.*";
		} else {
			mask = p+1;
		}
	} else {
		mask = path_mask;
	}

	ok = smbd_dirptr_get_entry(ctx,
				   dirptr,
				   mask,
				   dirtype,
				   dont_descend,
				   ask_sharemode,
				   get_dosmode,
				   smbd_dirptr_lanman2_match_fn,
				   smbd_dirptr_lanman2_mode_fn,
				   &state,
				   &fname,
				   &smb_fname,
				   &mode,
				   &prev_dirpos);
	if (!ok) {
		return NT_STATUS_END_OF_FILE;
	}

	*got_exact_match = state.got_exact_match;

	marshall_with_83_names = (mangled_names == MANGLED_NAMES_YES);

	status = smbd_marshall_dir_entry(ctx,
				     conn,
				     flags2,
				     info_level,
				     name_list,
				     marshall_with_83_names,
				     requires_resume_key,
				     mode,
				     fname,
				     smb_fname,
				     space_remaining,
				     align,
				     do_pad,
				     base_data,
				     ppdata,
				     end_data,
				     &last_entry_off);
	if (NT_STATUS_EQUAL(status, NT_STATUS_ILLEGAL_CHARACTER)) {
		DEBUG(1,("Conversion error: illegal character: %s\n",
			 smb_fname_str_dbg(smb_fname)));
	}

	if (file_id != NULL) {
		*file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
	}

	if (!NT_STATUS_IS_OK(status) &&
	    !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES))
	{
		TALLOC_FREE(smb_fname);
		TALLOC_FREE(fname);
		return status;
	}

	if (_smb_fname != NULL) {
		/*
		 * smb_fname is already talloc'ed off ctx.
		 * We just need to make sure we don't return
		 * any stream_name, and replace base_name
		 * with fname in case base_name got mangled.
		 * This allows us to preserve any smb_fname->fsp
		 * for asynchronous handle lookups.
		 */
		TALLOC_FREE(smb_fname->stream_name);
		TALLOC_FREE(smb_fname->base_name);
		smb_fname->base_name = talloc_strdup(smb_fname, fname);

		if (smb_fname->base_name == NULL) {
			TALLOC_FREE(smb_fname);
			TALLOC_FREE(fname);
			return NT_STATUS_NO_MEMORY;
		}
		*_smb_fname = smb_fname;
	} else {
		TALLOC_FREE(smb_fname);
	}
	TALLOC_FREE(fname);

	if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
		dptr_SeekDir(dirptr, prev_dirpos);
		return status;
	}

	*_last_entry_off = last_entry_off;
	return NT_STATUS_OK;
}

Project: samba
File: trans2.c

static void call_trans2mkdir(connection_struct *conn, struct smb_request *req,
			     char **pparams, int total_params,
			     char **ppdata, int total_data,
			     unsigned int max_data_bytes)
{
	struct files_struct *fsp = NULL;
	struct smb_filename *smb_dname = NULL;
	char *params = *pparams;
	char *pdata = *ppdata;
	char *directory = NULL;
	NTSTATUS status = NT_STATUS_OK;
	struct ea_list *ea_list = NULL;
	uint32_t ucf_flags = ucf_flags_from_smb_request(req);
	TALLOC_CTX *ctx = talloc_tos();

	if (!CAN_WRITE(conn)) {
		reply_nterror(req, NT_STATUS_ACCESS_DENIED);
		return;
	}

	if (total_params < 5) {
		reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
		return;
	}

	if (req->posix_pathnames) {
		srvstr_get_path_posix(ctx,
			params,
			req->flags2,
			&directory,
			&params[4],
			total_params - 4,
			STR_TERMINATE,
			&status);
	} else {
		srvstr_get_path(ctx,
			params,
			req->flags2,
			&directory,
			&params[4],
			total_params - 4,
			STR_TERMINATE,
			&status);
	}
	if (!NT_STATUS_IS_OK(status)) {
		reply_nterror(req, status);
		return;
	}

	DEBUG(3,("call_trans2mkdir : name = %s\n", directory));

	status = filename_convert(ctx,
				conn,
				directory,
				ucf_flags,
				0,
				&smb_dname);

	if (!NT_STATUS_IS_OK(status)) {
		if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) {
			reply_botherror(req,
				NT_STATUS_PATH_NOT_COVERED,
				ERRSRV, ERRbadpath);
			return;
		}
		reply_nterror(req, status);
		return;
        }

	/*
	 * OS/2 workplace shell seems to send SET_EA requests of "null"
	 * length (4 bytes containing IVAL 4).
	 * They seem to have no effect. Bug #3212. JRA.
	 */

	if (total_data && (total_data != 4)) {
		/* Any data in this call is an EA list. */
		if (total_data < 10) {
			reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
			goto out;
		}

		if (IVAL(pdata,0) > total_data) {
			DEBUG(10,("call_trans2mkdir: bad total data size (%u) > %u\n",
				IVAL(pdata,0), (unsigned int)total_data));
			reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
			goto out;
		}

		ea_list = read_ea_list(talloc_tos(), pdata + 4,
				       total_data - 4);
		if (!ea_list) {
			reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
			goto out;
		}

		if (!lp_ea_support(SNUM(conn))) {
			reply_nterror(req, NT_STATUS_EAS_NOT_SUPPORTED);
			goto out;
		}
	}
	/* If total_data == 4 Windows doesn't care what values
	 * are placed in that field, it just ignores them.
	 * The System i QNTC IBM SMB client puts bad values here,
	 * so ignore them. */

	status = SMB_VFS_CREATE_FILE(
		conn,					/* conn */
		req,					/* req */
		smb_dname,				/* fname */
		MAXIMUM_ALLOWED_ACCESS,			/* access_mask */
		FILE_SHARE_NONE,			/* share_access */
		FILE_CREATE,				/* create_disposition*/
		FILE_DIRECTORY_FILE,			/* create_options */
		FILE_ATTRIBUTE_DIRECTORY,		/* file_attributes */
		0,					/* oplock_request */
		NULL,					/* lease */
		0,					/* allocation_size */
		0,					/* private_flags */
		NULL,					/* sd */
		NULL,					/* ea_list */
		&fsp,					/* result */
		NULL,					/* pinfo */
		NULL, NULL);				/* create context */
	if (!NT_STATUS_IS_OK(status)) {
		reply_nterror(req, status);
		goto out;
	}

	/* Try and set any given EA. */
	if (ea_list) {
		status = set_ea(conn, fsp, ea_list);
		if (!NT_STATUS_IS_OK(status)) {
			reply_nterror(req, status);
			goto out;
		}
	}

	/* Realloc the parameter and data sizes */
	*pparams = (char *)SMB_REALLOC(*pparams,2);
	if(*pparams == NULL) {
		reply_nterror(req, NT_STATUS_NO_MEMORY);
		goto out;
	}
	params = *pparams;

	SSVAL(params,0,0);

	send_trans2_replies(conn, req, NT_STATUS_OK, params, 2, *ppdata, 0, max_data_bytes);

 out:
	if (fsp != NULL) {
		close_file(NULL, fsp, NORMAL_CLOSE);
		fsp = NULL;
	}
	TALLOC_FREE(smb_dname);
	return;
}

Project: samba
File: trans2.c

static void call_trans2findnotifyfirst(connection_struct *conn,
				       struct smb_request *req,
				       char **pparams, int total_params,
				       char **ppdata, int total_data,
				       unsigned int max_data_bytes)
{
	char *params = *pparams;
	uint16_t info_level;

	if (total_params < 6) {
		reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
		return;
	}

	info_level = SVAL(params,4);
	DEBUG(3,("call_trans2findnotifyfirst - info_level %d\n", info_level));

	switch (info_level) {
		case 1:
		case 2:
			break;
		default:
			reply_nterror(req, NT_STATUS_INVALID_LEVEL);
			return;
	}

	/* Realloc the parameter and data sizes */
	*pparams = (char *)SMB_REALLOC(*pparams,6);
	if (*pparams == NULL) {
		reply_nterror(req, NT_STATUS_NO_MEMORY);
		return;
	}
	params = *pparams;

	SSVAL(params,0,fnf_handle);
	SSVAL(params,2,0); /* No changes */
	SSVAL(params,4,0); /* No EA errors */

	fnf_handle++;

	if(fnf_handle == 0)
		fnf_handle = 257;

	send_trans2_replies(conn, req, NT_STATUS_OK, params, 6, *ppdata, 0, max_data_bytes);

	return;
}

Project: samba
File: trans2.c

static void call_trans2findnotifynext(connection_struct *conn,
				      struct smb_request *req,
				      char **pparams, int total_params,
				      char **ppdata, int total_data,
				      unsigned int max_data_bytes)
{
	char *params = *pparams;

	DEBUG(3,("call_trans2findnotifynext\n"));

	/* Realloc the parameter and data sizes */
	*pparams = (char *)SMB_REALLOC(*pparams,4);
	if (*pparams == NULL) {
		reply_nterror(req, NT_STATUS_NO_MEMORY);
		return;
	}
	params = *pparams;

	SSVAL(params,0,0); /* No changes */
	SSVAL(params,2,0); /* No EA errors */

	send_trans2_replies(conn, req, NT_STATUS_OK, params, 4, *ppdata, 0, max_data_bytes);

	return;
}

Project: samba
File: trans2.c

static void handle_trans2(connection_struct *conn, struct smb_request *req,
			  struct trans_state *state)
{
	if (get_Protocol() >= PROTOCOL_NT1) {
		req->flags2 |= 0x40; /* IS_LONG_NAME */
		SSVAL((discard_const_p(uint8_t, req->inbuf)),smb_flg2,req->flags2);
	}

	if (ENCRYPTION_REQUIRED(conn) && !req->encrypted) {
		if (state->call != TRANSACT2_QFSINFO &&
		    state->call != TRANSACT2_SETFSINFO) {
			DEBUG(0,("handle_trans2: encryption required "
				"with call 0x%x\n",
				(unsigned int)state->call));
			reply_nterror(req, NT_STATUS_ACCESS_DENIED);
			return;
		}
	}

	SMB_PERFCOUNT_SET_SUBOP(&req->pcd, state->call);

	/* Now we must call the relevant TRANS2 function */
	switch(state->call)  {
	case TRANSACT2_OPEN:
	{
		START_PROFILE(Trans2_open);
		call_trans2open(conn, req,
				&state->param, state->total_param,
				&state->data, state->total_data,
				state->max_data_return);
		END_PROFILE(Trans2_open);
		break;
	}

	case TRANSACT2_FINDFIRST:
	{
		START_PROFILE(Trans2_findfirst);
		call_trans2findfirst(conn, req,
				     &state->param, state->total_param,
				     &state->data, state->total_data,
				     state->max_data_return);
		END_PROFILE(Trans2_findfirst);
		break;
	}

	case TRANSACT2_FINDNEXT:
	{
		START_PROFILE(Trans2_findnext);
		call_trans2findnext(conn, req,
				    &state->param, state->total_param,
				    &state->data, state->total_data,
				    state->max_data_return);
		END_PROFILE(Trans2_findnext);
		break;
	}

	case TRANSACT2_QFSINFO:
	{
		START_PROFILE(Trans2_qfsinfo);
		call_trans2qfsinfo(conn, req,
				   &state->param, state->total_param,
				   &state->data, state->total_data,
				   state->max_data_return);
		END_PROFILE(Trans2_qfsinfo);
	    break;
	}

	case TRANSACT2_SETFSINFO:
	{
		START_PROFILE(Trans2_setfsinfo);
		call_trans2setfsinfo(conn, req,
				     &state->param, state->total_param,
				     &state->data, state->total_data,
				     state->max_data_return);
		END_PROFILE(Trans2_setfsinfo);
		break;
	}

	case TRANSACT2_QPATHINFO:
	case TRANSACT2_QFILEINFO:
	{
		START_PROFILE(Trans2_qpathinfo);
		call_trans2qfilepathinfo(conn, req, state->call,
					 &state->param, state->total_param,
					 &state->data, state->total_data,
					 state->max_data_return);
		END_PROFILE(Trans2_qpathinfo);
		break;
	}

	case TRANSACT2_SETPATHINFO:
	case TRANSACT2_SETFILEINFO:
	{
		START_PROFILE(Trans2_setpathinfo);
		call_trans2setfilepathinfo(conn, req, state->call,
					   &state->param, state->total_param,
					   &state->data, state->total_data,
					   state->max_data_return);
		END_PROFILE(Trans2_setpathinfo);
		break;
	}

	case TRANSACT2_FINDNOTIFYFIRST:
	{
		START_PROFILE(Trans2_findnotifyfirst);
		call_trans2findnotifyfirst(conn, req,
					   &state->param, state->total_param,
					   &state->data, state->total_data,
					   state->max_data_return);
		END_PROFILE(Trans2_findnotifyfirst);
		break;
	}

	case TRANSACT2_FINDNOTIFYNEXT:
	{
		START_PROFILE(Trans2_findnotifynext);
		call_trans2findnotifynext(conn, req,
					  &state->param, state->total_param,
					  &state->data, state->total_data,
					  state->max_data_return);
		END_PROFILE(Trans2_findnotifynext);
		break;
	}

	case TRANSACT2_MKDIR:
	{
		START_PROFILE(Trans2_mkdir);
		call_trans2mkdir(conn, req,
				 &state->param, state->total_param,
				 &state->data, state->total_data,
				 state->max_data_return);
		END_PROFILE(Trans2_mkdir);
		break;
	}

	case TRANSACT2_GET_DFS_REFERRAL:
	{
		START_PROFILE(Trans2_get_dfs_referral);
		call_trans2getdfsreferral(conn, req,
					  &state->param, state->total_param,
					  &state->data, state->total_data,
					  state->max_data_return);
		END_PROFILE(Trans2_get_dfs_referral);
		break;
	}

	case TRANSACT2_IOCTL:
	{
		START_PROFILE(Trans2_ioctl);
		call_trans2ioctl(conn, req,
				 &state->param, state->total_param,
				 &state->data, state->total_data,
				 state->max_data_return);
		END_PROFILE(Trans2_ioctl);
		break;
	}

	default:
		/* Error in request */
		DEBUG(2,("Unknown request %d in trans2 call\n", state->call));
		reply_nterror(req, NT_STATUS_NOT_IMPLEMENTED);
	}
}

Project: samba
File: trans2.c

bool samba_private_attr_name(const char *unix_ea_name)
{
	static const char * const prohibited_ea_names[] = {
		SAMBA_POSIX_INHERITANCE_EA_NAME,
		SAMBA_XATTR_DOS_ATTRIB,
		SAMBA_XATTR_MARKER,
		XATTR_NTACL_NAME,
		AFPINFO_EA_NETATALK,
		NULL
	};

	int i;

	for (i = 0; prohibited_ea_names[i]; i++) {
		if (strequal( prohibited_ea_names[i], unix_ea_name))
			return true;
	}
	if (strncasecmp_m(unix_ea_name, SAMBA_XATTR_DOSSTREAM_PREFIX,
			strlen(SAMBA_XATTR_DOSSTREAM_PREFIX)) == 0) {
		return true;
	}
	return false;
}

Project: samba
File: trans2.c

static NTSTATUS fill_ea_chained_buffer(TALLOC_CTX *mem_ctx,
				       char *pdata,
				       unsigned int total_data_size,
				       unsigned int *ret_data_size,
				       connection_struct *conn,
				       struct ea_list *ea_list)
{
	uint8_t *p = (uint8_t *)pdata;
	uint8_t *last_start = NULL;
	bool do_store_data = (pdata != NULL);

	*ret_data_size = 0;

	if (!lp_ea_support(SNUM(conn))) {
		return NT_STATUS_NO_EAS_ON_FILE;
	}

	for (; ea_list; ea_list = ea_list->next) {
		size_t dos_namelen;
		fstring dos_ea_name;
		size_t this_size;
		size_t pad = 0;

		if (last_start != NULL && do_store_data) {
			SIVAL(last_start, 0, PTR_DIFF(p, last_start));
		}
		last_start = p;

		push_ascii_fstring(dos_ea_name, ea_list->ea.name);
		dos_namelen = strlen(dos_ea_name);
		if (dos_namelen > 255 || dos_namelen == 0) {
			return NT_STATUS_INTERNAL_ERROR;
		}
		if (ea_list->ea.value.length > 65535) {
			return NT_STATUS_INTERNAL_ERROR;
		}

		this_size = 0x08 + dos_namelen + 1 + ea_list->ea.value.length;

		if (ea_list->next) {
			pad = (4 - (this_size % 4)) % 4;
			this_size += pad;
		}

		if (do_store_data) {
			if (this_size > total_data_size) {
				return NT_STATUS_INFO_LENGTH_MISMATCH;
			}

			/* We know we have room. */
			SIVAL(p, 0x00, 0); /* next offset */
			SCVAL(p, 0x04, ea_list->ea.flags);
			SCVAL(p, 0x05, dos_namelen);
			SSVAL(p, 0x06, ea_list->ea.value.length);
			strlcpy((char *)(p+0x08), dos_ea_name, dos_namelen+1);
			memcpy(p + 0x08 + dos_namelen + 1, ea_list->ea.value.data, ea_list->ea.value.length);
			if (pad) {
				memset(p + 0x08 + dos_namelen + 1 + ea_list->ea.value.length,
					'\0',
					pad);
			}
			total_data_size -= this_size;
		}

		p += this_size;
	}

	*ret_data_size = PTR_DIFF(p, pdata);
	DEBUG(10,("fill_ea_chained_buffer: data_size = %u\n", *ret_data_size));
	return NT_STATUS_OK;
}

Project: samba
File: trans2.c

NTSTATUS check_access_fsp(struct files_struct *fsp,
			  uint32_t access_mask)
{
	if (!fsp->fsp_flags.is_fsa) {
		return smbd_check_access_rights_fsp(fsp->conn->cwd_fsp,
						    fsp,
						    false,
						    access_mask);
	}
	if (!(fsp->access_mask & access_mask)) {
		return NT_STATUS_ACCESS_DENIED;
	}
	return NT_STATUS_OK;
}

Project: samba
File: trans2.c

static NTSTATUS smb_file_position_information(connection_struct *conn,
				const char *pdata,
				int total_data,
				files_struct *fsp)
{
	uint64_t position_information;

	if (total_data < 8) {
		return NT_STATUS_INVALID_PARAMETER;
	}

	if (fsp == NULL) {
		/* Ignore on pathname based set. */
		return NT_STATUS_OK;
	}

	position_information = (uint64_t)IVAL(pdata,0);
	position_information |= (((uint64_t)IVAL(pdata,4)) << 32);

	DEBUG(10,("smb_file_position_information: Set file position "
		  "information for file %s to %.0f\n", fsp_str_dbg(fsp),
		  (double)position_information));
	fh_set_position_information(fsp->fh, position_information);
	return NT_STATUS_OK;
}

Project: samba
File: trans2.c

static NTSTATUS smb_set_file_disposition_info(connection_struct *conn,
				const char *pdata,
				int total_data,
				files_struct *fsp,
				struct smb_filename *smb_fname)
{
	NTSTATUS status = NT_STATUS_OK;
	bool delete_on_close;
	uint32_t dosmode = 0;

	if (total_data < 1) {
		return NT_STATUS_INVALID_PARAMETER;
	}

	if (fsp == NULL) {
		return NT_STATUS_INVALID_HANDLE;
	}

	delete_on_close = (CVAL(pdata,0) ? True : False);
	dosmode = fdos_mode(fsp);

	DEBUG(10,("smb_set_file_disposition_info: file %s, dosmode = %u, "
		"delete_on_close = %u\n",
		smb_fname_str_dbg(smb_fname),
		(unsigned int)dosmode,
		(unsigned int)delete_on_close ));

	if (delete_on_close) {
		status = can_set_delete_on_close(fsp, dosmode);
		if (!NT_STATUS_IS_OK(status)) {
			return status;
		}
	}

	/* The set is across all open files on this dev/inode pair. */
	if (!set_delete_on_close(fsp, delete_on_close,
				 conn->session_info->security_token,
				 conn->session_info->unix_token)) {
		return NT_STATUS_ACCESS_DENIED;
	}
	return NT_STATUS_OK;
}

Project: samba
File: trans2.c

static char *store_file_unix_basic_info2(connection_struct *conn,
				char *pdata,
				files_struct *fsp,
				const SMB_STRUCT_STAT *psbuf)
{
	uint32_t file_flags = 0;
	uint32_t flags_mask = 0;

	pdata = store_file_unix_basic(conn, pdata, fsp, psbuf);

	/* Create (birth) time 64 bit */
	put_long_date_full_timespec(TIMESTAMP_SET_NT_OR_BETTER,pdata, &psbuf->st_ex_btime);
	pdata += 8;

	map_info2_flags_from_sbuf(psbuf, &file_flags, &flags_mask);
	SIVAL(pdata, 0, file_flags); /* flags */
	SIVAL(pdata, 4, flags_mask); /* mask */
	pdata += 8;

	return pdata;
}

Project: samba
File: trans2.c

static void samba_extended_info_version(struct smb_extended_info *extended_info)
{
	SMB_ASSERT(extended_info != NULL);

	extended_info->samba_magic = SAMBA_EXTENDED_INFO_MAGIC;
	extended_info->samba_version = ((SAMBA_VERSION_MAJOR & 0xff) << 24)
				       | ((SAMBA_VERSION_MINOR & 0xff) << 16)
				       | ((SAMBA_VERSION_RELEASE & 0xff) << 8);
#ifdef SAMBA_VERSION_REVISION
	extended_info->samba_version |= (tolower(*SAMBA_VERSION_REVISION) - 'a' + 1) & 0xff;
#endif
	extended_info->samba_subversion = 0;
#ifdef SAMBA_VERSION_RC_RELEASE
	extended_info->samba_subversion |= (SAMBA_VERSION_RC_RELEASE & 0xff) << 24;
#else
#ifdef SAMBA_VERSION_PRE_RELEASE
	extended_info->samba_subversion |= (SAMBA_VERSION_PRE_RELEASE & 0xff) << 16;
#endif
#endif
#ifdef SAMBA_VERSION_VENDOR_PATCH
	extended_info->samba_subversion |= (SAMBA_VERSION_VENDOR_PATCH & 0xffff);
#endif
	extended_info->samba_gitcommitdate = 0;
#ifdef SAMBA_VERSION_COMMIT_TIME
	unix_to_nt_time(&extended_info->samba_gitcommitdate, SAMBA_VERSION_COMMIT_TIME);
#endif

	memset(extended_info->samba_version_string, 0,
	       sizeof(extended_info->samba_version_string));

	snprintf (extended_info->samba_version_string,
		  sizeof(extended_info->samba_version_string),
		  "%s", samba_version_string());
}

Project: samba
File: trans2.c

static void call_trans2qpipeinfo(connection_struct *conn,
				 struct smb_request *req,
				 unsigned int tran_call,
				 char **pparams, int total_params,
				 char **ppdata, int total_data,
				 unsigned int max_data_bytes)
{
	char *params = *pparams;
	char *pdata = *ppdata;
	unsigned int data_size = 0;
	unsigned int param_size = 2;
	uint16_t info_level;
	files_struct *fsp;

	if (!params) {
		reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
		return;
	}

	if (total_params < 4) {
		reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
		return;
	}

	fsp = file_fsp(req, SVAL(params,0));
	if (!fsp_is_np(fsp)) {
		reply_nterror(req, NT_STATUS_INVALID_HANDLE);
		return;
	}

	info_level = SVAL(params,2);

	*pparams = (char *)SMB_REALLOC(*pparams,2);
	if (*pparams == NULL) {
		reply_nterror(req, NT_STATUS_NO_MEMORY);
		return;
	}
	params = *pparams;
	SSVAL(params,0,0);
	if (max_data_bytes + DIR_ENTRY_SAFETY_MARGIN < max_data_bytes) {
		reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
		return;
	}
	data_size = max_data_bytes + DIR_ENTRY_SAFETY_MARGIN;
	*ppdata = (char *)SMB_REALLOC(*ppdata, data_size); 
	if (*ppdata == NULL ) {
		reply_nterror(req, NT_STATUS_NO_MEMORY);
		return;
	}
	pdata = *ppdata;

	switch (info_level) {
		case SMB_FILE_STANDARD_INFORMATION:
			memset(pdata,0,24);
			SOFF_T(pdata,0,4096LL);
			SIVAL(pdata,16,1);
			SIVAL(pdata,20,1);
			data_size = 24;
			break;

		default:
			reply_nterror(req, NT_STATUS_INVALID_LEVEL);
			return;
	}

	send_trans2_replies(conn, req, NT_STATUS_OK, params, param_size, *ppdata, data_size,
			    max_data_bytes);

	return;
}

Project: samba
File: trans2.c

static NTSTATUS smb_set_fsquota(connection_struct *conn,
			struct smb_request *req,
			files_struct *fsp,
			const DATA_BLOB *qdata)
{
	const struct loadparm_substitution *lp_sub =
		loadparm_s3_global_substitution();
	NTSTATUS status;
	SMB_NTQUOTA_STRUCT quotas;

	ZERO_STRUCT(quotas);

	/* access check */
	if ((get_current_uid(conn) != 0) || !CAN_WRITE(conn)) {
		DEBUG(3, ("set_fsquota: access_denied service [%s] user [%s]\n",
			  lp_servicename(talloc_tos(), lp_sub, SNUM(conn)),
			  conn->session_info->unix_info->unix_name));
		return NT_STATUS_ACCESS_DENIED;
	}

	if (!check_fsp_ntquota_handle(conn, req,
				      fsp)) {
		DEBUG(1, ("set_fsquota: no valid QUOTA HANDLE\n"));
		return NT_STATUS_INVALID_HANDLE;
	}

	/* note: normally there're 48 bytes,
	 * but we didn't use the last 6 bytes for now
	 * --metze
	 */
	if (qdata->length < 42) {
		DEBUG(0,("set_fsquota: requires total_data(%u) >= 42 bytes!\n",
			(unsigned int)qdata->length));
		return NT_STATUS_INVALID_PARAMETER;
	}

	/* unknown_1 24 NULL bytes in pdata*/

	/* the soft quotas 8 bytes (uint64_t)*/
	quotas.softlim = BVAL(qdata->data,24);

	/* the hard quotas 8 bytes (uint64_t)*/
	quotas.hardlim = BVAL(qdata->data,32);

	/* quota_flags 2 bytes **/
	quotas.qflags = SVAL(qdata->data,40);

	/* unknown_2 6 NULL bytes follow*/

	/* now set the quotas */
	if (vfs_set_ntquota(fsp, SMB_USER_FS_QUOTA_TYPE, NULL, &quotas)!=0) {
		DEBUG(1, ("vfs_set_ntquota() failed for service [%s]\n",
			  lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
		status =  map_nt_error_from_unix(errno);
	} else {
		status = NT_STATUS_OK;
	}
	return status;
}

Project: samba
File: trans2.c

static bool check_msdfs_link(struct files_struct *dirfsp,
			     struct smb_filename *atname,
			     struct smb_filename *smb_fname)
{
	int saved_errno = errno;
	if(lp_host_msdfs() &&
		lp_msdfs_root(SNUM(dirfsp->conn)) &&
		is_msdfs_link(dirfsp, atname)) {

		/*
		 * Copy the returned stat struct from the relative
		 * to the full pathname.
		 */
		smb_fname->st = atname->st;

		DEBUG(5,("check_msdfs_link: Masquerading msdfs link %s "
			"as a directory\n",
			smb_fname->base_name));
		smb_fname->st.st_ex_mode =
			(smb_fname->st.st_ex_mode & 0xFFF) | S_IFDIR;
		errno = saved_errno;
		return true;
	}
	errno = saved_errno;
	return false;
}