X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source3%2Fsmbd%2Fnttrans.c;h=8bb121a13515fe6c43a36cf9550485d642200e94;hb=11f2583420310e0278188935f31be3131eb85fd4;hp=a0a3f59e0811608605945e1f75972c324be3ac4e;hpb=fe72740e8221575921c22030d6d4fcb19201b03b;p=samba.git diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c index a0a3f59e081..8bb121a1351 100644 --- a/source3/smbd/nttrans.c +++ b/source3/smbd/nttrans.c @@ -29,6 +29,9 @@ #include "auth.h" #include "smbprofile.h" #include "libsmb/libsmb.h" +#include "lib/util_ea.h" +#include "librpc/gen_ndr/ndr_quota.h" +#include "librpc/gen_ndr/ndr_security.h" extern const struct generic_mapping file_generic_mapping; @@ -66,8 +69,8 @@ static void send_nt_replies(connection_struct *conn, int params_sent_thistime, data_sent_thistime, total_sent_thistime; int alignment_offset = 1; int data_alignment_offset = 0; - struct smbd_server_connection *sconn = req->sconn; - int max_send = sconn->smb1.sessions.max_send; + struct smbXsrv_connection *xconn = req->xconn; + int max_send = xconn->smb1.sessions.max_send; /* * If there genuinely are no parameters or data to send just send @@ -82,7 +85,7 @@ static void send_nt_replies(connection_struct *conn, __LINE__,__FILE__); } show_msg((char *)req->outbuf); - if (!srv_send_smb(sconn, + if (!srv_send_smb(xconn, (char *)req->outbuf, true, req->seqnum+1, IS_CONN_ENCRYPTED(conn), @@ -246,7 +249,7 @@ static void send_nt_replies(connection_struct *conn, /* Send the packet */ show_msg((char *)req->outbuf); - if (!srv_send_smb(sconn, + if (!srv_send_smb(xconn, (char *)req->outbuf, true, req->seqnum+1, IS_CONN_ENCRYPTED(conn), @@ -316,7 +319,7 @@ static void do_ntcreate_pipe_open(connection_struct *conn, char *fname = NULL; uint16_t pnum = FNUM_FIELD_INVALID; char *p = NULL; - uint32 flags = IVAL(req->vwv+3, 1); + uint32_t flags = IVAL(req->vwv+3, 1); TALLOC_CTX *ctx = talloc_tos(); srvstr_pull_req_talloc(ctx, req, &fname, req->buf, STR_TERMINATE); @@ -437,17 +440,17 @@ void reply_ntcreate_and_X(struct smb_request *req) connection_struct *conn = req->conn; struct smb_filename *smb_fname = NULL; char *fname = NULL; - uint32 flags; - uint32 access_mask; - uint32 file_attributes; - uint32 share_access; - uint32 create_disposition; - uint32 create_options; - uint16 root_dir_fid; + uint32_t flags; + uint32_t access_mask; + uint32_t file_attributes; + uint32_t share_access; + uint32_t create_disposition; + uint32_t create_options; + uint16_t root_dir_fid; uint64_t allocation_size; /* Breakout the oplock request bits so we can set the reply bits separately. */ - uint32 fattr=0; + uint32_t fattr=0; off_t file_len = 0; int info = 0; files_struct *fsp = NULL; @@ -456,11 +459,11 @@ void reply_ntcreate_and_X(struct smb_request *req) struct timespec c_timespec; struct timespec a_timespec; struct timespec m_timespec; - struct timespec write_time_ts; NTSTATUS status; int oplock_request; uint8_t oplock_granted = NO_OPLOCK_RETURN; struct case_semantics_state *case_state = NULL; + uint32_t ucf_flags; TALLOC_CTX *ctx = talloc_tos(); START_PROFILE(SMBntcreateX); @@ -476,7 +479,7 @@ void reply_ntcreate_and_X(struct smb_request *req) share_access = IVAL(req->vwv+15, 1); create_disposition = IVAL(req->vwv+17, 1); create_options = IVAL(req->vwv+19, 1); - root_dir_fid = (uint16)IVAL(req->vwv+5, 1); + root_dir_fid = (uint16_t)IVAL(req->vwv+5, 1); allocation_size = BVAL(req->vwv+9, 1); @@ -534,11 +537,12 @@ void reply_ntcreate_and_X(struct smb_request *req) } } + ucf_flags = filename_create_ucf_flags(req, create_disposition); status = filename_convert(ctx, conn, - req->flags2 & FLAGS2_DFS_PATHNAMES, fname, - 0, + ucf_flags, + NULL, NULL, &smb_fname); @@ -573,15 +577,17 @@ void reply_ntcreate_and_X(struct smb_request *req) create_options, /* create_options */ file_attributes, /* file_attributes */ oplock_request, /* oplock_request */ + NULL, /* lease */ allocation_size, /* allocation_size */ 0, /* private_flags */ NULL, /* sd */ NULL, /* ea_list */ &fsp, /* result */ - &info); /* pinfo */ + &info, /* pinfo */ + NULL, NULL); /* create context */ if (!NT_STATUS_IS_OK(status)) { - if (open_was_deferred(req->sconn, req->mid)) { + if (open_was_deferred(req->xconn, req->mid)) { /* We have re-scheduled this call, no error. */ goto out; } @@ -655,14 +661,6 @@ void reply_ntcreate_and_X(struct smb_request *req) fattr = FILE_ATTRIBUTE_NORMAL; } - /* Deal with other possible opens having a modified - write time. JRA. */ - ZERO_STRUCT(write_time_ts); - get_file_infos(fsp->file_id, 0, NULL, &write_time_ts); - if (!null_timespec(write_time_ts)) { - update_stat_ex_mtime(&smb_fname->st, write_time_ts); - } - /* Create time. */ create_timespec = get_create_timespec(conn, fsp, smb_fname); a_timespec = smb_fname->st.st_ex_atime; @@ -692,17 +690,20 @@ void reply_ntcreate_and_X(struct smb_request *req) p += 8; if (flags & EXTENDED_RESPONSE_REQUIRED) { uint16_t file_status = (NO_EAS|NO_SUBSTREAMS|NO_REPARSETAG); - size_t num_names = 0; unsigned int num_streams = 0; struct stream_struct *streams = NULL; - /* Do we have any EA's ? */ - status = get_ea_names_from_file(ctx, conn, fsp, - smb_fname->base_name, NULL, &num_names); - if (NT_STATUS_IS_OK(status) && num_names) { - file_status &= ~NO_EAS; + if (lp_ea_support(SNUM(conn))) { + size_t num_names = 0; + /* Do we have any EA's ? */ + status = get_ea_names_from_file( + ctx, conn, fsp, smb_fname, NULL, &num_names); + if (NT_STATUS_IS_OK(status) && num_names) { + file_status &= ~NO_EAS; + } } - status = vfs_streaminfo(conn, NULL, smb_fname->base_name, ctx, + + status = vfs_streaminfo(conn, NULL, smb_fname, ctx, &num_streams, &streams); /* There is always one stream, ::$DATA. */ if (NT_STATUS_IS_OK(status) && num_streams > 1) { @@ -715,9 +716,10 @@ void reply_ntcreate_and_X(struct smb_request *req) SCVAL(p,0,fsp->is_directory ? 1 : 0); if (flags & EXTENDED_RESPONSE_REQUIRED) { - uint32 perms = 0; + uint32_t perms = 0; p += 25; if (fsp->is_directory || + fsp->can_write || can_write_to_file(conn, smb_fname)) { perms = FILE_GENERIC_ALL; } else { @@ -740,9 +742,9 @@ void reply_ntcreate_and_X(struct smb_request *req) static void do_nt_transact_create_pipe(connection_struct *conn, struct smb_request *req, - uint16 **ppsetup, uint32 setup_count, - char **ppparams, uint32 parameter_count, - char **ppdata, uint32 data_count) + uint16_t **ppsetup, uint32_t setup_count, + char **ppparams, uint32_t parameter_count, + char **ppdata, uint32_t data_count) { char *fname = NULL; char *params = *ppparams; @@ -750,7 +752,7 @@ static void do_nt_transact_create_pipe(connection_struct *conn, char *p = NULL; NTSTATUS status; size_t param_len; - uint32 flags; + uint32_t flags; TALLOC_CTX *ctx = talloc_tos(); /* @@ -765,9 +767,25 @@ static void do_nt_transact_create_pipe(connection_struct *conn, flags = IVAL(params,0); - srvstr_get_path(ctx, params, req->flags2, &fname, params+53, - parameter_count-53, STR_TERMINATE, + if (req->posix_pathnames) { + srvstr_get_path_posix(ctx, + params, + req->flags2, + &fname, + params+53, + parameter_count-53, + STR_TERMINATE, &status); + } else { + srvstr_get_path(ctx, + params, + req->flags2, + &fname, + params+53, + parameter_count-53, + STR_TERMINATE, + &status); + } if (!NT_STATUS_IS_OK(status)) { reply_nterror(req, status); return; @@ -830,20 +848,48 @@ static void do_nt_transact_create_pipe(connection_struct *conn, return; } +/********************************************************************* + Windows seems to do canonicalization of inheritance bits. Do the + same. +*********************************************************************/ + +static void canonicalize_inheritance_bits(struct security_descriptor *psd) +{ + bool set_auto_inherited = false; + + /* + * We need to filter out the + * SEC_DESC_DACL_AUTO_INHERITED|SEC_DESC_DACL_AUTO_INHERIT_REQ + * bits. If both are set we store SEC_DESC_DACL_AUTO_INHERITED + * as this alters whether SEC_ACE_FLAG_INHERITED_ACE is set + * when an ACE is inherited. Otherwise we zero these bits out. + * See: + * + * http://social.msdn.microsoft.com/Forums/eu/os_fileservices/thread/11f77b68-731e-407d-b1b3-064750716531 + * + * for details. + */ + + if ((psd->type & (SEC_DESC_DACL_AUTO_INHERITED|SEC_DESC_DACL_AUTO_INHERIT_REQ)) + == (SEC_DESC_DACL_AUTO_INHERITED|SEC_DESC_DACL_AUTO_INHERIT_REQ)) { + set_auto_inherited = true; + } + + psd->type &= ~(SEC_DESC_DACL_AUTO_INHERITED|SEC_DESC_DACL_AUTO_INHERIT_REQ); + if (set_auto_inherited) { + psd->type |= SEC_DESC_DACL_AUTO_INHERITED; + } +} + /**************************************************************************** Internal fn to set security descriptors. ****************************************************************************/ -NTSTATUS set_sd(files_struct *fsp, uint8_t *data, uint32_t sd_len, +NTSTATUS set_sd(files_struct *fsp, struct security_descriptor *psd, uint32_t security_info_sent) { - struct security_descriptor *psd = NULL; NTSTATUS status; - if (sd_len == 0) { - return NT_STATUS_INVALID_PARAMETER; - } - if (!CAN_WRITE(fsp->conn)) { return NT_STATUS_ACCESS_DENIED; } @@ -852,10 +898,10 @@ NTSTATUS set_sd(files_struct *fsp, uint8_t *data, uint32_t sd_len, return NT_STATUS_OK; } - status = unmarshall_sec_desc(talloc_tos(), data, sd_len, &psd); - - if (!NT_STATUS_IS_OK(status)) { - return status; + if (S_ISLNK(fsp->fsp_name->st.st_ex_mode)) { + DEBUG(10, ("ACL set on symlink %s denied.\n", + fsp_str_dbg(fsp))); + return NT_STATUS_ACCESS_DENIED; } if (psd->owner_sid == NULL) { @@ -867,13 +913,8 @@ NTSTATUS set_sd(files_struct *fsp, uint8_t *data, uint32_t sd_len, /* Ensure we have at least one thing set. */ if ((security_info_sent & (SECINFO_OWNER|SECINFO_GROUP|SECINFO_DACL|SECINFO_SACL)) == 0) { - if (security_info_sent & SECINFO_LABEL) { - /* Only consider SECINFO_LABEL if no other - bits are set. Just like W2K3 we don't - store this. */ - return NT_STATUS_OK; - } - return NT_STATUS_INVALID_PARAMETER; + /* Just like W2K3 */ + return NT_STATUS_OK; } /* Ensure we have the rights to do this. */ @@ -909,6 +950,8 @@ NTSTATUS set_sd(files_struct *fsp, uint8_t *data, uint32_t sd_len, } } + canonicalize_inheritance_bits(psd); + if (DEBUGLEVEL >= 10) { DEBUG(10,("set_sd for file %s\n", fsp_str_dbg(fsp))); NDR_PRINT_DEBUG(security_descriptor, psd); @@ -922,34 +965,26 @@ NTSTATUS set_sd(files_struct *fsp, uint8_t *data, uint32_t sd_len, } /**************************************************************************** - Read a list of EA names and data from an incoming data buffer. Create an ea_list with them. + Internal fn to set security descriptors from a data blob. ****************************************************************************/ -struct ea_list *read_nttrans_ea_list(TALLOC_CTX *ctx, const char *pdata, size_t data_size) +NTSTATUS set_sd_blob(files_struct *fsp, uint8_t *data, uint32_t sd_len, + uint32_t security_info_sent) { - struct ea_list *ea_list_head = NULL; - size_t offset = 0; + struct security_descriptor *psd = NULL; + NTSTATUS status; - if (data_size < 4) { - return NULL; + if (sd_len == 0) { + return NT_STATUS_INVALID_PARAMETER; } - while (offset + 4 <= data_size) { - size_t next_offset = IVAL(pdata,offset); - struct ea_list *eal = read_ea_list_entry(ctx, pdata + offset + 4, data_size - offset - 4, NULL); - - if (!eal) { - return NULL; - } + status = unmarshall_sec_desc(talloc_tos(), data, sd_len, &psd); - DLIST_ADD_END(ea_list_head, eal, struct ea_list *); - if (next_offset == 0) { - break; - } - offset += next_offset; + if (!NT_STATUS_IS_OK(status)) { + return status; } - return ea_list_head; + return set_sd(fsp, psd, security_info_sent); } /**************************************************************************** @@ -958,36 +993,35 @@ struct ea_list *read_nttrans_ea_list(TALLOC_CTX *ctx, const char *pdata, size_t static void call_nt_transact_create(connection_struct *conn, struct smb_request *req, - uint16 **ppsetup, uint32 setup_count, - char **ppparams, uint32 parameter_count, - char **ppdata, uint32 data_count, - uint32 max_data_count) + uint16_t **ppsetup, uint32_t setup_count, + char **ppparams, uint32_t parameter_count, + char **ppdata, uint32_t data_count, + uint32_t max_data_count) { struct smb_filename *smb_fname = NULL; char *fname = NULL; char *params = *ppparams; char *data = *ppdata; /* Breakout the oplock request bits so we can set the reply bits separately. */ - uint32 fattr=0; + uint32_t fattr=0; off_t file_len = 0; int info = 0; files_struct *fsp = NULL; char *p = NULL; - uint32 flags; - uint32 access_mask; - uint32 file_attributes; - uint32 share_access; - uint32 create_disposition; - uint32 create_options; - uint32 sd_len; + uint32_t flags; + uint32_t access_mask; + uint32_t file_attributes; + uint32_t share_access; + uint32_t create_disposition; + uint32_t create_options; + uint32_t sd_len; struct security_descriptor *sd = NULL; - uint32 ea_len; - uint16 root_dir_fid; + uint32_t ea_len; + uint16_t root_dir_fid; struct timespec create_timespec; struct timespec c_timespec; struct timespec a_timespec; struct timespec m_timespec; - struct timespec write_time_ts; struct ea_list *ea_list = NULL; NTSTATUS status; size_t param_len; @@ -995,6 +1029,7 @@ static void call_nt_transact_create(connection_struct *conn, int oplock_request; uint8_t oplock_granted; struct case_semantics_state *case_state = NULL; + uint32_t ucf_flags; TALLOC_CTX *ctx = talloc_tos(); DEBUG(5,("call_nt_transact_create\n")); @@ -1034,7 +1069,7 @@ static void call_nt_transact_create(connection_struct *conn, create_options = IVAL(params,32); sd_len = IVAL(params,36); ea_len = IVAL(params,40); - root_dir_fid = (uint16)IVAL(params,4); + root_dir_fid = (uint16_t)IVAL(params,4); allocation_size = BVAL(params,12); /* @@ -1043,6 +1078,60 @@ static void call_nt_transact_create(connection_struct *conn, */ create_options &= ~NTCREATEX_OPTIONS_MUST_IGNORE_MASK; + if (req->posix_pathnames) { + srvstr_get_path_posix(ctx, + params, + req->flags2, + &fname, + params+53, + parameter_count-53, + STR_TERMINATE, + &status); + } else { + srvstr_get_path(ctx, + params, + req->flags2, + &fname, + params+53, + parameter_count-53, + STR_TERMINATE, + &status); + } + if (!NT_STATUS_IS_OK(status)) { + reply_nterror(req, status); + goto out; + } + + if (file_attributes & FILE_FLAG_POSIX_SEMANTICS) { + case_state = set_posix_case_semantics(ctx, conn); + if (!case_state) { + reply_nterror(req, NT_STATUS_NO_MEMORY); + goto out; + } + } + + ucf_flags = filename_create_ucf_flags(req, create_disposition); + status = filename_convert(ctx, + conn, + fname, + ucf_flags, + NULL, + NULL, + &smb_fname); + + TALLOC_FREE(case_state); + + 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); + goto out; + } + reply_nterror(req, status); + goto out; + } + /* Ensure the data_len is correct for the sd and ea values given. */ if ((ea_len + sd_len > data_count) || (ea_len > data_count) || (sd_len > data_count) @@ -1093,43 +1182,27 @@ static void call_nt_transact_create(connection_struct *conn, reply_nterror(req, NT_STATUS_INVALID_PARAMETER); goto out; } - } - - srvstr_get_path(ctx, params, req->flags2, &fname, - params+53, parameter_count-53, - STR_TERMINATE, &status); - if (!NT_STATUS_IS_OK(status)) { - reply_nterror(req, status); - goto out; - } - - if (file_attributes & FILE_FLAG_POSIX_SEMANTICS) { - case_state = set_posix_case_semantics(ctx, conn); - if (!case_state) { - reply_nterror(req, NT_STATUS_NO_MEMORY); - goto out; - } - } - - status = filename_convert(ctx, - conn, - req->flags2 & FLAGS2_DFS_PATHNAMES, - fname, - 0, - NULL, - &smb_fname); - TALLOC_FREE(case_state); + if (!req->posix_pathnames && + ea_list_has_invalid_name(ea_list)) { + /* Realloc the size of parameters and data we will return */ + if (flags & EXTENDED_RESPONSE_REQUIRED) { + /* Extended response is 32 more byyes. */ + param_len = 101; + } else { + param_len = 69; + } + params = nttrans_realloc(ppparams, param_len); + if(params == NULL) { + reply_nterror(req, NT_STATUS_NO_MEMORY); + goto out; + } - 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); + memset(params, '\0', param_len); + send_nt_replies(conn, req, STATUS_INVALID_EA_NAME, + params, param_len, NULL, 0); goto out; } - reply_nterror(req, status); - goto out; } oplock_request = (flags & REQUEST_OPLOCK) ? EXCLUSIVE_OPLOCK : 0; @@ -1156,15 +1229,17 @@ static void call_nt_transact_create(connection_struct *conn, create_options, /* create_options */ file_attributes, /* file_attributes */ oplock_request, /* oplock_request */ + NULL, /* lease */ allocation_size, /* allocation_size */ 0, /* private_flags */ sd, /* sd */ ea_list, /* ea_list */ &fsp, /* result */ - &info); /* pinfo */ + &info, /* pinfo */ + NULL, NULL); /* create context */ if(!NT_STATUS_IS_OK(status)) { - if (open_was_deferred(req->sconn, req->mid)) { + if (open_was_deferred(req->xconn, req->mid)) { /* We have re-scheduled this call, no error. */ return; } @@ -1235,14 +1310,6 @@ static void call_nt_transact_create(connection_struct *conn, fattr = FILE_ATTRIBUTE_NORMAL; } - /* Deal with other possible opens having a modified - write time. JRA. */ - ZERO_STRUCT(write_time_ts); - get_file_infos(fsp->file_id, 0, NULL, &write_time_ts); - if (!null_timespec(write_time_ts)) { - update_stat_ex_mtime(&smb_fname->st, write_time_ts); - } - /* Create time. */ create_timespec = get_create_timespec(conn, fsp, smb_fname); a_timespec = smb_fname->st.st_ex_atime; @@ -1272,17 +1339,20 @@ static void call_nt_transact_create(connection_struct *conn, p += 8; if (flags & EXTENDED_RESPONSE_REQUIRED) { uint16_t file_status = (NO_EAS|NO_SUBSTREAMS|NO_REPARSETAG); - size_t num_names = 0; unsigned int num_streams = 0; struct stream_struct *streams = NULL; - /* Do we have any EA's ? */ - status = get_ea_names_from_file(ctx, conn, fsp, - smb_fname->base_name, NULL, &num_names); - if (NT_STATUS_IS_OK(status) && num_names) { - file_status &= ~NO_EAS; + if (lp_ea_support(SNUM(conn))) { + size_t num_names = 0; + /* Do we have any EA's ? */ + status = get_ea_names_from_file( + ctx, conn, fsp, smb_fname, NULL, &num_names); + if (NT_STATUS_IS_OK(status) && num_names) { + file_status &= ~NO_EAS; + } } - status = vfs_streaminfo(conn, NULL, smb_fname->base_name, ctx, + + status = vfs_streaminfo(conn, NULL, smb_fname, ctx, &num_streams, &streams); /* There is always one stream, ::$DATA. */ if (NT_STATUS_IS_OK(status) && num_streams > 1) { @@ -1295,9 +1365,10 @@ static void call_nt_transact_create(connection_struct *conn, SCVAL(p,0,fsp->is_directory ? 1 : 0); if (flags & EXTENDED_RESPONSE_REQUIRED) { - uint32 perms = 0; + uint32_t perms = 0; p += 25; if (fsp->is_directory || + fsp->can_write || can_write_to_file(conn, smb_fname)) { perms = FILE_GENERIC_ALL; } else { @@ -1322,14 +1393,17 @@ static void call_nt_transact_create(connection_struct *conn, void reply_ntcancel(struct smb_request *req) { + struct smbXsrv_connection *xconn = req->xconn; + struct smbd_server_connection *sconn = req->sconn; + /* * Go through and cancel any pending change notifies. */ START_PROFILE(SMBntcancel); - srv_cancel_sign_response(req->sconn); - remove_pending_change_notify_requests_by_mid(req->sconn, req->mid); - remove_pending_lock_requests_by_mid_smb1(req->sconn, req->mid); + srv_cancel_sign_response(xconn); + remove_pending_change_notify_requests_by_mid(sconn, req->mid); + remove_pending_lock_requests_by_mid_smb1(sconn, req->mid); DEBUG(3,("reply_ntcancel: cancel called on mid = %llu.\n", (unsigned long long)req->mid)); @@ -1347,10 +1421,10 @@ static NTSTATUS copy_internals(TALLOC_CTX *ctx, struct smb_request *req, struct smb_filename *smb_fname_src, struct smb_filename *smb_fname_dst, - uint32 attrs) + uint32_t attrs) { files_struct *fsp1,*fsp2; - uint32 fattr; + uint32_t fattr; int info; off_t ret=-1; NTSTATUS status = NT_STATUS_OK; @@ -1403,12 +1477,14 @@ static NTSTATUS copy_internals(TALLOC_CTX *ctx, 0, /* create_options */ FILE_ATTRIBUTE_NORMAL, /* file_attributes */ NO_OPLOCK, /* oplock_request */ + NULL, /* lease */ 0, /* allocation_size */ 0, /* private_flags */ NULL, /* sd */ NULL, /* ea_list */ &fsp1, /* result */ - &info); /* pinfo */ + &info, /* pinfo */ + NULL, NULL); /* create context */ if (!NT_STATUS_IS_OK(status)) { goto out; @@ -1427,12 +1503,14 @@ static NTSTATUS copy_internals(TALLOC_CTX *ctx, 0, /* create_options */ fattr, /* file_attributes */ NO_OPLOCK, /* oplock_request */ + NULL, /* lease */ 0, /* allocation_size */ 0, /* private_flags */ NULL, /* sd */ NULL, /* ea_list */ &fsp2, /* result */ - &info); /* pinfo */ + &info, /* pinfo */ + NULL, NULL); /* create context */ if (!NT_STATUS_IS_OK(status)) { close_file(NULL, fsp1, ERROR_CLOSE); @@ -1496,10 +1574,10 @@ void reply_ntrename(struct smb_request *req) NTSTATUS status; bool src_has_wcard = False; bool dest_has_wcard = False; - uint32 attrs; - uint32_t ucf_flags_src = 0; - uint32_t ucf_flags_dst = 0; - uint16 rename_type; + uint32_t attrs; + uint32_t ucf_flags_src = ucf_flags_from_smb_request(req); + uint32_t ucf_flags_dst = ucf_flags_from_smb_request(req); + uint16_t rename_type; TALLOC_CTX *ctx = talloc_tos(); bool stream_rename = false; @@ -1521,7 +1599,7 @@ void reply_ntrename(struct smb_request *req) goto out; } - if (ms_has_wild(oldname)) { + if (!req->posix_pathnames && ms_has_wild(oldname)) { reply_nterror(req, NT_STATUS_OBJECT_PATH_SYNTAX_BAD); goto out; } @@ -1534,7 +1612,7 @@ void reply_ntrename(struct smb_request *req) goto out; } - if (!lp_posix_pathnames()) { + if (!req->posix_pathnames) { /* The newname must begin with a ':' if the oldname contains a ':'. */ if (strchr_m(oldname, ':')) { @@ -1551,16 +1629,16 @@ void reply_ntrename(struct smb_request *req) * destination's last component. */ if (rename_type == RENAME_FLAG_RENAME) { - ucf_flags_src = UCF_COND_ALLOW_WCARD_LCOMP; - ucf_flags_dst = UCF_COND_ALLOW_WCARD_LCOMP | UCF_SAVE_LCOMP; + ucf_flags_src |= UCF_COND_ALLOW_WCARD_LCOMP; + ucf_flags_dst |= UCF_COND_ALLOW_WCARD_LCOMP | UCF_SAVE_LCOMP; } /* rename_internals() calls unix_convert(), so don't call it here. */ status = filename_convert(ctx, conn, - req->flags2 & FLAGS2_DFS_PATHNAMES, oldname, ucf_flags_src, NULL, + NULL, &smb_fname_old); if (!NT_STATUS_IS_OK(status)) { if (NT_STATUS_EQUAL(status, @@ -1575,9 +1653,9 @@ void reply_ntrename(struct smb_request *req) } status = filename_convert(ctx, conn, - req->flags2 & FLAGS2_DFS_PATHNAMES, newname, ucf_flags_dst, + NULL, &dest_has_wcard, &smb_fname_new); if (!NT_STATUS_IS_OK(status)) { @@ -1647,7 +1725,7 @@ void reply_ntrename(struct smb_request *req) } if (!NT_STATUS_IS_OK(status)) { - if (open_was_deferred(req->sconn, req->mid)) { + if (open_was_deferred(req->xconn, req->mid)) { /* We have re-scheduled this call. */ goto out; } @@ -1676,17 +1754,17 @@ static void smbd_smb1_notify_reply(struct smb_request *req, static void call_nt_transact_notify_change(connection_struct *conn, struct smb_request *req, - uint16 **ppsetup, - uint32 setup_count, + uint16_t **ppsetup, + uint32_t setup_count, char **ppparams, - uint32 parameter_count, - char **ppdata, uint32 data_count, - uint32 max_data_count, - uint32 max_param_count) + uint32_t parameter_count, + char **ppdata, uint32_t data_count, + uint32_t max_data_count, + uint32_t max_param_count) { - uint16 *setup = *ppsetup; + uint16_t *setup = *ppsetup; files_struct *fsp; - uint32 filter; + uint32_t filter; NTSTATUS status; bool recursive; @@ -1728,8 +1806,10 @@ static void call_nt_transact_notify_change(connection_struct *conn, if (fsp->notify == NULL) { - status = change_notify_create(fsp, filter, recursive); - + status = change_notify_create(fsp, + max_param_count, + filter, + recursive); if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("change_notify_create returned %s\n", nt_errstr(status))); @@ -1783,10 +1863,10 @@ static void call_nt_transact_notify_change(connection_struct *conn, static void call_nt_transact_rename(connection_struct *conn, struct smb_request *req, - uint16 **ppsetup, uint32 setup_count, - char **ppparams, uint32 parameter_count, - char **ppdata, uint32 data_count, - uint32 max_data_count) + uint16_t **ppsetup, uint32_t setup_count, + char **ppparams, uint32_t parameter_count, + char **ppdata, uint32_t data_count, + uint32_t max_data_count) { char *params = *ppparams; char *new_name = NULL; @@ -1804,9 +1884,28 @@ static void call_nt_transact_rename(connection_struct *conn, if (!check_fsp(conn, req, fsp)) { return; } - srvstr_get_path_wcard(ctx, params, req->flags2, &new_name, params+4, - parameter_count - 4, - STR_TERMINATE, &status, &dest_has_wcard); + if (req->posix_pathnames) { + srvstr_get_path_wcard_posix(ctx, + params, + req->flags2, + &new_name, + params+4, + parameter_count - 4, + STR_TERMINATE, + &status, + &dest_has_wcard); + } else { + srvstr_get_path_wcard(ctx, + params, + req->flags2, + &new_name, + params+4, + parameter_count - 4, + STR_TERMINATE, + &status, + &dest_has_wcard); + } + if (!NT_STATUS_IS_OK(status)) { reply_nterror(req, status); return; @@ -1843,7 +1942,7 @@ static NTSTATUS get_null_nt_acl(TALLOC_CTX *mem_ctx, struct security_descriptor /**************************************************************************** Reply to query a security descriptor. - Callable from SMB2 and SMB2. + Callable from SMB1 and SMB2. If it returns NT_STATUS_BUFFER_TOO_SMALL, pdata_size is initialized with the required size. ****************************************************************************/ @@ -1858,6 +1957,7 @@ NTSTATUS smbd_do_query_security_desc(connection_struct *conn, { NTSTATUS status; struct security_descriptor *psd = NULL; + TALLOC_CTX *frame = talloc_stackframe(); /* * Get the permissions to return. @@ -1866,12 +1966,21 @@ NTSTATUS smbd_do_query_security_desc(connection_struct *conn, if ((security_info_wanted & SECINFO_SACL) && !(fsp->access_mask & SEC_FLAG_SYSTEM_SECURITY)) { DEBUG(10, ("Access to SACL denied.\n")); + TALLOC_FREE(frame); return NT_STATUS_ACCESS_DENIED; } if ((security_info_wanted & (SECINFO_DACL|SECINFO_OWNER|SECINFO_GROUP)) && !(fsp->access_mask & SEC_STD_READ_CONTROL)) { DEBUG(10, ("Access to DACL, OWNER, or GROUP denied.\n")); + TALLOC_FREE(frame); + return NT_STATUS_ACCESS_DENIED; + } + + if (S_ISLNK(fsp->fsp_name->st.st_ex_mode)) { + DEBUG(10, ("ACL get on symlink %s denied.\n", + fsp_str_dbg(fsp))); + TALLOC_FREE(frame); return NT_STATUS_ACCESS_DENIED; } @@ -1883,15 +1992,16 @@ NTSTATUS smbd_do_query_security_desc(connection_struct *conn, } if (!lp_nt_acl_support(SNUM(conn))) { - status = get_null_nt_acl(mem_ctx, &psd); + status = get_null_nt_acl(frame, &psd); } else if (security_info_wanted & SECINFO_LABEL) { /* Like W2K3 return a null object. */ - status = get_null_nt_acl(mem_ctx, &psd); + status = get_null_nt_acl(frame, &psd); } else { status = SMB_VFS_FGET_NT_ACL( - fsp, security_info_wanted, &psd); + fsp, security_info_wanted, frame, &psd); } if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); return status; } @@ -1940,7 +2050,7 @@ NTSTATUS smbd_do_query_security_desc(connection_struct *conn, } if (max_data_count < *psd_size) { - TALLOC_FREE(psd); + TALLOC_FREE(frame); return NT_STATUS_BUFFER_TOO_SMALL; } @@ -1948,11 +2058,11 @@ NTSTATUS smbd_do_query_security_desc(connection_struct *conn, ppmarshalled_sd, psd_size); if (!NT_STATUS_IS_OK(status)) { - TALLOC_FREE(psd); + TALLOC_FREE(frame); return status; } - TALLOC_FREE(psd); + TALLOC_FREE(frame); return NT_STATUS_OK; } @@ -1962,18 +2072,18 @@ NTSTATUS smbd_do_query_security_desc(connection_struct *conn, static void call_nt_transact_query_security_desc(connection_struct *conn, struct smb_request *req, - uint16 **ppsetup, - uint32 setup_count, + uint16_t **ppsetup, + uint32_t setup_count, char **ppparams, - uint32 parameter_count, + uint32_t parameter_count, char **ppdata, - uint32 data_count, - uint32 max_data_count) + uint32_t data_count, + uint32_t max_data_count) { char *params = *ppparams; char *data = *ppdata; size_t sd_size = 0; - uint32 security_info_wanted; + uint32_t security_info_wanted; files_struct *fsp = NULL; NTSTATUS status; uint8_t *marshalled_sd = NULL; @@ -2008,7 +2118,8 @@ static void call_nt_transact_query_security_desc(connection_struct *conn, status = smbd_do_query_security_desc(conn, talloc_tos(), fsp, - security_info_wanted, + security_info_wanted & + SMB_SUPPORTED_SECINFO_FLAGS, max_data_count, &marshalled_sd, &sd_size); @@ -2058,18 +2169,18 @@ static void call_nt_transact_query_security_desc(connection_struct *conn, static void call_nt_transact_set_security_desc(connection_struct *conn, struct smb_request *req, - uint16 **ppsetup, - uint32 setup_count, + uint16_t **ppsetup, + uint32_t setup_count, char **ppparams, - uint32 parameter_count, + uint32_t parameter_count, char **ppdata, - uint32 data_count, - uint32 max_data_count) + uint32_t data_count, + uint32_t max_data_count) { char *params= *ppparams; char *data = *ppdata; files_struct *fsp = NULL; - uint32 security_info_sent = 0; + uint32_t security_info_sent = 0; NTSTATUS status; if(parameter_count < 8) { @@ -2101,8 +2212,8 @@ static void call_nt_transact_set_security_desc(connection_struct *conn, return; } - status = set_sd(fsp, (uint8 *)data, data_count, security_info_sent); - + status = set_sd_blob(fsp, (uint8_t *)data, data_count, + security_info_sent & SMB_SUPPORTED_SECINFO_FLAGS); if (!NT_STATUS_IS_OK(status)) { reply_nterror(req, status); return; @@ -2119,19 +2230,19 @@ static void call_nt_transact_set_security_desc(connection_struct *conn, static void call_nt_transact_ioctl(connection_struct *conn, struct smb_request *req, - uint16 **ppsetup, uint32 setup_count, - char **ppparams, uint32 parameter_count, - char **ppdata, uint32 data_count, - uint32 max_data_count) + uint16_t **ppsetup, uint32_t setup_count, + char **ppparams, uint32_t parameter_count, + char **ppdata, uint32_t data_count, + uint32_t max_data_count) { NTSTATUS status; - uint32 function; - uint16 fidnum; + uint32_t function; + uint16_t fidnum; files_struct *fsp; - uint8 isFSctl; - uint8 compfilter; + uint8_t isFSctl; + uint8_t compfilter; char *out_data = NULL; - uint32 out_data_len = 0; + uint32_t out_data_len = 0; char *pdata = *ppdata; TALLOC_CTX *ctx = talloc_tos(); @@ -2190,284 +2301,401 @@ static void call_nt_transact_ioctl(connection_struct *conn, #ifdef HAVE_SYS_QUOTAS -/**************************************************************************** - Reply to get user quota -****************************************************************************/ - -static void call_nt_transact_get_user_quota(connection_struct *conn, - struct smb_request *req, - uint16 **ppsetup, - uint32 setup_count, - char **ppparams, - uint32 parameter_count, - char **ppdata, - uint32 data_count, - uint32 max_data_count) +static enum ndr_err_code fill_qtlist_from_sids(TALLOC_CTX *mem_ctx, + struct files_struct *fsp, + SMB_NTQUOTA_HANDLE *qt_handle, + struct dom_sid *sids, + uint32_t elems) { - NTSTATUS nt_status = NT_STATUS_OK; - char *params = *ppparams; - char *pdata = *ppdata; - char *entry; - int data_len=0,param_len=0; - int qt_len=0; - int entry_len = 0; - files_struct *fsp = NULL; - uint16 level = 0; - size_t sid_len; - struct dom_sid sid; - bool start_enum = True; - SMB_NTQUOTA_STRUCT qt; - SMB_NTQUOTA_LIST *tmp_list; - SMB_NTQUOTA_HANDLE *qt_handle = NULL; - - ZERO_STRUCT(qt); - - /* access check */ - if (get_current_uid(conn) != 0) { - DEBUG(1,("get_user_quota: access_denied service [%s] user " - "[%s]\n", lp_servicename(talloc_tos(), SNUM(conn)), - conn->session_info->unix_info->unix_name)); - reply_nterror(req, NT_STATUS_ACCESS_DENIED); - return; - } + uint32_t i; + TALLOC_CTX *list_ctx = NULL; - /* - * Ensure minimum number of parameters sent. - */ + list_ctx = talloc_init("quota_sid_list"); - if (parameter_count < 4) { - DEBUG(0,("TRANSACT_GET_USER_QUOTA: requires %d >= 4 bytes parameters\n",parameter_count)); - reply_nterror(req, NT_STATUS_INVALID_PARAMETER); - return; + if (list_ctx == NULL) { + DBG_ERR("failed to allocate\n"); + return NDR_ERR_ALLOC; } - /* maybe we can check the quota_fnum */ - fsp = file_fsp(req, SVAL(params,0)); - if (!check_fsp_ntquota_handle(conn, req, fsp)) { - DEBUG(3,("TRANSACT_GET_USER_QUOTA: no valid QUOTA HANDLE\n")); - reply_nterror(req, NT_STATUS_INVALID_HANDLE); - return; + if (qt_handle->quota_list!=NULL) { + free_ntquota_list(&(qt_handle->quota_list)); } + for (i = 0; i < elems; i++) { + SMB_NTQUOTA_STRUCT qt; + SMB_NTQUOTA_LIST *list_item; + bool ok; - /* the NULL pointer checking for fsp->fake_file_handle->pd - * is done by CHECK_NTQUOTA_HANDLE_OK() - */ - qt_handle = (SMB_NTQUOTA_HANDLE *)fsp->fake_file_handle->private_data; - - level = SVAL(params,2); - - /* unknown 12 bytes leading in params */ + if (!NT_STATUS_IS_OK(vfs_get_ntquota(fsp, + SMB_USER_QUOTA_TYPE, + &sids[i], &qt))) { + /* non fatal error, return empty item in result */ + ZERO_STRUCT(qt); + continue; + } - switch (level) { - case TRANSACT_GET_USER_QUOTA_LIST_CONTINUE: - /* seems that we should continue with the enum here --metze */ - if (qt_handle->quota_list!=NULL && - qt_handle->tmp_list==NULL) { + list_item = talloc_zero(list_ctx, SMB_NTQUOTA_LIST); + if (list_item == NULL) { + DBG_ERR("failed to allocate\n"); + return NDR_ERR_ALLOC; + } - /* free the list */ - free_ntquota_list(&(qt_handle->quota_list)); + ok = sid_to_uid(&sids[i], &list_item->uid); + if (!ok) { + struct dom_sid_buf buf; + DBG_WARNING("Could not convert SID %s to uid\n", + dom_sid_str_buf(&sids[i], &buf)); + /* No idea what to return here... */ + return NDR_ERR_INVALID_POINTER; + } - /* Realloc the size of parameters and data we will return */ - param_len = 4; - params = nttrans_realloc(ppparams, param_len); - if(params == NULL) { - reply_nterror(req, NT_STATUS_NO_MEMORY); - return; - } + list_item->quotas = talloc_zero(list_item, SMB_NTQUOTA_STRUCT); + if (list_item->quotas == NULL) { + DBG_ERR("failed to allocate\n"); + return NDR_ERR_ALLOC; + } - data_len = 0; - SIVAL(params,0,data_len); + *list_item->quotas = qt; + list_item->mem_ctx = list_ctx; + DLIST_ADD(qt_handle->quota_list, list_item); + } + qt_handle->tmp_list = qt_handle->quota_list; + return NDR_ERR_SUCCESS; +} - break; - } +static enum ndr_err_code extract_sids_from_buf(TALLOC_CTX *mem_ctx, + uint32_t sidlistlength, + DATA_BLOB *sid_buf, + struct dom_sid **sids, + uint32_t *num) +{ + DATA_BLOB blob; + uint32_t i = 0; + enum ndr_err_code err; + + struct sid_list_elem { + struct sid_list_elem *prev, *next; + struct dom_sid sid; + }; + + struct sid_list_elem *sid_list = NULL; + struct sid_list_elem *iter = NULL; + TALLOC_CTX *list_ctx = talloc_init("sid_list"); + if (!list_ctx) { + DBG_ERR("OOM\n"); + err = NDR_ERR_ALLOC; + goto done; + } - start_enum = False; + *num = 0; + *sids = NULL; - case TRANSACT_GET_USER_QUOTA_LIST_START: + if (sidlistlength) { + uint32_t offset = 0; + struct ndr_pull *ndr_pull = NULL; - if (qt_handle->quota_list==NULL && - qt_handle->tmp_list==NULL) { - start_enum = True; + if (sidlistlength > sid_buf->length) { + DBG_ERR("sid_list_length 0x%x exceeds " + "available bytes %zx\n", + sidlistlength, + sid_buf->length); + err = NDR_ERR_OFFSET; + goto done; + } + while (true) { + struct file_get_quota_info info; + struct sid_list_elem *item = NULL; + uint32_t new_offset = 0; + blob.data = sid_buf->data + offset; + blob.length = sidlistlength - offset; + ndr_pull = ndr_pull_init_blob(&blob, list_ctx); + if (!ndr_pull) { + DBG_ERR("OOM\n"); + err = NDR_ERR_ALLOC; + goto done; } - - if (start_enum && vfs_get_user_ntquota_list(fsp,&(qt_handle->quota_list))!=0) { - reply_nterror(req, NT_STATUS_INTERNAL_ERROR); - return; + err = ndr_pull_file_get_quota_info(ndr_pull, + NDR_SCALARS | NDR_BUFFERS, &info); + if (!NDR_ERR_CODE_IS_SUCCESS(err)) { + DBG_ERR("Failed to pull file_get_quota_info " + "from sidlist buffer\n"); + goto done; } - - /* Realloc the size of parameters and data we will return */ - param_len = 4; - params = nttrans_realloc(ppparams, param_len); - if(params == NULL) { - reply_nterror(req, NT_STATUS_NO_MEMORY); - return; + item = talloc_zero(list_ctx, struct sid_list_elem); + if (!item) { + DBG_ERR("OOM\n"); + err = NDR_ERR_ALLOC; + goto done; } - - /* we should not trust the value in max_data_count*/ - max_data_count = MIN(max_data_count,2048); - - pdata = nttrans_realloc(ppdata, max_data_count);/* should be max data count from client*/ - if(pdata == NULL) { - reply_nterror(req, NT_STATUS_NO_MEMORY); - return; + item->sid = info.sid; + DLIST_ADD(sid_list, item); + i++; + if (i == UINT32_MAX) { + DBG_ERR("Integer overflow\n"); + err = NDR_ERR_ARRAY_SIZE; + goto done; } + new_offset = info.next_entry_offset; - entry = pdata; - - /* set params Size of returned Quota Data 4 bytes*/ - /* but set it later when we know it */ - - /* for each entry push the data */ - - if (start_enum) { - qt_handle->tmp_list = qt_handle->quota_list; - } - - tmp_list = qt_handle->tmp_list; - - for (;((tmp_list!=NULL)&&((qt_len +40+SID_MAX_SIZE)next,entry+=entry_len,qt_len+=entry_len) { - - sid_len = ndr_size_dom_sid( - &tmp_list->quotas->sid, 0); - entry_len = 40 + sid_len; - - /* nextoffset entry 4 bytes */ - SIVAL(entry,0,entry_len); - - /* then the len of the SID 4 bytes */ - SIVAL(entry,4,sid_len); - - /* unknown data 8 bytes uint64_t */ - SBIG_UINT(entry,8,(uint64_t)0); /* this is not 0 in windows...-metze*/ - - /* the used disk space 8 bytes uint64_t */ - SBIG_UINT(entry,16,tmp_list->quotas->usedspace); - - /* the soft quotas 8 bytes uint64_t */ - SBIG_UINT(entry,24,tmp_list->quotas->softlim); - - /* the hard quotas 8 bytes uint64_t */ - SBIG_UINT(entry,32,tmp_list->quotas->hardlim); - - /* and now the SID */ - sid_linearize(entry+40, sid_len, &tmp_list->quotas->sid); - } - - qt_handle->tmp_list = tmp_list; - - /* overwrite the offset of the last entry */ - SIVAL(entry-entry_len,0,0); - - data_len = 4+qt_len; - /* overwrite the params quota_data_len */ - SIVAL(params,0,data_len); - - break; - - case TRANSACT_GET_USER_QUOTA_FOR_SID: - - /* unknown 4 bytes IVAL(pdata,0) */ - - if (data_count < 8) { - DEBUG(0,("TRANSACT_GET_USER_QUOTA_FOR_SID: requires %d >= %d bytes data\n",data_count,8)); - reply_nterror(req, NT_STATUS_INVALID_LEVEL); - return; - } - - sid_len = IVAL(pdata,4); - /* Ensure this is less than 1mb. */ - if (sid_len > (1024*1024)) { - reply_nterror(req, NT_STATUS_NO_MEMORY); - return; + /* if new_offset == 0 no more sid(s) to read. */ + if (new_offset == 0) { + break; } - if (data_count < 8+sid_len) { - DEBUG(0,("TRANSACT_GET_USER_QUOTA_FOR_SID: requires %d >= %lu bytes data\n",data_count,(unsigned long)(8+sid_len))); - reply_nterror(req, NT_STATUS_INVALID_LEVEL); - return; + /* Integer wrap? */ + if ((offset + new_offset) < offset) { + DBG_ERR("Integer wrap while adding " + "new_offset 0x%x to current " + "buffer offset 0x%x\n", + new_offset, offset); + err = NDR_ERR_OFFSET; + goto done; } - data_len = 4+40+sid_len; - - if (max_data_count < data_len) { - DEBUG(0,("TRANSACT_GET_USER_QUOTA_FOR_SID: max_data_count(%d) < data_len(%d)\n", - max_data_count, data_len)); - param_len = 4; - SIVAL(params,0,data_len); - data_len = 0; - nt_status = NT_STATUS_BUFFER_TOO_SMALL; - break; - } + offset += new_offset; - if (!sid_parse(pdata+8,sid_len,&sid)) { - reply_nterror(req, NT_STATUS_INVALID_PARAMETER); - return; + /* check if new offset is outside buffer boundry. */ + if (offset >= sidlistlength) { + DBG_ERR("bufsize 0x%x exceeded by " + "new offset 0x%x)\n", + sidlistlength, + offset); + err = NDR_ERR_OFFSET; + goto done; } + } + *sids = talloc_zero_array(mem_ctx, struct dom_sid, i); + if (*sids == NULL) { + DBG_ERR("OOM\n"); + err = NDR_ERR_ALLOC; + goto done; + } - if (vfs_get_ntquota(fsp, SMB_USER_QUOTA_TYPE, &sid, &qt)!=0) { - ZERO_STRUCT(qt); - /* - * we have to return zero's in all fields - * instead of returning an error here - * --metze - */ - } + *num = i; - /* Realloc the size of parameters and data we will return */ - param_len = 4; - params = nttrans_realloc(ppparams, param_len); - if(params == NULL) { - reply_nterror(req, NT_STATUS_NO_MEMORY); - return; - } + for (iter = sid_list, i = 0; iter; iter = iter->next, i++) { + struct dom_sid_buf buf; + (*sids)[i] = iter->sid; + DBG_DEBUG("quota SID[%u] %s\n", + (unsigned int)i, + dom_sid_str_buf(&iter->sid, &buf)); + } + } + err = NDR_ERR_SUCCESS; +done: + TALLOC_FREE(list_ctx); + return err; +} - pdata = nttrans_realloc(ppdata, data_len); - if(pdata == NULL) { - reply_nterror(req, NT_STATUS_NO_MEMORY); - return; - } +NTSTATUS smbd_do_query_getinfo_quota(TALLOC_CTX *mem_ctx, + files_struct *fsp, + bool restart_scan, + bool return_single, + uint32_t sid_list_length, + DATA_BLOB *sid_buf, + uint32_t max_data_count, + uint8_t **p_data, + uint32_t *p_data_size) +{ + NTSTATUS status; + SMB_NTQUOTA_HANDLE *qt_handle = NULL; + SMB_NTQUOTA_LIST *qt_list = NULL; + DATA_BLOB blob = data_blob_null; + enum ndr_err_code err; - entry = pdata; + qt_handle = + (SMB_NTQUOTA_HANDLE *)fsp->fake_file_handle->private_data; - /* set params Size of returned Quota Data 4 bytes*/ - SIVAL(params,0,data_len); + if (sid_list_length ) { + struct dom_sid *sids; + uint32_t elems = 0; + /* + * error check pulled offsets and lengths for wrap and + * exceeding available bytes. + */ + if (sid_list_length > sid_buf->length) { + DBG_ERR("sid_list_length 0x%x exceeds " + "available bytes %zx\n", + sid_list_length, + sid_buf->length); + return NT_STATUS_INVALID_PARAMETER; + } - /* nextoffset entry 4 bytes */ - SIVAL(entry,0,0); + err = extract_sids_from_buf(mem_ctx, sid_list_length, + sid_buf, &sids, &elems); + if (!NDR_ERR_CODE_IS_SUCCESS(err) || elems == 0) { + return NT_STATUS_INVALID_PARAMETER; + } + err = fill_qtlist_from_sids(mem_ctx, + fsp, + qt_handle, + sids, + elems); + if (!NDR_ERR_CODE_IS_SUCCESS(err)) { + return NT_STATUS_INVALID_PARAMETER; + } + } else if (restart_scan) { + if (vfs_get_user_ntquota_list(fsp, + &(qt_handle->quota_list))!=0) { + return NT_STATUS_INTERNAL_ERROR; + } + } else { + if (qt_handle->quota_list!=NULL && + qt_handle->tmp_list==NULL) { + free_ntquota_list(&(qt_handle->quota_list)); + } + } - /* then the len of the SID 4 bytes */ - SIVAL(entry,4,sid_len); + if (restart_scan !=0 ) { + qt_list = qt_handle->quota_list; + } else { + qt_list = qt_handle->tmp_list; + } + status = fill_quota_buffer(mem_ctx, qt_list, + return_single != 0, + max_data_count, + &blob, + &qt_handle->tmp_list); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (blob.length > max_data_count) { + return NT_STATUS_BUFFER_TOO_SMALL; + } - /* unknown data 8 bytes uint64_t */ - SBIG_UINT(entry,8,(uint64_t)0); /* this is not 0 in windows...-mezte*/ + *p_data = blob.data; + *p_data_size = blob.length; + return NT_STATUS_OK; +} - /* the used disk space 8 bytes uint64_t */ - SBIG_UINT(entry,16,qt.usedspace); +/**************************************************************************** + Reply to get user quota +****************************************************************************/ - /* the soft quotas 8 bytes uint64_t */ - SBIG_UINT(entry,24,qt.softlim); +static void call_nt_transact_get_user_quota(connection_struct *conn, + struct smb_request *req, + uint16_t **ppsetup, + uint32_t setup_count, + char **ppparams, + uint32_t parameter_count, + char **ppdata, + uint32_t data_count, + uint32_t max_data_count) +{ + NTSTATUS nt_status = NT_STATUS_OK; + char *params = *ppparams; + char *pdata = *ppdata; + int data_len = 0; + int param_len = 0; + files_struct *fsp = NULL; + DATA_BLOB blob = data_blob_null; + struct nttrans_query_quota_params info = {0}; + enum ndr_err_code err; + TALLOC_CTX *tmp_ctx = NULL; + uint32_t resp_len = 0; + uint8_t *resp_data = 0; - /* the hard quotas 8 bytes uint64_t */ - SBIG_UINT(entry,32,qt.hardlim); + tmp_ctx = talloc_init("ntquota_list"); + if (!tmp_ctx) { + nt_status = NT_STATUS_NO_MEMORY; + goto error; + } - /* and now the SID */ - sid_linearize(entry+40, sid_len, &sid); + /* access check */ + if (get_current_uid(conn) != sec_initial_uid()) { + DEBUG(1,("get_user_quota: access_denied service [%s] user " + "[%s]\n", lp_servicename(talloc_tos(), SNUM(conn)), + conn->session_info->unix_info->unix_name)); + nt_status = NT_STATUS_ACCESS_DENIED; + goto error; + } + + blob.data = (uint8_t*)params; + blob.length = parameter_count; + + err = ndr_pull_struct_blob(&blob, tmp_ctx, &info, + (ndr_pull_flags_fn_t)ndr_pull_nttrans_query_quota_params); + + if (!NDR_ERR_CODE_IS_SUCCESS(err)) { + DEBUG(0,("TRANSACT_GET_USER_QUOTA: failed to pull " + "query_quota_params.")); + nt_status = NT_STATUS_INVALID_PARAMETER; + goto error; + } + DBG_DEBUG("info.return_single_entry = %u, info.restart_scan = %u, " + "info.sid_list_length = %u, info.start_sid_length = %u, " + "info.start_sid_offset = %u\n", + (unsigned int)info.return_single_entry, + (unsigned int)info.restart_scan, + (unsigned int)info.sid_list_length, + (unsigned int)info.start_sid_length, + (unsigned int)info.start_sid_offset); + + /* set blob to point at data for further parsing */ + blob.data = (uint8_t*)pdata; + blob.length = data_count; + /* + * Although MS-SMB ref is ambiguous here, a microsoft client will + * only ever send a start sid (as part of a list) with + * sid_list_length & start_sid_offset both set to the actual list + * length. Note: Only a single result is returned in this case + * In the case where either start_sid_offset or start_sid_length + * are set alone or if both set (but have different values) then + * it seems windows will return a number of entries from the start + * of the list of users with quotas set. This behaviour is undocumented + * and windows clients do not send messages of that type. As such we + * currently will reject these requests. + */ + if (info.start_sid_length + || (info.sid_list_length != info.start_sid_offset)) { + DBG_ERR("TRANSACT_GET_USER_QUOTA: unsupported single or " + "compound sid format\n"); + nt_status = NT_STATUS_INVALID_PARAMETER; + goto error; + } - break; + /* maybe we can check the quota_fnum */ + fsp = file_fsp(req, info.fid); + if (!check_fsp_ntquota_handle(conn, req, fsp)) { + DEBUG(3,("TRANSACT_GET_USER_QUOTA: no valid QUOTA HANDLE\n")); + nt_status = NT_STATUS_INVALID_HANDLE; + goto error; + } + nt_status = smbd_do_query_getinfo_quota(tmp_ctx, + fsp, + info.restart_scan, + info.return_single_entry, + info.sid_list_length, + &blob, + max_data_count, + &resp_data, + &resp_len); + if (!NT_STATUS_IS_OK(nt_status)) { + if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_MORE_ENTRIES)) { + goto error; + } + nt_status = NT_STATUS_OK; + } - default: - DEBUG(0, ("do_nt_transact_get_user_quota: %s: unknown " - "level 0x%04hX\n", - fsp_fnum_dbg(fsp), level)); - reply_nterror(req, NT_STATUS_INVALID_LEVEL); - return; - break; + param_len = 4; + params = nttrans_realloc(ppparams, param_len); + if(params == NULL) { + nt_status = NT_STATUS_NO_MEMORY; + goto error; } + data_len = resp_len; + SIVAL(params, 0, data_len); + pdata = nttrans_realloc(ppdata, data_len); + memcpy(pdata, resp_data, data_len); + + TALLOC_FREE(tmp_ctx); send_nt_replies(conn, req, nt_status, params, param_len, pdata, data_len); + return; +error: + TALLOC_FREE(tmp_ctx); + reply_nterror(req, nt_status); } /**************************************************************************** @@ -2476,31 +2704,34 @@ static void call_nt_transact_get_user_quota(connection_struct *conn, static void call_nt_transact_set_user_quota(connection_struct *conn, struct smb_request *req, - uint16 **ppsetup, - uint32 setup_count, + uint16_t **ppsetup, + uint32_t setup_count, char **ppparams, - uint32 parameter_count, + uint32_t parameter_count, char **ppdata, - uint32 data_count, - uint32 max_data_count) + uint32_t data_count, + uint32_t max_data_count) { char *params = *ppparams; char *pdata = *ppdata; int data_len=0,param_len=0; SMB_NTQUOTA_STRUCT qt; - size_t sid_len; + struct file_quota_information info = {0}; + enum ndr_err_code err; struct dom_sid sid; + DATA_BLOB inblob; files_struct *fsp = NULL; - + TALLOC_CTX *ctx = NULL; + NTSTATUS status = NT_STATUS_OK; ZERO_STRUCT(qt); /* access check */ - if (get_current_uid(conn) != 0) { + if (get_current_uid(conn) != sec_initial_uid()) { DEBUG(1,("set_user_quota: access_denied service [%s] user " "[%s]\n", lp_servicename(talloc_tos(), SNUM(conn)), conn->session_info->unix_info->unix_name)); - reply_nterror(req, NT_STATUS_ACCESS_DENIED); - return; + status = NT_STATUS_ACCESS_DENIED; + goto error; } /* @@ -2509,67 +2740,58 @@ static void call_nt_transact_set_user_quota(connection_struct *conn, if (parameter_count < 2) { DEBUG(0,("TRANSACT_SET_USER_QUOTA: requires %d >= 2 bytes parameters\n",parameter_count)); - reply_nterror(req, NT_STATUS_INVALID_PARAMETER); - return; + status = NT_STATUS_INVALID_PARAMETER; + goto error; } /* maybe we can check the quota_fnum */ fsp = file_fsp(req, SVAL(params,0)); if (!check_fsp_ntquota_handle(conn, req, fsp)) { DEBUG(3,("TRANSACT_GET_USER_QUOTA: no valid QUOTA HANDLE\n")); - reply_nterror(req, NT_STATUS_INVALID_HANDLE); - return; + status = NT_STATUS_INVALID_HANDLE; + goto error; } - if (data_count < 40) { - DEBUG(0,("TRANSACT_SET_USER_QUOTA: requires %d >= %d bytes data\n",data_count,40)); - reply_nterror(req, NT_STATUS_INVALID_PARAMETER); - return; + ctx = talloc_init("set_user_quota"); + if (!ctx) { + status = NT_STATUS_NO_MEMORY; + goto error; } + inblob.data = (uint8_t*)pdata; + inblob.length = data_count; - /* offset to next quota record. - * 4 bytes IVAL(pdata,0) - * unused here... - */ - - /* sid len */ - sid_len = IVAL(pdata,4); + err = ndr_pull_struct_blob( + &inblob, + ctx, + &info, + (ndr_pull_flags_fn_t)ndr_pull_file_quota_information); - if (data_count < 40+sid_len || (40+sid_len < sid_len)) { - DEBUG(0,("TRANSACT_SET_USER_QUOTA: requires %d >= %lu bytes data\n",data_count,(unsigned long)40+sid_len)); - reply_nterror(req, NT_STATUS_INVALID_PARAMETER); - return; + if (!NDR_ERR_CODE_IS_SUCCESS(err)) { + DEBUG(0,("TRANSACT_SET_USER_QUOTA: failed to pull " + "file_quota_information\n")); + status = NT_STATUS_INVALID_PARAMETER; + goto error; } + qt.usedspace = info.quota_used; - /* unknown 8 bytes in pdata - * maybe its the change time in NTTIME - */ - - /* the used space 8 bytes (uint64_t)*/ - qt.usedspace = BVAL(pdata,16); - - /* the soft quotas 8 bytes (uint64_t)*/ - qt.softlim = BVAL(pdata,24); - - /* the hard quotas 8 bytes (uint64_t)*/ - qt.hardlim = BVAL(pdata,32); - - if (!sid_parse(pdata+40,sid_len,&sid)) { - reply_nterror(req, NT_STATUS_INVALID_PARAMETER); - return; - } + qt.softlim = info.quota_threshold; - DEBUGADD(8,("SID: %s\n", sid_string_dbg(&sid))); + qt.hardlim = info.quota_limit; - /* 44 unknown bytes left... */ + sid = info.sid; if (vfs_set_ntquota(fsp, SMB_USER_QUOTA_TYPE, &sid, &qt)!=0) { - reply_nterror(req, NT_STATUS_INTERNAL_ERROR); - return; + status = NT_STATUS_INTERNAL_ERROR; + goto error; } send_nt_replies(conn, req, NT_STATUS_OK, params, param_len, pdata, data_len); + TALLOC_FREE(ctx); + return; +error: + TALLOC_FREE(ctx); + reply_nterror(req, status); } #endif /* HAVE_SYS_QUOTAS */ @@ -2715,7 +2937,7 @@ void reply_nttrans(struct smb_request *req) uint32_t psoff; uint32_t dscnt; uint32_t dsoff; - uint16 function_code; + uint16_t function_code; NTSTATUS result; struct trans_state *state; @@ -2864,7 +3086,7 @@ void reply_nttrans(struct smb_request *req) goto bad_param; } - state->setup = (uint16 *)TALLOC(state, state->setup_count); + state->setup = (uint16_t *)TALLOC(state, state->setup_count); if (state->setup == NULL) { DEBUG(0,("reply_nttrans : Out of memory\n")); SAFE_FREE(state->data); @@ -2876,7 +3098,7 @@ void reply_nttrans(struct smb_request *req) } memcpy(state->setup, req->vwv+19, state->setup_count); - dump_data(10, (uint8 *)state->setup, state->setup_count); + dump_data(10, (uint8_t *)state->setup, state->setup_count); } if ((state->received_data == state->total_data) &&