diff options
Diffstat (limited to 'source3/smbd')
42 files changed, 3449 insertions, 1596 deletions
diff --git a/source3/smbd/aio.c b/source3/smbd/aio.c index 994b10d6a8..86fdfe31b7 100644 --- a/source3/smbd/aio.c +++ b/source3/smbd/aio.c @@ -202,7 +202,8 @@ bool schedule_aio_read_and_X(connection_struct *conn, size_t bufsize; size_t min_aio_read_size = lp_aio_read_size(SNUM(conn)); - if (!min_aio_read_size || (smb_maxcnt < min_aio_read_size)) { + if ((!min_aio_read_size || (smb_maxcnt < min_aio_read_size)) + && !SMB_VFS_AIO_FORCE(fsp)) { /* Too small a read for aio request. */ DEBUG(10,("schedule_aio_read_and_X: read size (%u) too small " "for minimum aio_read of %u\n", @@ -236,7 +237,7 @@ bool schedule_aio_read_and_X(connection_struct *conn, } construct_reply_common((char *)req->inbuf, aio_ex->outbuf); - set_message(aio_ex->outbuf, 12, 0, True); + srv_set_message(aio_ex->outbuf, 12, 0, True); SCVAL(aio_ex->outbuf,smb_vwv0,0xFF); /* Never a chained reply. */ a = &aio_ex->acb; @@ -284,7 +285,8 @@ bool schedule_aio_write_and_X(connection_struct *conn, bool write_through = BITSETW(req->inbuf+smb_vwv7,0); size_t min_aio_write_size = lp_aio_write_size(SNUM(conn)); - if (!min_aio_write_size || (numtowrite < min_aio_write_size)) { + if ((!min_aio_write_size || (numtowrite < min_aio_write_size)) + && !SMB_VFS_AIO_FORCE(fsp)) { /* Too small a write for aio request. */ DEBUG(10,("schedule_aio_write_and_X: write size (%u) too " "small for minimum aio_write of %u\n", @@ -356,8 +358,9 @@ bool schedule_aio_write_and_X(connection_struct *conn, SSVAL(aio_ex->outbuf,smb_vwv2,numtowrite); SSVAL(aio_ex->outbuf,smb_vwv4,(numtowrite>>16)&1); show_msg(aio_ex->outbuf); - if (!send_smb(smbd_server_fd(),aio_ex->outbuf)) { - exit_server_cleanly("handle_aio_write: send_smb " + if (!srv_send_smb(smbd_server_fd(),aio_ex->outbuf, + IS_CONN_ENCRYPTED(fsp->conn))) { + exit_server_cleanly("handle_aio_write: srv_send_smb " "failed."); } DEBUG(10,("schedule_aio_write_and_X: scheduled aio_write " @@ -407,10 +410,11 @@ static int handle_aio_read_complete(struct aio_extra *aio_ex) "Error = %s\n", aio_ex->fsp->fsp_name, strerror(errno) )); - outsize = (UNIXERROR(ERRDOS,ERRnoaccess)); ret = errno; + ERROR_NT(map_nt_error_from_unix(ret)); + outsize = srv_set_message(outbuf,0,0,true); } else { - outsize = set_message(outbuf,12,nread,False); + outsize = srv_set_message(outbuf,12,nread,False); SSVAL(outbuf,smb_vwv2,0xFFFF); /* Remaining - must be * -1. */ SSVAL(outbuf,smb_vwv5,nread); SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf)); @@ -425,8 +429,9 @@ static int handle_aio_read_complete(struct aio_extra *aio_ex) } smb_setlen(outbuf,outsize - 4); show_msg(outbuf); - if (!send_smb(smbd_server_fd(),outbuf)) { - exit_server_cleanly("handle_aio_read_complete: send_smb " + if (!srv_send_smb(smbd_server_fd(),outbuf, + IS_CONN_ENCRYPTED(aio_ex->fsp->conn))) { + exit_server_cleanly("handle_aio_read_complete: srv_send_smb " "failed."); } @@ -492,8 +497,9 @@ static int handle_aio_write_complete(struct aio_extra *aio_ex) return 0; } - UNIXERROR(ERRHRD,ERRdiskfull); ret = errno; + ERROR_BOTH(map_nt_error_from_unix(ret), ERRHRD, ERRdiskfull); + srv_set_message(outbuf,0,0,true); } else { bool write_through = BITSETW(aio_ex->inbuf+smb_vwv7,0); NTSTATUS status; @@ -509,16 +515,18 @@ static int handle_aio_write_complete(struct aio_extra *aio_ex) fsp->fnum, (int)numtowrite, (int)nwritten)); status = sync_file(fsp->conn,fsp, write_through); if (!NT_STATUS_IS_OK(status)) { - UNIXERROR(ERRHRD,ERRdiskfull); ret = errno; + ERROR_BOTH(map_nt_error_from_unix(ret), + ERRHRD, ERRdiskfull); + srv_set_message(outbuf,0,0,true); DEBUG(5,("handle_aio_write: sync_file for %s returned %s\n", fsp->fsp_name, nt_errstr(status) )); } } show_msg(outbuf); - if (!send_smb(smbd_server_fd(),outbuf)) { - exit_server_cleanly("handle_aio_write: send_smb failed."); + if (!srv_send_smb(smbd_server_fd(),outbuf,IS_CONN_ENCRYPTED(fsp->conn))) { + exit_server_cleanly("handle_aio_write: srv_send_smb failed."); } DEBUG(10,("handle_aio_write_complete: scheduled aio_write completed " @@ -739,7 +747,7 @@ void cancel_aio_by_fsp(files_struct *fsp) /* Don't delete the aio_extra record as we may have completed and don't yet know it. Just do the aio_cancel call and return. */ - SMB_VFS_AIO_CANCEL(fsp,fsp->fh->fd, &aio_ex->acb); + SMB_VFS_AIO_CANCEL(fsp, &aio_ex->acb); aio_ex->fsp = NULL; /* fsp will be closed when we * return. */ } diff --git a/source3/smbd/blocking.c b/source3/smbd/blocking.c index 0078bb7d13..479361a8c1 100644 --- a/source3/smbd/blocking.c +++ b/source3/smbd/blocking.c @@ -41,6 +41,7 @@ typedef struct _blocking_lock_record { enum brl_type lock_type; char *inbuf; int length; + bool encrypted; } blocking_lock_record; /* dlink list we store pending lock records on. */ @@ -149,7 +150,7 @@ static bool recalc_brl_timeout(void) ****************************************************************************/ bool push_blocking_lock_request( struct byte_range_lock *br_lck, - const char *inbuf, int length, + const struct smb_request *req, files_struct *fsp, int lock_timeout, int lock_num, @@ -161,6 +162,7 @@ bool push_blocking_lock_request( struct byte_range_lock *br_lck, uint32 blocking_pid) { static bool set_lock_msg; + size_t length = smb_len(req->inbuf)+4; blocking_lock_record *blr; NTSTATUS status; @@ -188,7 +190,7 @@ bool push_blocking_lock_request( struct byte_range_lock *br_lck, return False; } - blr->com_type = CVAL(inbuf,smb_com); + blr->com_type = CVAL(req->inbuf,smb_com); blr->fsp = fsp; if (lock_timeout == -1) { blr->expire_time.tv_sec = 0; @@ -204,8 +206,9 @@ bool push_blocking_lock_request( struct byte_range_lock *br_lck, blr->lock_type = lock_type; blr->offset = offset; blr->count = count; - memcpy(blr->inbuf, inbuf, length); + memcpy(blr->inbuf, req->inbuf, length); blr->length = length; + blr->encrypted = req->encrypted; /* Add a pending lock record for this. */ status = brl_lock(smbd_messaging_context(), br_lck, @@ -235,14 +238,14 @@ bool push_blocking_lock_request( struct byte_range_lock *br_lck, set_lock_msg = True; } - DEBUG(3,("push_blocking_lock_request: lock request length=%d blocked with " + DEBUG(3,("push_blocking_lock_request: lock request length=%u blocked with " "expiry time (%u sec. %u usec) (+%d msec) for fnum = %d, name = %s\n", - length, (unsigned int)blr->expire_time.tv_sec, + (unsigned int)length, (unsigned int)blr->expire_time.tv_sec, (unsigned int)blr->expire_time.tv_usec, lock_timeout, blr->fsp->fnum, blr->fsp->fsp_name )); /* Push the MID of this packet on the signing queue. */ - srv_defer_sign_response(SVAL(inbuf,smb_mid)); + srv_defer_sign_response(SVAL(req->inbuf,smb_mid)); return True; } @@ -259,7 +262,7 @@ static void reply_lockingX_success(blocking_lock_record *blr) smb_panic("Could not allocate smb_request"); } - init_smb_request(req, (uint8 *)blr->inbuf, 0); + init_smb_request(req, (uint8 *)blr->inbuf, 0, blr->encrypted); reply_outbuf(req, 2, 0); /* @@ -272,8 +275,10 @@ static void reply_lockingX_success(blocking_lock_record *blr) chain_reply(req); - if (!send_smb(smbd_server_fd(),(char *)req->outbuf)) { - exit_server_cleanly("send_blocking_reply: send_smb failed."); + if (!srv_send_smb(smbd_server_fd(), + (char *)req->outbuf, + IS_CONN_ENCRYPTED(blr->fsp->conn))) { + exit_server_cleanly("send_blocking_reply: srv_send_smb failed."); } } @@ -298,19 +303,21 @@ static void generic_blocking_lock_error(blocking_lock_record *blr, NTSTATUS stat /* Store the last lock error. */ files_struct *fsp = blr->fsp; - fsp->last_lock_failure.context.smbpid = blr->lock_pid; - fsp->last_lock_failure.context.tid = fsp->conn->cnum; - fsp->last_lock_failure.context.pid = procid_self(); - fsp->last_lock_failure.start = blr->offset; - fsp->last_lock_failure.size = blr->count; - fsp->last_lock_failure.fnum = fsp->fnum; - fsp->last_lock_failure.lock_type = READ_LOCK; /* Don't care. */ - fsp->last_lock_failure.lock_flav = blr->lock_flav; + if (fsp) { + fsp->last_lock_failure.context.smbpid = blr->lock_pid; + fsp->last_lock_failure.context.tid = fsp->conn->cnum; + fsp->last_lock_failure.context.pid = procid_self(); + fsp->last_lock_failure.start = blr->offset; + fsp->last_lock_failure.size = blr->count; + fsp->last_lock_failure.fnum = fsp->fnum; + fsp->last_lock_failure.lock_type = READ_LOCK; /* Don't care. */ + fsp->last_lock_failure.lock_flav = blr->lock_flav; + } } ERROR_NT(status); - if (!send_smb(smbd_server_fd(),outbuf)) { - exit_server_cleanly("generic_blocking_lock_error: send_smb failed."); + if (!srv_send_smb(smbd_server_fd(),outbuf, blr->encrypted)) { + exit_server_cleanly("generic_blocking_lock_error: srv_send_smb failed."); } } @@ -388,8 +395,10 @@ static void blocking_lock_reply_error(blocking_lock_record *blr, NTSTATUS status */ SCVAL(outbuf,smb_com,SMBtrans2); ERROR_NT(status); - if (!send_smb(smbd_server_fd(),outbuf)) { - exit_server_cleanly("blocking_lock_reply_error: send_smb failed."); + if (!srv_send_smb(smbd_server_fd(), + outbuf, + IS_CONN_ENCRYPTED(blr->fsp->conn))) { + exit_server_cleanly("blocking_lock_reply_error: srv_send_smb failed."); } break; } @@ -531,12 +540,12 @@ static bool process_trans2(blocking_lock_record *blr) return True; } - init_smb_request(req, (uint8 *)blr->inbuf, 0); + init_smb_request(req, (uint8 *)blr->inbuf, 0, blr->encrypted); SCVAL(req->inbuf, smb_com, SMBtrans2); SSVAL(params,0,0); /* Fake up max_data_bytes here - we know it fits. */ - send_trans2_replies(req, params, 2, NULL, 0, 0xffff); + send_trans2_replies(blr->fsp->conn, req, params, 2, NULL, 0, 0xffff); return True; } @@ -597,6 +606,9 @@ file %s fnum = %d\n", blr->com_type, fsp->fsp_name, fsp->fnum )); locktype, NT_STATUS_RANGE_NOT_LOCKED); } + /* We're closing the file fsp here, so ensure + * we don't have a dangling pointer. */ + blr->fsp = NULL; } } } @@ -613,7 +625,7 @@ void remove_pending_lock_requests_by_mid(int mid) next = blr->next; if(SVAL(blr->inbuf,smb_mid) == mid) { files_struct *fsp = blr->fsp; - struct byte_range_lock *br_lck = brl_get_locks(NULL, fsp); + struct byte_range_lock *br_lck = brl_get_locks(talloc_tos(), fsp); if (br_lck) { DEBUG(10,("remove_pending_lock_requests_by_mid - removing request type %d for \ @@ -703,7 +715,7 @@ static void process_blocking_lock_queue(void) fsp->fnum, fsp->fsp_name )); if(!change_to_user(conn,vuid)) { - struct byte_range_lock *br_lck = brl_get_locks(NULL, fsp); + struct byte_range_lock *br_lck = brl_get_locks(talloc_tos(), fsp); /* * Remove the entry and return an error to the client. @@ -729,7 +741,7 @@ static void process_blocking_lock_queue(void) } if(!set_current_service(conn,SVAL(blr->inbuf,smb_flg),True)) { - struct byte_range_lock *br_lck = brl_get_locks(NULL, fsp); + struct byte_range_lock *br_lck = brl_get_locks(talloc_tos(), fsp); /* * Remove the entry and return an error to the client. @@ -761,7 +773,7 @@ static void process_blocking_lock_queue(void) */ if(blocking_lock_record_process(blr)) { - struct byte_range_lock *br_lck = brl_get_locks(NULL, fsp); + struct byte_range_lock *br_lck = brl_get_locks(talloc_tos(), fsp); if (br_lck) { brl_lock_cancel(br_lck, @@ -788,7 +800,7 @@ static void process_blocking_lock_queue(void) */ if (!timeval_is_zero(&blr->expire_time) && timeval_compare(&blr->expire_time, &tv_curr) <= 0) { - struct byte_range_lock *br_lck = brl_get_locks(NULL, fsp); + struct byte_range_lock *br_lck = brl_get_locks(talloc_tos(), fsp); /* * Lock expired - throw away all previously diff --git a/source3/smbd/chgpasswd.c b/source3/smbd/chgpasswd.c index 5ccf3ed9da..668c8e2095 100644 --- a/source3/smbd/chgpasswd.c +++ b/source3/smbd/chgpasswd.c @@ -159,19 +159,19 @@ static int dochild(int master, const char *slavedev, const struct passwd *pass, DEBUG(3, ("More weirdness, could not open %s\n", slavedev)); return (False); } -#if defined(I_PUSH) && defined(I_FIND) +#if defined(TIOCSCTTY) + if (ioctl(slave, TIOCSCTTY, 0) < 0) + { + DEBUG(3, ("Error in ioctl call for slave pty\n")); + /* return(False); */ + } +#elif defined(I_PUSH) && defined(I_FIND) if (ioctl(slave, I_FIND, "ptem") == 0) { ioctl(slave, I_PUSH, "ptem"); } if (ioctl(slave, I_FIND, "ldterm") == 0) { ioctl(slave, I_PUSH, "ldterm"); } -#elif defined(TIOCSCTTY) - if (ioctl(slave, TIOCSCTTY, 0) < 0) - { - DEBUG(3, ("Error in ioctl call for slave pty\n")); - /* return(False); */ - } #endif /* Close master. */ @@ -1092,7 +1092,7 @@ NTSTATUS change_oem_password(struct samu *hnd, char *old_passwd, char *new_passw if (!pdb_get_pass_can_change(hnd)) { DEBUG(1, ("user %s does not have permissions to change password\n", username)); if (samr_reject_reason) { - *samr_reject_reason = REJECT_REASON_OTHER; + *samr_reject_reason = SAMR_REJECT_OTHER; } return NT_STATUS_ACCOUNT_RESTRICTION; } @@ -1106,7 +1106,7 @@ NTSTATUS change_oem_password(struct samu *hnd, char *old_passwd, char *new_passw "denied by Refuse Machine Password Change policy\n", username)); if (samr_reject_reason) { - *samr_reject_reason = REJECT_REASON_OTHER; + *samr_reject_reason = SAMR_REJECT_OTHER; } return NT_STATUS_ACCOUNT_RESTRICTION; } @@ -1119,7 +1119,7 @@ NTSTATUS change_oem_password(struct samu *hnd, char *old_passwd, char *new_passw "wait until %s\n", username, http_timestring(can_change_time))); if (samr_reject_reason) { - *samr_reject_reason = REJECT_REASON_OTHER; + *samr_reject_reason = SAMR_REJECT_OTHER; } return NT_STATUS_ACCOUNT_RESTRICTION; } @@ -1129,7 +1129,7 @@ NTSTATUS change_oem_password(struct samu *hnd, char *old_passwd, char *new_passw username)); DEBUGADD(1, (" account policy min password len = %d\n", min_len)); if (samr_reject_reason) { - *samr_reject_reason = REJECT_REASON_TOO_SHORT; + *samr_reject_reason = SAMR_REJECT_TOO_SHORT; } return NT_STATUS_PASSWORD_RESTRICTION; /* return NT_STATUS_PWD_TOO_SHORT; */ @@ -1137,12 +1137,12 @@ NTSTATUS change_oem_password(struct samu *hnd, char *old_passwd, char *new_passw if (check_passwd_history(hnd,new_passwd)) { if (samr_reject_reason) { - *samr_reject_reason = REJECT_REASON_IN_HISTORY; + *samr_reject_reason = SAMR_REJECT_IN_HISTORY; } return NT_STATUS_PASSWORD_RESTRICTION; } - pass = Get_Pwnam(username); + pass = Get_Pwnam_alloc(talloc_tos(), username); if (!pass) { DEBUG(1, ("change_oem_password: Username %s does not exist in system !?!\n", username)); return NT_STATUS_ACCESS_DENIED; @@ -1158,8 +1158,9 @@ NTSTATUS change_oem_password(struct samu *hnd, char *old_passwd, char *new_passw if (check_ret != 0) { DEBUG(1, ("change_oem_password: check password script said new password is not good enough!\n")); if (samr_reject_reason) { - *samr_reject_reason = REJECT_REASON_NOT_COMPLEX; + *samr_reject_reason = SAMR_REJECT_COMPLEXITY; } + TALLOC_FREE(pass); return NT_STATUS_PASSWORD_RESTRICTION; } } @@ -1178,9 +1179,12 @@ NTSTATUS change_oem_password(struct samu *hnd, char *old_passwd, char *new_passw if(lp_unix_password_sync() && !chgpasswd(username, pass, old_passwd, new_passwd, as_root)) { + TALLOC_FREE(pass); return NT_STATUS_ACCESS_DENIED; } + TALLOC_FREE(pass); + if (!pdb_set_plaintext_passwd (hnd, new_passwd)) { return NT_STATUS_ACCESS_DENIED; } diff --git a/source3/smbd/close.c b/source3/smbd/close.c index eb610f7d1a..4bd23a35fc 100644 --- a/source3/smbd/close.c +++ b/source3/smbd/close.c @@ -27,7 +27,7 @@ extern struct current_user current_user; Run a file if it is a magic script. ****************************************************************************/ -static void check_magic(files_struct *fsp,connection_struct *conn) +static void check_magic(struct files_struct *fsp) { int ret; const char *magic_output = NULL; @@ -35,6 +35,7 @@ static void check_magic(files_struct *fsp,connection_struct *conn) int tmp_fd, outfd; TALLOC_CTX *ctx = NULL; const char *p; + struct connection_struct *conn = fsp->conn; if (!*lp_magicscript(SNUM(conn))) { return; @@ -155,6 +156,75 @@ static void notify_deferred_opens(struct share_mode_lock *lck) } /**************************************************************************** + Delete all streams +****************************************************************************/ + +static NTSTATUS delete_all_streams(connection_struct *conn, const char *fname) +{ + struct stream_struct *stream_info; + int i; + unsigned int num_streams; + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status; + + status = SMB_VFS_STREAMINFO(conn, NULL, fname, talloc_tos(), + &num_streams, &stream_info); + + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) { + DEBUG(10, ("no streams around\n")); + TALLOC_FREE(frame); + return NT_STATUS_OK; + } + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("SMB_VFS_STREAMINFO failed: %s\n", + nt_errstr(status))); + goto fail; + } + + DEBUG(10, ("delete_all_streams found %d streams\n", + num_streams)); + + if (num_streams == 0) { + TALLOC_FREE(frame); + return NT_STATUS_OK; + } + + for (i=0; i<num_streams; i++) { + int res; + char *streamname; + + if (strequal(stream_info[i].name, "::$DATA")) { + continue; + } + + streamname = talloc_asprintf(talloc_tos(), "%s%s", fname, + stream_info[i].name); + + if (streamname == NULL) { + DEBUG(0, ("talloc_aprintf failed\n")); + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + res = SMB_VFS_UNLINK(conn, streamname); + + TALLOC_FREE(streamname); + + if (res == -1) { + status = map_nt_error_from_unix(errno); + DEBUG(10, ("Could not delete stream %s: %s\n", + streamname, strerror(errno))); + break; + } + } + + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** Deal with removing a share mode on last close. ****************************************************************************/ @@ -162,7 +232,8 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp, enum file_close_type close_type) { connection_struct *conn = fsp->conn; - bool delete_file = False; + bool delete_file = false; + bool changed_user = false; struct share_mode_lock *lck; SMB_STRUCT_STAT sbuf; NTSTATUS status = NT_STATUS_OK; @@ -175,7 +246,7 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp, * This prevents race conditions with the file being created. JRA. */ - lck = get_share_mode_lock(NULL, fsp->file_id, NULL, NULL); + lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL); if (lck == NULL) { DEBUG(0, ("close_remove_share_mode: Could not get share mode " @@ -245,18 +316,27 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp, DEBUG(5,("close_remove_share_mode: file %s. Delete on close was set " "- deleting file.\n", fsp->fsp_name)); - /* Become the user who requested the delete. */ + if (!unix_token_equal(lck->delete_token, ¤t_user.ut)) { + /* Become the user who requested the delete. */ - if (!push_sec_ctx()) { - smb_panic("close_remove_share_mode: file %s. failed to push " - "sec_ctx.\n"); - } + DEBUG(5,("close_remove_share_mode: file %s. " + "Change user to uid %u\n", + fsp->fsp_name, + (unsigned int)lck->delete_token->uid)); - set_sec_ctx(lck->delete_token->uid, - lck->delete_token->gid, - lck->delete_token->ngroups, - lck->delete_token->groups, - NULL); + if (!push_sec_ctx()) { + smb_panic("close_remove_share_mode: file %s. failed to push " + "sec_ctx.\n"); + } + + set_sec_ctx(lck->delete_token->uid, + lck->delete_token->gid, + lck->delete_token->ngroups, + lck->delete_token->groups, + NULL); + + changed_user = true; + } /* We can only delete the file if the name we have is still valid and hasn't been renamed. */ @@ -294,6 +374,19 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp, goto done; } + if ((conn->fs_capabilities & FILE_NAMED_STREAMS) + && !is_ntfs_stream_name(fsp->fsp_name)) { + + status = delete_all_streams(conn, fsp->fsp_name); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(5, ("delete_all_streams failed: %s\n", + nt_errstr(status))); + goto done; + } + } + + if (SMB_VFS_UNLINK(conn,fsp->fsp_name) != 0) { /* * This call can potentially fail as another smbd may @@ -325,9 +418,11 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp, done: - /* unbecome user. */ - pop_sec_ctx(); - + if (changed_user) { + /* unbecome user. */ + pop_sec_ctx(); + } + TALLOC_FREE(lck); return status; } @@ -389,11 +484,11 @@ static NTSTATUS close_normal_file(files_struct *fsp, enum file_close_type close_ locking_close_file(smbd_messaging_context(), fsp); - status = fd_close(conn, fsp); + status = fd_close(fsp); /* check for magic scripts */ if (close_type == NORMAL_CLOSE) { - check_magic(fsp,conn); + check_magic(fsp); } /* @@ -440,7 +535,7 @@ static NTSTATUS close_directory(files_struct *fsp, enum file_close_type close_ty * reference to a directory also. */ - lck = get_share_mode_lock(NULL, fsp->file_id, NULL, NULL); + lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL); if (lck == NULL) { DEBUG(0, ("close_directory: Could not get share mode lock for %s\n", fsp->fsp_name)); @@ -557,12 +652,37 @@ static NTSTATUS close_stat(files_struct *fsp) NTSTATUS close_file(files_struct *fsp, enum file_close_type close_type) { + NTSTATUS status; + struct files_struct *base_fsp = fsp->base_fsp; + if(fsp->is_directory) { - return close_directory(fsp, close_type); + status = close_directory(fsp, close_type); } else if (fsp->is_stat) { - return close_stat(fsp); + status = close_stat(fsp); } else if (fsp->fake_file_handle != NULL) { - return close_fake_file(fsp); + status = close_fake_file(fsp); + } else { + status = close_normal_file(fsp, close_type); + } + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if ((base_fsp != NULL) && (close_type != SHUTDOWN_CLOSE)) { + + /* + * fsp was a stream, the base fsp can't be a stream as well + * + * For SHUTDOWN_CLOSE this is not possible here, because + * SHUTDOWN_CLOSE only happens from files.c which walks the + * complete list of files. If we mess with more than one fsp + * those loops will become confused. + */ + + SMB_ASSERT(base_fsp->base_fsp == NULL); + close_file(base_fsp, close_type); } - return close_normal_file(fsp, close_type); + + return status; } diff --git a/source3/smbd/connection.c b/source3/smbd/connection.c index 95e45a6990..d7063c9989 100644 --- a/source3/smbd/connection.c +++ b/source3/smbd/connection.c @@ -118,6 +118,15 @@ int count_current_connections( const char *sharename, bool clear ) } /**************************************************************************** + Count the number of connections open across all shares. +****************************************************************************/ + +int count_all_current_connections(void) +{ + return count_current_connections(NULL, True /* clear stale entries */); +} + +/**************************************************************************** Claim an entry in the connections database. ****************************************************************************/ @@ -132,7 +141,7 @@ bool claim_connection(connection_struct *conn, const char *name, DEBUG(5,("claiming [%s]\n", name)); - if (!(rec = connections_fetch_entry(NULL, conn, name))) { + if (!(rec = connections_fetch_entry(talloc_tos(), conn, name))) { DEBUG(0, ("connections_fetch_entry failed\n")); return False; } diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c index 05679ee0ee..ca6f8bfd8d 100644 --- a/source3/smbd/dir.c +++ b/source3/smbd/dir.c @@ -142,8 +142,7 @@ static void dptr_idle(struct dptr_struct *dptr) { if (dptr->dir_hnd) { DEBUG(4,("Idling dptr dnum %d\n",dptr->dnum)); - CloseDir(dptr->dir_hnd); - dptr->dir_hnd = NULL; + TALLOC_FREE(dptr->dir_hnd); } } @@ -192,7 +191,9 @@ static struct dptr_struct *dptr_get(int key, bool forclose) if (dirhandles_open >= MAX_OPEN_DIRECTORIES) dptr_idleoldest(); DEBUG(4,("dptr_get: Reopening dptr key %d\n",key)); - if (!(dptr->dir_hnd = OpenDir(dptr->conn, dptr->path, dptr->wcard, dptr->attr))) { + if (!(dptr->dir_hnd = OpenDir( + NULL, dptr->conn, dptr->path, + dptr->wcard, dptr->attr))) { DEBUG(4,("dptr_get: Failed to open %s (%s)\n",dptr->path, strerror(errno))); return False; @@ -263,9 +264,7 @@ static void dptr_close_internal(struct dptr_struct *dptr) bitmap_clear(dptr_bmap, dptr->dnum - 1); - if (dptr->dir_hnd) { - CloseDir(dptr->dir_hnd); - } + TALLOC_FREE(dptr->dir_hnd); /* Lanman 2 specific code */ SAFE_FREE(dptr->wcard); @@ -411,7 +410,7 @@ NTSTATUS dptr_create(connection_struct *conn, const char *path, bool old_handle, return status; } - dir_hnd = OpenDir(conn, path, wcard, attr); + dir_hnd = OpenDir(NULL, conn, path, wcard, attr); if (!dir_hnd) { return map_nt_error_from_unix(errno); } @@ -425,7 +424,7 @@ NTSTATUS dptr_create(connection_struct *conn, const char *path, bool old_handle, dptr = SMB_MALLOC_P(struct dptr_struct); if(!dptr) { DEBUG(0,("malloc fail in dptr_create.\n")); - CloseDir(dir_hnd); + TALLOC_FREE(dir_hnd); return NT_STATUS_NO_MEMORY; } @@ -455,7 +454,7 @@ NTSTATUS dptr_create(connection_struct *conn, const char *path, bool old_handle, if(dptr->dnum == -1 || dptr->dnum > 254) { DEBUG(0,("dptr_create: returned %d: Error - all old dirptrs in use ?\n", dptr->dnum)); SAFE_FREE(dptr); - CloseDir(dir_hnd); + TALLOC_FREE(dir_hnd); return NT_STATUS_TOO_MANY_OPENED_FILES; } } @@ -485,7 +484,7 @@ NTSTATUS dptr_create(connection_struct *conn, const char *path, bool old_handle, if(dptr->dnum == -1 || dptr->dnum < 255) { DEBUG(0,("dptr_create: returned %d: Error - all new dirptrs in use ?\n", dptr->dnum)); SAFE_FREE(dptr); - CloseDir(dir_hnd); + TALLOC_FREE(dir_hnd); return NT_STATUS_TOO_MANY_OPENED_FILES; } } @@ -504,7 +503,7 @@ NTSTATUS dptr_create(connection_struct *conn, const char *path, bool old_handle, if (!dptr->wcard) { bitmap_clear(dptr_bmap, dptr->dnum - 1); SAFE_FREE(dptr); - CloseDir(dir_hnd); + TALLOC_FREE(dir_hnd); return NT_STATUS_NO_MEMORY; } if (lp_posix_pathnames() || (wcard[0] == '.' && wcard[1] == 0)) { @@ -533,7 +532,8 @@ NTSTATUS dptr_create(connection_struct *conn, const char *path, bool old_handle, int dptr_CloseDir(struct dptr_struct *dptr) { DLIST_REMOVE(dirptrs, dptr); - return CloseDir(dptr->dir_hnd); + TALLOC_FREE(dptr->dir_hnd); + return 0; } void dptr_SeekDir(struct dptr_struct *dptr, long offset) @@ -646,10 +646,13 @@ const char *dptr_ReadDirName(TALLOC_CTX *ctx, TALLOC_FREE(pathreal); - /* In case sensitive mode we don't search - we know if it doesn't exist - with a stat we will fail. */ + /* Stat failed. We know this is authoratiative if we are + * providing case sensitive semantics or the underlying + * filesystem is case sensitive. + */ - if (dptr->conn->case_sensitive) { + if (dptr->conn->case_sensitive || + !(dptr->conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH)) { /* We need to set the underlying dir_hnd offset to -1 also as this function is usually called with the output from TellDir. */ dptr->dir_hnd->offset = *poffset = END_OF_DIRECTORY_OFFSET; @@ -924,12 +927,7 @@ static bool user_can_read_file(connection_struct *conn, char *name, SMB_STRUCT_S return True; } - /* If we can't stat it does not show it */ - if (!VALID_STAT(*pst) && (SMB_VFS_STAT(conn, name, pst) != 0)) { - DEBUG(10,("user_can_read_file: SMB_VFS_STAT failed for file %s with error %s\n", - name, strerror(errno) )); - return False; - } + SMB_ASSERT(VALID_STAT(*pst)); /* Pseudo-open the file (note - no fd's created). */ @@ -950,7 +948,7 @@ static bool user_can_read_file(connection_struct *conn, char *name, SMB_STRUCT_S } /* Get NT ACL -allocated in main loop talloc context. No free needed here. */ - status = SMB_VFS_FGET_NT_ACL(fsp, fsp->fh->fd, + status = SMB_VFS_FGET_NT_ACL(fsp, (OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION), &psd); close_file(fsp, NORMAL_CLOSE); @@ -987,10 +985,7 @@ static bool user_can_write_file(connection_struct *conn, char *name, SMB_STRUCT_ return True; } - /* If we can't stat it does not show it */ - if (!VALID_STAT(*pst) && (SMB_VFS_STAT(conn, name, pst) != 0)) { - return False; - } + SMB_ASSERT(VALID_STAT(*pst)); /* Pseudo-open the file */ @@ -1012,7 +1007,7 @@ static bool user_can_write_file(connection_struct *conn, char *name, SMB_STRUCT_ } /* Get NT ACL -allocated in main loop talloc context. No free needed here. */ - status = SMB_VFS_FGET_NT_ACL(fsp, fsp->fh->fd, + status = SMB_VFS_FGET_NT_ACL(fsp, (OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION), &psd); close_file(fsp, NORMAL_CLOSE); @@ -1039,9 +1034,7 @@ static bool file_is_special(connection_struct *conn, char *name, SMB_STRUCT_STAT if (conn->admin_user) return False; - /* If we can't stat it does not show it */ - if (!VALID_STAT(*pst) && (SMB_VFS_STAT(conn, name, pst) != 0)) - return True; + SMB_ASSERT(VALID_STAT(*pst)); if (S_ISREG(pst->st_mode) || S_ISDIR(pst->st_mode) || S_ISLNK(pst->st_mode)) return False; @@ -1050,7 +1043,9 @@ static bool file_is_special(connection_struct *conn, char *name, SMB_STRUCT_STAT } /******************************************************************* - Should the file be seen by the client ? + Should the file be seen by the client ? NOTE: A successful return + is no guarantee of the file's existence ... you also have to check + whether pst is valid. ********************************************************************/ bool is_visible_file(connection_struct *conn, const char *dir_path, const char *name, SMB_STRUCT_STAT *pst, bool use_veto) @@ -1086,6 +1081,15 @@ bool is_visible_file(connection_struct *conn, const char *dir_path, const char * return True; } + /* If the file name does not exist, there's no point checking + * the configuration options. We succeed, on the basis that the + * checks *might* have passed if the file was present. + */ + if (SMB_VFS_STAT(conn, entry, pst) != 0) { + SAFE_FREE(entry); + return True; + } + /* Honour _hide unreadable_ option */ if (hide_unreadable && !user_can_read_file(conn, entry, pst)) { DEBUG(10,("is_visible_file: file %s is unreadable.\n", entry )); @@ -1109,82 +1113,53 @@ bool is_visible_file(connection_struct *conn, const char *dir_path, const char * return True; } +static int smb_Dir_destructor(struct smb_Dir *dirp) +{ + if (dirp->dir) { + SMB_VFS_CLOSEDIR(dirp->conn,dirp->dir); + } + dirhandles_open--; + return 0; +} + /******************************************************************* Open a directory. ********************************************************************/ -struct smb_Dir *OpenDir(connection_struct *conn, const char *name, const char *mask, uint32 attr) +struct smb_Dir *OpenDir(TALLOC_CTX *mem_ctx, connection_struct *conn, + const char *name, const char *mask, uint32 attr) { - struct smb_Dir *dirp = SMB_MALLOC_P(struct smb_Dir); + struct smb_Dir *dirp = TALLOC_ZERO_P(mem_ctx, struct smb_Dir); if (!dirp) { return NULL; } - ZERO_STRUCTP(dirp); dirp->conn = conn; dirp->name_cache_size = lp_directory_name_cache_size(SNUM(conn)); - dirp->dir_path = SMB_STRDUP(name); + dirp->dir_path = talloc_strdup(dirp, name); if (!dirp->dir_path) { goto fail; } + + dirhandles_open++; + talloc_set_destructor(dirp, smb_Dir_destructor); + dirp->dir = SMB_VFS_OPENDIR(conn, dirp->dir_path, mask, attr); if (!dirp->dir) { - DEBUG(5,("OpenDir: Can't open %s. %s\n", dirp->dir_path, strerror(errno) )); + DEBUG(5,("OpenDir: Can't open %s. %s\n", dirp->dir_path, + strerror(errno) )); goto fail; } - if (dirp->name_cache_size) { - dirp->name_cache = SMB_CALLOC_ARRAY(struct name_cache_entry, - dirp->name_cache_size); - if (!dirp->name_cache) { - goto fail; - } - } else { - dirp->name_cache = NULL; - } - - dirhandles_open++; return dirp; fail: - - if (dirp) { - if (dirp->dir) { - SMB_VFS_CLOSEDIR(conn,dirp->dir); - } - SAFE_FREE(dirp->dir_path); - SAFE_FREE(dirp->name_cache); - SAFE_FREE(dirp); - } + TALLOC_FREE(dirp); return NULL; } - -/******************************************************************* - Close a directory. -********************************************************************/ - -int CloseDir(struct smb_Dir *dirp) -{ - int i, ret = 0; - - if (dirp->dir) { - ret = SMB_VFS_CLOSEDIR(dirp->conn,dirp->dir); - } - SAFE_FREE(dirp->dir_path); - if (dirp->name_cache) { - for (i = 0; i < dirp->name_cache_size; i++) { - SAFE_FREE(dirp->name_cache[i].name); - } - } - SAFE_FREE(dirp->name_cache); - SAFE_FREE(dirp); - dirhandles_open--; - return ret; -} - /******************************************************************* Read from a directory. Also return current offset. Don't check for veto or invisible files. @@ -1291,15 +1266,24 @@ void DirCacheAdd(struct smb_Dir *dirp, const char *name, long offset) { struct name_cache_entry *e; - if (!dirp->name_cache_size || !dirp->name_cache) { + if (dirp->name_cache_size == 0) { return; } + if (dirp->name_cache == NULL) { + dirp->name_cache = TALLOC_ZERO_ARRAY( + dirp, struct name_cache_entry, dirp->name_cache_size); + + if (dirp->name_cache == NULL) { + return; + } + } + dirp->name_cache_index = (dirp->name_cache_index+1) % dirp->name_cache_size; e = &dirp->name_cache[dirp->name_cache_index]; - SAFE_FREE(e->name); - e->name = SMB_STRDUP(name); + TALLOC_FREE(e->name); + e->name = talloc_strdup(dirp, name); e->offset = offset; } @@ -1356,7 +1340,8 @@ NTSTATUS can_delete_directory(struct connection_struct *conn, NTSTATUS status = NT_STATUS_OK; long dirpos = 0; const char *dname; - struct smb_Dir *dir_hnd = OpenDir(conn, dirname, NULL, 0); + struct smb_Dir *dir_hnd = OpenDir(talloc_tos(), conn, dirname, + NULL, 0); if (!dir_hnd) { return map_nt_error_from_unix(errno); @@ -1380,7 +1365,7 @@ NTSTATUS can_delete_directory(struct connection_struct *conn, status = NT_STATUS_DIRECTORY_NOT_EMPTY; break; } - CloseDir(dir_hnd); + TALLOC_FREE(dir_hnd); return status; } diff --git a/source3/smbd/dmapi.c b/source3/smbd/dmapi.c index 05e9138ea9..620baf199e 100644 --- a/source3/smbd/dmapi.c +++ b/source3/smbd/dmapi.c @@ -46,7 +46,7 @@ bool dmapi_have_session(void) { return False; } #define DMAPI_SESSION_NAME "samba" #define DMAPI_TRACE 10 -static dm_sessid_t dmapi_session = DM_NO_SESSION; +static dm_sessid_t samba_dmapi_session = DM_NO_SESSION; /* Initialise the DMAPI interface. Make sure that we only end up initialising * once per process to avoid resource leaks across different DMAPI @@ -75,7 +75,7 @@ static int init_dmapi_service(void) bool dmapi_have_session(void) { - return dmapi_session != DM_NO_SESSION; + return samba_dmapi_session != DM_NO_SESSION; } static dm_sessid_t *realloc_session_list(dm_sessid_t * sessions, int count) @@ -110,7 +110,7 @@ int dmapi_init_session(void) */ SMB_WARN(getuid() == 0, "dmapi_init_session must be called as root"); - dmapi_session = DM_NO_SESSION; + samba_dmapi_session = DM_NO_SESSION; if (init_dmapi_service() < 0) { return -1; } @@ -139,7 +139,7 @@ retry: err = dm_query_session(sessions[i], sizeof(buf), buf, &buflen); buf[sizeof(buf) - 1] = '\0'; if (err == 0 && strcmp(DMAPI_SESSION_NAME, buf) == 0) { - dmapi_session = sessions[i]; + samba_dmapi_session = sessions[i]; DEBUGADD(DMAPI_TRACE, ("attached to existing DMAPI session " "named '%s'\n", buf)); @@ -150,16 +150,15 @@ retry: TALLOC_FREE(sessions); /* No session already defined. */ - if (dmapi_session == DM_NO_SESSION) { - err = dm_create_session(DM_NO_SESSION, - CONST_DISCARD(char *, - DMAPI_SESSION_NAME), - &dmapi_session); + if (samba_dmapi_session == DM_NO_SESSION) { + err = dm_create_session(DM_NO_SESSION, + CONST_DISCARD(char *, DMAPI_SESSION_NAME), + &samba_dmapi_session); if (err < 0) { DEBUGADD(DMAPI_TRACE, ("failed to create new DMAPI session: %s\n", strerror(errno))); - dmapi_session = DM_NO_SESSION; + samba_dmapi_session = DM_NO_SESSION; return -1; } @@ -185,22 +184,22 @@ static int reattach_dmapi_session(void) char buf[DM_SESSION_INFO_LEN]; size_t buflen; - if (dmapi_session != DM_NO_SESSION ) { + if (samba_dmapi_session != DM_NO_SESSION ) { become_root(); /* NOTE: On Linux, this call opens /dev/dmapi, costing us a * file descriptor. Ideally, we would close this when we fork. */ if (init_dmapi_service() < 0) { - dmapi_session = DM_NO_SESSION; + samba_dmapi_session = DM_NO_SESSION; unbecome_root(); return -1; } - if (dm_query_session(dmapi_session, sizeof(buf), + if (dm_query_session(samba_dmapi_session, sizeof(buf), buf, &buflen) < 0) { /* Session is stale. Disable DMAPI. */ - dmapi_session = DM_NO_SESSION; + samba_dmapi_session = DM_NO_SESSION; unbecome_root(); return -1; } @@ -214,33 +213,42 @@ static int reattach_dmapi_session(void) return 0; } -uint32 dmapi_file_flags(const char * const path) +/* If a DMAPI session has been initialised, then we need to make sure + * we are attached to it and have the correct privileges. This is + * necessary to be able to do DMAPI operations across a fork(2). If + * it fails, there is no likelihood of that failure being transient. + * + * Note that this use of the static attached flag relies on the fact + * that dmapi_file_flags() is never called prior to forking the + * per-client server process. + */ +const void * dmapi_get_current_session(void) { static int attached = 0; + if (dmapi_have_session() && !attached) { + attached++; + if (reattach_dmapi_session() < 0) { + return DM_NO_SESSION; + } + } + return &samba_dmapi_session; +} +uint32 dmapi_file_flags(const char * const path) +{ + dm_sessid_t dmapi_session; int err; dm_eventset_t events = {0}; uint nevents; - void *dm_handle; - size_t dm_handle_len; + void *dm_handle = NULL; + size_t dm_handle_len = 0; uint32 flags = 0; - /* If a DMAPI session has been initialised, then we need to make sure - * we are attached to it and have the correct privileges. This is - * necessary to be able to do DMAPI operations across a fork(2). If - * it fails, there is no liklihood of that failure being transient. - * - * Note that this use of the static attached flag relies on the fact - * that dmapi_file_flags() is never called prior to forking the - * per-client server process. - */ - if (dmapi_have_session() && !attached) { - attached++; - if (reattach_dmapi_session() < 0) { - return 0; - } + dmapi_session = *(dm_sessid_t*) dmapi_get_current_session(); + if (dmapi_session == DM_NO_SESSION) { + return 0; } /* AIX has DMAPI but no POSIX capablities support. In this case, @@ -313,4 +321,5 @@ done: return flags; } + #endif /* USE_DMAPI */ diff --git a/source3/smbd/dnsregister.c b/source3/smbd/dnsregister.c index 44bd39f64e..2319097ca5 100644 --- a/source3/smbd/dnsregister.c +++ b/source3/smbd/dnsregister.c @@ -41,7 +41,6 @@ struct dns_reg_state { void dns_register_close(struct dns_reg_state **dns_state_ptr) { - int mdnsd_conn_fd; struct dns_reg_state *dns_state = *dns_state_ptr; if (dns_state == NULL) { @@ -74,7 +73,7 @@ static void dns_register_smbd_retry(struct event_context *ctx, /* Clear previous registration state to force new * registration attempt. Clears event handler. */ - dns_register_close(dns_state); + dns_register_close(&dns_state); } static void schedule_dns_register_smbd_retry(struct dns_reg_state *dns_state, diff --git a/source3/smbd/dosmode.c b/source3/smbd/dosmode.c index 8e3c9b4c91..eb18f65fca 100644 --- a/source3/smbd/dosmode.c +++ b/source3/smbd/dosmode.c @@ -31,23 +31,6 @@ static int set_sparse_flag(const SMB_STRUCT_STAT * const sbuf) } /**************************************************************************** - Work out whether this file is offline -****************************************************************************/ - -static uint32 set_offline_flag(connection_struct *conn, const char *const path) -{ - if (ISDOT(path) || ISDOTDOT(path)) { - return 0; - } - - if (!lp_dmapi_support(SNUM(conn)) || !dmapi_have_session()) { - return 0; - } - - return dmapi_file_flags(path); -} - -/**************************************************************************** Change a dos mode to a unix mode. Base permission for files: if creating file and inheriting (i.e. parent_dir != NULL) @@ -366,6 +349,7 @@ uint32 dos_mode_msdfs(connection_struct *conn, const char *path,SMB_STRUCT_STAT uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf) { uint32 result = 0; + bool offline; DEBUG(8,("dos_mode: %s\n", path)); @@ -395,8 +379,10 @@ uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf) result |= dos_mode_from_sbuf(conn, path, sbuf); } - if (S_ISREG(sbuf->st_mode)) { - result |= set_offline_flag(conn, path); + + offline = SMB_VFS_IS_OFFLINE(conn, path, sbuf); + if (S_ISREG(sbuf->st_mode) && offline) { + result |= FILE_ATTRIBUTE_OFFLINE; } /* Optimization : Only call is_hidden_path if it's not already @@ -432,18 +418,25 @@ int file_set_dosmode(connection_struct *conn, const char *fname, int mask=0; mode_t tmp; mode_t unixmode; - int ret = -1; + int ret = -1, lret = -1; /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */ dosmode &= SAMBA_ATTRIBUTES_MASK; DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, fname)); - if (!st || (st && !VALID_STAT(*st))) { + + if (st == NULL) { + SET_STAT_INVALID(st1); st = &st1; + } + + if (!VALID_STAT(*st)) { if (SMB_VFS_STAT(conn,fname,st)) return(-1); } + unixmode = st->st_mode; + get_acl_group_bits(conn, fname, &st->st_mode); if (S_ISDIR(st->st_mode)) @@ -451,8 +444,10 @@ int file_set_dosmode(connection_struct *conn, const char *fname, else dosmode &= ~aDIR; - if (dos_mode(conn,fname,st) == dosmode) + if (dos_mode(conn,fname,st) == dosmode) { + st->st_mode = unixmode; return(0); + } /* Store the DOS attributes in an EA by preference. */ if (set_ea_dos_attribute(conn, fname, st, dosmode)) { @@ -460,6 +455,7 @@ int file_set_dosmode(connection_struct *conn, const char *fname, notify_fname(conn, NOTIFY_ACTION_MODIFIED, FILE_NOTIFY_CHANGE_ATTRIBUTES, fname); } + st->st_mode = unixmode; return 0; } @@ -495,11 +491,23 @@ int file_set_dosmode(connection_struct *conn, const char *fname, unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)); } - if ((ret = SMB_VFS_CHMOD(conn,fname,unixmode)) == 0) { - if (!newfile) { + if (dosmode & FILE_ATTRIBUTE_OFFLINE) { + lret = SMB_VFS_SET_OFFLINE(conn, fname); + if (lret == -1) { + DEBUG(0, ("set_dos_mode: client has asked to set " + "FILE_ATTRIBUTE_OFFLINE to %s/%s but there was " + "an error while setting it or it is not supported.\n", + parent_dir, fname)); + } + } + + ret = SMB_VFS_CHMOD(conn, fname, unixmode); + if (ret == 0) { + if(!newfile || (lret != -1)) { notify_fname(conn, NOTIFY_ACTION_MODIFIED, - FILE_NOTIFY_CHANGE_ATTRIBUTES, fname); + FILE_NOTIFY_CHANGE_ATTRIBUTES, fname); } + st->st_mode = unixmode; return 0; } @@ -527,13 +535,16 @@ int file_set_dosmode(connection_struct *conn, const char *fname, if (!NT_STATUS_IS_OK(open_file_fchmod(conn,fname,st,&fsp))) return -1; become_root(); - ret = SMB_VFS_FCHMOD(fsp, fsp->fh->fd, unixmode); + ret = SMB_VFS_FCHMOD(fsp, unixmode); unbecome_root(); close_file_fchmod(fsp); if (!newfile) { notify_fname(conn, NOTIFY_ACTION_MODIFIED, FILE_NOTIFY_CHANGE_ATTRIBUTES, fname); } + if (ret == 0) { + st->st_mode = unixmode; + } } return( ret ); diff --git a/source3/smbd/error.c b/source3/smbd/error.c index 12eff42023..de2de088ec 100644 --- a/source3/smbd/error.c +++ b/source3/smbd/error.c @@ -24,34 +24,6 @@ extern struct unix_error_map unix_dos_nt_errmap[]; extern uint32 global_client_caps; -/**************************************************************************** - Create an error packet from errno. -****************************************************************************/ - -int unix_error_packet(char *outbuf,int def_class,uint32 def_code, NTSTATUS def_status, int line, const char *file) -{ - int eclass=def_class; - int ecode=def_code; - NTSTATUS ntstatus = def_status; - int i=0; - - if (errno != 0) { - DEBUG(3,("unix_error_packet: error string = %s\n",strerror(errno))); - - while (unix_dos_nt_errmap[i].dos_class != 0) { - if (unix_dos_nt_errmap[i].unix_error == errno) { - eclass = unix_dos_nt_errmap[i].dos_class; - ecode = unix_dos_nt_errmap[i].dos_code; - ntstatus = unix_dos_nt_errmap[i].nt_error; - break; - } - i++; - } - } - - return error_packet(outbuf,eclass,ecode,ntstatus,line,file); -} - bool use_nt_status(void) { return lp_nt_status_support() && (global_client_caps & CAP_STATUS32); @@ -111,7 +83,7 @@ void error_packet_set(char *outbuf, uint8 eclass, uint32 ecode, NTSTATUS ntstatu int error_packet(char *outbuf, uint8 eclass, uint32 ecode, NTSTATUS ntstatus, int line, const char *file) { - int outsize = set_message(outbuf,0,0,True); + int outsize = srv_set_message(outbuf,0,0,True); error_packet_set(outbuf, eclass, ecode, ntstatus, line, file); return outsize; } @@ -150,8 +122,24 @@ void reply_both_error(struct smb_request *req, uint8 eclass, uint32 ecode, line, file); } +void reply_openerror(struct smb_request *req, NTSTATUS status) +{ + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) { + /* + * We hit an existing file, and if we're returning DOS + * error codes OBJECT_NAME_COLLISION would map to + * ERRDOS/183, we need to return ERRDOS/80, see bug + * 4852. + */ + reply_botherror(req, NT_STATUS_OBJECT_NAME_COLLISION, + ERRDOS, ERRfilexists); + } else { + reply_nterror(req, status); + } +} + void reply_unix_error(struct smb_request *req, uint8 defclass, uint32 defcode, - NTSTATUS defstatus, int line, const char *file) + NTSTATUS defstatus, int line, const char *file) { int eclass=defclass; int ecode=defcode; @@ -163,7 +151,7 @@ void reply_unix_error(struct smb_request *req, uint8 defclass, uint32 defcode, if (errno != 0) { DEBUG(3,("unix_error_packet: error string = %s\n", - strerror(errno))); + strerror(errno))); while (unix_dos_nt_errmap[i].dos_class != 0) { if (unix_dos_nt_errmap[i].unix_error == errno) { @@ -177,22 +165,5 @@ void reply_unix_error(struct smb_request *req, uint8 defclass, uint32 defcode, } error_packet_set((char *)req->outbuf, eclass, ecode, ntstatus, - line, file); + line, file); } - -void reply_openerror(struct smb_request *req, NTSTATUS status) -{ - if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) { - /* - * We hit an existing file, and if we're returning DOS - * error codes OBJECT_NAME_COLLISION would map to - * ERRDOS/183, we need to return ERRDOS/80, see bug - * 4852. - */ - reply_botherror(req, NT_STATUS_OBJECT_NAME_COLLISION, - ERRDOS, ERRfilexists); - } else { - reply_nterror(req, status); - } -} - diff --git a/source3/smbd/file_access.c b/source3/smbd/file_access.c index a58bcdd891..964d1af258 100644 --- a/source3/smbd/file_access.c +++ b/source3/smbd/file_access.c @@ -25,67 +25,6 @@ extern struct current_user current_user; #undef DBGC_CLASS #define DBGC_CLASS DBGC_ACLS -/**************************************************************************** - Helper function that gets a security descriptor by connection and - file name. - NOTE: This is transitional, in the sense that SMB_VFS_GET_NT_ACL really - should *not* get a files_struct pointer but a connection_struct ptr - (automatic by the vfs handle) and the file name and _use_ that! -****************************************************************************/ -static NTSTATUS conn_get_nt_acl(TALLOC_CTX *mem_ctx, - struct connection_struct *conn, - const char *fname, - SMB_STRUCT_STAT *psbuf, - struct security_descriptor **psd) -{ - NTSTATUS status; - struct files_struct *fsp = NULL; - struct security_descriptor *secdesc = NULL; - - if (!VALID_STAT(*psbuf)) { - if (SMB_VFS_STAT(conn, fname, psbuf) != 0) { - return map_nt_error_from_unix(errno); - } - } - - /* fake a files_struct ptr: */ - - if (S_ISDIR(psbuf->st_mode)) { - status = open_directory(conn, NULL, fname, psbuf, - READ_CONTROL_ACCESS, - FILE_SHARE_READ|FILE_SHARE_WRITE, - FILE_OPEN, - 0, - FILE_ATTRIBUTE_DIRECTORY, - NULL, &fsp); - } - else { - status = open_file_stat(conn, NULL, fname, psbuf, &fsp); - } - - if (!NT_STATUS_IS_OK(status)) { - DEBUG(3, ("Unable to open file %s: %s\n", fname, - nt_errstr(status))); - return status; - } - - status = SMB_VFS_GET_NT_ACL(fsp, fname, - (OWNER_SECURITY_INFORMATION | - GROUP_SECURITY_INFORMATION | - DACL_SECURITY_INFORMATION), - &secdesc); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(5, ("Unable to get NT ACL for file %s\n", fname)); - goto done; - } - - *psd = talloc_move(mem_ctx, &secdesc); - -done: - close_file(fsp, NORMAL_CLOSE); - return status; -} - static bool can_access_file_acl(struct connection_struct *conn, const char * fname, SMB_STRUCT_STAT *psbuf, uint32_t access_mask) @@ -95,7 +34,11 @@ static bool can_access_file_acl(struct connection_struct *conn, uint32_t access_granted; struct security_descriptor *secdesc = NULL; - status = conn_get_nt_acl(talloc_tos(), conn, fname, psbuf, &secdesc); + status = SMB_VFS_GET_NT_ACL(conn, fname, + (OWNER_SECURITY_INFORMATION | + GROUP_SECURITY_INFORMATION | + DACL_SECURITY_INFORMATION), + &secdesc); if (!NT_STATUS_IS_OK(status)) { DEBUG(5, ("Could not get acl: %s\n", nt_errstr(status))); return false; diff --git a/source3/smbd/fileio.c b/source3/smbd/fileio.c index 8037510d80..8cea4989f5 100644 --- a/source3/smbd/fileio.c +++ b/source3/smbd/fileio.c @@ -77,7 +77,7 @@ ssize_t read_file(files_struct *fsp,char *data,SMB_OFF_T pos,size_t n) #ifdef DMF_FIX int numretries = 3; tryagain: - readret = SMB_VFS_PREAD(fsp,fsp->fh->fd,data,n,pos); + readret = SMB_VFS_PREAD(fsp,data,n,pos); if (readret == -1) { if ((errno == EAGAIN) && numretries) { @@ -89,7 +89,7 @@ tryagain: return -1; } #else /* NO DMF fix. */ - readret = SMB_VFS_PREAD(fsp,fsp->fh->fd,data,n,pos); + readret = SMB_VFS_PREAD(fsp,data,n,pos); if (readret == -1) { return -1; @@ -184,7 +184,7 @@ static int wcp_file_size_change(files_struct *fsp) write_cache *wcp = fsp->wcp; wcp->file_size = wcp->offset + wcp->data_size; - ret = SMB_VFS_FTRUNCATE(fsp, fsp->fh->fd, wcp->file_size); + ret = SMB_VFS_FTRUNCATE(fsp, wcp->file_size); if (ret == -1) { DEBUG(0,("wcp_file_size_change (%s): ftruncate of size %.0f error %s\n", fsp->fsp_name, (double)wcp->file_size, strerror(errno) )); @@ -229,7 +229,7 @@ ssize_t write_file(struct smb_request *req, SMB_STRUCT_STAT st; fsp->modified = True; - if (SMB_VFS_FSTAT(fsp,fsp->fh->fd,&st) == 0) { + if (SMB_VFS_FSTAT(fsp, &st) == 0) { int dosmode = dos_mode(fsp->conn,fsp->fsp_name,&st); if ((lp_store_dos_attributes(SNUM(fsp->conn)) || MAP_ARCHIVE(fsp->conn)) && @@ -879,7 +879,7 @@ NTSTATUS sync_file(connection_struct *conn, files_struct *fsp, bool write_throug if (ret == -1) { return map_nt_error_from_unix(errno); } - ret = SMB_VFS_FSYNC(fsp,fsp->fh->fd); + ret = SMB_VFS_FSYNC(fsp); if (ret == -1) { return map_nt_error_from_unix(errno); } @@ -896,6 +896,6 @@ int fsp_stat(files_struct *fsp, SMB_STRUCT_STAT *pst) if (fsp->fh->fd == -1) { return SMB_VFS_STAT(fsp->conn, fsp->fsp_name, pst); } else { - return SMB_VFS_FSTAT(fsp,fsp->fh->fd, pst); + return SMB_VFS_FSTAT(fsp, pst); } } diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c index dc733d4568..10e9583049 100644 --- a/source3/smbd/filename.c +++ b/source3/smbd/filename.c @@ -28,6 +28,13 @@ static bool scan_directory(connection_struct *conn, const char *path, char *name, char **found_name); +static NTSTATUS build_stream_path(TALLOC_CTX *mem_ctx, + connection_struct *conn, + const char *orig_path, + const char *basepath, + const char *streamname, + SMB_STRUCT_STAT *pst, + char **path); /**************************************************************************** Mangle the 2nd name and check if it is then equal to the first name. @@ -119,6 +126,7 @@ NTSTATUS unix_convert(TALLOC_CTX *ctx, char *start, *end; char *dirpath = NULL; char *name = NULL; + char *stream = NULL; bool component_was_mangled = False; bool name_has_wildcard = False; NTSTATUS result; @@ -206,6 +214,20 @@ NTSTATUS unix_convert(TALLOC_CTX *ctx, return NT_STATUS_NO_MEMORY; } + if (!lp_posix_pathnames()) { + stream = strchr_m(name, ':'); + + if (stream != NULL) { + char *tmp = talloc_strdup(ctx, stream); + if (tmp == NULL) { + TALLOC_FREE(name); + return NT_STATUS_NO_MEMORY; + } + *stream = '\0'; + stream = tmp; + } + } + /* * Large directory fix normalization. If we're case sensitive, and * the case preserving parameters are set to "no", normalize the case of @@ -222,8 +244,14 @@ NTSTATUS unix_convert(TALLOC_CTX *ctx, start = name; - if(!conn->case_sensitive - && stat_cache_lookup(conn, &name, &dirpath, &start, &st)) { + /* If we're providing case insentive semantics or + * the underlying filesystem is case insensitive, + * then a case-normalized hit in the stat-cache is + * authoratitive. JRA. + */ + + if((!conn->case_sensitive || !(conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH)) && + stat_cache_lookup(conn, &name, &dirpath, &start, &st)) { *pst = st; goto done; } @@ -269,10 +297,11 @@ NTSTATUS unix_convert(TALLOC_CTX *ctx, /* * A special case - if we don't have any mangling chars and are case - * sensitive then searching won't help. + * sensitive or the underlying filesystem is case insentive then searching + * won't help. */ - if (conn->case_sensitive && + if ((conn->case_sensitive || !(conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH)) && !mangle_is_mangled(name, conn->params)) { goto done; } @@ -646,6 +675,20 @@ NTSTATUS unix_convert(TALLOC_CTX *ctx, DEBUG(5,("conversion finished %s -> %s\n",orig_path, name)); done: + if (stream != NULL) { + char *tmp = NULL; + + result = build_stream_path(ctx, conn, orig_path, name, stream, + pst, &tmp); + if (!NT_STATUS_IS_OK(result)) { + goto fail; + } + + DEBUG(10, ("build_stream_path returned %s\n", tmp)); + + TALLOC_FREE(name); + name = tmp; + } *pp_conv_path = name; TALLOC_FREE(dirpath); return NT_STATUS_OK; @@ -735,6 +778,15 @@ static bool scan_directory(connection_struct *conn, const char *path, path = "."; } + /* If we have a case-sensitive filesystem, it doesn't do us any + * good to search for a name. If a case variation of the name was + * there, then the original stat(2) would have found it. + */ + if (!mangled && !(conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH)) { + errno = ENOENT; + return False; + } + /* * The incoming name can be mangled, and if we de-mangle it * here it will not compare correctly against the filename (name2) @@ -762,7 +814,7 @@ static bool scan_directory(connection_struct *conn, const char *path, } /* open the directory */ - if (!(cur_dir = OpenDir(conn, path, NULL, 0))) { + if (!(cur_dir = OpenDir(talloc_tos(), conn, path, NULL, 0))) { DEBUG(3,("scan dir didn't open dir [%s]\n",path)); TALLOC_FREE(unmangled_name); return(False); @@ -793,7 +845,7 @@ static bool scan_directory(connection_struct *conn, const char *path, /* we've found the file, change it's name and return */ *found_name = talloc_strdup(ctx,dname); TALLOC_FREE(unmangled_name); - CloseDir(cur_dir); + TALLOC_FREE(cur_dir); if (!*found_name) { errno = ENOMEM; return False; @@ -803,7 +855,94 @@ static bool scan_directory(connection_struct *conn, const char *path, } TALLOC_FREE(unmangled_name); - CloseDir(cur_dir); + TALLOC_FREE(cur_dir); errno = ENOENT; return False; } + +static NTSTATUS build_stream_path(TALLOC_CTX *mem_ctx, + connection_struct *conn, + const char *orig_path, + const char *basepath, + const char *streamname, + SMB_STRUCT_STAT *pst, + char **path) +{ + SMB_STRUCT_STAT st; + char *result = NULL; + NTSTATUS status; + unsigned int i, num_streams; + struct stream_struct *streams = NULL; + + result = talloc_asprintf(mem_ctx, "%s%s", basepath, streamname); + if (result == NULL) { + return NT_STATUS_NO_MEMORY; + } + + if (SMB_VFS_STAT(conn, result, &st) == 0) { + *pst = st; + *path = result; + return NT_STATUS_OK; + } + + if (errno != ENOENT) { + status = map_nt_error_from_unix(errno); + DEBUG(10, ("vfs_stat failed: %s\n", nt_errstr(status))); + goto fail; + } + + status = SMB_VFS_STREAMINFO(conn, NULL, basepath, mem_ctx, + &num_streams, &streams); + + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { + SET_STAT_INVALID(*pst); + *path = result; + return NT_STATUS_OK; + } + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("vfs_streaminfo failed: %s\n", nt_errstr(status))); + goto fail; + } + + for (i=0; i<num_streams; i++) { + DEBUG(10, ("comparing [%s] and [%s]: ", + streamname, streams[i].name)); + if (fname_equal(streamname, streams[i].name, + conn->case_sensitive)) { + DEBUGADD(10, ("equal\n")); + break; + } + DEBUGADD(10, ("not equal\n")); + } + + if (i == num_streams) { + SET_STAT_INVALID(*pst); + *path = result; + TALLOC_FREE(streams); + return NT_STATUS_OK; + } + + TALLOC_FREE(result); + + result = talloc_asprintf(mem_ctx, "%s%s", basepath, streams[i].name); + if (result == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + SET_STAT_INVALID(*pst); + + if (SMB_VFS_STAT(conn, result, pst) == 0) { + stat_cache_add(orig_path, result, conn->case_sensitive); + } + + *path = result; + TALLOC_FREE(streams); + return NT_STATUS_OK; + + fail: + TALLOC_FREE(result); + TALLOC_FREE(streams); + return status; +} diff --git a/source3/smbd/files.c b/source3/smbd/files.c index 179963dae9..95f01b88ce 100644 --- a/source3/smbd/files.c +++ b/source3/smbd/files.c @@ -460,6 +460,11 @@ void file_free(files_struct *fsp) ZERO_STRUCT(fsp_fi_cache); } + /* Drop all remaining extensions. */ + while (fsp->vfs_extension) { + vfs_remove_fsp_extension(fsp->vfs_extension->owner, fsp); + } + SAFE_FREE(fsp); } diff --git a/source3/smbd/ipc.c b/source3/smbd/ipc.c index f28016ccb3..68a13d692f 100644 --- a/source3/smbd/ipc.c +++ b/source3/smbd/ipc.c @@ -30,7 +30,7 @@ extern int max_send; #define NERR_notsupported 50 -static void api_no_reply(struct smb_request *req); +static void api_no_reply(connection_struct *conn, struct smb_request *req); /******************************************************************* copies parameters and data, as needed, into the smb buffer @@ -81,7 +81,8 @@ static void copy_trans_params_and_data(char *outbuf, int align, Send a trans reply. ****************************************************************************/ -void send_trans_reply(struct smb_request *req, +void send_trans_reply(connection_struct *conn, + struct smb_request *req, char *rparam, int rparam_len, char *rdata, int rdata_len, bool buffer_too_large) @@ -129,8 +130,10 @@ void send_trans_reply(struct smb_request *req, } show_msg((char *)req->outbuf); - if (!send_smb(smbd_server_fd(),(char *)req->outbuf)) - exit_server_cleanly("send_trans_reply: send_smb failed."); + if (!srv_send_smb(smbd_server_fd(), + (char *)req->outbuf, + IS_CONN_ENCRYPTED(conn))) + exit_server_cleanly("send_trans_reply: srv_send_smb failed."); TALLOC_FREE(req->outbuf); @@ -175,8 +178,10 @@ void send_trans_reply(struct smb_request *req, } show_msg((char *)req->outbuf); - if (!send_smb(smbd_server_fd(), (char *)req->outbuf)) - exit_server_cleanly("send_trans_reply: send_smb failed."); + if (!srv_send_smb(smbd_server_fd(), + (char *)req->outbuf, + IS_CONN_ENCRYPTED(conn))) + exit_server_cleanly("send_trans_reply: srv_send_smb failed."); tot_data_sent += this_ldata; tot_param_sent += this_lparam; @@ -188,7 +193,7 @@ void send_trans_reply(struct smb_request *req, Start the first part of an RPC reply which began with an SMBtrans request. ****************************************************************************/ -static void api_rpc_trans_reply(struct smb_request *req, smb_np_struct *p) +static void api_rpc_trans_reply(connection_struct *conn, struct smb_request *req, smb_np_struct *p) { bool is_data_outstanding; char *rdata = (char *)SMB_MALLOC(p->max_trans_reply); @@ -203,11 +208,11 @@ static void api_rpc_trans_reply(struct smb_request *req, smb_np_struct *p) if((data_len = read_from_pipe( p, rdata, p->max_trans_reply, &is_data_outstanding)) < 0) { SAFE_FREE(rdata); - api_no_reply(req); + api_no_reply(conn,req); return; } - send_trans_reply(req, NULL, 0, rdata, data_len, is_data_outstanding); + send_trans_reply(conn, req, NULL, 0, rdata, data_len, is_data_outstanding); SAFE_FREE(rdata); return; } @@ -216,7 +221,7 @@ static void api_rpc_trans_reply(struct smb_request *req, smb_np_struct *p) WaitNamedPipeHandleState ****************************************************************************/ -static void api_WNPHS(struct smb_request *req, smb_np_struct *p, +static void api_WNPHS(connection_struct *conn, struct smb_request *req, smb_np_struct *p, char *param, int param_len) { uint16 priority; @@ -231,10 +236,10 @@ static void api_WNPHS(struct smb_request *req, smb_np_struct *p, if (wait_rpc_pipe_hnd_state(p, priority)) { /* now send the reply */ - send_trans_reply(req, NULL, 0, NULL, 0, False); + send_trans_reply(conn, req, NULL, 0, NULL, 0, False); return; } - api_no_reply(req); + api_no_reply(conn,req); } @@ -242,7 +247,7 @@ static void api_WNPHS(struct smb_request *req, smb_np_struct *p, SetNamedPipeHandleState ****************************************************************************/ -static void api_SNPHS(struct smb_request *req, smb_np_struct *p, +static void api_SNPHS(connection_struct *conn, struct smb_request *req, smb_np_struct *p, char *param, int param_len) { uint16 id; @@ -257,10 +262,10 @@ static void api_SNPHS(struct smb_request *req, smb_np_struct *p, if (set_rpc_pipe_hnd_state(p, id)) { /* now send the reply */ - send_trans_reply(req, NULL, 0, NULL, 0, False); + send_trans_reply(conn, req, NULL, 0, NULL, 0, False); return; } - api_no_reply(req); + api_no_reply(conn,req); } @@ -268,7 +273,7 @@ static void api_SNPHS(struct smb_request *req, smb_np_struct *p, When no reply is generated, indicate unsupported. ****************************************************************************/ -static void api_no_reply(struct smb_request *req) +static void api_no_reply(connection_struct *conn, struct smb_request *req) { char rparam[4]; @@ -279,7 +284,7 @@ static void api_no_reply(struct smb_request *req) DEBUG(3,("Unsupported API fd command\n")); /* now send the reply */ - send_trans_reply(req, rparam, 4, NULL, 0, False); + send_trans_reply(conn, req, rparam, 4, NULL, 0, False); return; } @@ -321,7 +326,7 @@ static void api_fd_reply(connection_struct *conn, uint16 vuid, /* Win9x does this call with a unicode pipe name, not a pnum. */ /* Just return success for now... */ DEBUG(3,("Got TRANSACT_WAITNAMEDPIPEHANDLESTATE on text pipe name\n")); - send_trans_reply(req, NULL, 0, NULL, 0, False); + send_trans_reply(conn, req, NULL, 0, NULL, 0, False); return; } @@ -349,18 +354,18 @@ static void api_fd_reply(connection_struct *conn, uint16 vuid, /* dce/rpc command */ reply = write_to_pipe(p, data, tdscnt); if (!reply) { - api_no_reply(req); + api_no_reply(conn, req); return; } - api_rpc_trans_reply(req, p); + api_rpc_trans_reply(conn, req, p); break; case TRANSACT_WAITNAMEDPIPEHANDLESTATE: /* Wait Named Pipe Handle state */ - api_WNPHS(req, p, params, tpscnt); + api_WNPHS(conn, req, p, params, tpscnt); break; case TRANSACT_SETNAMEDPIPEHANDLESTATE: /* Set Named Pipe Handle state */ - api_SNPHS(req, p, params, tpscnt); + api_SNPHS(conn, req, p, params, tpscnt); break; default: reply_nterror(req, NT_STATUS_INVALID_PARAMETER); @@ -472,8 +477,10 @@ static void handle_trans(connection_struct *conn, struct smb_request *req, state->max_data_return, state->max_param_return); - if (state->close_on_completion) + if (state->close_on_completion) { close_cnum(conn,state->vuid); + req->conn = NULL; + } return; } @@ -482,8 +489,9 @@ static void handle_trans(connection_struct *conn, struct smb_request *req, Reply to a SMBtrans. ****************************************************************************/ -void reply_trans(connection_struct *conn, struct smb_request *req) +void reply_trans(struct smb_request *req) { + connection_struct *conn = req->conn; unsigned int dsoff; unsigned int dscnt; unsigned int psoff; @@ -662,8 +670,9 @@ void reply_trans(connection_struct *conn, struct smb_request *req) Reply to a secondary SMBtrans. ****************************************************************************/ -void reply_transs(connection_struct *conn, struct smb_request *req) +void reply_transs(struct smb_request *req) { + connection_struct *conn = req->conn; unsigned int pcnt,poff,dcnt,doff,pdisp,ddisp; struct trans_state *state; int size; diff --git a/source3/smbd/lanman.c b/source3/smbd/lanman.c index 3ab216c062..5a6df1f139 100644 --- a/source3/smbd/lanman.c +++ b/source3/smbd/lanman.c @@ -4605,7 +4605,7 @@ void api_reply(connection_struct *conn, uint16 vuid, /* If api_Unsupported returns false we can't return anything. */ if (reply) { - send_trans_reply(req, rparam, rparam_len, + send_trans_reply(conn, req, rparam, rparam_len, rdata, rdata_len, False); } diff --git a/source3/smbd/mangle_hash.c b/source3/smbd/mangle_hash.c index c369f6eda4..1dc9c67dcc 100644 --- a/source3/smbd/mangle_hash.c +++ b/source3/smbd/mangle_hash.c @@ -57,8 +57,7 @@ static char magic_char = '~'; static const char basechars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-!@#$%"; #define MANGLE_BASE (sizeof(basechars)/sizeof(char)-1) -static unsigned char chartest[256] = { 0 }; -static bool ct_initialized = False; +static unsigned char *chartest; #define mangle(V) ((char)(basechars[(V) % MANGLE_BASE])) #define BASECHAR_MASK 0xf0 @@ -334,13 +333,13 @@ static void init_chartest( void ) { const unsigned char *s; - memset( (char *)chartest, '\0', 256 ); + chartest = SMB_MALLOC_ARRAY(unsigned char, 256); + + SMB_ASSERT(chartest != NULL); for( s = (const unsigned char *)basechars; *s; s++ ) { chartest[*s] |= BASECHAR_MASK; } - - ct_initialized = True; } /* ************************************************************************** ** @@ -367,8 +366,9 @@ static bool is_mangled(const char *s, const struct share_params *p) magic_char = lp_magicchar(p); - if( !ct_initialized ) + if (chartest == NULL) { init_chartest(); + } magic = strchr_m( s, magic_char ); while( magic && magic[1] && magic[2] ) { /* 3 chars, 1st is magic. */ diff --git a/source3/smbd/mangle_hash2.c b/source3/smbd/mangle_hash2.c index 7066c2a4e5..a9b94aabc3 100644 --- a/source3/smbd/mangle_hash2.c +++ b/source3/smbd/mangle_hash2.c @@ -93,15 +93,6 @@ static unsigned char char_flags[256]; */ static unsigned mangle_prefix; -/* we will use a very simple direct mapped prefix cache. The big - advantage of this cache structure is speed and low memory usage - - The cache is indexed by the low-order bits of the hash, and confirmed by - hashing the resulting cache entry to match the known hash -*/ -static char **prefix_cache; -static unsigned int *prefix_cache_hashes; - /* these are the characters we use in the 8.3 hash. Must be 36 chars long */ static const char *basechars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; static unsigned char base_reverse[256]; @@ -147,57 +138,39 @@ static unsigned int mangle_hash(const char *key, unsigned int length) return value & ~0x80000000; } -/* - initialise (ie. allocate) the prefix cache - */ -static bool cache_init(void) -{ - if (prefix_cache) { - return True; - } - - prefix_cache = SMB_CALLOC_ARRAY(char *,MANGLE_CACHE_SIZE); - if (!prefix_cache) { - return False; - } - - prefix_cache_hashes = SMB_CALLOC_ARRAY(unsigned int, MANGLE_CACHE_SIZE); - if (!prefix_cache_hashes) { - SAFE_FREE(prefix_cache); - return False; - } - - return True; -} - /* insert an entry into the prefix cache. The string might not be null terminated */ static void cache_insert(const char *prefix, int length, unsigned int hash) { - int i = hash % MANGLE_CACHE_SIZE; + char *str = SMB_STRNDUP(prefix, length); - if (prefix_cache[i]) { - free(prefix_cache[i]); + if (str == NULL) { + return; } - prefix_cache[i] = SMB_STRNDUP(prefix, length); - prefix_cache_hashes[i] = hash; + memcache_add(smbd_memcache(), MANGLE_HASH2_CACHE, + data_blob_const(&hash, sizeof(hash)), + data_blob_const(str, length+1)); + SAFE_FREE(str); } /* lookup an entry in the prefix cache. Return NULL if not found. */ -static const char *cache_lookup(unsigned int hash) +static char *cache_lookup(TALLOC_CTX *mem_ctx, unsigned int hash) { - int i = hash % MANGLE_CACHE_SIZE; + DATA_BLOB value; - if (!prefix_cache[i] || hash != prefix_cache_hashes[i]) { + if (!memcache_lookup(smbd_memcache(), MANGLE_HASH2_CACHE, + data_blob_const(&hash, sizeof(hash)), &value)) { return NULL; } - /* yep, it matched */ - return prefix_cache[i]; + SMB_ASSERT((value.length > 0) + && (value.data[value.length-1] == '\0')); + + return talloc_strdup(mem_ctx, (char *)value.data); } @@ -377,7 +350,7 @@ static bool lookup_name_from_8_3(TALLOC_CTX *ctx, { unsigned int hash, multiplier; unsigned int i; - const char *prefix; + char *prefix; char extension[4]; *pp_out = NULL; @@ -397,7 +370,7 @@ static bool lookup_name_from_8_3(TALLOC_CTX *ctx, } /* now look in the prefix cache for that hash */ - prefix = cache_lookup(hash); + prefix = cache_lookup(ctx, hash); if (!prefix) { M_DEBUG(10,("lookup_name_from_8_3: %s -> %08X -> not found\n", name, hash)); @@ -421,7 +394,9 @@ static bool lookup_name_from_8_3(TALLOC_CTX *ctx, *pp_out = talloc_strdup(ctx, prefix); } - if (!pp_out) { + TALLOC_FREE(prefix); + + if (!*pp_out) { M_DEBUG(0,("talloc_fail")); return False; } @@ -728,10 +703,6 @@ struct mangle_fns *mangle_hash2_init(void) init_tables(); mangle_reset(); - if (!cache_init()) { - return NULL; - } - return &mangle_fns; } diff --git a/source3/smbd/map_username.c b/source3/smbd/map_username.c index c04e0f1ae2..7290f70547 100644 --- a/source3/smbd/map_username.c +++ b/source3/smbd/map_username.c @@ -28,14 +28,49 @@ any incoming or new username - in order to canonicalize the name. This is being done to de-couple the case conversions from the user mapping function. Previously, the map_username was being called - every time Get_Pwnam was called. + every time Get_Pwnam_alloc was called. Returns True if username was changed, false otherwise. ********************************************************************/ +static char *last_from, *last_to; + +static const char *get_last_from(void) +{ + if (!last_from) { + return ""; + } + return last_from; +} + +static const char *get_last_to(void) +{ + if (!last_to) { + return ""; + } + return last_to; +} + +static bool set_last_from_to(const char *from, const char *to) +{ + char *orig_from = last_from; + char *orig_to = last_to; + + last_from = SMB_STRDUP(from); + last_to = SMB_STRDUP(to); + + SAFE_FREE(orig_from); + SAFE_FREE(orig_to); + + if (!last_from || !last_to) { + SAFE_FREE(last_from); + SAFE_FREE(last_to); + return false; + } + return true; +} + bool map_username(fstring user) { - static bool initialised=False; - static fstring last_from,last_to; XFILE *f; char *mapfile = lp_username_map(); char *s; @@ -46,12 +81,12 @@ bool map_username(fstring user) if (!*user) return false; - if (strequal(user,last_to)) + if (strequal(user,get_last_to())) return false; - if (strequal(user,last_from)) { - DEBUG(3,("Mapped user %s to %s\n",user,last_to)); - fstrcpy(user,last_to); + if (strequal(user,get_last_from())) { + DEBUG(3,("Mapped user %s to %s\n",user,get_last_to())); + fstrcpy(user,get_last_to()); return true; } @@ -98,15 +133,9 @@ bool map_username(fstring user) } /* ok. let's try the mapfile */ - if (!*mapfile) return False; - if (!initialised) { - *last_from = *last_to = 0; - initialised = True; - } - f = x_fopen(mapfile,O_RDONLY, 0); if (!f) { DEBUG(0,("can't open username map %s. Error %s\n",mapfile, strerror(errno) )); @@ -135,7 +164,7 @@ bool map_username(fstring user) while (*unixname && isspace((int)*unixname)) unixname++; } - + if (!*unixname || strchr_m("#;",*unixname)) continue; @@ -159,27 +188,28 @@ bool map_username(fstring user) user_in_list(user, (const char **)dosuserlist)) { DEBUG(3,("Mapped user %s to %s\n",user,unixname)); mapped_user = True; - fstrcpy( last_from,user ); + + set_last_from_to(user, unixname); fstrcpy( user, unixname ); - fstrcpy( last_to,user ); + if ( return_if_mapped ) { str_list_free (&dosuserlist); x_fclose(f); return True; } } - + str_list_free (&dosuserlist); } x_fclose(f); /* - * Setup the last_from and last_to as an optimization so + * Setup the last_from and last_to as an optimization so * that we don't scan the file again for the same user. */ - fstrcpy(last_from,user); - fstrcpy(last_to,user); + + set_last_from_to(user, user); return mapped_user; } diff --git a/source3/smbd/message.c b/source3/smbd/message.c index d0b524da0e..a870f03df9 100644 --- a/source3/smbd/message.c +++ b/source3/smbd/message.c @@ -137,7 +137,7 @@ static void msg_deliver(struct msg_state *state) conn POINTER CAN BE NULL HERE ! ****************************************************************************/ -void reply_sends(connection_struct *conn, struct smb_request *req) +void reply_sends(struct smb_request *req) { struct msg_state *state; int len; @@ -190,7 +190,7 @@ void reply_sends(connection_struct *conn, struct smb_request *req) conn POINTER CAN BE NULL HERE ! ****************************************************************************/ -void reply_sendstrt(connection_struct *conn, struct smb_request *req) +void reply_sendstrt(struct smb_request *req) { char *p; @@ -234,7 +234,7 @@ void reply_sendstrt(connection_struct *conn, struct smb_request *req) conn POINTER CAN BE NULL HERE ! ****************************************************************************/ -void reply_sendtxt(connection_struct *conn, struct smb_request *req) +void reply_sendtxt(struct smb_request *req) { int len; char *msg; @@ -287,7 +287,7 @@ void reply_sendtxt(connection_struct *conn, struct smb_request *req) conn POINTER CAN BE NULL HERE ! ****************************************************************************/ -void reply_sendend(connection_struct *conn, struct smb_request *req) +void reply_sendend(struct smb_request *req) { START_PROFILE(SMBsendend); diff --git a/source3/smbd/msdfs.c b/source3/smbd/msdfs.c index 4ae735633b..8ffa0f7751 100644 --- a/source3/smbd/msdfs.c +++ b/source3/smbd/msdfs.c @@ -288,12 +288,13 @@ static bool parse_msdfs_symlink(TALLOC_CTX *ctx, char **alt_path = NULL; int count = 0, i; struct referral *reflist; + char *saveptr; temp = talloc_strdup(ctx, target); if (!temp) { return False; } - prot = strtok(temp,":"); + prot = strtok_r(temp, ":", &saveptr); if (!prot) { DEBUG(0,("parse_msdfs_symlink: invalid path !\n")); return False; @@ -306,7 +307,7 @@ static bool parse_msdfs_symlink(TALLOC_CTX *ctx, /* parse out the alternate paths */ while((count<MAX_REFERRAL_COUNT) && - ((alt_path[count] = strtok(NULL,",")) != NULL)) { + ((alt_path[count] = strtok_r(NULL, ",", &saveptr)) != NULL)) { count++; } diff --git a/source3/smbd/negprot.c b/source3/smbd/negprot.c index 02f752fd67..9f56949eeb 100644 --- a/source3/smbd/negprot.c +++ b/source3/smbd/negprot.c @@ -505,7 +505,7 @@ static const struct { conn POINTER CAN BE NULL HERE ! ****************************************************************************/ -void reply_negprot(connection_struct *conn, struct smb_request *req) +void reply_negprot(struct smb_request *req) { size_t size = smb_len(req->inbuf) + 4; int choice= -1; diff --git a/source3/smbd/notify.c b/source3/smbd/notify.c index 0dd7fbb20e..55009ce0b1 100644 --- a/source3/smbd/notify.c +++ b/source3/smbd/notify.c @@ -65,6 +65,8 @@ static bool notify_marshall_changes(int num_changes, int i; UNISTR uni_name; + uni_name.buffer = NULL; + for (i=0; i<num_changes; i++) { struct notify_change *c; size_t namelen; @@ -128,7 +130,8 @@ static bool notify_marshall_changes(int num_changes, Setup the common parts of the return packet and send it. *****************************************************************************/ -static void change_notify_reply_packet(const uint8 *request_buf, +static void change_notify_reply_packet(connection_struct *conn, + const uint8 *request_buf, NTSTATUS error_code) { char outbuf[smb_size+38]; @@ -142,15 +145,18 @@ static void change_notify_reply_packet(const uint8 *request_buf, * Seems NT needs a transact command with an error code * in it. This is a longer packet than a simple error. */ - set_message(outbuf,18,0,False); + srv_set_message(outbuf,18,0,False); show_msg(outbuf); - if (!send_smb(smbd_server_fd(),outbuf)) - exit_server_cleanly("change_notify_reply_packet: send_smb " + if (!srv_send_smb(smbd_server_fd(), + outbuf, + IS_CONN_ENCRYPTED(conn))) + exit_server_cleanly("change_notify_reply_packet: srv_send_smb " "failed."); } -void change_notify_reply(const uint8 *request_buf, uint32 max_param, +void change_notify_reply(connection_struct *conn, + const uint8 *request_buf, uint32 max_param, struct notify_change_buf *notify_buf) { prs_struct ps; @@ -158,7 +164,7 @@ void change_notify_reply(const uint8 *request_buf, uint32 max_param, uint8 tmp_request[smb_size]; if (notify_buf->num_changes == -1) { - change_notify_reply_packet(request_buf, NT_STATUS_OK); + change_notify_reply_packet(conn, request_buf, NT_STATUS_OK); notify_buf->num_changes = 0; return; } @@ -171,12 +177,12 @@ void change_notify_reply(const uint8 *request_buf, uint32 max_param, * We exceed what the client is willing to accept. Send * nothing. */ - change_notify_reply_packet(request_buf, NT_STATUS_OK); + change_notify_reply_packet(conn, request_buf, NT_STATUS_OK); goto done; } if (!(req = talloc(talloc_tos(), struct smb_request))) { - change_notify_reply_packet(request_buf, NT_STATUS_NO_MEMORY); + change_notify_reply_packet(conn, request_buf, NT_STATUS_NO_MEMORY); goto done; } @@ -189,9 +195,9 @@ void change_notify_reply(const uint8 *request_buf, uint32 max_param, smb_setlen((char *)tmp_request, smb_size); SCVAL(tmp_request, smb_wct, 0); - init_smb_request(req, tmp_request,0); + init_smb_request(req, tmp_request,0, conn->encrypted_tid); - send_nt_replies(req, NT_STATUS_OK, prs_data_p(&ps), + send_nt_replies(conn, req, NT_STATUS_OK, prs_data_p(&ps), prs_offset(&ps), NULL, 0); done: @@ -229,6 +235,7 @@ NTSTATUS change_notify_create(struct files_struct *fsp, uint32 filter, return NT_STATUS_NO_MEMORY; } + ZERO_STRUCT(e); e.path = fullpath; e.filter = filter; e.subdir_filter = 0; @@ -242,9 +249,10 @@ NTSTATUS change_notify_create(struct files_struct *fsp, uint32 filter, return status; } -NTSTATUS change_notify_add_request(const uint8 *inbuf, uint32 max_param, - uint32 filter, bool recursive, - struct files_struct *fsp) +NTSTATUS change_notify_add_request(const struct smb_request *req, + uint32 max_param, + uint32 filter, bool recursive, + struct files_struct *fsp) { struct notify_change_request *request = NULL; struct notify_mid_map *map = NULL; @@ -258,7 +266,7 @@ NTSTATUS change_notify_add_request(const uint8 *inbuf, uint32 max_param, request->mid_map = map; map->req = request; - memcpy(request->request_buf, inbuf, sizeof(request->request_buf)); + memcpy(request->request_buf, req->inbuf, sizeof(request->request_buf)); request->max_param = max_param; request->filter = filter; request->fsp = fsp; @@ -267,11 +275,11 @@ NTSTATUS change_notify_add_request(const uint8 *inbuf, uint32 max_param, DLIST_ADD_END(fsp->notify->requests, request, struct notify_change_request *); - map->mid = SVAL(inbuf, smb_mid); + map->mid = SVAL(req->inbuf, smb_mid); DLIST_ADD(notify_changes_by_mid, map); /* Push the MID of this packet on the signing queue. */ - srv_defer_sign_response(SVAL(inbuf,smb_mid)); + srv_defer_sign_response(SVAL(req->inbuf,smb_mid)); return NT_STATUS_OK; } @@ -324,7 +332,8 @@ void remove_pending_change_notify_requests_by_mid(uint16 mid) return; } - change_notify_reply_packet(map->req->request_buf, NT_STATUS_CANCELLED); + change_notify_reply_packet(map->req->fsp->conn, + map->req->request_buf, NT_STATUS_CANCELLED); change_notify_remove_request(map->req); } @@ -340,7 +349,7 @@ void remove_pending_change_notify_requests_by_fid(files_struct *fsp, } while (fsp->notify->requests != NULL) { - change_notify_reply_packet( + change_notify_reply_packet(fsp->conn, fsp->notify->requests->request_buf, status); change_notify_remove_request(fsp->notify->requests); } @@ -434,7 +443,8 @@ static void notify_fsp(files_struct *fsp, uint32 action, const char *name) * TODO: do we have to walk the lists of requests pending? */ - change_notify_reply(fsp->notify->requests->request_buf, + change_notify_reply(fsp->conn, + fsp->notify->requests->request_buf, fsp->notify->requests->max_param, fsp->notify); diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c index 1fbb681c72..9381174af0 100644 --- a/source3/smbd/nttrans.c +++ b/source3/smbd/nttrans.c @@ -66,7 +66,8 @@ static char *nttrans_realloc(char **ptr, size_t size) HACK ! Always assumes smb_setup field is zero. ****************************************************************************/ -void send_nt_replies(struct smb_request *req, NTSTATUS nt_error, +void send_nt_replies(connection_struct *conn, + struct smb_request *req, NTSTATUS nt_error, char *params, int paramsize, char *pdata, int datasize) { @@ -242,8 +243,10 @@ void send_nt_replies(struct smb_request *req, NTSTATUS nt_error, /* Send the packet */ show_msg((char *)req->outbuf); - if (!send_smb(smbd_server_fd(),(char *)req->outbuf)) { - exit_server_cleanly("send_nt_replies: send_smb failed."); + if (!srv_send_smb(smbd_server_fd(), + (char *)req->outbuf, + IS_CONN_ENCRYPTED(conn))) { + exit_server_cleanly("send_nt_replies: srv_send_smb failed."); } TALLOC_FREE(req->outbuf); @@ -268,6 +271,9 @@ void send_nt_replies(struct smb_request *req, NTSTATUS nt_error, /**************************************************************************** Is it an NTFS stream name ? + An NTFS file name is <path>.<extention>:<stream name>:<stream type> + $DATA can be used as both a stream name and a stream type. A missing stream + name or type implies $DATA. ****************************************************************************/ bool is_ntfs_stream_name(const char *fname) @@ -410,8 +416,9 @@ static void do_ntcreate_pipe_open(connection_struct *conn, Reply to an NT create and X call. ****************************************************************************/ -void reply_ntcreate_and_X(connection_struct *conn, struct smb_request *req) +void reply_ntcreate_and_X(struct smb_request *req) { + connection_struct *conn = req->conn; char *fname = NULL; uint32 flags; uint32 access_mask; @@ -726,7 +733,7 @@ static void do_nt_transact_create_pipe(connection_struct *conn, DEBUG(5,("do_nt_transact_create_pipe: open name = %s\n", fname)); /* Send the required number of replies */ - send_nt_replies(req, NT_STATUS_OK, params, param_len, *ppdata, 0); + send_nt_replies(conn, req, NT_STATUS_OK, params, param_len, *ppdata, 0); return; } @@ -765,8 +772,7 @@ static NTSTATUS set_sd(files_struct *fsp, uint8 *data, uint32 sd_len, } if (fsp->fh->fd != -1) { - status = SMB_VFS_FSET_NT_ACL(fsp, fsp->fh->fd, - security_info_sent, psd); + status = SMB_VFS_FSET_NT_ACL(fsp, security_info_sent, psd); } else { status = SMB_VFS_SET_NT_ACL(fsp, fsp->fsp_name, @@ -1080,7 +1086,7 @@ static void call_nt_transact_create(connection_struct *conn, DEBUG(5,("call_nt_transact_create: open name = %s\n", fname)); /* Send the required number of replies */ - send_nt_replies(req, NT_STATUS_OK, params, param_len, *ppdata, 0); + send_nt_replies(conn, req, NT_STATUS_OK, params, param_len, *ppdata, 0); return; } @@ -1090,7 +1096,7 @@ static void call_nt_transact_create(connection_struct *conn, conn POINTER CAN BE NULL HERE ! ****************************************************************************/ -void reply_ntcancel(connection_struct *conn, struct smb_request *req) +void reply_ntcancel(struct smb_request *req) { /* * Go through and cancel any pending change notifies. @@ -1252,8 +1258,9 @@ static NTSTATUS copy_internals(TALLOC_CTX *ctx, Reply to a NT rename request. ****************************************************************************/ -void reply_ntrename(connection_struct *conn, struct smb_request *req) +void reply_ntrename(struct smb_request *req) { + connection_struct *conn = req->conn; char *oldname = NULL; char *newname = NULL; char *p; @@ -1474,7 +1481,7 @@ static void call_nt_transact_notify_change(connection_struct *conn, * here. */ - change_notify_reply(req->inbuf, max_param_count, fsp->notify); + change_notify_reply(fsp->conn, req->inbuf, max_param_count, fsp->notify); /* * change_notify_reply() above has independently sent its @@ -1487,7 +1494,9 @@ static void call_nt_transact_notify_change(connection_struct *conn, * No changes pending, queue the request */ - status = change_notify_add_request(req->inbuf, max_param_count, filter, + status = change_notify_add_request(req, + max_param_count, + filter, recursive, fsp); if (!NT_STATUS_IS_OK(status)) { reply_nterror(req, status); @@ -1554,7 +1563,7 @@ static void call_nt_transact_rename(connection_struct *conn, /* * Rename was successful. */ - send_nt_replies(req, NT_STATUS_OK, NULL, 0, NULL, 0); + send_nt_replies(conn, req, NT_STATUS_OK, NULL, 0, NULL, 0); DEBUG(3,("nt transact rename from = %s, to = %s succeeded.\n", fsp->fsp_name, new_name)); @@ -1598,7 +1607,6 @@ static void call_nt_transact_query_security_desc(connection_struct *conn, SEC_DESC *psd = NULL; size_t sd_size; uint32 security_info_wanted; - TALLOC_CTX *frame; files_struct *fsp = NULL; NTSTATUS status; DATA_BLOB blob; @@ -1625,8 +1633,6 @@ static void call_nt_transact_query_security_desc(connection_struct *conn, return; } - frame = talloc_stackframe(); - /* * Get the permissions to return. */ @@ -1636,30 +1642,28 @@ static void call_nt_transact_query_security_desc(connection_struct *conn, } else { if (fsp->fh->fd != -1) { status = SMB_VFS_FGET_NT_ACL( - fsp, fsp->fh->fd, security_info_wanted, &psd); + fsp, security_info_wanted, &psd); } else { status = SMB_VFS_GET_NT_ACL( - fsp, fsp->fsp_name, security_info_wanted, &psd); + conn, fsp->fsp_name, security_info_wanted, &psd); } } if (!NT_STATUS_IS_OK(status)) { - TALLOC_FREE(frame); reply_nterror(req, status); return; } - sd_size = sec_desc_size(psd); + sd_size = ndr_size_security_descriptor(psd, 0); DEBUG(3,("call_nt_transact_query_security_desc: sd_size = %lu.\n",(unsigned long)sd_size)); SIVAL(params,0,(uint32)sd_size); if (max_data_count < sd_size) { - send_nt_replies(req, NT_STATUS_BUFFER_TOO_SMALL, + send_nt_replies(conn, req, NT_STATUS_BUFFER_TOO_SMALL, params, 4, *ppdata, 0); - TALLOC_FREE(frame); return; } @@ -1669,7 +1673,6 @@ static void call_nt_transact_query_security_desc(connection_struct *conn, data = nttrans_realloc(ppdata, sd_size); if(data == NULL) { - TALLOC_FREE(frame); reply_doserror(req, ERRDOS, ERRnomem); return; } @@ -1678,7 +1681,6 @@ static void call_nt_transact_query_security_desc(connection_struct *conn, &blob.data, &blob.length); if (!NT_STATUS_IS_OK(status)) { - TALLOC_FREE(frame); reply_nterror(req, status); return; } @@ -1686,9 +1688,8 @@ static void call_nt_transact_query_security_desc(connection_struct *conn, SMB_ASSERT(sd_size == blob.length); memcpy(data, blob.data, sd_size); - send_nt_replies(req, NT_STATUS_OK, params, 4, data, (int)sd_size); + send_nt_replies(conn, req, NT_STATUS_OK, params, 4, data, (int)sd_size); - TALLOC_FREE(frame); return; } @@ -1744,7 +1745,7 @@ static void call_nt_transact_set_security_desc(connection_struct *conn, } done: - send_nt_replies(req, NT_STATUS_OK, NULL, 0, NULL, 0); + send_nt_replies(conn, req, NT_STATUS_OK, NULL, 0, NULL, 0); return; } @@ -1793,7 +1794,7 @@ static void call_nt_transact_ioctl(connection_struct *conn, so we can know if we need to pre-allocate or not */ DEBUG(10,("FSCTL_SET_SPARSE: called on FID[0x%04X](but not implemented)\n", fidnum)); - send_nt_replies(req, NT_STATUS_OK, NULL, 0, NULL, 0); + send_nt_replies(conn, req, NT_STATUS_OK, NULL, 0, NULL, 0); return; case FSCTL_CREATE_OR_GET_OBJECT_ID: @@ -1819,7 +1820,7 @@ static void call_nt_transact_ioctl(connection_struct *conn, push_file_id_16(pdata, &fsp->file_id); memcpy(pdata+16,create_volume_objectid(conn,objid),16); push_file_id_16(pdata+32, &fsp->file_id); - send_nt_replies(req, NT_STATUS_OK, NULL, 0, + send_nt_replies(conn, req, NT_STATUS_OK, NULL, 0, pdata, data_count); return; } @@ -1964,7 +1965,7 @@ static void call_nt_transact_ioctl(connection_struct *conn, talloc_destroy(shadow_data->mem_ctx); - send_nt_replies(req, NT_STATUS_OK, NULL, 0, + send_nt_replies(conn, req, NT_STATUS_OK, NULL, 0, pdata, data_count); return; @@ -2020,7 +2021,7 @@ static void call_nt_transact_ioctl(connection_struct *conn, */ /* this works for now... */ - send_nt_replies(req, NT_STATUS_OK, NULL, 0, NULL, 0); + send_nt_replies(conn, req, NT_STATUS_OK, NULL, 0, NULL, 0); return; } default: @@ -2174,7 +2175,8 @@ static void call_nt_transact_get_user_quota(connection_struct *conn, for (;((tmp_list!=NULL)&&((qt_len +40+SID_MAX_SIZE)<max_data_count)); tmp_list=tmp_list->next,entry+=entry_len,qt_len+=entry_len) { - sid_len = sid_size(&tmp_list->quotas->sid); + sid_len = ndr_size_dom_sid( + &tmp_list->quotas->sid, 0); entry_len = 40 + sid_len; /* nextoffset entry 4 bytes */ @@ -2305,7 +2307,7 @@ static void call_nt_transact_get_user_quota(connection_struct *conn, break; } - send_nt_replies(req, nt_status, params, param_len, + send_nt_replies(conn, req, nt_status, params, param_len, pdata, data_len); } @@ -2435,7 +2437,7 @@ static void call_nt_transact_set_user_quota(connection_struct *conn, return; } - send_nt_replies(req, NT_STATUS_OK, params, param_len, + send_nt_replies(conn, req, NT_STATUS_OK, params, param_len, pdata, data_len); } #endif /* HAVE_SYS_QUOTAS */ @@ -2572,8 +2574,9 @@ static void handle_nttrans(connection_struct *conn, Reply to a SMBNTtrans. ****************************************************************************/ -void reply_nttrans(connection_struct *conn, struct smb_request *req) +void reply_nttrans(struct smb_request *req) { + connection_struct *conn = req->conn; uint32 pscnt; uint32 psoff; uint32 dscnt; @@ -2763,8 +2766,9 @@ void reply_nttrans(connection_struct *conn, struct smb_request *req) Reply to a SMBnttranss ****************************************************************************/ -void reply_nttranss(connection_struct *conn, struct smb_request *req) +void reply_nttranss(struct smb_request *req) { + connection_struct *conn = req->conn; unsigned int pcnt,poff,dcnt,doff,pdisp,ddisp; struct trans_state *state; diff --git a/source3/smbd/open.c b/source3/smbd/open.c index b83d6844d6..aa4bc48f3d 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -70,7 +70,7 @@ static NTSTATUS fd_open(struct connection_struct *conn, Close the file associated with a fsp. ****************************************************************************/ -NTSTATUS fd_close(struct connection_struct *conn, files_struct *fsp) +NTSTATUS fd_close(files_struct *fsp) { if (fsp->fh->fd == -1) { return NT_STATUS_OK; /* What we used to call a stat open. */ @@ -78,7 +78,7 @@ NTSTATUS fd_close(struct connection_struct *conn, files_struct *fsp) if (fsp->fh->ref_count > 1) { return NT_STATUS_OK; /* Shared handle. Only close last reference. */ } - return fd_close_posix(conn, fsp); + return fd_close_posix(fsp); } /**************************************************************************** @@ -102,7 +102,7 @@ static void change_file_owner_to_parent(connection_struct *conn, } become_root(); - ret = SMB_VFS_FCHOWN(fsp, fsp->fh->fd, parent_st.st_uid, (gid_t)-1); + ret = SMB_VFS_FCHOWN(fsp, parent_st.st_uid, (gid_t)-1); unbecome_root(); if (ret == -1) { DEBUG(0,("change_file_owner_to_parent: failed to fchown " @@ -125,7 +125,7 @@ static NTSTATUS change_dir_owner_to_parent(connection_struct *conn, char *saved_dir = NULL; SMB_STRUCT_STAT sbuf; SMB_STRUCT_STAT parent_st; - TALLOC_CTX *ctx = talloc_stackframe(); + TALLOC_CTX *ctx = talloc_tos(); NTSTATUS status = NT_STATUS_OK; int ret; @@ -135,7 +135,6 @@ static NTSTATUS change_dir_owner_to_parent(connection_struct *conn, DEBUG(0,("change_dir_owner_to_parent: failed to stat parent " "directory %s. Error was %s\n", inherit_from_dir, strerror(errno) )); - TALLOC_FREE(ctx); return status; } @@ -152,7 +151,6 @@ static NTSTATUS change_dir_owner_to_parent(connection_struct *conn, DEBUG(0,("change_dir_owner_to_parent: failed to get " "current working directory. Error was %s\n", strerror(errno))); - TALLOC_FREE(ctx); return status; } @@ -202,7 +200,6 @@ static NTSTATUS change_dir_owner_to_parent(connection_struct *conn, out: - TALLOC_FREE(ctx); vfs_ChDir(conn,saved_dir); return status; } @@ -342,7 +339,7 @@ static NTSTATUS open_file(files_struct *fsp, if (fsp->fh->fd == -1) { ret = SMB_VFS_STAT(conn, path, psbuf); } else { - ret = SMB_VFS_FSTAT(fsp,fsp->fh->fd,psbuf); + ret = SMB_VFS_FSTAT(fsp, psbuf); /* If we have an fd, this stat should succeed. */ if (ret == -1) { DEBUG(0,("Error doing fstat on open file %s " @@ -353,7 +350,7 @@ static NTSTATUS open_file(files_struct *fsp, /* For a non-io open, this stat failing means file not found. JRA */ if (ret == -1) { status = map_nt_error_from_unix(errno); - fd_close(conn, fsp); + fd_close(fsp); return status; } } @@ -365,7 +362,7 @@ static NTSTATUS open_file(files_struct *fsp, */ if(S_ISDIR(psbuf->st_mode)) { - fd_close(conn, fsp); + fd_close(fsp); errno = EISDIR; return NT_STATUS_FILE_IS_A_DIRECTORY; } @@ -601,12 +598,6 @@ static NTSTATUS open_mode_check(connection_struct *conn, } *file_existed = True; - - if (is_stat_open(access_mask)) { - /* Stat open that doesn't trigger oplock breaks or share mode - * checks... ! JRA. */ - return NT_STATUS_OK; - } /* A delete on close prohibits everything */ @@ -614,6 +605,12 @@ static NTSTATUS open_mode_check(connection_struct *conn, return NT_STATUS_DELETE_PENDING; } + if (is_stat_open(access_mask)) { + /* Stat open that doesn't trigger oplock breaks or share mode + * checks... ! JRA. */ + return NT_STATUS_OK; + } + /* * Check if the share modes will give us access. */ @@ -1224,7 +1221,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, request_time = pml->request_time; /* Remove the deferred open entry under lock. */ - lck = get_share_mode_lock(NULL, state->id, NULL, NULL); + lck = get_share_mode_lock(talloc_tos(), state->id, NULL, NULL); if (lck == NULL) { DEBUG(0, ("could not get share mode lock\n")); } else { @@ -1405,7 +1402,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, } #endif /* O_SYNC */ - if (posix_open & (access_mask & FILE_APPEND_DATA)) { + if (posix_open && (access_mask & FILE_APPEND_DATA)) { flags2 |= O_APPEND; } @@ -1454,7 +1451,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, if (file_existed) { id = vfs_file_id_from_sbuf(conn, psbuf); - lck = get_share_mode_lock(NULL, id, + lck = get_share_mode_lock(talloc_tos(), id, conn->connectpath, fname); @@ -1681,14 +1678,14 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, id = fsp->file_id; - lck = get_share_mode_lock(NULL, id, + lck = get_share_mode_lock(talloc_tos(), id, conn->connectpath, fname); if (lck == NULL) { DEBUG(0, ("open_file_ntcreate: Could not get share " "mode lock for %s\n", fname)); - fd_close(conn, fsp); + fd_close(fsp); file_free(fsp); return NT_STATUS_SHARING_VIOLATION; } @@ -1699,7 +1696,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, oplock_request)) { schedule_defer_open(lck, request_time, req); TALLOC_FREE(lck); - fd_close(conn, fsp); + fd_close(fsp); file_free(fsp); return NT_STATUS_SHARING_VIOLATION; } @@ -1718,7 +1715,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, oplock_request)) { schedule_defer_open(lck, request_time, req); TALLOC_FREE(lck); - fd_close(conn, fsp); + fd_close(fsp); file_free(fsp); return NT_STATUS_SHARING_VIOLATION; } @@ -1727,7 +1724,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, if (!NT_STATUS_IS_OK(status)) { struct deferred_open_record state; - fd_close(conn, fsp); + fd_close(fsp); file_free(fsp); state.delayed_for_oplocks = False; @@ -1764,14 +1761,16 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, the kernel refuses the operations then the kernel is wrong. note that GPFS supports it as well - jmcd */ - ret_flock = SMB_VFS_KERNEL_FLOCK(fsp, fsp->fh->fd, share_access); - if(ret_flock == -1 ){ + if (fsp->fh->fd != -1) { + ret_flock = SMB_VFS_KERNEL_FLOCK(fsp, share_access); + if(ret_flock == -1 ){ - TALLOC_FREE(lck); - fd_close(conn, fsp); - file_free(fsp); - - return NT_STATUS_SHARING_VIOLATION; + TALLOC_FREE(lck); + fd_close(fsp); + file_free(fsp); + + return NT_STATUS_SHARING_VIOLATION; + } } /* @@ -1789,11 +1788,11 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, * We are modifing the file after open - update the stat * struct.. */ - if ((SMB_VFS_FTRUNCATE(fsp,fsp->fh->fd,0) == -1) || - (SMB_VFS_FSTAT(fsp,fsp->fh->fd,psbuf)==-1)) { + if ((SMB_VFS_FTRUNCATE(fsp, 0) == -1) || + (SMB_VFS_FSTAT(fsp, psbuf)==-1)) { status = map_nt_error_from_unix(errno); TALLOC_FREE(lck); - fd_close(conn,fsp); + fd_close(fsp); file_free(fsp); return status; } @@ -1843,14 +1842,16 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, set_share_mode(lck, fsp, current_user.ut.uid, 0, fsp->oplock_type, new_file_created); /* Handle strange delete on close create semantics. */ - if ((create_options & FILE_DELETE_ON_CLOSE) && can_set_initial_delete_on_close(lck)) { + if ((create_options & FILE_DELETE_ON_CLOSE) + && (is_ntfs_stream_name(fname) + || can_set_initial_delete_on_close(lck))) { status = can_set_delete_on_close(fsp, True, new_dos_attributes); if (!NT_STATUS_IS_OK(status)) { /* Remember to delete the mode we just added. */ del_share_mode(lck, fsp); TALLOC_FREE(lck); - fd_close(conn,fsp); + fd_close(fsp); file_free(fsp); return status; } @@ -1864,10 +1865,15 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, if (lp_map_archive(SNUM(conn)) || lp_store_dos_attributes(SNUM(conn))) { if (!posix_open) { - file_set_dosmode(conn, fname, - new_dos_attributes | aARCH, NULL, - parent_dir, - true); + SMB_STRUCT_STAT tmp_sbuf; + SET_STAT_INVALID(tmp_sbuf); + if (file_set_dosmode( + conn, fname, + new_dos_attributes | aARCH, + &tmp_sbuf, parent_dir, + true) == 0) { + unx_mode = tmp_sbuf.st_mode; + } } } } @@ -1882,7 +1888,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, int saved_errno = errno; /* We might get ENOSYS in the next * call.. */ - if (SMB_VFS_FCHMOD_ACL(fsp, fsp->fh->fd, unx_mode) == -1 && + if (SMB_VFS_FCHMOD_ACL(fsp, unx_mode) == -1 && errno == ENOSYS) { errno = saved_errno; /* Ignore ENOSYS */ } @@ -1896,8 +1902,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, { int saved_errno = errno; /* We might get ENOSYS in the * next call.. */ - ret = SMB_VFS_FCHMOD_ACL(fsp, fsp->fh->fd, - new_unx_mode); + ret = SMB_VFS_FCHMOD_ACL(fsp, new_unx_mode); if (ret == -1 && errno == ENOSYS) { errno = saved_errno; /* Ignore ENOSYS */ @@ -1910,7 +1915,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, } if ((ret == -1) && - (SMB_VFS_FCHMOD(fsp, fsp->fh->fd, new_unx_mode) == -1)) + (SMB_VFS_FCHMOD(fsp, new_unx_mode) == -1)) DEBUG(5, ("open_file_ntcreate: failed to reset " "attributes of file %s to 0%o\n", fname, (unsigned int)new_unx_mode)); @@ -1974,7 +1979,7 @@ NTSTATUS open_file_fchmod(connection_struct *conn, const char *fname, NTSTATUS close_file_fchmod(files_struct *fsp) { - NTSTATUS status = fd_close(fsp->conn, fsp); + NTSTATUS status = fd_close(fsp); file_free(fsp); return status; } @@ -2101,7 +2106,7 @@ NTSTATUS open_directory(connection_struct *conn, (unsigned int)create_disposition, (unsigned int)file_attributes)); - if (is_ntfs_stream_name(fname)) { + if (!(file_attributes & FILE_FLAG_POSIX_SEMANTICS) && is_ntfs_stream_name(fname)) { DEBUG(2, ("open_directory: %s is a stream name!\n", fname)); return NT_STATUS_NOT_A_DIRECTORY; } @@ -2211,7 +2216,7 @@ NTSTATUS open_directory(connection_struct *conn, string_set(&fsp->fsp_name,fname); - lck = get_share_mode_lock(NULL, fsp->file_id, + lck = get_share_mode_lock(talloc_tos(), fsp->file_id, conn->connectpath, fname); @@ -2262,7 +2267,7 @@ NTSTATUS open_directory(connection_struct *conn, return NT_STATUS_OK; } -NTSTATUS create_directory(connection_struct *conn, const char *directory) +NTSTATUS create_directory(connection_struct *conn, struct smb_request *req, const char *directory) { NTSTATUS status; SMB_STRUCT_STAT sbuf; @@ -2270,7 +2275,7 @@ NTSTATUS create_directory(connection_struct *conn, const char *directory) SET_STAT_INVALID(sbuf); - status = open_directory(conn, NULL, directory, &sbuf, + status = open_directory(conn, req, directory, &sbuf, FILE_READ_ATTRIBUTES, /* Just a stat open */ FILE_SHARE_NONE, /* Ignored for stat opens */ FILE_CREATE, @@ -2441,6 +2446,116 @@ static struct case_semantics_state *set_posix_case_semantics(TALLOC_CTX *mem_ctx } /* + * If a main file is opened for delete, all streams need to be checked for + * !FILE_SHARE_DELETE. Do this by opening with DELETE_ACCESS. + * If that works, delete them all by setting the delete on close and close. + */ + +static NTSTATUS open_streams_for_delete(connection_struct *conn, + const char *fname) +{ + struct stream_struct *stream_info; + files_struct **streams; + int i; + unsigned int num_streams; + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status; + + status = SMB_VFS_STREAMINFO(conn, NULL, fname, talloc_tos(), + &num_streams, &stream_info); + + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED) + || NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { + DEBUG(10, ("no streams around\n")); + TALLOC_FREE(frame); + return NT_STATUS_OK; + } + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("SMB_VFS_STREAMINFO failed: %s\n", + nt_errstr(status))); + goto fail; + } + + DEBUG(10, ("open_streams_for_delete found %d streams\n", + num_streams)); + + if (num_streams == 0) { + TALLOC_FREE(frame); + return NT_STATUS_OK; + } + + streams = TALLOC_ARRAY(talloc_tos(), files_struct *, num_streams); + if (streams == NULL) { + DEBUG(0, ("talloc failed\n")); + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + for (i=0; i<num_streams; i++) { + char *streamname; + + if (strequal(stream_info[i].name, "::$DATA")) { + streams[i] = NULL; + continue; + } + + streamname = talloc_asprintf(talloc_tos(), "%s%s", fname, + stream_info[i].name); + + if (streamname == NULL) { + DEBUG(0, ("talloc_aprintf failed\n")); + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + status = create_file_unixpath + (conn, /* conn */ + NULL, /* req */ + streamname, /* fname */ + DELETE_ACCESS, /* access_mask */ + FILE_SHARE_READ | FILE_SHARE_WRITE + | FILE_SHARE_DELETE, /* share_access */ + FILE_OPEN, /* create_disposition*/ + NTCREATEX_OPTIONS_PRIVATE_STREAM_DELETE, /* create_options */ + FILE_ATTRIBUTE_NORMAL, /* file_attributes */ + 0, /* oplock_request */ + 0, /* allocation_size */ + NULL, /* sd */ + NULL, /* ea_list */ + &streams[i], /* result */ + NULL, /* pinfo */ + NULL); /* psbuf */ + + TALLOC_FREE(streamname); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("Could not open stream %s: %s\n", + streamname, nt_errstr(status))); + break; + } + } + + /* + * don't touch the variable "status" beyond this point :-) + */ + + for (i -= 1 ; i >= 0; i--) { + if (streams[i] == NULL) { + continue; + } + + DEBUG(10, ("Closing stream # %d, %s\n", i, + streams[i]->fsp_name)); + close_file(streams[i], NORMAL_CLOSE); + } + + fail: + TALLOC_FREE(frame); + return status; +} + +/* * Wrapper around open_file_ntcreate and open_directory */ @@ -2463,6 +2578,7 @@ NTSTATUS create_file_unixpath(connection_struct *conn, { SMB_STRUCT_STAT sbuf; int info = FILE_WAS_OPENED; + files_struct *base_fsp = NULL; files_struct *fsp = NULL; NTSTATUS status; @@ -2492,7 +2608,23 @@ NTSTATUS create_file_unixpath(connection_struct *conn, sbuf = *psbuf; } else { - SET_STAT_INVALID(sbuf); + if (SMB_VFS_STAT(conn, fname, &sbuf) == -1) { + SET_STAT_INVALID(sbuf); + } + } + + if ((conn->fs_capabilities & FILE_NAMED_STREAMS) + && (access_mask & DELETE_ACCESS) + && !is_ntfs_stream_name(fname)) { + /* + * We can't open a file with DELETE access if any of the + * streams is open without FILE_SHARE_DELETE + */ + status = open_streams_for_delete(conn, fname); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } } /* This is the correct thing to do (check every time) but can_delete @@ -2525,12 +2657,61 @@ NTSTATUS create_file_unixpath(connection_struct *conn, } #endif + if ((conn->fs_capabilities & FILE_NAMED_STREAMS) + && is_ntfs_stream_name(fname) + && (!(create_options & NTCREATEX_OPTIONS_PRIVATE_STREAM_DELETE))) { + char *base; + uint32 base_create_disposition; + + if (create_options & FILE_DIRECTORY_FILE) { + status = NT_STATUS_NOT_A_DIRECTORY; + goto fail; + } + + status = split_ntfs_stream_name(talloc_tos(), fname, + &base, NULL); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("split_ntfs_stream_name failed: %s\n", + nt_errstr(status))); + goto fail; + } + + SMB_ASSERT(!is_ntfs_stream_name(base)); /* paranoia.. */ + + switch (create_disposition) { + case FILE_OPEN: + base_create_disposition = FILE_OPEN; + break; + default: + base_create_disposition = FILE_OPEN_IF; + break; + } + + status = create_file_unixpath(conn, NULL, base, 0, + FILE_SHARE_READ + | FILE_SHARE_WRITE + | FILE_SHARE_DELETE, + base_create_disposition, + 0, 0, 0, 0, NULL, NULL, + &base_fsp, NULL, NULL); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("create_file_unixpath for base %s failed: " + "%s\n", base, nt_errstr(status))); + goto fail; + } + } + /* * If it's a request for a directory open, deal with it separately. */ if (create_options & FILE_DIRECTORY_FILE) { + if (create_options & FILE_NON_DIRECTORY_FILE) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + /* Can't open a temp directory. IFS kit test. */ if (file_attributes & FILE_ATTRIBUTE_TEMPORARY) { status = NT_STATUS_INVALID_PARAMETER; @@ -2601,23 +2782,22 @@ NTSTATUS create_file_unixpath(connection_struct *conn, uint32_t sec_info_sent = ALL_SECURITY_INFORMATION; uint32_t saved_access_mask = fsp->access_mask; - if (sd->owner_sid==0) { + if (sd->owner_sid == NULL) { sec_info_sent &= ~OWNER_SECURITY_INFORMATION; } - if (sd->group_sid==0) { + if (sd->group_sid == NULL) { sec_info_sent &= ~GROUP_SECURITY_INFORMATION; } - if (sd->sacl==0) { + if (sd->sacl == NULL) { sec_info_sent &= ~SACL_SECURITY_INFORMATION; } - if (sd->dacl==0) { + if (sd->dacl == NULL) { sec_info_sent &= ~DACL_SECURITY_INFORMATION; } fsp->access_mask = FILE_GENERIC_ALL; - status = SMB_VFS_FSET_NT_ACL( - fsp, fsp->fh->fd, sec_info_sent, sd); + status = SMB_VFS_FSET_NT_ACL(fsp, sec_info_sent, sd); fsp->access_mask = saved_access_mask; @@ -2662,12 +2842,27 @@ NTSTATUS create_file_unixpath(connection_struct *conn, DEBUG(10, ("create_file: info=%d\n", info)); + /* + * Set fsp->base_fsp late enough that we can't "goto fail" anymore. In + * the fail: branch we call close_file(fsp, ERROR_CLOSE) which would + * also close fsp->base_fsp which we have to also do explicitly in + * this routine here, as not in all "goto fail:" we have the fsp set + * up already to be initialized with the base_fsp. + */ + + fsp->base_fsp = base_fsp; + *result = fsp; if (pinfo != NULL) { *pinfo = info; } if (psbuf != NULL) { - *psbuf = sbuf; + if ((fsp->fh == NULL) || (fsp->fh->fd == -1)) { + *psbuf = sbuf; + } + else { + SMB_VFS_FSTAT(fsp, psbuf); + } } return NT_STATUS_OK; @@ -2678,6 +2873,10 @@ NTSTATUS create_file_unixpath(connection_struct *conn, close_file(fsp, ERROR_CLOSE); fsp = NULL; } + if (base_fsp != NULL) { + close_file(base_fsp, ERROR_CLOSE); + base_fsp = NULL; + } return status; } @@ -2699,7 +2898,6 @@ NTSTATUS create_file(connection_struct *conn, int *pinfo, SMB_STRUCT_STAT *psbuf) { - TALLOC_CTX *frame = talloc_stackframe(); struct case_semantics_state *case_state = NULL; SMB_STRUCT_STAT sbuf; int info = FILE_WAS_OPENED; @@ -2806,19 +3004,18 @@ NTSTATUS create_file(connection_struct *conn, status = NT_STATUS_NO_MEMORY; goto fail; } - } else { - /* - * Check to see if this is a mac fork of some kind. - */ + } - if (is_ntfs_stream_name(fname)) { - enum FAKE_FILE_TYPE fake_file_type; + /* + * Check to see if this is a mac fork of some kind. + */ - fake_file_type = is_fake_file(fname); + if (is_ntfs_stream_name(fname)) { + enum FAKE_FILE_TYPE fake_file_type; - if (fake_file_type == FAKE_FILE_TYPE_NONE) { - return NT_STATUS_OBJECT_PATH_NOT_FOUND; - } + fake_file_type = is_fake_file(fname); + + if (fake_file_type != FAKE_FILE_TYPE_NONE) { /* * Here we go! support for changing the disk quotas @@ -2837,6 +3034,7 @@ NTSTATUS create_file(connection_struct *conn, goto fail; } + ZERO_STRUCT(sbuf); goto done; } } @@ -2881,6 +3079,8 @@ NTSTATUS create_file(connection_struct *conn, fname = converted_fname; } + TALLOC_FREE(case_state); + /* All file access must go through check_name() */ status = check_name(conn, fname); @@ -2908,7 +3108,6 @@ NTSTATUS create_file(connection_struct *conn, if (psbuf != NULL) { *psbuf = sbuf; } - TALLOC_FREE(frame); return NT_STATUS_OK; fail: @@ -2918,6 +3117,5 @@ NTSTATUS create_file(connection_struct *conn, close_file(fsp, ERROR_CLOSE); fsp = NULL; } - TALLOC_FREE(frame); return status; } diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c index 961abd277b..420aa94fe6 100644 --- a/source3/smbd/oplock.c +++ b/source3/smbd/oplock.c @@ -181,7 +181,7 @@ bool remove_oplock(files_struct *fsp) struct share_mode_lock *lck; /* Remove the oplock flag from the sharemode. */ - lck = get_share_mode_lock(NULL, fsp->file_id, NULL, NULL); + lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL); if (lck == NULL) { DEBUG(0,("remove_oplock: failed to lock share entry for " "file %s\n", fsp->fsp_name )); @@ -206,7 +206,7 @@ bool downgrade_oplock(files_struct *fsp) bool ret; struct share_mode_lock *lck; - lck = get_share_mode_lock(NULL, fsp->file_id, NULL, NULL); + lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL); if (lck == NULL) { DEBUG(0,("downgrade_oplock: failed to lock share entry for " "file %s\n", fsp->fsp_name )); @@ -252,7 +252,7 @@ static char *new_break_smb_message(TALLOC_CTX *mem_ctx, } memset(result,'\0',smb_size); - set_message(result,8,0,True); + srv_set_message(result,8,0,true); SCVAL(result,smb_com,SMBlockingX); SSVAL(result,smb_tid,fsp->conn->cnum); SSVAL(result,smb_pid,0xFFFF); @@ -449,8 +449,10 @@ static void process_oplock_async_level2_break_message(struct messaging_context * sign_state = srv_oplock_set_signing(False); show_msg(break_msg); - if (!send_smb(smbd_server_fd(), break_msg)) { - exit_server_cleanly("oplock_break: send_smb failed."); + if (!srv_send_smb(smbd_server_fd(), + break_msg, + IS_CONN_ENCRYPTED(fsp->conn))) { + exit_server_cleanly("oplock_break: srv_send_smb failed."); } /* Restore the sign state to what it was. */ @@ -554,8 +556,10 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx, sign_state = srv_oplock_set_signing(False); show_msg(break_msg); - if (!send_smb(smbd_server_fd(), break_msg)) { - exit_server_cleanly("oplock_break: send_smb failed."); + if (!srv_send_smb(smbd_server_fd(), + break_msg, + IS_CONN_ENCRYPTED(fsp->conn))) { + exit_server_cleanly("oplock_break: srv_send_smb failed."); } /* Restore the sign state to what it was. */ @@ -631,8 +635,10 @@ static void process_kernel_oplock_break(struct messaging_context *msg_ctx, sign_state = srv_oplock_set_signing(False); show_msg(break_msg); - if (!send_smb(smbd_server_fd(), break_msg)) { - exit_server_cleanly("oplock_break: send_smb failed."); + if (!srv_send_smb(smbd_server_fd(), + break_msg, + IS_CONN_ENCRYPTED(fsp->conn))) { + exit_server_cleanly("oplock_break: srv_send_smb failed."); } /* Restore the sign state to what it was. */ @@ -751,7 +757,7 @@ void release_level_2_oplocks_on_change(files_struct *fsp) if (!LEVEL_II_OPLOCK_TYPE(fsp->oplock_type)) return; - lck = get_share_mode_lock(NULL, fsp->file_id, NULL, NULL); + lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL); if (lck == NULL) { DEBUG(0,("release_level_2_oplocks_on_change: failed to lock " "share mode entry for file %s.\n", fsp->fsp_name )); diff --git a/source3/smbd/oplock_linux.c b/source3/smbd/oplock_linux.c index 086f105b89..fa7cb42bc6 100644 --- a/source3/smbd/oplock_linux.c +++ b/source3/smbd/oplock_linux.c @@ -93,17 +93,27 @@ static void set_capability(unsigned capability) return; } - data.effective |= (1<<capability); + if (0 == (data.effective & (1<<capability))) { + data.effective |= (1<<capability); - if (capset(&header, &data) == -1) { - DEBUG(3,("Unable to set %d capability (%s)\n", - capability, strerror(errno))); + if (capset(&header, &data) == -1) { + DEBUG(3,("Unable to set %d capability (%s)\n", + capability, strerror(errno))); + } } } /* - Call to set the kernel lease signal handler -*/ + * public function to get linux lease capability. Needed by some VFS modules (eg. gpfs.c) + */ +void linux_set_lease_capability(void) +{ + set_capability(CAP_LEASE); +} + +/* + * Call to set the kernel lease signal handler + */ int linux_set_lease_sighandler(int fd) { if (fcntl(fd, F_SETSIG, RT_SIGNAL_LEASE) == -1) { @@ -164,7 +174,7 @@ static files_struct *linux_oplock_receive_message(fd_set *fds) static bool linux_set_kernel_oplock(files_struct *fsp, int oplock_type) { - if ( SMB_VFS_LINUX_SETLEASE(fsp,fsp->fh->fd, F_WRLCK) == -1) { + if ( SMB_VFS_LINUX_SETLEASE(fsp, F_WRLCK) == -1) { DEBUG(3,("linux_set_kernel_oplock: Refused oplock on file %s, " "fd = %d, file_id = %s. (%s)\n", fsp->fsp_name, fsp->fh->fd, @@ -202,7 +212,7 @@ static void linux_release_kernel_oplock(files_struct *fsp) /* * Remove the kernel oplock on this file. */ - if ( SMB_VFS_LINUX_SETLEASE(fsp,fsp->fh->fd, F_UNLCK) == -1) { + if ( SMB_VFS_LINUX_SETLEASE(fsp, F_UNLCK) == -1) { if (DEBUGLVL(0)) { dbgtext("linux_release_kernel_oplock: Error when " "removing kernel oplock on file " ); diff --git a/source3/smbd/password.c b/source3/smbd/password.c index b3005ba082..85e1ccf0a7 100644 --- a/source3/smbd/password.c +++ b/source3/smbd/password.c @@ -759,6 +759,7 @@ bool authorise_login(int snum, fstring user, DATA_BLOB password, if (!ok) { char *auser; char *user_list = NULL; + char *saveptr; if ( session_userlist ) user_list = SMB_STRDUP(session_userlist); @@ -768,8 +769,9 @@ bool authorise_login(int snum, fstring user, DATA_BLOB password, if (!user_list) return(False); - for (auser=strtok(user_list,LIST_SEP); !ok && auser; - auser = strtok(NULL,LIST_SEP)) { + for (auser = strtok_r(user_list, LIST_SEP, &saveptr); + !ok && auser; + auser = strtok_r(NULL, LIST_SEP, &saveptr)) { fstring user2; fstrcpy(user2,auser); if (!user_ok(user2,snum)) @@ -792,6 +794,7 @@ bool authorise_login(int snum, fstring user, DATA_BLOB password, TALLOC_CTX *ctx = talloc_tos(); char *auser; char *user_list = talloc_strdup(ctx, lp_username(snum)); + char *saveptr; if (!user_list) { goto check_guest; @@ -806,8 +809,9 @@ bool authorise_login(int snum, fstring user, DATA_BLOB password, goto check_guest; } - for (auser=strtok(user_list,LIST_SEP); auser && !ok; - auser = strtok(NULL,LIST_SEP)) { + for (auser = strtok_r(user_list, LIST_SEP, &saveptr); + auser && !ok; + auser = strtok_r(NULL, LIST_SEP, &saveptr)) { if (*auser == '@') { auser = validate_group(auser+1,password,snum); if (auser) { @@ -837,9 +841,11 @@ bool authorise_login(int snum, fstring user, DATA_BLOB password, /* check for a normal guest connection */ if (!ok && GUEST_OK(snum)) { + struct passwd *guest_pw; fstring guestname; fstrcpy(guestname,lp_guestaccount()); - if (Get_Pwnam(guestname)) { + guest_pw = Get_Pwnam_alloc(talloc_tos(), guestname); + if (guest_pw != NULL) { fstrcpy(user,guestname); ok = True; DEBUG(3,("authorise_login: ACCEPTED: guest account " @@ -848,6 +854,7 @@ bool authorise_login(int snum, fstring user, DATA_BLOB password, DEBUG(0,("authorise_login: Invalid guest account " "%s??\n",guestname)); } + TALLOC_FREE(guest_pw); *guest = True; } diff --git a/source3/smbd/pipes.c b/source3/smbd/pipes.c index 0ddc00c767..6b4b83d97d 100644 --- a/source3/smbd/pipes.c +++ b/source3/smbd/pipes.c @@ -291,7 +291,7 @@ void reply_pipe_read_and_X(struct smb_request *req) return; } - set_message((char *)req->outbuf, 12, nread, False); + srv_set_message((char *)req->outbuf, 12, nread, False); SSVAL(req->outbuf,smb_vwv5,nread); SSVAL(req->outbuf,smb_vwv6,smb_offset(data,req->outbuf)); diff --git a/source3/smbd/posix_acls.c b/source3/smbd/posix_acls.c index ccfed69721..f60329a039 100644 --- a/source3/smbd/posix_acls.c +++ b/source3/smbd/posix_acls.c @@ -229,7 +229,7 @@ static void store_inheritance_attributes(files_struct *fsp, canon_ace *file_ace_ if (!pai_protected && num_inherited_entries(file_ace_list) == 0 && num_inherited_entries(dir_ace_list) == 0) { /* Instead just remove the attribute if it exists. */ if (fsp->fh->fd != -1) - SMB_VFS_FREMOVEXATTR(fsp, fsp->fh->fd, SAMBA_POSIX_INHERITANCE_EA_NAME); + SMB_VFS_FREMOVEXATTR(fsp, SAMBA_POSIX_INHERITANCE_EA_NAME); else SMB_VFS_REMOVEXATTR(fsp->conn, fsp->fsp_name, SAMBA_POSIX_INHERITANCE_EA_NAME); return; @@ -238,7 +238,7 @@ static void store_inheritance_attributes(files_struct *fsp, canon_ace *file_ace_ pai_buf = create_pai_buf(file_ace_list, dir_ace_list, pai_protected, &store_size); if (fsp->fh->fd != -1) - ret = SMB_VFS_FSETXATTR(fsp, fsp->fh->fd, SAMBA_POSIX_INHERITANCE_EA_NAME, + ret = SMB_VFS_FSETXATTR(fsp, SAMBA_POSIX_INHERITANCE_EA_NAME, pai_buf, store_size, 0); else ret = SMB_VFS_SETXATTR(fsp->conn,fsp->fsp_name, SAMBA_POSIX_INHERITANCE_EA_NAME, @@ -430,7 +430,7 @@ static struct pai_val *create_pai_val(char *buf, size_t size) Load the user.SAMBA_PAI attribute. ************************************************************************/ -static struct pai_val *load_inherited_info(files_struct *fsp) +static struct pai_val *fload_inherited_info(files_struct *fsp) { char *pai_buf; size_t pai_buf_size = 1024; @@ -445,7 +445,7 @@ static struct pai_val *load_inherited_info(files_struct *fsp) do { if (fsp->fh->fd != -1) - ret = SMB_VFS_FGETXATTR(fsp, fsp->fh->fd, SAMBA_POSIX_INHERITANCE_EA_NAME, + ret = SMB_VFS_FGETXATTR(fsp, SAMBA_POSIX_INHERITANCE_EA_NAME, pai_buf, pai_buf_size); else ret = SMB_VFS_GETXATTR(fsp->conn,fsp->fsp_name,SAMBA_POSIX_INHERITANCE_EA_NAME, @@ -490,6 +490,71 @@ static struct pai_val *load_inherited_info(files_struct *fsp) return paiv; } +/************************************************************************ + Load the user.SAMBA_PAI attribute. +************************************************************************/ + +static struct pai_val *load_inherited_info(const struct connection_struct *conn, + const char *fname) +{ + char *pai_buf; + size_t pai_buf_size = 1024; + struct pai_val *paiv = NULL; + ssize_t ret; + + if (!lp_map_acl_inherit(SNUM(conn))) { + return NULL; + } + + if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL) { + return NULL; + } + + do { + ret = SMB_VFS_GETXATTR(conn, fname, + SAMBA_POSIX_INHERITANCE_EA_NAME, + pai_buf, pai_buf_size); + + if (ret == -1) { + if (errno != ERANGE) { + break; + } + /* Buffer too small - enlarge it. */ + pai_buf_size *= 2; + SAFE_FREE(pai_buf); + if (pai_buf_size > 1024*1024) { + return NULL; /* Limit malloc to 1mb. */ + } + if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL) + return NULL; + } + } while (ret == -1); + + DEBUG(10,("load_inherited_info: ret = %lu for file %s\n", (unsigned long)ret, fname)); + + if (ret == -1) { + /* No attribute or not supported. */ +#if defined(ENOATTR) + if (errno != ENOATTR) + DEBUG(10,("load_inherited_info: Error %s\n", strerror(errno) )); +#else + if (errno != ENOSYS) + DEBUG(10,("load_inherited_info: Error %s\n", strerror(errno) )); +#endif + SAFE_FREE(pai_buf); + return NULL; + } + + paiv = create_pai_val(pai_buf, ret); + + if (paiv && paiv->pai_protected) { + DEBUG(10,("load_inherited_info: ACL is protected for file %s\n", fname)); + } + + SAFE_FREE(pai_buf); + return paiv; +} + /**************************************************************************** Functions to manipulate the internal ACE format. ****************************************************************************/ @@ -826,7 +891,7 @@ static bool nt4_compatible_acls(void) ****************************************************************************/ static SEC_ACCESS map_canon_ace_perms(int snum, - int *pacl_type, + enum security_ace_type *pacl_type, mode_t perms, bool directory_ace) { @@ -999,20 +1064,21 @@ NTSTATUS unpack_nt_owners(int snum, uid_t *puser, gid_t *pgrp, uint32 security_i Ensure the enforced permissions for this share apply. ****************************************************************************/ -static void apply_default_perms(const files_struct *fsp, canon_ace *pace, mode_t type) +static void apply_default_perms(const struct share_params *params, + const bool is_directory, canon_ace *pace, + mode_t type) { - int snum = SNUM(fsp->conn); mode_t and_bits = (mode_t)0; mode_t or_bits = (mode_t)0; /* Get the initial bits to apply. */ - if (fsp->is_directory) { - and_bits = lp_dir_security_mask(snum); - or_bits = lp_force_dir_security_mode(snum); + if (is_directory) { + and_bits = lp_dir_security_mask(params->service); + or_bits = lp_force_dir_security_mode(params->service); } else { - and_bits = lp_security_mask(snum); - or_bits = lp_force_security_mode(snum); + and_bits = lp_security_mask(params->service); + or_bits = lp_force_security_mode(params->service); } /* Now bounce them into the S_USR space. */ @@ -1020,7 +1086,7 @@ static void apply_default_perms(const files_struct *fsp, canon_ace *pace, mode_t case S_IRUSR: /* Ensure owner has read access. */ pace->perms |= S_IRUSR; - if (fsp->is_directory) + if (is_directory) pace->perms |= (S_IWUSR|S_IXUSR); and_bits = unix_perms_to_acl_perms(and_bits, S_IRUSR, S_IWUSR, S_IXUSR); or_bits = unix_perms_to_acl_perms(or_bits, S_IRUSR, S_IWUSR, S_IXUSR); @@ -1076,7 +1142,8 @@ static bool uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace ) ****************************************************************************/ static bool ensure_canon_entry_valid(canon_ace **pp_ace, - const files_struct *fsp, + const struct share_params *params, + const bool is_directory, const DOM_SID *pfile_owner_sid, const DOM_SID *pfile_grp_sid, const SMB_STRUCT_STAT *pst, @@ -1092,7 +1159,7 @@ static bool ensure_canon_entry_valid(canon_ace **pp_ace, if (pace->type == SMB_ACL_USER_OBJ) { if (setting_acl) - apply_default_perms(fsp, pace, S_IRUSR); + apply_default_perms(params, is_directory, pace, S_IRUSR); got_user = True; } else if (pace->type == SMB_ACL_GROUP_OBJ) { @@ -1102,7 +1169,7 @@ static bool ensure_canon_entry_valid(canon_ace **pp_ace, */ if (setting_acl) - apply_default_perms(fsp, pace, S_IRGRP); + apply_default_perms(params, is_directory, pace, S_IRGRP); got_grp = True; } else if (pace->type == SMB_ACL_OTHER) { @@ -1112,7 +1179,7 @@ static bool ensure_canon_entry_valid(canon_ace **pp_ace, */ if (setting_acl) - apply_default_perms(fsp, pace, S_IROTH); + apply_default_perms(params, is_directory, pace, S_IROTH); got_other = True; pace_other = pace; } @@ -1155,7 +1222,7 @@ static bool ensure_canon_entry_valid(canon_ace **pp_ace, pace->perms = 0; } - apply_default_perms(fsp, pace, S_IRUSR); + apply_default_perms(params, is_directory, pace, S_IRUSR); } else { pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRUSR, S_IWUSR, S_IXUSR); } @@ -1181,7 +1248,7 @@ static bool ensure_canon_entry_valid(canon_ace **pp_ace, pace->perms = pace_other->perms; else pace->perms = 0; - apply_default_perms(fsp, pace, S_IRGRP); + apply_default_perms(params, is_directory, pace, S_IRGRP); } else { pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRGRP, S_IWGRP, S_IXGRP); } @@ -1203,7 +1270,7 @@ static bool ensure_canon_entry_valid(canon_ace **pp_ace, pace->attr = ALLOW_ACE; if (setting_acl) { pace->perms = 0; - apply_default_perms(fsp, pace, S_IROTH); + apply_default_perms(params, is_directory, pace, S_IROTH); } else pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IROTH, S_IWOTH, S_IXOTH); @@ -1341,12 +1408,12 @@ static bool create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst, psa1->flags |= (psa2->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT)); psa2->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT); - + } else if (psa2->flags & SEC_ACE_FLAG_INHERIT_ONLY) { psa2->flags |= (psa1->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT)); psa1->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT); - + } } } @@ -1407,10 +1474,22 @@ static bool create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst, } else if (sid_to_uid( ¤t_ace->trustee, ¤t_ace->unix_ug.uid)) { current_ace->owner_type = UID_ACE; - current_ace->type = SMB_ACL_USER; + /* If it's the owning user, this is a user_obj, not + * a user. */ + if (current_ace->unix_ug.uid == pst->st_uid) { + current_ace->type = SMB_ACL_USER_OBJ; + } else { + current_ace->type = SMB_ACL_USER; + } } else if (sid_to_gid( ¤t_ace->trustee, ¤t_ace->unix_ug.gid)) { current_ace->owner_type = GID_ACE; - current_ace->type = SMB_ACL_GROUP; + /* If it's the primary group, this is a group_obj, not + * a group. */ + if (current_ace->unix_ug.gid == pst->st_gid) { + current_ace->type = SMB_ACL_GROUP_OBJ; + } else { + current_ace->type = SMB_ACL_GROUP; + } } else { /* * Silently ignore map failures in non-mappable SIDs (NT Authority, BUILTIN etc). @@ -2014,7 +2093,7 @@ static bool unpack_canon_ace(files_struct *fsp, pst->st_mode = create_default_mode(fsp, False); - if (!ensure_canon_entry_valid(&file_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) { + if (!ensure_canon_entry_valid(&file_ace, fsp->conn->params, fsp->is_directory, pfile_owner_sid, pfile_grp_sid, pst, True)) { free_canon_ace_list(file_ace); free_canon_ace_list(dir_ace); return False; @@ -2030,7 +2109,7 @@ static bool unpack_canon_ace(files_struct *fsp, pst->st_mode = create_default_mode(fsp, True); - if (dir_ace && !ensure_canon_entry_valid(&dir_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) { + if (dir_ace && !ensure_canon_entry_valid(&dir_ace, fsp->conn->params, fsp->is_directory, pfile_owner_sid, pfile_grp_sid, pst, True)) { free_canon_ace_list(file_ace); free_canon_ace_list(dir_ace); return False; @@ -2108,10 +2187,11 @@ static void arrange_posix_perms(const char *filename, canon_ace **pp_list_head) Create a linked list of canonical ACE entries. ****************************************************************************/ -static canon_ace *canonicalise_acl( const files_struct *fsp, SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf, - const DOM_SID *powner, const DOM_SID *pgroup, struct pai_val *pal, SMB_ACL_TYPE_T the_acl_type) +static canon_ace *canonicalise_acl(struct connection_struct *conn, + const char *fname, SMB_ACL_T posix_acl, + const SMB_STRUCT_STAT *psbuf, + const DOM_SID *powner, const DOM_SID *pgroup, struct pai_val *pal, SMB_ACL_TYPE_T the_acl_type) { - connection_struct *conn = fsp->conn; mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR); canon_ace *list_head = NULL; canon_ace *ace = NULL; @@ -2226,7 +2306,9 @@ static canon_ace *canonicalise_acl( const files_struct *fsp, SMB_ACL_T posix_acl * This next call will ensure we have at least a user/group/world set. */ - if (!ensure_canon_entry_valid(&list_head, fsp, powner, pgroup, psbuf, False)) + if (!ensure_canon_entry_valid(&list_head, conn->params, + S_ISDIR(psbuf->st_mode), powner, pgroup, + psbuf, False)) goto fail; /* @@ -2252,7 +2334,7 @@ static canon_ace *canonicalise_acl( const files_struct *fsp, SMB_ACL_T posix_acl } } - arrange_posix_perms(fsp->fsp_name,&list_head ); + arrange_posix_perms(fname,&list_head ); print_canon_ace_list( "canonicalise_acl: ace entries after arrange", list_head ); @@ -2503,7 +2585,7 @@ static bool set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, bool defau } } } else { - if (SMB_VFS_SYS_ACL_SET_FD(fsp, fsp->fh->fd, the_acl) == -1) { + if (SMB_VFS_SYS_ACL_SET_FD(fsp, the_acl) == -1) { /* * Some systems allow all the above calls and only fail with no ACL support * when attempting to apply the acl. HPUX with HFS is an example of this. JRA. @@ -2519,7 +2601,7 @@ static bool set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, bool defau fsp->fsp_name )); become_root(); - sret = SMB_VFS_SYS_ACL_SET_FD(fsp, fsp->fh->fd, the_acl); + sret = SMB_VFS_SYS_ACL_SET_FD(fsp, the_acl); unbecome_root(); if (sret == 0) { ret = True; @@ -2719,6 +2801,7 @@ static size_t merge_default_aces( SEC_ACE *nt_ace_list, size_t num_aces) return num_aces; } + /**************************************************************************** Reply to query a security descriptor from an fsp. If it succeeds it allocates the space for the return elements and returns the size needed to return the @@ -2726,11 +2809,15 @@ static size_t merge_default_aces( SEC_ACE *nt_ace_list, size_t num_aces) the UNIX style get ACL. ****************************************************************************/ -NTSTATUS get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc) +static NTSTATUS posix_get_nt_acl_common(struct connection_struct *conn, + const char *name, + const SMB_STRUCT_STAT *sbuf, + struct pai_val *pal, + SMB_ACL_T posix_acl, + SMB_ACL_T def_acl, + uint32_t security_info, + SEC_DESC **ppdesc) { - connection_struct *conn = fsp->conn; - SMB_STRUCT_STAT sbuf; - SEC_ACE *nt_ace_list = NULL; DOM_SID owner_sid; DOM_SID group_sid; size_t sd_size = 0; @@ -2738,57 +2825,12 @@ NTSTATUS get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc) size_t num_acls = 0; size_t num_def_acls = 0; size_t num_aces = 0; - SMB_ACL_T posix_acl = NULL; - SMB_ACL_T def_acl = NULL; canon_ace *file_ace = NULL; canon_ace *dir_ace = NULL; + SEC_ACE *nt_ace_list = NULL; size_t num_profile_acls = 0; - struct pai_val *pal = NULL; SEC_DESC *psd = NULL; - *ppdesc = NULL; - - DEBUG(10,("get_nt_acl: called for file %s\n", fsp->fsp_name )); - - if(fsp->is_directory || fsp->fh->fd == -1) { - - /* Get the stat struct for the owner info. */ - if(SMB_VFS_STAT(fsp->conn,fsp->fsp_name, &sbuf) != 0) { - return map_nt_error_from_unix(errno); - } - /* - * Get the ACL from the path. - */ - - posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fsp->fsp_name, SMB_ACL_TYPE_ACCESS); - - /* - * If it's a directory get the default POSIX ACL. - */ - - if(fsp->is_directory) { - def_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fsp->fsp_name, SMB_ACL_TYPE_DEFAULT); - def_acl = free_empty_sys_acl(conn, def_acl); - } - - } else { - - /* Get the stat struct for the owner info. */ - if(SMB_VFS_FSTAT(fsp,fsp->fh->fd,&sbuf) != 0) { - return map_nt_error_from_unix(errno); - } - /* - * Get the ACL from the fd. - */ - posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fsp->fh->fd); - } - - DEBUG(5,("get_nt_acl : file ACL %s, directory ACL %s\n", - posix_acl ? "present" : "absent", - def_acl ? "present" : "absent" )); - - pal = load_inherited_info(fsp); - /* * Get the owner, group and world SIDs. */ @@ -2799,7 +2841,7 @@ NTSTATUS get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc) sid_copy(&group_sid, &global_sid_Builtin_Users); num_profile_acls = 2; } else { - create_file_sids(&sbuf, &owner_sid, &group_sid); + create_file_sids(sbuf, &owner_sid, &group_sid); } if ((security_info & DACL_SECURITY_INFORMATION) && !(security_info & PROTECTED_DACL_SECURITY_INFORMATION)) { @@ -2814,19 +2856,23 @@ NTSTATUS get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc) */ /* Create the canon_ace lists. */ - file_ace = canonicalise_acl( fsp, posix_acl, &sbuf, &owner_sid, &group_sid, pal, SMB_ACL_TYPE_ACCESS ); + file_ace = canonicalise_acl(conn, name, posix_acl, sbuf, + &owner_sid, &group_sid, pal, + SMB_ACL_TYPE_ACCESS); /* We must have *some* ACLS. */ if (count_canon_ace_list(file_ace) == 0) { - DEBUG(0,("get_nt_acl : No ACLs on file (%s) !\n", fsp->fsp_name )); + DEBUG(0,("get_nt_acl : No ACLs on file (%s) !\n", name)); goto done; } - if (fsp->is_directory && def_acl) { - dir_ace = canonicalise_acl(fsp, def_acl, &sbuf, - &global_sid_Creator_Owner, - &global_sid_Creator_Group, pal, SMB_ACL_TYPE_DEFAULT ); + if (S_ISDIR(sbuf->st_mode) && def_acl) { + dir_ace = canonicalise_acl(conn, name, def_acl, + sbuf, + &global_sid_Creator_Owner, + &global_sid_Creator_Group, + pal, SMB_ACL_TYPE_DEFAULT); } /* @@ -2835,7 +2881,7 @@ NTSTATUS get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc) { canon_ace *ace; - int nt_acl_type; + enum security_ace_type nt_acl_type; int i; if (nt4_compatible_acls() && dir_ace) { @@ -2910,7 +2956,7 @@ NTSTATUS get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc) acc = map_canon_ace_perms(SNUM(conn), &nt_acl_type, ace->perms, - fsp->is_directory); + S_ISDIR(sbuf->st_mode)); init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, @@ -2939,7 +2985,7 @@ NTSTATUS get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc) acc = map_canon_ace_perms(SNUM(conn), &nt_acl_type, ace->perms, - fsp->is_directory); + S_ISDIR(sbuf->st_mode)); init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, @@ -3028,6 +3074,69 @@ NTSTATUS get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc) return NT_STATUS_OK; } +NTSTATUS posix_fget_nt_acl(struct files_struct *fsp, uint32_t security_info, + SEC_DESC **ppdesc) +{ + SMB_STRUCT_STAT sbuf; + SMB_ACL_T posix_acl = NULL; + struct pai_val *pal; + + *ppdesc = NULL; + + DEBUG(10,("posix_fget_nt_acl: called for file %s\n", fsp->fsp_name )); + + /* can it happen that fsp_name == NULL ? */ + if (fsp->is_directory || fsp->fh->fd == -1) { + return posix_get_nt_acl(fsp->conn, fsp->fsp_name, + security_info, ppdesc); + } + + /* Get the stat struct for the owner info. */ + if(SMB_VFS_FSTAT(fsp, &sbuf) != 0) { + return map_nt_error_from_unix(errno); + } + + /* Get the ACL from the fd. */ + posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp); + + pal = fload_inherited_info(fsp); + + return posix_get_nt_acl_common(fsp->conn, fsp->fsp_name, &sbuf, pal, + posix_acl, NULL, security_info, ppdesc); +} + +NTSTATUS posix_get_nt_acl(struct connection_struct *conn, const char *name, + uint32_t security_info, SEC_DESC **ppdesc) +{ + SMB_STRUCT_STAT sbuf; + SMB_ACL_T posix_acl = NULL; + SMB_ACL_T def_acl = NULL; + struct pai_val *pal; + + *ppdesc = NULL; + + DEBUG(10,("posix_get_nt_acl: called for file %s\n", name )); + + /* Get the stat struct for the owner info. */ + if(SMB_VFS_STAT(conn, name, &sbuf) != 0) { + return map_nt_error_from_unix(errno); + } + + /* Get the ACL from the path. */ + posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, name, SMB_ACL_TYPE_ACCESS); + + /* If it's a directory get the default POSIX ACL. */ + if(S_ISDIR(sbuf.st_mode)) { + def_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, name, SMB_ACL_TYPE_DEFAULT); + def_acl = free_empty_sys_acl(conn, def_acl); + } + + pal = load_inherited_info(conn, name); + + return posix_get_nt_acl_common(conn, name, &sbuf, pal, posix_acl, + def_acl, security_info, ppdesc); +} + /**************************************************************************** Try to chown a file. We will be able to chown it under the following conditions. @@ -3097,7 +3206,7 @@ int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid) become_root(); /* Keep the current file gid the same. */ - ret = SMB_VFS_FCHOWN(fsp, fsp->fh->fd, uid, (gid_t)-1); + ret = SMB_VFS_FCHOWN(fsp, uid, (gid_t)-1); unbecome_root(); close_file_fchmod(fsp); @@ -3105,57 +3214,8 @@ int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid) return ret; } -static NTSTATUS append_ugw_ace(files_struct *fsp, - SMB_STRUCT_STAT *psbuf, - mode_t unx_mode, - int ugw, - SEC_ACE *se) -{ - mode_t perms; - SEC_ACCESS acc; - int nt_acl_type; - DOM_SID trustee; - - switch (ugw) { - case S_IRUSR: - perms = unix_perms_to_acl_perms(unx_mode, - S_IRUSR, - S_IWUSR, - S_IXUSR); - uid_to_sid(&trustee, psbuf->st_uid ); - break; - case S_IRGRP: - perms = unix_perms_to_acl_perms(unx_mode, - S_IRGRP, - S_IWGRP, - S_IXGRP); - gid_to_sid(&trustee, psbuf->st_gid ); - break; - case S_IROTH: - perms = unix_perms_to_acl_perms(unx_mode, - S_IROTH, - S_IWOTH, - S_IXOTH); - sid_copy(&trustee, &global_sid_World); - break; - default: - return NT_STATUS_INVALID_PARAMETER; - } - acc = map_canon_ace_perms(SNUM(fsp->conn), - &nt_acl_type, - perms, - fsp->is_directory); - - init_sec_ace(se, - &trustee, - nt_acl_type, - acc, - 0); - return NT_STATUS_OK; -} - /**************************************************************************** - If this is an + Take care of parent ACL inheritance. ****************************************************************************/ static NTSTATUS append_parent_acl(files_struct *fsp, @@ -3173,7 +3233,7 @@ static NTSTATUS append_parent_acl(files_struct *fsp, NTSTATUS status; int info; unsigned int i, j; - mode_t unx_mode; + bool is_dacl_protected = (psd->type & SE_DESC_DACL_PROTECTED); ZERO_STRUCT(sbuf); @@ -3188,12 +3248,6 @@ static NTSTATUS append_parent_acl(files_struct *fsp, return NT_STATUS_NO_MEMORY; } - /* Create a default mode for u/g/w. */ - unx_mode = unix_mode(fsp->conn, - aARCH | (fsp->is_directory ? aDIR : 0), - fsp->fsp_name, - parent_name); - status = open_directory(fsp->conn, NULL, parent_name, @@ -3210,7 +3264,7 @@ static NTSTATUS append_parent_acl(files_struct *fsp, return status; } - status = SMB_VFS_GET_NT_ACL(parent_fsp, parent_fsp->fsp_name, + status = SMB_VFS_GET_NT_ACL(parent_fsp->conn, parent_fsp->fsp_name, DACL_SECURITY_INFORMATION, &parent_sd ); close_file(parent_fsp, NORMAL_CLOSE); @@ -3221,20 +3275,23 @@ static NTSTATUS append_parent_acl(files_struct *fsp, /* * Make room for potentially all the ACLs from - * the parent, plus the user/group/other triple. + * the parent. We used to add the ugw triple here, + * as we knew we were dealing with POSIX ACLs. + * We no longer need to do so as we can guarentee + * that a default ACL from the parent directory will + * be well formed for POSIX ACLs if it came from a + * POSIX ACL source, and if we're not writing to a + * POSIX ACL sink then we don't care if it's not well + * formed. JRA. */ - num_aces += parent_sd->dacl->num_aces + 3; + num_aces += parent_sd->dacl->num_aces; if((new_ace = TALLOC_ZERO_ARRAY(mem_ctx, SEC_ACE, num_aces)) == NULL) { return NT_STATUS_NO_MEMORY; } - DEBUG(10,("append_parent_acl: parent ACL has %u entries. New " - "ACL has %u entries\n", - parent_sd->dacl->num_aces, num_aces )); - /* Start by copying in all the given ACE entries. */ for (i = 0; i < psd->dacl->num_aces; i++) { sec_ace_copy(&new_ace[i], &psd->dacl->aces[i]); @@ -3245,49 +3302,95 @@ static NTSTATUS append_parent_acl(files_struct *fsp, * as that really only applies to newly created files. JRA. */ - /* - * Append u/g/w. - */ - - status = append_ugw_ace(fsp, psbuf, unx_mode, S_IRUSR, &new_ace[i++]); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - status = append_ugw_ace(fsp, psbuf, unx_mode, S_IRGRP, &new_ace[i++]); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - status = append_ugw_ace(fsp, psbuf, unx_mode, S_IROTH, &new_ace[i++]); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - /* Finally append any inherited ACEs. */ for (j = 0; j < parent_sd->dacl->num_aces; j++) { SEC_ACE *se = &parent_sd->dacl->aces[j]; - uint32 i_flags = se->flags & (SEC_ACE_FLAG_OBJECT_INHERIT| - SEC_ACE_FLAG_CONTAINER_INHERIT| - SEC_ACE_FLAG_INHERIT_ONLY); if (fsp->is_directory) { - if (i_flags == SEC_ACE_FLAG_OBJECT_INHERIT) { - /* Should only apply to a file - ignore. */ + if (!(se->flags & SEC_ACE_FLAG_CONTAINER_INHERIT)) { + /* Doesn't apply to a directory - ignore. */ + DEBUG(10,("append_parent_acl: directory %s " + "ignoring non container " + "inherit flags %u on ACE with sid %s " + "from parent %s\n", + fsp->fsp_name, + (unsigned int)se->flags, + sid_string_dbg(&se->trustee), + parent_name)); continue; } } else { - if ((i_flags & (SEC_ACE_FLAG_OBJECT_INHERIT| - SEC_ACE_FLAG_INHERIT_ONLY)) != - SEC_ACE_FLAG_OBJECT_INHERIT) { - /* Should not apply to a file - ignore. */ + if (!(se->flags & SEC_ACE_FLAG_OBJECT_INHERIT)) { + /* Doesn't apply to a file - ignore. */ + DEBUG(10,("append_parent_acl: file %s " + "ignoring non object " + "inherit flags %u on ACE with sid %s " + "from parent %s\n", + fsp->fsp_name, + (unsigned int)se->flags, + sid_string_dbg(&se->trustee), + parent_name)); + continue; + } + } + + if (is_dacl_protected) { + /* If the DACL is protected it means we must + * not overwrite an existing ACE entry with the + * same SID. This is order N^2. Ouch :-(. JRA. */ + unsigned int k; + for (k = 0; k < psd->dacl->num_aces; k++) { + if (sid_equal(&psd->dacl->aces[k].trustee, + &se->trustee)) { + break; + } + } + if (k < psd->dacl->num_aces) { + /* SID matched. Ignore. */ + DEBUG(10,("append_parent_acl: path %s " + "ignoring ACE with protected sid %s " + "from parent %s\n", + fsp->fsp_name, + sid_string_dbg(&se->trustee), + parent_name)); continue; } } + sec_ace_copy(&new_ace[i], se); if (se->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) { new_ace[i].flags &= ~(SEC_ACE_FLAG_VALID_INHERIT); } new_ace[i].flags |= SEC_ACE_FLAG_INHERITED_ACE; + + if (fsp->is_directory) { + /* + * Strip off any inherit only. It's applied. + */ + new_ace[i].flags &= ~(SEC_ACE_FLAG_INHERIT_ONLY); + if (se->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) { + /* No further inheritance. */ + new_ace[i].flags &= + ~(SEC_ACE_FLAG_CONTAINER_INHERIT| + SEC_ACE_FLAG_OBJECT_INHERIT); + } + } else { + /* + * Strip off any container or inherit + * flags, they can't apply to objects. + */ + new_ace[i].flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT| + SEC_ACE_FLAG_INHERIT_ONLY| + SEC_ACE_FLAG_NO_PROPAGATE_INHERIT); + } i++; + + DEBUG(10,("append_parent_acl: path %s " + "inheriting ACE with sid %s " + "from parent %s\n", + fsp->fsp_name, + sid_string_dbg(&se->trustee), + parent_name)); } parent_sd->dacl->aces = new_ace; @@ -3316,6 +3419,9 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd) bool acl_perms = False; mode_t orig_mode = (mode_t)0; NTSTATUS status; + uid_t orig_uid; + gid_t orig_gid; + bool need_chown = False; DEBUG(10,("set_nt_acl: called for file %s\n", fsp->fsp_name )); @@ -3332,12 +3438,14 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd) if(SMB_VFS_STAT(fsp->conn,fsp->fsp_name, &sbuf) != 0) return map_nt_error_from_unix(errno); } else { - if(SMB_VFS_FSTAT(fsp,fsp->fh->fd,&sbuf) != 0) + if(SMB_VFS_FSTAT(fsp, &sbuf) != 0) return map_nt_error_from_unix(errno); } /* Save the original elements we check against. */ orig_mode = sbuf.st_mode; + orig_uid = sbuf.st_uid; + orig_gid = sbuf.st_gid; /* * Unpack the user/group/world id's. @@ -3352,7 +3460,11 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd) * Do we need to chown ? */ - if (((user != (uid_t)-1) && (sbuf.st_uid != user)) || (( grp != (gid_t)-1) && (sbuf.st_gid != grp))) { + if (((user != (uid_t)-1) && (orig_uid != user)) || (( grp != (gid_t)-1) && (orig_gid != grp))) { + need_chown = True; + } + + if (need_chown && (user == (uid_t)-1 || user == current_user.ut.uid)) { DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n", fsp->fsp_name, (unsigned int)user, (unsigned int)grp )); @@ -3382,7 +3494,7 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd) if(fsp->fh->fd == -1) ret = SMB_VFS_STAT(fsp->conn, fsp->fsp_name, &sbuf); else - ret = SMB_VFS_FSTAT(fsp,fsp->fh->fd,&sbuf); + ret = SMB_VFS_FSTAT(fsp, &sbuf); if(ret != 0) return map_nt_error_from_unix(errno); @@ -3390,6 +3502,11 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd) /* Save the original elements we check against. */ orig_mode = sbuf.st_mode; + orig_uid = sbuf.st_uid; + orig_gid = sbuf.st_gid; + + /* We did chown already, drop the flag */ + need_chown = False; } create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid); @@ -3533,6 +3650,21 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd) free_canon_ace_list(dir_ace_list); } + /* Any chown pending? */ + if (need_chown) { + DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n", + fsp->fsp_name, (unsigned int)user, (unsigned int)grp )); + + if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) { + DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n", + fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) )); + if (errno == EPERM) { + return NT_STATUS_INVALID_OWNER; + } + return map_nt_error_from_unix(errno); + } + } + return NT_STATUS_OK; } @@ -3705,19 +3837,19 @@ int inherit_access_acl(connection_struct *conn, const char *inherit_from_dir, and set the mask to rwx. Needed to preserve complex ACLs set by NT. ****************************************************************************/ -int fchmod_acl(files_struct *fsp, int fd, mode_t mode) +int fchmod_acl(files_struct *fsp, mode_t mode) { connection_struct *conn = fsp->conn; SMB_ACL_T posix_acl = NULL; int ret = -1; - if ((posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fd)) == NULL) + if ((posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp)) == NULL) return -1; if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1) goto done; - ret = SMB_VFS_SYS_ACL_SET_FD(fsp, fd, posix_acl); + ret = SMB_VFS_SYS_ACL_SET_FD(fsp, posix_acl); done: @@ -4002,7 +4134,7 @@ static bool remove_posix_acl(connection_struct *conn, files_struct *fsp, const c /* Get the current file ACL. */ if (fsp && fsp->fh->fd != -1) { - file_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fsp->fh->fd); + file_acl = SMB_VFS_SYS_ACL_GET_FD(fsp); } else { file_acl = SMB_VFS_SYS_ACL_GET_FILE( conn, fname, SMB_ACL_TYPE_ACCESS); } @@ -4055,7 +4187,7 @@ static bool remove_posix_acl(connection_struct *conn, files_struct *fsp, const c /* Set the new empty file ACL. */ if (fsp && fsp->fh->fd != -1) { - if (SMB_VFS_SYS_ACL_SET_FD(fsp, fsp->fh->fd, new_file_acl) == -1) { + if (SMB_VFS_SYS_ACL_SET_FD(fsp, new_file_acl) == -1) { DEBUG(5,("remove_posix_acl: acl_set_file failed on %s (%s)\n", fname, strerror(errno) )); goto done; @@ -4102,7 +4234,7 @@ bool set_unix_posix_acl(connection_struct *conn, files_struct *fsp, const char * if (fsp && fsp->fh->fd != -1) { /* The preferred way - use an open fd. */ - if (SMB_VFS_SYS_ACL_SET_FD(fsp, fsp->fh->fd, file_acl) == -1) { + if (SMB_VFS_SYS_ACL_SET_FD(fsp, file_acl) == -1) { DEBUG(5,("set_unix_posix_acl: acl_set_file failed on %s (%s)\n", fname, strerror(errno) )); SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl); @@ -4171,8 +4303,7 @@ SEC_DESC *get_nt_acl_no_snum( TALLOC_CTX *ctx, const char *fname) finfo.fh->fd = -1; finfo.fsp_name = CONST_DISCARD(char *,fname); - if (!NT_STATUS_IS_OK(get_nt_acl( &finfo, DACL_SECURITY_INFORMATION, - &psd ))) { + if (!NT_STATUS_IS_OK(posix_fget_nt_acl( &finfo, DACL_SECURITY_INFORMATION, &psd))) { DEBUG(0,("get_nt_acl_no_snum: get_nt_acl returned zero.\n")); conn_free_internal( &conn ); return NULL; diff --git a/source3/smbd/process.c b/source3/smbd/process.c index ffc9e106f6..4672510d8d 100644 --- a/source3/smbd/process.c +++ b/source3/smbd/process.c @@ -20,10 +20,8 @@ #include "includes.h" -extern struct auth_context *negprot_global_auth_context; extern int smb_echo_count; -const int total_buffer_size = (BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN); static enum smb_read_errors smb_read_error = SMB_READ_OK; /* @@ -38,7 +36,6 @@ int max_send = BUFFER_SIZE; */ int max_recv = BUFFER_SIZE; -extern int last_message; SIG_ATOMIC_T reload_after_sighup = 0; SIG_ATOMIC_T got_sig_term = 0; extern bool global_machine_password_needs_changing; @@ -51,6 +48,73 @@ enum smb_read_errors *get_srv_read_error(void) return &smb_read_error; } +/**************************************************************************** + Send an smb to a fd. +****************************************************************************/ + +bool srv_send_smb(int fd, char *buffer, bool do_encrypt) +{ + size_t len; + size_t nwritten=0; + ssize_t ret; + char *buf_out = buffer; + + /* Sign the outgoing packet if required. */ + srv_calculate_sign_mac(buf_out); + + if (do_encrypt) { + NTSTATUS status = srv_encrypt_buffer(buffer, &buf_out); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("send_smb: SMB encryption failed " + "on outgoing packet! Error %s\n", + nt_errstr(status) )); + return false; + } + } + + len = smb_len(buf_out) + 4; + + while (nwritten < len) { + ret = write_data(fd,buf_out+nwritten,len - nwritten); + if (ret <= 0) { + DEBUG(0,("Error writing %d bytes to client. %d. (%s)\n", + (int)len,(int)ret, strerror(errno) )); + srv_free_enc_buffer(buf_out); + return false; + } + nwritten += ret; + } + + srv_free_enc_buffer(buf_out); + return true; +} + +/******************************************************************* + Setup the word count and byte count for a smb message. +********************************************************************/ + +int srv_set_message(char *buf, + int num_words, + int num_bytes, + bool zero) +{ + if (zero && (num_words || num_bytes)) { + memset(buf + smb_size,'\0',num_words*2 + num_bytes); + } + SCVAL(buf,smb_wct,num_words); + SSVAL(buf,smb_vwv + num_words*SIZEOFWORD,num_bytes); + smb_setlen(buf,(smb_size + num_words*2 + num_bytes - 4)); + return (smb_size + num_words*2 + num_bytes); +} + +static bool valid_smb_header(const uint8_t *inbuf) +{ + if (is_encrypted_packet(inbuf)) { + return true; + } + return (strncmp(smb_base(inbuf),"\377SMB",4) == 0); +} + /* Socket functions for smbd packet processing. */ static bool valid_packet_size(size_t len) @@ -90,16 +154,8 @@ static ssize_t read_packet_remainder(int fd, return len; } - if (timeout > 0) { - ret = read_socket_with_timeout(fd, - buffer, - len, - len, - timeout, - get_srv_read_error()); - } else { - ret = read_data(fd, buffer, len, get_srv_read_error()); - } + ret = read_socket_with_timeout(fd, buffer, len, len, timeout, + get_srv_read_error()); if (ret != len) { cond_set_smb_read_error(get_srv_read_error(), @@ -126,7 +182,7 @@ static ssize_t read_packet_remainder(int fd, (2*14) + /* word count (including bcc) */ \ 1 /* pad byte */) -ssize_t receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx, +static ssize_t receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx, const char lenbuf[4], int fd, char **buffer, @@ -141,19 +197,10 @@ ssize_t receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx, memcpy(writeX_header, lenbuf, sizeof(lenbuf)); - if (timeout > 0) { - ret = read_socket_with_timeout(fd, - writeX_header + 4, - STANDARD_WRITE_AND_X_HEADER_SIZE, - STANDARD_WRITE_AND_X_HEADER_SIZE, - timeout, - get_srv_read_error()); - } else { - ret = read_data(fd, - writeX_header+4, - STANDARD_WRITE_AND_X_HEADER_SIZE, - get_srv_read_error()); - } + ret = read_socket_with_timeout(fd, writeX_header + 4, + STANDARD_WRITE_AND_X_HEADER_SIZE, + STANDARD_WRITE_AND_X_HEADER_SIZE, + timeout, get_srv_read_error()); if (ret != STANDARD_WRITE_AND_X_HEADER_SIZE) { cond_set_smb_read_error(get_srv_read_error(), @@ -166,7 +213,7 @@ ssize_t receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx, * valid writeX call. */ - if (is_valid_writeX_buffer(writeX_header)) { + if (is_valid_writeX_buffer((uint8_t *)writeX_header)) { /* * If the data offset is beyond what * we've read, drain the extra bytes. @@ -274,7 +321,7 @@ static ssize_t receive_smb_raw_talloc(TALLOC_CTX *mem_ctx, return -1; } - if (CVAL(lenbuf,0) != SMBkeepalive && + if (CVAL(lenbuf,0) == 0 && min_recv_size && smb_len_large(lenbuf) > min_recv_size && /* Could be a UNIX large writeX. */ !srv_is_signing_active()) { @@ -314,17 +361,36 @@ static ssize_t receive_smb_raw_talloc(TALLOC_CTX *mem_ctx, return len + 4; } -ssize_t receive_smb_talloc(TALLOC_CTX *mem_ctx, int fd, char **buffer, - unsigned int timeout, size_t *p_unread) +static ssize_t receive_smb_talloc(TALLOC_CTX *mem_ctx, + int fd, + char **buffer, + unsigned int timeout, + size_t *p_unread, + bool *p_encrypted) { ssize_t len; + *p_encrypted = false; + len = receive_smb_raw_talloc(mem_ctx, fd, buffer, timeout, p_unread); if (len < 0) { return -1; } + if (is_encrypted_packet((uint8_t *)*buffer)) { + NTSTATUS status = srv_decrypt_buffer(*buffer); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("receive_smb_talloc: SMB decryption failed on " + "incoming packet! Error %s\n", + nt_errstr(status) )); + cond_set_smb_read_error(get_srv_read_error(), + SMB_READ_BAD_DECRYPT); + return -1; + } + *p_encrypted = true; + } + /* Check the incoming SMB signature. */ if (!srv_check_sign_mac(*buffer, true)) { DEBUG(0, ("receive_smb: SMB Signature verification failed on " @@ -342,7 +408,8 @@ ssize_t receive_smb_talloc(TALLOC_CTX *mem_ctx, int fd, char **buffer, void init_smb_request(struct smb_request *req, const uint8 *inbuf, - size_t unread_bytes) + size_t unread_bytes, + bool encrypted) { size_t req_size = smb_len(inbuf) + 4; /* Ensure we have at least smb_size bytes. */ @@ -358,6 +425,8 @@ void init_smb_request(struct smb_request *req, req->tid = SVAL(inbuf, smb_tid); req->wct = CVAL(inbuf, smb_wct); req->unread_bytes = unread_bytes; + req->encrypted = encrypted; + req->conn = conn_find(req->tid); /* Ensure we have at least wct words and 2 bytes of bcc. */ if (smb_size + req->wct*2 > req_size) { @@ -415,6 +484,7 @@ static bool push_queued_message(struct smb_request *req, msg->request_time = request_time; msg->end_time = end_time; + msg->encrypted = req->encrypted; if (private_data) { msg->private_data = data_blob_talloc(msg, private_data, @@ -690,7 +760,8 @@ static bool receive_message_or_smb(TALLOC_CTX *mem_ctx, char **buffer, size_t *buffer_len, int timeout, - size_t *p_unread) + size_t *p_unread, + bool *p_encrypted) { fd_set r_fds, w_fds; int selrtn; @@ -757,6 +828,7 @@ static bool receive_message_or_smb(TALLOC_CTX *mem_ctx, return False; } *buffer_len = msg->buf.length; + *p_encrypted = msg->encrypted; /* We leave this message on the queue so the open code can know this is a retry. */ @@ -873,7 +945,8 @@ static bool receive_message_or_smb(TALLOC_CTX *mem_ctx, goto again; } - len = receive_smb_talloc(mem_ctx, smbd_server_fd(), buffer, 0, p_unread); + len = receive_smb_talloc(mem_ctx, smbd_server_fd(), + buffer, 0, p_unread, p_encrypted); if (len == -1) { return False; @@ -953,7 +1026,7 @@ force write permissions on print services. */ static const struct smb_message_struct { const char *name; - void (*fn_new)(connection_struct *conn, struct smb_request *req); + void (*fn_new)(struct smb_request *req); int flags; } smb_messages[256] = { @@ -1240,7 +1313,7 @@ void reply_outbuf(struct smb_request *req, uint8 num_words, uint32 num_bytes) } construct_reply_common((char *)req->inbuf, (char *)req->outbuf); - set_message((char *)req->outbuf, num_words, num_bytes, False); + srv_set_message((char *)req->outbuf, num_words, num_bytes, false); /* * Zero out the word area, the caller has to take care of the bcc area * himself @@ -1298,21 +1371,19 @@ static void smb_dump(const char *name, int type, const char *data, ssize_t len) find. ****************************************************************************/ -static void switch_message(uint8 type, struct smb_request *req, int size) +static connection_struct *switch_message(uint8 type, struct smb_request *req, int size) { int flags; uint16 session_tag; - connection_struct *conn; + connection_struct *conn = NULL; static uint16 last_session_tag = UID_FIELD_INVALID; errno = 0; - last_message = type; - /* Make sure this is an SMB packet. smb_size contains NetBIOS header * so subtract 4 from it. */ - if ((strncmp(smb_base(req->inbuf),"\377SMB",4) != 0) + if (!valid_smb_header(req->inbuf) || (size < (smb_size - 4))) { DEBUG(2,("Non-SMB packet of length %d. Terminating server\n", smb_len(req->inbuf))); @@ -1323,7 +1394,7 @@ static void switch_message(uint8 type, struct smb_request *req, int size) DEBUG(0,("Unknown message type %d!\n",type)); smb_dump("Unknown", 1, (char *)req->inbuf, size); reply_unknown_new(req, type); - return; + return NULL; } flags = smb_messages[type].flags; @@ -1331,7 +1402,7 @@ static void switch_message(uint8 type, struct smb_request *req, int size) /* In share mode security we must ignore the vuid. */ session_tag = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID : req->vuid; - conn = conn_find(req->tid); + conn = req->conn; DEBUG(3,("switch message %s (pid %d) conn 0x%lx\n", smb_fn_name(type), (int)sys_getpid(), (unsigned long)conn)); @@ -1376,12 +1447,12 @@ static void switch_message(uint8 type, struct smb_request *req, int size) } else { reply_doserror(req, ERRSRV, ERRinvnid); } - return; + return NULL; } if (!change_to_user(conn,session_tag)) { reply_nterror(req, NT_STATUS_DOS(ERRSRV, ERRbaduid)); - return; + return conn; } /* All NEED_WRITE and CAN_IPC flags must also have AS_USER. */ @@ -1389,13 +1460,13 @@ static void switch_message(uint8 type, struct smb_request *req, int size) /* Does it need write permission? */ if ((flags & NEED_WRITE) && !CAN_WRITE(conn)) { reply_nterror(req, NT_STATUS_MEDIA_WRITE_PROTECTED); - return; + return conn; } /* IPC services are limited */ if (IS_IPC(conn) && !(flags & CAN_IPC)) { reply_doserror(req, ERRSRV,ERRaccess); - return; + return conn; } } else { /* This call needs to be run as root */ @@ -1404,11 +1475,24 @@ static void switch_message(uint8 type, struct smb_request *req, int size) /* load service specific parameters */ if (conn) { + if (req->encrypted) { + conn->encrypted_tid = true; + /* encrypted required from now on. */ + conn->encrypt_level = Required; + } else if (ENCRYPTION_REQUIRED(conn)) { + uint8 com = CVAL(req->inbuf,smb_com); + if (com != SMBtrans2 && com != SMBtranss2) { + exit_server_cleanly("encryption required " + "on connection"); + return conn; + } + } + if (!set_current_service(conn,SVAL(req->inbuf,smb_flg), (flags & (AS_USER|DO_CHDIR) ?True:False))) { reply_doserror(req, ERRSRV, ERRaccess); - return; + return conn; } conn->num_smb_operations++; } @@ -1419,19 +1503,21 @@ static void switch_message(uint8 type, struct smb_request *req, int size) !check_access(smbd_server_fd(), lp_hostsallow(-1), lp_hostsdeny(-1)))) { reply_doserror(req, ERRSRV, ERRaccess); - return; + return conn; } - smb_messages[type].fn_new(conn, req); + smb_messages[type].fn_new(req); + return req->conn; } /**************************************************************************** Construct a reply to the incoming packet. ****************************************************************************/ -static void construct_reply(char *inbuf, int size, size_t unread_bytes) +static void construct_reply(char *inbuf, int size, size_t unread_bytes, bool encrypted) { uint8 type = CVAL(inbuf,smb_com); + connection_struct *conn; struct smb_request *req; chain_size = 0; @@ -1441,9 +1527,9 @@ static void construct_reply(char *inbuf, int size, size_t unread_bytes) if (!(req = talloc(talloc_tos(), struct smb_request))) { smb_panic("could not allocate smb_request"); } - init_smb_request(req, (uint8 *)inbuf, unread_bytes); + init_smb_request(req, (uint8 *)inbuf, unread_bytes, encrypted); - switch_message(type, req, size); + conn = switch_message(type, req, size); if (req->unread_bytes) { /* writeX failed. drain socket. */ @@ -1462,8 +1548,10 @@ static void construct_reply(char *inbuf, int size, size_t unread_bytes) show_msg((char *)req->outbuf); } - if (!send_smb(smbd_server_fd(), (char *)req->outbuf)) { - exit_server_cleanly("construct_reply: send_smb failed."); + if (!srv_send_smb(smbd_server_fd(), + (char *)req->outbuf, + IS_CONN_ENCRYPTED(conn)||req->encrypted)) { + exit_server_cleanly("construct_reply: srv_send_smb failed."); } TALLOC_FREE(req); @@ -1475,7 +1563,7 @@ static void construct_reply(char *inbuf, int size, size_t unread_bytes) Process an smb from the client ****************************************************************************/ -static void process_smb(char *inbuf, size_t nread, size_t unread_bytes) +static void process_smb(char *inbuf, size_t nread, size_t unread_bytes, bool encrypted) { static int trans_num; int msg_type = CVAL(inbuf,0); @@ -1496,7 +1584,7 @@ static void process_smb(char *inbuf, size_t nread, size_t unread_bytes) static unsigned char buf[5] = {0x83, 0, 0, 1, 0x81}; DEBUG( 1, ( "Connection denied from %s\n", client_addr(get_client_fd(),addr,sizeof(addr)) ) ); - (void)send_smb(smbd_server_fd(),(char *)buf); + (void)srv_send_smb(smbd_server_fd(),(char *)buf,false); exit_server_cleanly("connection denied"); } } @@ -1517,7 +1605,7 @@ static void process_smb(char *inbuf, size_t nread, size_t unread_bytes) show_msg(inbuf); - construct_reply(inbuf,nread,unread_bytes); + construct_reply(inbuf,nread,unread_bytes,encrypted); trans_num++; } @@ -1554,7 +1642,7 @@ void remove_from_common_flags2(uint32 v) void construct_reply_common(const char *inbuf, char *outbuf) { - set_message(outbuf,0,0,False); + srv_set_message(outbuf,0,0,false); SCVAL(outbuf,smb_com,CVAL(inbuf,smb_com)); SIVAL(outbuf,smb_rcls,0); @@ -1677,7 +1765,7 @@ void chain_reply(struct smb_request *req) if (!(req2 = talloc(talloc_tos(), struct smb_request))) { smb_panic("could not allocate smb_request"); } - init_smb_request(req2, (uint8 *)inbuf2,0); + init_smb_request(req2, (uint8 *)inbuf2,0, req->encrypted); /* process the request */ switch_message(smb_com2, req2, new_size); @@ -1963,7 +2051,8 @@ void smbd_process(void) int num_echos; char *inbuf; size_t inbuf_len; - TALLOC_CTX *frame = talloc_stackframe(); + bool encrypted = false; + TALLOC_CTX *frame = talloc_stackframe_pool(8192); errno = 0; @@ -1977,8 +2066,10 @@ void smbd_process(void) run_events(smbd_event_context(), 0, NULL, NULL); - while (!receive_message_or_smb(NULL, &inbuf, &inbuf_len, - select_timeout, &unread_bytes)) { + while (!receive_message_or_smb(talloc_tos(), &inbuf, &inbuf_len, + select_timeout, + &unread_bytes, + &encrypted)) { if(!timeout_processing(&select_timeout, &last_timeout_processing_time)) return; @@ -1997,7 +2088,7 @@ void smbd_process(void) */ num_echos = smb_echo_count; - process_smb(inbuf, inbuf_len, unread_bytes); + process_smb(inbuf, inbuf_len, unread_bytes, encrypted); TALLOC_FREE(inbuf); diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index 45081808e1..18376031ec 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -167,6 +167,7 @@ static NTSTATUS check_path_syntax_internal(char *path, } *d = '\0'; + return ret; } @@ -391,7 +392,7 @@ void reply_special(char *inbuf) /* * We only really use 4 bytes of the outbuf, but for the smb_setlen - * calculation & friends (send_smb uses that) we need the full smb + * calculation & friends (srv_send_smb uses that) we need the full smb * header. */ char outbuf[smb_size]; @@ -470,7 +471,7 @@ void reply_special(char *inbuf) DEBUG(5,("init msg_type=0x%x msg_flags=0x%x\n", msg_type, msg_flags)); - send_smb(smbd_server_fd(), outbuf); + srv_send_smb(smbd_server_fd(), outbuf, false); return; } @@ -479,8 +480,9 @@ void reply_special(char *inbuf) conn POINTER CAN BE NULL HERE ! ****************************************************************************/ -void reply_tcon(connection_struct *conn, struct smb_request *req) +void reply_tcon(struct smb_request *req) { + connection_struct *conn = req->conn; const char *service; char *service_buf = NULL; char *password = NULL; @@ -523,6 +525,7 @@ void reply_tcon(connection_struct *conn, struct smb_request *req) password_blob = data_blob(password, pwlen+1); conn = make_connection(service,password_blob,dev,req->vuid,&nt_status); + req->conn = conn; data_blob_clear_free(&password_blob); @@ -549,8 +552,9 @@ void reply_tcon(connection_struct *conn, struct smb_request *req) conn POINTER CAN BE NULL HERE ! ****************************************************************************/ -void reply_tcon_and_X(connection_struct *conn, struct smb_request *req) +void reply_tcon_and_X(struct smb_request *req) { + connection_struct *conn = req->conn; char *service = NULL; DATA_BLOB password; TALLOC_CTX *ctx = talloc_tos(); @@ -578,6 +582,8 @@ void reply_tcon_and_X(connection_struct *conn, struct smb_request *req) /* we might have to close an old one */ if ((tcon_flags & 0x1) && conn) { close_cnum(conn,req->vuid); + req->conn = NULL; + conn = NULL; } if ((passlen > MAX_PASS_LEN) || (passlen >= smb_buflen(req->inbuf))) { @@ -587,7 +593,8 @@ void reply_tcon_and_X(connection_struct *conn, struct smb_request *req) } if (global_encrypted_passwords_negotiated) { - password = data_blob(smb_buf(req->inbuf),passlen); + password = data_blob_talloc(talloc_tos(), smb_buf(req->inbuf), + passlen); if (lp_security() == SEC_SHARE) { /* * Security = share always has a pad byte @@ -598,7 +605,8 @@ void reply_tcon_and_X(connection_struct *conn, struct smb_request *req) p = smb_buf(req->inbuf) + passlen; } } else { - password = data_blob(smb_buf(req->inbuf),passlen+1); + password = data_blob_talloc(talloc_tos(), smb_buf(req->inbuf), + passlen+1); /* Ensure correct termination */ password.data[passlen]=0; p = smb_buf(req->inbuf) + passlen + 1; @@ -646,6 +654,7 @@ void reply_tcon_and_X(connection_struct *conn, struct smb_request *req) conn = make_connection(service, password, client_devicetype, req->vuid, &nt_status); + req->conn =conn; data_blob_clear_free(&password); @@ -731,17 +740,6 @@ void reply_tcon_and_X(connection_struct *conn, struct smb_request *req) Reply to an unknown type. ****************************************************************************/ -int reply_unknown(char *inbuf,char *outbuf) -{ - int type; - type = CVAL(inbuf,smb_com); - - DEBUG(0,("unknown command type (%s): type=%d (0x%X)\n", - smb_fn_name(type), type, type)); - - return(ERROR_DOS(ERRSRV,ERRunknownsmb)); -} - void reply_unknown_new(struct smb_request *req, uint8 type) { DEBUG(0, ("unknown command type (%s): type=%d (0x%X)\n", @@ -755,8 +753,9 @@ void reply_unknown_new(struct smb_request *req, uint8 type) conn POINTER CAN BE NULL HERE ! ****************************************************************************/ -void reply_ioctl(connection_struct *conn, struct smb_request *req) +void reply_ioctl(struct smb_request *req) { + connection_struct *conn = req->conn; uint16 device; uint16 function; uint32 ioctl_code; @@ -844,8 +843,9 @@ static NTSTATUS map_checkpath_error(const char *inbuf, NTSTATUS status) Reply to a checkpath. ****************************************************************************/ -void reply_checkpath(connection_struct *conn, struct smb_request *req) +void reply_checkpath(struct smb_request *req) { + connection_struct *conn = req->conn; char *name = NULL; SMB_STRUCT_STAT sbuf; NTSTATUS status; @@ -938,8 +938,9 @@ void reply_checkpath(connection_struct *conn, struct smb_request *req) Reply to a getatr. ****************************************************************************/ -void reply_getatr(connection_struct *conn, struct smb_request *req) +void reply_getatr(struct smb_request *req) { + connection_struct *conn = req->conn; char *fname = NULL; SMB_STRUCT_STAT sbuf; int mode=0; @@ -1039,8 +1040,9 @@ void reply_getatr(connection_struct *conn, struct smb_request *req) Reply to a setatr. ****************************************************************************/ -void reply_setatr(connection_struct *conn, struct smb_request *req) +void reply_setatr(struct smb_request *req) { + connection_struct *conn = req->conn; char *fname = NULL; int mode; time_t mtime; @@ -1139,8 +1141,9 @@ void reply_setatr(connection_struct *conn, struct smb_request *req) Reply to a dskattr. ****************************************************************************/ -void reply_dskattr(connection_struct *conn, struct smb_request *req) +void reply_dskattr(struct smb_request *req) { + connection_struct *conn = req->conn; SMB_BIG_UINT dfree,dsize,bsize; START_PROFILE(SMBdskattr); @@ -1163,8 +1166,8 @@ void reply_dskattr(connection_struct *conn, struct smb_request *req) total_space = dsize * (double)bsize; free_space = dfree * (double)bsize; - dsize = (total_space+63*512) / (64*512); - dfree = (free_space+63*512) / (64*512); + dsize = (SMB_BIG_UINT)((total_space+63*512) / (64*512)); + dfree = (SMB_BIG_UINT)((free_space+63*512) / (64*512)); if (dsize > 0xFFFF) dsize = 0xFFFF; if (dfree > 0xFFFF) dfree = 0xFFFF; @@ -1191,8 +1194,9 @@ void reply_dskattr(connection_struct *conn, struct smb_request *req) Can be called from SMBsearch, SMBffirst or SMBfunique. ****************************************************************************/ -void reply_search(connection_struct *conn, struct smb_request *req) +void reply_search(struct smb_request *req) { + connection_struct *conn = req->conn; char *mask = NULL; char *directory = NULL; char *fname = NULL; @@ -1493,7 +1497,7 @@ void reply_search(connection_struct *conn, struct smb_request *req) Reply to a fclose (stop directory search). ****************************************************************************/ -void reply_fclose(connection_struct *conn, struct smb_request *req) +void reply_fclose(struct smb_request *req) { int status_len; char status[21]; @@ -1557,8 +1561,9 @@ void reply_fclose(connection_struct *conn, struct smb_request *req) Reply to an open. ****************************************************************************/ -void reply_open(connection_struct *conn, struct smb_request *req) +void reply_open(struct smb_request *req) { + connection_struct *conn = req->conn; char *fname = NULL; uint32 fattr=0; SMB_OFF_T size = 0; @@ -1597,51 +1602,30 @@ void reply_open(connection_struct *conn, struct smb_request *req) return; } - status = resolve_dfspath(ctx, conn, - req->flags2 & FLAGS2_DFS_PATHNAMES, - fname, - &fname); - 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); - END_PROFILE(SMBopen); - return; - } - reply_nterror(req, status); - END_PROFILE(SMBopen); - return; - } - - status = unix_convert(ctx, conn, fname, False, &fname, NULL, &sbuf); - if (!NT_STATUS_IS_OK(status)) { - reply_nterror(req, status); - END_PROFILE(SMBopen); - return; - } - - status = check_name(conn, fname); - if (!NT_STATUS_IS_OK(status)) { - reply_nterror(req, status); - END_PROFILE(SMBopen); - return; - } - - if (!map_open_params_to_ntcreate(fname, deny_mode, OPENX_FILE_EXISTS_OPEN, - &access_mask, &share_mode, &create_disposition, &create_options)) { + if (!map_open_params_to_ntcreate( + fname, deny_mode, OPENX_FILE_EXISTS_OPEN, &access_mask, + &share_mode, &create_disposition, &create_options)) { reply_nterror(req, NT_STATUS_DOS(ERRDOS, ERRbadaccess)); END_PROFILE(SMBopen); return; } - status = open_file_ntcreate(conn, req, fname, &sbuf, - access_mask, - share_mode, - create_disposition, - create_options, - dos_attr, - oplock_request, - &info, &fsp); + status = create_file(conn, /* conn */ + req, /* req */ + 0, /* root_dir_fid */ + fname, /* fname */ + access_mask, /* access_mask */ + share_mode, /* share_access */ + create_disposition, /* create_disposition*/ + create_options, /* create_options */ + dos_attr, /* file_attributes */ + oplock_request, /* oplock_request */ + 0, /* allocation_size */ + NULL, /* sd */ + NULL, /* ea_list */ + &fsp, /* result */ + &info, /* pinfo */ + &sbuf); /* psbuf */ if (!NT_STATUS_IS_OK(status)) { if (open_was_deferred(req->mid)) { @@ -1694,8 +1678,9 @@ void reply_open(connection_struct *conn, struct smb_request *req) Reply to an open and X. ****************************************************************************/ -void reply_open_and_X(connection_struct *conn, struct smb_request *req) +void reply_open_and_X(struct smb_request *req) { + connection_struct *conn = req->conn; char *fname = NULL; uint16 open_flags; int deny_mode; @@ -1762,53 +1747,30 @@ void reply_open_and_X(connection_struct *conn, struct smb_request *req) return; } - status = resolve_dfspath(ctx, conn, - req->flags2 & FLAGS2_DFS_PATHNAMES, - fname, - &fname); - if (!NT_STATUS_IS_OK(status)) { - END_PROFILE(SMBopenX); - 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; - } - - status = unix_convert(ctx, conn, fname, False, &fname, NULL, &sbuf); - if (!NT_STATUS_IS_OK(status)) { - reply_nterror(req, status); - END_PROFILE(SMBopenX); - return; - } - - status = check_name(conn, fname); - if (!NT_STATUS_IS_OK(status)) { - reply_nterror(req, status); - END_PROFILE(SMBopenX); - return; - } - - if (!map_open_params_to_ntcreate(fname, deny_mode, smb_ofun, - &access_mask, - &share_mode, - &create_disposition, - &create_options)) { + if (!map_open_params_to_ntcreate( + fname, deny_mode, smb_ofun, &access_mask, + &share_mode, &create_disposition, &create_options)) { reply_nterror(req, NT_STATUS_DOS(ERRDOS, ERRbadaccess)); END_PROFILE(SMBopenX); return; } - status = open_file_ntcreate(conn, req, fname, &sbuf, - access_mask, - share_mode, - create_disposition, - create_options, - smb_attr, - oplock_request, - &smb_action, &fsp); + status = create_file(conn, /* conn */ + req, /* req */ + 0, /* root_dir_fid */ + fname, /* fname */ + access_mask, /* access_mask */ + share_mode, /* share_access */ + create_disposition, /* create_disposition*/ + create_options, /* create_options */ + smb_attr, /* file_attributes */ + oplock_request, /* oplock_request */ + 0, /* allocation_size */ + NULL, /* sd */ + NULL, /* ea_list */ + &fsp, /* result */ + &smb_action, /* pinfo */ + &sbuf); /* psbuf */ if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBopenX); @@ -1905,10 +1867,9 @@ void reply_open_and_X(connection_struct *conn, struct smb_request *req) /**************************************************************************** Reply to a SMBulogoffX. - conn POINTER CAN BE NULL HERE ! ****************************************************************************/ -void reply_ulogoffX(connection_struct *conn, struct smb_request *req) +void reply_ulogoffX(struct smb_request *req) { user_struct *vuser; @@ -1941,8 +1902,9 @@ void reply_ulogoffX(connection_struct *conn, struct smb_request *req) Reply to a mknew or a create. ****************************************************************************/ -void reply_mknew(connection_struct *conn, struct smb_request *req) +void reply_mknew(struct smb_request *req) { + connection_struct *conn = req->conn; char *fname = NULL; int com; uint32 fattr = 0; @@ -1982,35 +1944,6 @@ void reply_mknew(connection_struct *conn, struct smb_request *req) return; } - status = resolve_dfspath(ctx, conn, - req->flags2 & FLAGS2_DFS_PATHNAMES, - fname, - &fname); - if (!NT_STATUS_IS_OK(status)) { - END_PROFILE(SMBcreate); - 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; - } - - status = unix_convert(ctx, conn, fname, False, &fname, NULL, &sbuf); - if (!NT_STATUS_IS_OK(status)) { - reply_nterror(req, status); - END_PROFILE(SMBcreate); - return; - } - - status = check_name(conn, fname); - if (!NT_STATUS_IS_OK(status)) { - reply_nterror(req, status); - END_PROFILE(SMBcreate); - return; - } - if (fattr & aVOLID) { DEBUG(0,("Attempt to create file (%s) with volid set - " "please report this\n", fname)); @@ -2024,15 +1957,22 @@ void reply_mknew(connection_struct *conn, struct smb_request *req) create_disposition = FILE_OVERWRITE_IF; } - /* Open file using ntcreate. */ - status = open_file_ntcreate(conn, req, fname, &sbuf, - access_mask, - share_mode, - create_disposition, - create_options, - fattr, - oplock_request, - NULL, &fsp); + status = create_file(conn, /* conn */ + req, /* req */ + 0, /* root_dir_fid */ + fname, /* fname */ + access_mask, /* access_mask */ + share_mode, /* share_access */ + create_disposition, /* create_disposition*/ + create_options, /* create_options */ + fattr, /* file_attributes */ + oplock_request, /* oplock_request */ + 0, /* allocation_size */ + NULL, /* sd */ + NULL, /* ea_list */ + &fsp, /* result */ + NULL, /* pinfo */ + &sbuf); /* psbuf */ if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBcreate); @@ -2072,8 +2012,9 @@ void reply_mknew(connection_struct *conn, struct smb_request *req) Reply to a create temporary file. ****************************************************************************/ -void reply_ctemp(connection_struct *conn, struct smb_request *req) +void reply_ctemp(struct smb_request *req) { + connection_struct *conn = req->conn; char *fname = NULL; uint32 fattr; files_struct *fsp; @@ -2349,14 +2290,22 @@ static NTSTATUS do_unlink(connection_struct *conn, /* On open checks the open itself will check the share mode, so don't do it here as we'll get it wrong. */ - status = open_file_ntcreate(conn, req, fname, &sbuf, - DELETE_ACCESS, - FILE_SHARE_NONE, - FILE_OPEN, - 0, - FILE_ATTRIBUTE_NORMAL, - req != NULL ? 0 : INTERNAL_OPEN_ONLY, - NULL, &fsp); + status = create_file_unixpath + (conn, /* conn */ + req, /* req */ + fname, /* fname */ + DELETE_ACCESS, /* access_mask */ + FILE_SHARE_NONE, /* share_access */ + FILE_OPEN, /* create_disposition*/ + FILE_NON_DIRECTORY_FILE, /* create_options */ + FILE_ATTRIBUTE_NORMAL, /* file_attributes */ + 0, /* oplock_request */ + 0, /* allocation_size */ + NULL, /* sd */ + NULL, /* ea_list */ + &fsp, /* result */ + NULL, /* pinfo */ + &sbuf); /* psbuf */ if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("open_file_ntcreate failed: %s\n", @@ -2470,7 +2419,8 @@ NTSTATUS unlink_internals(connection_struct *conn, struct smb_request *req, return status; } - dir_hnd = OpenDir(conn, directory, mask, dirtype); + dir_hnd = OpenDir(talloc_tos(), conn, directory, mask, + dirtype); if (dir_hnd == NULL) { return map_nt_error_from_unix(errno); } @@ -2508,7 +2458,7 @@ NTSTATUS unlink_internals(connection_struct *conn, struct smb_request *req, status = check_name(conn, fname); if (!NT_STATUS_IS_OK(status)) { - CloseDir(dir_hnd); + TALLOC_FREE(dir_hnd); return status; } @@ -2524,7 +2474,7 @@ NTSTATUS unlink_internals(connection_struct *conn, struct smb_request *req, TALLOC_FREE(fname); } - CloseDir(dir_hnd); + TALLOC_FREE(dir_hnd); } if (count == 0 && NT_STATUS_IS_OK(status)) { @@ -2538,8 +2488,9 @@ NTSTATUS unlink_internals(connection_struct *conn, struct smb_request *req, Reply to a unlink ****************************************************************************/ -void reply_unlink(connection_struct *conn, struct smb_request *req) +void reply_unlink(struct smb_request *req) { + connection_struct *conn = req->conn; char *name = NULL; uint32 dirtype; NTSTATUS status; @@ -2713,7 +2664,7 @@ void send_file_readbraw(connection_struct *conn, _smb_setlen(header,nread); header_blob = data_blob_const(header, 4); - if ( SMB_VFS_SENDFILE( smbd_server_fd(), fsp, fsp->fh->fd, + if (SMB_VFS_SENDFILE(smbd_server_fd(), fsp, &header_blob, startpos, nread) == -1) { /* Returning ENOSYS means no data at all was sent. * Do this as a normal read. */ @@ -2780,8 +2731,9 @@ normal_readbraw: Reply to a readbraw (core+ protocol). ****************************************************************************/ -void reply_readbraw(connection_struct *conn, struct smb_request *req) +void reply_readbraw(struct smb_request *req) { + connection_struct *conn = req->conn; ssize_t maxcount,mincount; size_t nread = 0; SMB_OFF_T startpos; @@ -2791,8 +2743,8 @@ void reply_readbraw(connection_struct *conn, struct smb_request *req) START_PROFILE(SMBreadbraw); - if (srv_is_signing_active()) { - exit_server_cleanly("reply_readbraw: SMB signing is active - " + if (srv_is_signing_active() || is_encrypted_packet(req->inbuf)) { + exit_server_cleanly("reply_readbraw: SMB signing/sealing is active - " "raw reads/writes are disallowed."); } @@ -2895,7 +2847,7 @@ void reply_readbraw(connection_struct *conn, struct smb_request *req) return; } - if (SMB_VFS_FSTAT(fsp,fsp->fh->fd,&st) == 0) { + if (SMB_VFS_FSTAT(fsp, &st) == 0) { size = st.st_size; } @@ -2930,8 +2882,9 @@ void reply_readbraw(connection_struct *conn, struct smb_request *req) Reply to a lockread (core+ protocol). ****************************************************************************/ -void reply_lockread(connection_struct *conn, struct smb_request *req) +void reply_lockread(struct smb_request *req) { + connection_struct *conn = req->conn; ssize_t nread = -1; char *data; SMB_OFF_T startpos; @@ -3017,7 +2970,7 @@ Returning short read of maximum allowed for compatibility with Windows 2000.\n", return; } - set_message((char *)req->outbuf, 5, nread+3, False); + srv_set_message((char *)req->outbuf, 5, nread+3, False); SSVAL(req->outbuf,smb_vwv0,nread); SSVAL(req->outbuf,smb_vwv5,nread+3); @@ -3039,8 +2992,9 @@ Returning short read of maximum allowed for compatibility with Windows 2000.\n", Reply to a read. ****************************************************************************/ -void reply_read(connection_struct *conn, struct smb_request *req) +void reply_read(struct smb_request *req) { + connection_struct *conn = req->conn; size_t numtoread; ssize_t nread = 0; char *data; @@ -3104,7 +3058,7 @@ Returning short read of maximum allowed for compatibility with Windows 2000.\n", return; } - set_message((char *)req->outbuf, 5, nread+3, False); + srv_set_message((char *)req->outbuf, 5, nread+3, False); SSVAL(req->outbuf,smb_vwv0,nread); SSVAL(req->outbuf,smb_vwv5,nread+3); @@ -3127,7 +3081,7 @@ static int setup_readX_header(char *outbuf, size_t smb_maxcnt) int outsize; char *data; - outsize = set_message(outbuf,12,smb_maxcnt,False); + outsize = srv_set_message(outbuf,12,smb_maxcnt,False); data = smb_buf(outbuf); memset(outbuf+smb_vwv0,'\0',24); /* valgrind init. */ @@ -3154,7 +3108,7 @@ static void send_file_readX(connection_struct *conn, struct smb_request *req, SMB_STRUCT_STAT sbuf; ssize_t nread = -1; - if(SMB_VFS_FSTAT(fsp,fsp->fh->fd, &sbuf) == -1) { + if(SMB_VFS_FSTAT(fsp, &sbuf) == -1) { reply_unixerror(req, ERRDOS, ERRnoaccess); return; } @@ -3177,6 +3131,7 @@ static void send_file_readX(connection_struct *conn, struct smb_request *req, */ if ((chain_size == 0) && (CVAL(req->inbuf,smb_vwv0) == 0xFF) && + !is_encrypted_packet(req->inbuf) && lp_use_sendfile(SNUM(conn)) && (fsp->wcp == NULL) ) { uint8 headerbuf[smb_size + 12 * 2]; DATA_BLOB header; @@ -3192,7 +3147,7 @@ static void send_file_readX(connection_struct *conn, struct smb_request *req, construct_reply_common((char *)req->inbuf, (char *)headerbuf); setup_readX_header((char *)headerbuf, smb_maxcnt); - if ((nread = SMB_VFS_SENDFILE( smbd_server_fd(), fsp, fsp->fh->fd, &header, startpos, smb_maxcnt)) == -1) { + if ((nread = SMB_VFS_SENDFILE(smbd_server_fd(), fsp, &header, startpos, smb_maxcnt)) == -1) { /* Returning ENOSYS means no data at all was sent. Do this as a normal read. */ if (errno == ENOSYS) { goto normal_read; @@ -3283,8 +3238,9 @@ normal_read: Reply to a read and X. ****************************************************************************/ -void reply_read_and_X(connection_struct *conn, struct smb_request *req) +void reply_read_and_X(struct smb_request *req) { + connection_struct *conn = req->conn; files_struct *fsp; SMB_OFF_T startpos; size_t smb_maxcnt; @@ -3332,8 +3288,8 @@ void reply_read_and_X(connection_struct *conn, struct smb_request *req) END_PROFILE(SMBreadX); return; } - /* We currently don't do this on signed data. */ - if (srv_is_signing_active()) { + /* We currently don't do this on signed or sealed data. */ + if (srv_is_signing_active() || is_encrypted_packet(req->inbuf)) { reply_nterror(req, NT_STATUS_NOT_SUPPORTED); END_PROFILE(SMBreadX); return; @@ -3382,8 +3338,8 @@ void reply_read_and_X(connection_struct *conn, struct smb_request *req) return; } - if (!big_readX - && schedule_aio_read_and_X(conn, req, fsp, startpos, smb_maxcnt)) { + if (!big_readX && + schedule_aio_read_and_X(conn, req, fsp, startpos, smb_maxcnt)) { END_PROFILE(SMBreadX); return; } @@ -3412,9 +3368,9 @@ void error_to_writebrawerr(struct smb_request *req) Reply to a writebraw (core+ or LANMAN1.0 protocol). ****************************************************************************/ -void reply_writebraw(connection_struct *conn, struct smb_request *req) +void reply_writebraw(struct smb_request *req) { - int outsize = 0; + connection_struct *conn = req->conn; char *buf = NULL; ssize_t nwritten=0; ssize_t total_written=0; @@ -3524,13 +3480,14 @@ void reply_writebraw(connection_struct *conn, struct smb_request *req) * it to send more bytes */ memcpy(buf, req->inbuf, smb_size); - outsize = set_message(buf, - Protocol>PROTOCOL_COREPLUS?1:0,0,True); + srv_set_message(buf,Protocol>PROTOCOL_COREPLUS?1:0,0,True); SCVAL(buf,smb_com,SMBwritebraw); SSVALS(buf,smb_vwv0,0xFFFF); show_msg(buf); - if (!send_smb(smbd_server_fd(),buf)) { - exit_server_cleanly("reply_writebraw: send_smb " + if (!srv_send_smb(smbd_server_fd(), + buf, + IS_CONN_ENCRYPTED(conn))) { + exit_server_cleanly("reply_writebraw: srv_send_smb " "failed."); } @@ -3639,8 +3596,9 @@ void reply_writebraw(connection_struct *conn, struct smb_request *req) Reply to a writeunlock (core+). ****************************************************************************/ -void reply_writeunlock(connection_struct *conn, struct smb_request *req) +void reply_writeunlock(struct smb_request *req) { + connection_struct *conn = req->conn; ssize_t nwritten = -1; size_t numtowrite; SMB_OFF_T startpos; @@ -3738,8 +3696,9 @@ void reply_writeunlock(connection_struct *conn, struct smb_request *req) Reply to a write. ****************************************************************************/ -void reply_write(connection_struct *conn, struct smb_request *req) +void reply_write(struct smb_request *req) { + connection_struct *conn = req->conn; size_t numtowrite; ssize_t nwritten = -1; SMB_OFF_T startpos; @@ -3849,13 +3808,19 @@ void reply_write(connection_struct *conn, struct smb_request *req) (2*14) + /* word count (including bcc) */ \ 1 /* pad byte */) -bool is_valid_writeX_buffer(const char *inbuf) +bool is_valid_writeX_buffer(const uint8_t *inbuf) { size_t numtowrite; connection_struct *conn = NULL; unsigned int doff = 0; size_t len = smb_len_large(inbuf); + if (is_encrypted_packet(inbuf)) { + /* Can't do this on encrypted + * connections. */ + return false; + } + if (CVAL(inbuf,smb_com) != SMBwriteX) { return false; } @@ -3920,8 +3885,9 @@ bool is_valid_writeX_buffer(const char *inbuf) Reply to a write and X. ****************************************************************************/ -void reply_write_and_X(connection_struct *conn, struct smb_request *req) +void reply_write_and_X(struct smb_request *req) { + connection_struct *conn = req->conn; files_struct *fsp; SMB_OFF_T startpos; size_t numtowrite; @@ -4042,13 +4008,13 @@ void reply_write_and_X(connection_struct *conn, struct smb_request *req) nwritten = 0; } else { - if (req->unread_bytes == 0 && - schedule_aio_write_and_X(conn, req, fsp, data, - startpos, numtowrite)) { + if ((req->unread_bytes == 0) && + schedule_aio_write_and_X(conn, req, fsp, data, startpos, + numtowrite)) { END_PROFILE(SMBwriteX); return; } - + nwritten = write_file(req,fsp,data,startpos,numtowrite); } @@ -4088,8 +4054,9 @@ void reply_write_and_X(connection_struct *conn, struct smb_request *req) Reply to a lseek. ****************************************************************************/ -void reply_lseek(connection_struct *conn, struct smb_request *req) +void reply_lseek(struct smb_request *req) { + connection_struct *conn = req->conn; SMB_OFF_T startpos; SMB_OFF_T res= -1; int mode,umode; @@ -4134,12 +4101,12 @@ void reply_lseek(connection_struct *conn, struct smb_request *req) } if (umode == SEEK_END) { - if((res = SMB_VFS_LSEEK(fsp,fsp->fh->fd,startpos,umode)) == -1) { + if((res = SMB_VFS_LSEEK(fsp,startpos,umode)) == -1) { if(errno == EINVAL) { SMB_OFF_T current_pos = startpos; SMB_STRUCT_STAT sbuf; - if(SMB_VFS_FSTAT(fsp,fsp->fh->fd, &sbuf) == -1) { + if(SMB_VFS_FSTAT(fsp, &sbuf) == -1) { reply_unixerror(req, ERRDOS, ERRnoaccess); END_PROFILE(SMBlseek); @@ -4148,7 +4115,7 @@ void reply_lseek(connection_struct *conn, struct smb_request *req) current_pos += sbuf.st_size; if(current_pos < 0) - res = SMB_VFS_LSEEK(fsp,fsp->fh->fd,0,SEEK_SET); + res = SMB_VFS_LSEEK(fsp,0,SEEK_SET); } } @@ -4175,8 +4142,9 @@ void reply_lseek(connection_struct *conn, struct smb_request *req) Reply to a flush. ****************************************************************************/ -void reply_flush(connection_struct *conn, struct smb_request *req) +void reply_flush(struct smb_request *req) { + connection_struct *conn = req->conn; uint16 fnum; files_struct *fsp; @@ -4219,7 +4187,7 @@ void reply_flush(connection_struct *conn, struct smb_request *req) conn POINTER CAN BE NULL HERE ! ****************************************************************************/ -void reply_exit(connection_struct *conn, struct smb_request *req) +void reply_exit(struct smb_request *req) { START_PROFILE(SMBexit); @@ -4237,8 +4205,9 @@ void reply_exit(connection_struct *conn, struct smb_request *req) Reply to a close - has to deal with closing a directory opened by NT SMB's. ****************************************************************************/ -void reply_close(connection_struct *conn, struct smb_request *req) +void reply_close(struct smb_request *req) { + connection_struct *conn = req->conn; NTSTATUS status = NT_STATUS_OK; files_struct *fsp = NULL; START_PROFILE(SMBclose); @@ -4315,8 +4284,9 @@ void reply_close(connection_struct *conn, struct smb_request *req) Reply to a writeclose (Core+ protocol). ****************************************************************************/ -void reply_writeclose(connection_struct *conn, struct smb_request *req) +void reply_writeclose(struct smb_request *req) { + connection_struct *conn = req->conn; size_t numtowrite; ssize_t nwritten = -1; NTSTATUS close_status = NT_STATUS_OK; @@ -4404,8 +4374,9 @@ void reply_writeclose(connection_struct *conn, struct smb_request *req) Reply to a lock. ****************************************************************************/ -void reply_lock(connection_struct *conn, struct smb_request *req) +void reply_lock(struct smb_request *req) { + connection_struct *conn = req->conn; SMB_BIG_UINT count,offset; NTSTATUS status; files_struct *fsp; @@ -4463,8 +4434,9 @@ void reply_lock(connection_struct *conn, struct smb_request *req) Reply to a unlock. ****************************************************************************/ -void reply_unlock(connection_struct *conn, struct smb_request *req) +void reply_unlock(struct smb_request *req) { + connection_struct *conn = req->conn; SMB_BIG_UINT count,offset; NTSTATUS status; files_struct *fsp; @@ -4517,8 +4489,9 @@ void reply_unlock(connection_struct *conn, struct smb_request *req) conn POINTER CAN BE NULL HERE ! ****************************************************************************/ -void reply_tdis(connection_struct *conn, struct smb_request *req) +void reply_tdis(struct smb_request *req) { + connection_struct *conn = req->conn; START_PROFILE(SMBtdis); if (!conn) { @@ -4531,6 +4504,7 @@ void reply_tdis(connection_struct *conn, struct smb_request *req) conn->used = False; close_cnum(conn,req->vuid); + req->conn = NULL; reply_outbuf(req, 0, 0); END_PROFILE(SMBtdis); @@ -4542,8 +4516,9 @@ void reply_tdis(connection_struct *conn, struct smb_request *req) conn POINTER CAN BE NULL HERE ! ****************************************************************************/ -void reply_echo(connection_struct *conn, struct smb_request *req) +void reply_echo(struct smb_request *req) { + connection_struct *conn = req->conn; int smb_reverb; int seq_num; unsigned int data_len = smb_buflen(req->inbuf); @@ -4581,8 +4556,10 @@ void reply_echo(connection_struct *conn, struct smb_request *req) SSVAL(req->outbuf,smb_vwv0,seq_num); show_msg((char *)req->outbuf); - if (!send_smb(smbd_server_fd(),(char *)req->outbuf)) - exit_server_cleanly("reply_echo: send_smb failed."); + if (!srv_send_smb(smbd_server_fd(), + (char *)req->outbuf, + IS_CONN_ENCRYPTED(conn)||req->encrypted)) + exit_server_cleanly("reply_echo: srv_send_smb failed."); } DEBUG(3,("echo %d times\n", smb_reverb)); @@ -4599,8 +4576,9 @@ void reply_echo(connection_struct *conn, struct smb_request *req) Reply to a printopen. ****************************************************************************/ -void reply_printopen(connection_struct *conn, struct smb_request *req) +void reply_printopen(struct smb_request *req) { + connection_struct *conn = req->conn; files_struct *fsp; NTSTATUS status; @@ -4641,8 +4619,9 @@ void reply_printopen(connection_struct *conn, struct smb_request *req) Reply to a printclose. ****************************************************************************/ -void reply_printclose(connection_struct *conn, struct smb_request *req) +void reply_printclose(struct smb_request *req) { + connection_struct *conn = req->conn; files_struct *fsp; NTSTATUS status; @@ -4686,8 +4665,9 @@ void reply_printclose(connection_struct *conn, struct smb_request *req) Reply to a printqueue. ****************************************************************************/ -void reply_printqueue(connection_struct *conn, struct smb_request *req) +void reply_printqueue(struct smb_request *req) { + connection_struct *conn = req->conn; int max_count; int start_index; @@ -4778,8 +4758,9 @@ void reply_printqueue(connection_struct *conn, struct smb_request *req) Reply to a printwrite. ****************************************************************************/ -void reply_printwrite(connection_struct *conn, struct smb_request *req) +void reply_printwrite(struct smb_request *req) { + connection_struct *conn = req->conn; int numtowrite; char *data; files_struct *fsp; @@ -4837,8 +4818,9 @@ void reply_printwrite(connection_struct *conn, struct smb_request *req) Reply to a mkdir. ****************************************************************************/ -void reply_mkdir(connection_struct *conn, struct smb_request *req) +void reply_mkdir(struct smb_request *req) { + connection_struct *conn = req->conn; char *directory = NULL; NTSTATUS status; SMB_STRUCT_STAT sbuf; @@ -4885,7 +4867,7 @@ void reply_mkdir(connection_struct *conn, struct smb_request *req) return; } - status = create_directory(conn, directory); + status = create_directory(conn, req, directory); DEBUG(5, ("create_directory returned %s\n", nt_errstr(status))); @@ -4927,7 +4909,8 @@ static bool recursive_rmdir(TALLOC_CTX *ctx, const char *dname = NULL; bool ret = True; long offset = 0; - struct smb_Dir *dir_hnd = OpenDir(conn, directory, NULL, 0); + struct smb_Dir *dir_hnd = OpenDir(talloc_tos(), conn, directory, + NULL, 0); if(dir_hnd == NULL) return False; @@ -4975,7 +4958,7 @@ static bool recursive_rmdir(TALLOC_CTX *ctx, } TALLOC_FREE(fullname); } - CloseDir(dir_hnd); + TALLOC_FREE(dir_hnd); return ret; } @@ -5023,7 +5006,8 @@ NTSTATUS rmdir_internals(TALLOC_CTX *ctx, */ const char *dname; long dirpos = 0; - struct smb_Dir *dir_hnd = OpenDir(conn, directory, NULL, 0); + struct smb_Dir *dir_hnd = OpenDir(talloc_tos(), conn, + directory, NULL, 0); if(dir_hnd == NULL) { errno = ENOTEMPTY; @@ -5036,7 +5020,7 @@ NTSTATUS rmdir_internals(TALLOC_CTX *ctx, if (!is_visible_file(conn, directory, dname, &st, False)) continue; if(!IS_VETO_PATH(conn, dname)) { - CloseDir(dir_hnd); + TALLOC_FREE(dir_hnd); errno = ENOTEMPTY; goto err; } @@ -5081,7 +5065,7 @@ NTSTATUS rmdir_internals(TALLOC_CTX *ctx, } TALLOC_FREE(fullname); } - CloseDir(dir_hnd); + TALLOC_FREE(dir_hnd); /* Retry the rmdir */ ret = SMB_VFS_RMDIR(conn,directory); } @@ -5105,8 +5089,9 @@ NTSTATUS rmdir_internals(TALLOC_CTX *ctx, Reply to a rmdir. ****************************************************************************/ -void reply_rmdir(connection_struct *conn, struct smb_request *req) +void reply_rmdir(struct smb_request *req) { + connection_struct *conn = req->conn; char *directory = NULL; SMB_STRUCT_STAT sbuf; NTSTATUS status; @@ -5512,7 +5497,7 @@ NTSTATUS rename_internals_fsp(connection_struct *conn, /* Ensure we have a valid stat struct for the source. */ if (fsp->fh->fd != -1) { - if (SMB_VFS_FSTAT(fsp,fsp->fh->fd,&sbuf) == -1) { + if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) { return map_nt_error_from_unix(errno); } } else { @@ -5535,7 +5520,7 @@ NTSTATUS rename_internals_fsp(connection_struct *conn, return NT_STATUS_ACCESS_DENIED; } - lck = get_share_mode_lock(NULL, fsp->file_id, NULL, NULL); + lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL); /* * We have the file open ourselves, so not being able to get the @@ -5776,7 +5761,7 @@ NTSTATUS rename_internals(TALLOC_CTX *ctx, return status; } - dir_hnd = OpenDir(conn, directory, mask, attrs); + dir_hnd = OpenDir(talloc_tos(), conn, directory, mask, attrs); if (dir_hnd == NULL) { return map_nt_error_from_unix(errno); } @@ -5876,7 +5861,7 @@ NTSTATUS rename_internals(TALLOC_CTX *ctx, TALLOC_FREE(fname); TALLOC_FREE(destname); } - CloseDir(dir_hnd); + TALLOC_FREE(dir_hnd); if (count == 0 && NT_STATUS_IS_OK(status)) { status = map_nt_error_from_unix(errno); @@ -5889,8 +5874,9 @@ NTSTATUS rename_internals(TALLOC_CTX *ctx, Reply to a mv. ****************************************************************************/ -void reply_mv(connection_struct *conn, struct smb_request *req) +void reply_mv(struct smb_request *req) { + connection_struct *conn = req->conn; char *name = NULL; char *newname = NULL; char *p; @@ -6078,7 +6064,7 @@ NTSTATUS copy_file(TALLOC_CTX *ctx, } if ((ofun&3) == 1) { - if(SMB_VFS_LSEEK(fsp2,fsp2->fh->fd,0,SEEK_END) == -1) { + if(SMB_VFS_LSEEK(fsp2,0,SEEK_END) == -1) { DEBUG(0,("copy_file: error - vfs lseek returned error %s\n", strerror(errno) )); /* * Stop the copy from occurring. @@ -6120,8 +6106,9 @@ NTSTATUS copy_file(TALLOC_CTX *ctx, Reply to a file copy. ****************************************************************************/ -void reply_copy(connection_struct *conn, struct smb_request *req) +void reply_copy(struct smb_request *req) { + connection_struct *conn = req->conn; char *name = NULL; char *newname = NULL; char *directory = NULL; @@ -6348,7 +6335,7 @@ void reply_copy(connection_struct *conn, struct smb_request *req) return; } - dir_hnd = OpenDir(conn, directory, mask, 0); + dir_hnd = OpenDir(talloc_tos(), conn, directory, mask, 0); if (dir_hnd == NULL) { status = map_nt_error_from_unix(errno); reply_nterror(req, status); @@ -6380,6 +6367,7 @@ void reply_copy(connection_struct *conn, struct smb_request *req) directory, dname); if (!fname) { + TALLOC_FREE(dir_hnd); reply_nterror(req, NT_STATUS_NO_MEMORY); END_PROFILE(SMBcopy); return; @@ -6390,6 +6378,7 @@ void reply_copy(connection_struct *conn, struct smb_request *req) continue; } if (!destname) { + TALLOC_FREE(dir_hnd); reply_nterror(req, NT_STATUS_NO_MEMORY); END_PROFILE(SMBcopy); return; @@ -6397,6 +6386,7 @@ void reply_copy(connection_struct *conn, struct smb_request *req) status = check_name(conn, fname); if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(dir_hnd); reply_nterror(req, status); END_PROFILE(SMBcopy); return; @@ -6404,6 +6394,7 @@ void reply_copy(connection_struct *conn, struct smb_request *req) status = check_name(conn, destname); if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(dir_hnd); reply_nterror(req, status); END_PROFILE(SMBcopy); return; @@ -6419,7 +6410,7 @@ void reply_copy(connection_struct *conn, struct smb_request *req) TALLOC_FREE(fname); TALLOC_FREE(destname); } - CloseDir(dir_hnd); + TALLOC_FREE(dir_hnd); } if (count == 0) { @@ -6583,8 +6574,9 @@ SMB_BIG_UINT get_lock_offset( char *data, int data_offset, bool large_file_forma Reply to a lockingX request. ****************************************************************************/ -void reply_lockingX(connection_struct *conn, struct smb_request *req) +void reply_lockingX(struct smb_request *req) { + connection_struct *conn = req->conn; files_struct *fsp; unsigned char locktype; unsigned char oplocklevel; @@ -6858,8 +6850,7 @@ void reply_lockingX(connection_struct *conn, struct smb_request *req) * onto the blocking lock queue. */ if(push_blocking_lock_request(br_lck, - (char *)req->inbuf, - smb_len(req->inbuf)+4, + req, fsp, lock_timeout, i, @@ -6942,7 +6933,7 @@ void reply_lockingX(connection_struct *conn, struct smb_request *req) please contact vl@samba.org ****************************************************************************/ -void reply_readbmpx(connection_struct *conn, struct smb_request *req) +void reply_readbmpx(struct smb_request *req) { START_PROFILE(SMBreadBmpx); reply_doserror(req, ERRSRV, ERRuseSTD); @@ -6956,7 +6947,7 @@ void reply_readbmpx(connection_struct *conn, struct smb_request *req) please contact vl@samba.org ****************************************************************************/ -void reply_readbs(connection_struct *conn, struct smb_request *req) +void reply_readbs(struct smb_request *req) { START_PROFILE(SMBreadBs); reply_doserror(req, ERRSRV, ERRuseSTD); @@ -6968,8 +6959,9 @@ void reply_readbs(connection_struct *conn, struct smb_request *req) Reply to a SMBsetattrE. ****************************************************************************/ -void reply_setattrE(connection_struct *conn, struct smb_request *req) +void reply_setattrE(struct smb_request *req) { + connection_struct *conn = req->conn; struct timespec ts[2]; files_struct *fsp; @@ -7046,7 +7038,7 @@ void reply_setattrE(connection_struct *conn, struct smb_request *req) please contact vl@samba.org ****************************************************************************/ -void reply_writebmpx(connection_struct *conn, struct smb_request *req) +void reply_writebmpx(struct smb_request *req) { START_PROFILE(SMBwriteBmpx); reply_doserror(req, ERRSRV, ERRuseSTD); @@ -7060,7 +7052,7 @@ void reply_writebmpx(connection_struct *conn, struct smb_request *req) please contact vl@samba.org ****************************************************************************/ -void reply_writebs(connection_struct *conn, struct smb_request *req) +void reply_writebs(struct smb_request *req) { START_PROFILE(SMBwriteBs); reply_doserror(req, ERRSRV, ERRuseSTD); @@ -7072,8 +7064,9 @@ void reply_writebs(connection_struct *conn, struct smb_request *req) Reply to a SMBgetattrE. ****************************************************************************/ -void reply_getattrE(connection_struct *conn, struct smb_request *req) +void reply_getattrE(struct smb_request *req) { + connection_struct *conn = req->conn; SMB_STRUCT_STAT sbuf; int mode; files_struct *fsp; diff --git a/source3/smbd/seal.c b/source3/smbd/seal.c new file mode 100644 index 0000000000..ea017e08d8 --- /dev/null +++ b/source3/smbd/seal.c @@ -0,0 +1,728 @@ +/* + Unix SMB/CIFS implementation. + SMB Transport encryption (sealing) code - server code. + Copyright (C) Jeremy Allison 2007. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" + +/****************************************************************************** + Server side encryption. +******************************************************************************/ + +/****************************************************************************** + Global server state. +******************************************************************************/ + +struct smb_srv_trans_enc_ctx { + struct smb_trans_enc_state *es; + AUTH_NTLMSSP_STATE *auth_ntlmssp_state; /* Must be kept in sync with pointer in ec->ntlmssp_state. */ +}; + +static struct smb_srv_trans_enc_ctx *partial_srv_trans_enc_ctx; +static struct smb_srv_trans_enc_ctx *srv_trans_enc_ctx; + +/****************************************************************************** + Return global enc context - this must change if we ever do multiple contexts. +******************************************************************************/ + +uint16_t srv_enc_ctx(void) +{ + return srv_trans_enc_ctx->es->enc_ctx_num; +} + +/****************************************************************************** + Is this an incoming encrypted packet ? +******************************************************************************/ + +bool is_encrypted_packet(const uint8_t *inbuf) +{ + NTSTATUS status; + uint16_t enc_num; + + /* Ignore non-session messages or non 0xFF'E' messages. */ + if(CVAL(inbuf,0) || !(inbuf[4] == 0xFF && inbuf[5] == 'E')) { + return false; + } + + status = get_enc_ctx_num(inbuf, &enc_num); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + /* Encrypted messages are 0xFF'E'<ctx> */ + if (srv_trans_enc_ctx && enc_num == srv_enc_ctx()) { + return true; + } + return false; +} + +/****************************************************************************** + Create an auth_ntlmssp_state and ensure pointer copy is correct. +******************************************************************************/ + +static NTSTATUS make_auth_ntlmssp(struct smb_srv_trans_enc_ctx *ec) +{ + NTSTATUS status = auth_ntlmssp_start(&ec->auth_ntlmssp_state); + if (!NT_STATUS_IS_OK(status)) { + return nt_status_squash(status); + } + + /* + * We must remember to update the pointer copy for the common + * functions after any auth_ntlmssp_start/auth_ntlmssp_end. + */ + ec->es->s.ntlmssp_state = ec->auth_ntlmssp_state->ntlmssp_state; + return status; +} + +/****************************************************************************** + Destroy an auth_ntlmssp_state and ensure pointer copy is correct. +******************************************************************************/ + +static void destroy_auth_ntlmssp(struct smb_srv_trans_enc_ctx *ec) +{ + /* + * We must remember to update the pointer copy for the common + * functions after any auth_ntlmssp_start/auth_ntlmssp_end. + */ + + if (ec->auth_ntlmssp_state) { + auth_ntlmssp_end(&ec->auth_ntlmssp_state); + /* The auth_ntlmssp_end killed this already. */ + ec->es->s.ntlmssp_state = NULL; + } +} + +#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5) + +/****************************************************************************** + Import a name. +******************************************************************************/ + +static NTSTATUS get_srv_gss_creds(const char *service, + const char *name, + gss_cred_usage_t cred_type, + gss_cred_id_t *p_srv_cred) +{ + OM_uint32 ret; + OM_uint32 min; + gss_name_t srv_name; + gss_buffer_desc input_name; + char *host_princ_s = NULL; + NTSTATUS status = NT_STATUS_OK; + + gss_OID_desc nt_hostbased_service = + {10, CONST_DISCARD(char *,"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04")}; + + asprintf(&host_princ_s, "%s@%s", service, name); + if (host_princ_s == NULL) { + return NT_STATUS_NO_MEMORY; + } + + input_name.value = host_princ_s; + input_name.length = strlen(host_princ_s) + 1; + + ret = gss_import_name(&min, + &input_name, + &nt_hostbased_service, + &srv_name); + + DEBUG(10,("get_srv_gss_creds: imported name %s\n", + host_princ_s )); + + if (ret != GSS_S_COMPLETE) { + SAFE_FREE(host_princ_s); + return map_nt_error_from_gss(ret, min); + } + + /* + * We're accessing the krb5.keytab file here. + * ensure we have permissions to do so. + */ + become_root(); + + ret = gss_acquire_cred(&min, + srv_name, + GSS_C_INDEFINITE, + GSS_C_NULL_OID_SET, + cred_type, + p_srv_cred, + NULL, + NULL); + unbecome_root(); + + if (ret != GSS_S_COMPLETE) { + ADS_STATUS adss = ADS_ERROR_GSS(ret, min); + DEBUG(10,("get_srv_gss_creds: gss_acquire_cred failed with %s\n", + ads_errstr(adss))); + status = map_nt_error_from_gss(ret, min); + } + + SAFE_FREE(host_princ_s); + gss_release_name(&min, &srv_name); + return status; +} + +/****************************************************************************** + Create a gss state. + Try and get the cifs/server@realm principal first, then fall back to + host/server@realm. +******************************************************************************/ + +static NTSTATUS make_auth_gss(struct smb_srv_trans_enc_ctx *ec) +{ + NTSTATUS status; + gss_cred_id_t srv_cred; + fstring fqdn; + + name_to_fqdn(fqdn, global_myname()); + strlower_m(fqdn); + + status = get_srv_gss_creds("cifs", fqdn, GSS_C_ACCEPT, &srv_cred); + if (!NT_STATUS_IS_OK(status)) { + status = get_srv_gss_creds("host", fqdn, GSS_C_ACCEPT, &srv_cred); + if (!NT_STATUS_IS_OK(status)) { + return nt_status_squash(status); + } + } + + ec->es->s.gss_state = SMB_MALLOC_P(struct smb_tran_enc_state_gss); + if (!ec->es->s.gss_state) { + OM_uint32 min; + gss_release_cred(&min, &srv_cred); + return NT_STATUS_NO_MEMORY; + } + ZERO_STRUCTP(ec->es->s.gss_state); + ec->es->s.gss_state->creds = srv_cred; + + /* No context yet. */ + ec->es->s.gss_state->gss_ctx = GSS_C_NO_CONTEXT; + + return NT_STATUS_OK; +} +#endif + +/****************************************************************************** + Shutdown a server encryption context. +******************************************************************************/ + +static void srv_free_encryption_context(struct smb_srv_trans_enc_ctx **pp_ec) +{ + struct smb_srv_trans_enc_ctx *ec = *pp_ec; + + if (!ec) { + return; + } + + if (ec->es) { + switch (ec->es->smb_enc_type) { + case SMB_TRANS_ENC_NTLM: + destroy_auth_ntlmssp(ec); + break; +#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5) + case SMB_TRANS_ENC_GSS: + break; +#endif + } + common_free_encryption_state(&ec->es); + } + + SAFE_FREE(ec); + *pp_ec = NULL; +} + +/****************************************************************************** + Create a server encryption context. +******************************************************************************/ + +static NTSTATUS make_srv_encryption_context(enum smb_trans_enc_type smb_enc_type, struct smb_srv_trans_enc_ctx **pp_ec) +{ + struct smb_srv_trans_enc_ctx *ec; + + *pp_ec = NULL; + + ec = SMB_MALLOC_P(struct smb_srv_trans_enc_ctx); + if (!ec) { + return NT_STATUS_NO_MEMORY; + } + ZERO_STRUCTP(partial_srv_trans_enc_ctx); + ec->es = SMB_MALLOC_P(struct smb_trans_enc_state); + if (!ec->es) { + SAFE_FREE(ec); + return NT_STATUS_NO_MEMORY; + } + ZERO_STRUCTP(ec->es); + ec->es->smb_enc_type = smb_enc_type; + switch (smb_enc_type) { + case SMB_TRANS_ENC_NTLM: + { + NTSTATUS status = make_auth_ntlmssp(ec); + if (!NT_STATUS_IS_OK(status)) { + srv_free_encryption_context(&ec); + return status; + } + } + break; + +#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5) + case SMB_TRANS_ENC_GSS: + /* Acquire our credentials by calling gss_acquire_cred here. */ + { + NTSTATUS status = make_auth_gss(ec); + if (!NT_STATUS_IS_OK(status)) { + srv_free_encryption_context(&ec); + return status; + } + } + break; +#endif + default: + srv_free_encryption_context(&ec); + return NT_STATUS_INVALID_PARAMETER; + } + *pp_ec = ec; + return NT_STATUS_OK; +} + +/****************************************************************************** + Free an encryption-allocated buffer. +******************************************************************************/ + +void srv_free_enc_buffer(char *buf) +{ + /* We know this is an smb buffer, and we + * didn't malloc, only copy, for a keepalive, + * so ignore non-session messages. */ + + if(CVAL(buf,0)) { + return; + } + + if (srv_trans_enc_ctx) { + common_free_enc_buffer(srv_trans_enc_ctx->es, buf); + } +} + +/****************************************************************************** + Decrypt an incoming buffer. +******************************************************************************/ + +NTSTATUS srv_decrypt_buffer(char *buf) +{ + /* Ignore non-session messages. */ + if(CVAL(buf,0)) { + return NT_STATUS_OK; + } + + if (srv_trans_enc_ctx) { + return common_decrypt_buffer(srv_trans_enc_ctx->es, buf); + } + + return NT_STATUS_OK; +} + +/****************************************************************************** + Encrypt an outgoing buffer. Return the encrypted pointer in buf_out. +******************************************************************************/ + +NTSTATUS srv_encrypt_buffer(char *buf, char **buf_out) +{ + *buf_out = buf; + + /* Ignore non-session messages. */ + if(CVAL(buf,0)) { + return NT_STATUS_OK; + } + + if (srv_trans_enc_ctx) { + return common_encrypt_buffer(srv_trans_enc_ctx->es, buf, buf_out); + } + /* Not encrypting. */ + return NT_STATUS_OK; +} + +/****************************************************************************** + Do the gss encryption negotiation. Parameters are in/out. + Until success we do everything on the partial enc ctx. +******************************************************************************/ + +#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5) +static NTSTATUS srv_enc_spnego_gss_negotiate(unsigned char **ppdata, size_t *p_data_size, DATA_BLOB secblob) +{ + OM_uint32 ret; + OM_uint32 min; + OM_uint32 flags = 0; + gss_buffer_desc in_buf, out_buf; + struct smb_tran_enc_state_gss *gss_state; + DATA_BLOB auth_reply = data_blob_null; + DATA_BLOB response = data_blob_null; + NTSTATUS status; + + if (!partial_srv_trans_enc_ctx) { + status = make_srv_encryption_context(SMB_TRANS_ENC_GSS, &partial_srv_trans_enc_ctx); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + gss_state = partial_srv_trans_enc_ctx->es->s.gss_state; + + in_buf.value = secblob.data; + in_buf.length = secblob.length; + + out_buf.value = NULL; + out_buf.length = 0; + + become_root(); + + ret = gss_accept_sec_context(&min, + &gss_state->gss_ctx, + gss_state->creds, + &in_buf, + GSS_C_NO_CHANNEL_BINDINGS, + NULL, + NULL, /* Ignore oids. */ + &out_buf, /* To return. */ + &flags, + NULL, /* Ingore time. */ + NULL); /* Ignore delegated creds. */ + unbecome_root(); + + status = gss_err_to_ntstatus(ret, min); + if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED) { + return status; + } + + /* Ensure we've got sign+seal available. */ + if (ret == GSS_S_COMPLETE) { + if ((flags & (GSS_C_INTEG_FLAG|GSS_C_CONF_FLAG|GSS_C_REPLAY_FLAG|GSS_C_SEQUENCE_FLAG)) != + (GSS_C_INTEG_FLAG|GSS_C_CONF_FLAG|GSS_C_REPLAY_FLAG|GSS_C_SEQUENCE_FLAG)) { + DEBUG(0,("srv_enc_spnego_gss_negotiate: quality of service not good enough " + "for SMB sealing.\n")); + gss_release_buffer(&min, &out_buf); + return NT_STATUS_ACCESS_DENIED; + } + } + + auth_reply = data_blob(out_buf.value, out_buf.length); + gss_release_buffer(&min, &out_buf); + + /* Wrap in SPNEGO. */ + response = spnego_gen_auth_response(&auth_reply, status, OID_KERBEROS5); + data_blob_free(&auth_reply); + + SAFE_FREE(*ppdata); + *ppdata = response.data; + *p_data_size = response.length; + + return status; +} +#endif + +/****************************************************************************** + Do the NTLM SPNEGO (or raw) encryption negotiation. Parameters are in/out. + Until success we do everything on the partial enc ctx. +******************************************************************************/ + +static NTSTATUS srv_enc_ntlm_negotiate(unsigned char **ppdata, size_t *p_data_size, DATA_BLOB secblob, bool spnego_wrap) +{ + NTSTATUS status; + DATA_BLOB chal = data_blob_null; + DATA_BLOB response = data_blob_null; + + status = make_srv_encryption_context(SMB_TRANS_ENC_NTLM, &partial_srv_trans_enc_ctx); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = auth_ntlmssp_update(partial_srv_trans_enc_ctx->auth_ntlmssp_state, secblob, &chal); + + /* status here should be NT_STATUS_MORE_PROCESSING_REQUIRED + * for success ... */ + + if (spnego_wrap) { + response = spnego_gen_auth_response(&chal, status, OID_NTLMSSP); + data_blob_free(&chal); + } else { + /* Return the raw blob. */ + response = chal; + } + + SAFE_FREE(*ppdata); + *ppdata = response.data; + *p_data_size = response.length; + return status; +} + +/****************************************************************************** + Do the SPNEGO encryption negotiation. Parameters are in/out. + Based off code in smbd/sesssionsetup.c + Until success we do everything on the partial enc ctx. +******************************************************************************/ + +static NTSTATUS srv_enc_spnego_negotiate(connection_struct *conn, + unsigned char **ppdata, + size_t *p_data_size, + unsigned char **pparam, + size_t *p_param_size) +{ + NTSTATUS status; + DATA_BLOB blob = data_blob_null; + DATA_BLOB secblob = data_blob_null; + bool got_kerberos_mechanism = false; + + blob = data_blob_const(*ppdata, *p_data_size); + + status = parse_spnego_mechanisms(blob, &secblob, &got_kerberos_mechanism); + if (!NT_STATUS_IS_OK(status)) { + return nt_status_squash(status); + } + + /* We should have no partial context at this point. */ + + srv_free_encryption_context(&partial_srv_trans_enc_ctx); + +#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5) + if (got_kerberos_mechanism && lp_use_kerberos_keytab() ) { + status = srv_enc_spnego_gss_negotiate(ppdata, p_data_size, secblob); + } else +#endif + { + status = srv_enc_ntlm_negotiate(ppdata, p_data_size, secblob, true); + } + + data_blob_free(&secblob); + + if (!NT_STATUS_EQUAL(status,NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(status)) { + srv_free_encryption_context(&partial_srv_trans_enc_ctx); + return nt_status_squash(status); + } + + if (NT_STATUS_IS_OK(status)) { + /* Return the context we're using for this encryption state. */ + if (!(*pparam = SMB_MALLOC_ARRAY(unsigned char, 2))) { + return NT_STATUS_NO_MEMORY; + } + SSVAL(*pparam,0,partial_srv_trans_enc_ctx->es->enc_ctx_num); + *p_param_size = 2; + } + + return status; +} + +/****************************************************************************** + Complete a SPNEGO encryption negotiation. Parameters are in/out. + We only get this for a NTLM auth second stage. +******************************************************************************/ + +static NTSTATUS srv_enc_spnego_ntlm_auth(connection_struct *conn, + unsigned char **ppdata, + size_t *p_data_size, + unsigned char **pparam, + size_t *p_param_size) +{ + NTSTATUS status; + DATA_BLOB blob = data_blob_null; + DATA_BLOB auth = data_blob_null; + DATA_BLOB auth_reply = data_blob_null; + DATA_BLOB response = data_blob_null; + struct smb_srv_trans_enc_ctx *ec = partial_srv_trans_enc_ctx; + + /* We must have a partial context here. */ + + if (!ec || !ec->es || ec->auth_ntlmssp_state == NULL || ec->es->smb_enc_type != SMB_TRANS_ENC_NTLM) { + srv_free_encryption_context(&partial_srv_trans_enc_ctx); + return NT_STATUS_INVALID_PARAMETER; + } + + blob = data_blob_const(*ppdata, *p_data_size); + if (!spnego_parse_auth(blob, &auth)) { + srv_free_encryption_context(&partial_srv_trans_enc_ctx); + return NT_STATUS_INVALID_PARAMETER; + } + + status = auth_ntlmssp_update(ec->auth_ntlmssp_state, auth, &auth_reply); + data_blob_free(&auth); + + response = spnego_gen_auth_response(&auth_reply, status, OID_NTLMSSP); + data_blob_free(&auth_reply); + + if (NT_STATUS_IS_OK(status)) { + /* Return the context we're using for this encryption state. */ + if (!(*pparam = SMB_MALLOC_ARRAY(unsigned char, 2))) { + return NT_STATUS_NO_MEMORY; + } + SSVAL(*pparam,0,ec->es->enc_ctx_num); + *p_param_size = 2; + } + + SAFE_FREE(*ppdata); + *ppdata = response.data; + *p_data_size = response.length; + return status; +} + +/****************************************************************************** + Raw NTLM encryption negotiation. Parameters are in/out. + This function does both steps. +******************************************************************************/ + +static NTSTATUS srv_enc_raw_ntlm_auth(connection_struct *conn, + unsigned char **ppdata, + size_t *p_data_size, + unsigned char **pparam, + size_t *p_param_size) +{ + NTSTATUS status; + DATA_BLOB blob = data_blob_const(*ppdata, *p_data_size); + DATA_BLOB response = data_blob_null; + struct smb_srv_trans_enc_ctx *ec; + + if (!partial_srv_trans_enc_ctx) { + /* This is the initial step. */ + status = srv_enc_ntlm_negotiate(ppdata, p_data_size, blob, false); + if (!NT_STATUS_EQUAL(status,NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(status)) { + srv_free_encryption_context(&partial_srv_trans_enc_ctx); + return nt_status_squash(status); + } + return status; + } + + ec = partial_srv_trans_enc_ctx; + if (!ec || !ec->es || ec->auth_ntlmssp_state == NULL || ec->es->smb_enc_type != SMB_TRANS_ENC_NTLM) { + srv_free_encryption_context(&partial_srv_trans_enc_ctx); + return NT_STATUS_INVALID_PARAMETER; + } + + /* Second step. */ + status = auth_ntlmssp_update(partial_srv_trans_enc_ctx->auth_ntlmssp_state, blob, &response); + + if (NT_STATUS_IS_OK(status)) { + /* Return the context we're using for this encryption state. */ + if (!(*pparam = SMB_MALLOC_ARRAY(unsigned char, 2))) { + return NT_STATUS_NO_MEMORY; + } + SSVAL(*pparam,0,ec->es->enc_ctx_num); + *p_param_size = 2; + } + + /* Return the raw blob. */ + SAFE_FREE(*ppdata); + *ppdata = response.data; + *p_data_size = response.length; + return status; +} + +/****************************************************************************** + Do the SPNEGO encryption negotiation. Parameters are in/out. +******************************************************************************/ + +NTSTATUS srv_request_encryption_setup(connection_struct *conn, + unsigned char **ppdata, + size_t *p_data_size, + unsigned char **pparam, + size_t *p_param_size) +{ + unsigned char *pdata = *ppdata; + + SAFE_FREE(*pparam); + *p_param_size = 0; + + if (*p_data_size < 1) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (pdata[0] == ASN1_APPLICATION(0)) { + /* its a negTokenTarg packet */ + return srv_enc_spnego_negotiate(conn, ppdata, p_data_size, pparam, p_param_size); + } + + if (pdata[0] == ASN1_CONTEXT(1)) { + /* It's an auth packet */ + return srv_enc_spnego_ntlm_auth(conn, ppdata, p_data_size, pparam, p_param_size); + } + + /* Maybe it's a raw unwrapped auth ? */ + if (*p_data_size < 7) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (strncmp((char *)pdata, "NTLMSSP", 7) == 0) { + return srv_enc_raw_ntlm_auth(conn, ppdata, p_data_size, pparam, p_param_size); + } + + DEBUG(1,("srv_request_encryption_setup: Unknown packet\n")); + + return NT_STATUS_LOGON_FAILURE; +} + +/****************************************************************************** + Negotiation was successful - turn on server-side encryption. +******************************************************************************/ + +static NTSTATUS check_enc_good(struct smb_srv_trans_enc_ctx *ec) +{ + if (!ec || !ec->es) { + return NT_STATUS_LOGON_FAILURE; + } + + if (ec->es->smb_enc_type == SMB_TRANS_ENC_NTLM) { + if ((ec->es->s.ntlmssp_state->neg_flags & (NTLMSSP_NEGOTIATE_SIGN|NTLMSSP_NEGOTIATE_SEAL)) != + (NTLMSSP_NEGOTIATE_SIGN|NTLMSSP_NEGOTIATE_SEAL)) { + return NT_STATUS_INVALID_PARAMETER; + } + } + /* Todo - check gssapi case. */ + + return NT_STATUS_OK; +} + +/****************************************************************************** + Negotiation was successful - turn on server-side encryption. +******************************************************************************/ + +NTSTATUS srv_encryption_start(connection_struct *conn) +{ + NTSTATUS status; + + /* Check that we are really doing sign+seal. */ + status = check_enc_good(partial_srv_trans_enc_ctx); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + /* Throw away the context we're using currently (if any). */ + srv_free_encryption_context(&srv_trans_enc_ctx); + + /* Steal the partial pointer. Deliberate shallow copy. */ + srv_trans_enc_ctx = partial_srv_trans_enc_ctx; + srv_trans_enc_ctx->es->enc_on = true; + + partial_srv_trans_enc_ctx = NULL; + + DEBUG(1,("srv_encryption_start: context negotiated\n")); + return NT_STATUS_OK; +} + +/****************************************************************************** + Shutdown all server contexts. +******************************************************************************/ + +void server_encryption_shutdown(void) +{ + srv_free_encryption_context(&partial_srv_trans_enc_ctx); + srv_free_encryption_context(&srv_trans_enc_ctx); +} diff --git a/source3/smbd/sec_ctx.c b/source3/smbd/sec_ctx.c index 6edcc36764..0f307f6a64 100644 --- a/source3/smbd/sec_ctx.c +++ b/source3/smbd/sec_ctx.c @@ -33,6 +33,23 @@ static struct sec_ctx sec_ctx_stack[MAX_SEC_CTX_DEPTH + 1]; static int sec_ctx_stack_ndx; /**************************************************************************** + Are two UNIX tokens equal ? +****************************************************************************/ + +bool unix_token_equal(const UNIX_USER_TOKEN *t1, const UNIX_USER_TOKEN *t2) +{ + if (t1->uid != t2->uid || t1->gid != t2->gid || + t1->ngroups != t2->ngroups) { + return false; + } + if (memcmp(t1->groups, t2->groups, + t1->ngroups*sizeof(gid_t)) != 0) { + return false; + } + return true; +} + +/**************************************************************************** Become the specified uid. ****************************************************************************/ diff --git a/source3/smbd/server.c b/source3/smbd/server.c index 41d036a8b9..db241103ed 100644 --- a/source3/smbd/server.c +++ b/source3/smbd/server.c @@ -27,12 +27,6 @@ static_decl_rpc; static int am_parent = 1; -/* the last message the was processed */ -int last_message = -1; - -/* a useful macro to debug the last message processed */ -#define LAST_MESSAGE() smb_fn_name(last_message) - extern struct auth_context *negprot_global_auth_context; extern SIG_ATOMIC_T got_sig_term; extern SIG_ATOMIC_T reload_after_sighup; @@ -86,6 +80,19 @@ struct messaging_context *smbd_messaging_context(void) return ctx; } +struct memcache *smbd_memcache(void) +{ + static struct memcache *cache; + + if (!cache + && !(cache = memcache_init(NULL, + lp_max_stat_cache_size()*1024))) { + + smb_panic("Could not init smbd memcache"); + } + return cache; +} + /******************************************************************* What to do when smb.conf is updated. ********************************************************************/ @@ -261,10 +268,20 @@ static void add_child_pid(pid_t pid) num_children += 1; } -static void remove_child_pid(pid_t pid) +static void remove_child_pid(pid_t pid, bool unclean_shutdown) { struct child_pid *child; + if (unclean_shutdown) { + /* a child terminated uncleanly so tickle all processes to see + if they can grab any of the pending locks + */ + messaging_send_buf(smbd_messaging_context(), procid_self(), + MSG_SMB_BRL_VALIDATE, NULL, 0); + message_send_all(smbd_messaging_context(), + MSG_SMB_UNLOCK, NULL, 0, NULL); + } + if (lp_max_smbd_processes() == 0) { /* Don't bother with the child list if we don't care anyway */ return; @@ -383,8 +400,11 @@ static bool open_sockets_smbd(bool is_daemon, bool interactive, const char *smb_ } s = fd_listenset[num_sockets] = - open_socket_in(SOCK_STREAM, port, 0, - ifss, True); + open_socket_in(SOCK_STREAM, + port, + num_sockets == 0 ? 0 : 2, + ifss, + true); if(s == -1) { continue; } @@ -460,8 +480,11 @@ static bool open_sockets_smbd(bool is_daemon, bool interactive, const char *smb_ continue; } - s = open_socket_in(SOCK_STREAM, port, 0, - &ss, true); + s = open_socket_in(SOCK_STREAM, + port, + num_sockets == 0 ? 0 : 2, + &ss, + true); if (s == -1) { continue; } @@ -547,10 +570,27 @@ static bool open_sockets_smbd(bool is_daemon, bool interactive, const char *smb_ if (got_sig_cld) { pid_t pid; + int status; + got_sig_cld = False; - while ((pid = sys_waitpid(-1, NULL, WNOHANG)) > 0) { - remove_child_pid(pid); + while ((pid = sys_waitpid(-1, &status, WNOHANG)) > 0) { + bool unclean_shutdown = False; + + /* If the child terminated normally, assume + it was an unclean shutdown unless the + status is 0 + */ + if (WIFEXITED(status)) { + unclean_shutdown = WEXITSTATUS(status); + } + /* If the child terminated due to a signal + we always assume it was unclean. + */ + if (WIFSIGNALED(status)) { + unclean_shutdown = True; + } + remove_child_pid(pid, unclean_shutdown); } } @@ -590,6 +630,15 @@ static bool open_sockets_smbd(bool is_daemon, bool interactive, const char *smb_ continue; } + + + /* If the idle timeout fired and we don't have any connected + * users, exit gracefully. We should be running under a process + * controller that will restart us if necessry. + */ + if (num == 0 && count_all_current_connections() == 0) { + exit_server_cleanly("idle timeout"); + } /* process pending nDNS responses */ if (dns_register_smbd_reply(dns_reg, &r_fds, &idle_timeout)) { @@ -893,6 +942,29 @@ void exit_server_fault(void) exit_server("critical server fault"); } + +/**************************************************************************** +received when we should release a specific IP +****************************************************************************/ +static void msg_release_ip(struct messaging_context *msg_ctx, void *private_data, + uint32_t msg_type, struct server_id server_id, DATA_BLOB *data) +{ + const char *ip = (const char *)data->data; + char addr[INET6_ADDRSTRLEN]; + + if (strcmp(client_socket_addr(get_client_fd(),addr,sizeof(addr)), ip) == 0) { + /* we can't afford to do a clean exit - that involves + database writes, which would potentially mean we + are still running after the failover has finished - + we have to get rid of this process ID straight + away */ + DEBUG(0,("Got release IP message for our IP %s - exiting immediately\n", + ip)); + _exit(0); + } +} + + /**************************************************************************** Initialise connect, service and file structs. ****************************************************************************/ @@ -1184,6 +1256,12 @@ extern void build_options(bool screen); if (smbd_messaging_context() == NULL) exit(1); + if (smbd_memcache() == NULL) { + exit(1); + } + + memcache_set_global(smbd_memcache()); + /* Initialise the password backed before the global_sam_sid to ensure that we fetch from ldap before we make a domain sid up */ @@ -1286,6 +1364,8 @@ extern void build_options(bool screen); /* register our message handlers */ messaging_register(smbd_messaging_context(), NULL, MSG_SMB_FORCE_TDIS, msg_force_tdis); + messaging_register(smbd_messaging_context(), NULL, + MSG_SMB_RELEASE_IP, msg_release_ip); if ((lp_keepalive() != 0) && !(event_add_idle(smbd_event_context(), NULL, diff --git a/source3/smbd/service.c b/source3/smbd/service.c index 88ab9f0048..a8aa25405a 100644 --- a/source3/smbd/service.c +++ b/source3/smbd/service.c @@ -219,44 +219,6 @@ bool set_current_service(connection_struct *conn, uint16 flags, bool do_chdir) return(True); } -/**************************************************************************** - Add a home service. Returns the new service number or -1 if fail. -****************************************************************************/ - -int add_home_service(const char *service, const char *username, const char *homedir) -{ - int iHomeService; - - if (!service || !homedir) - return -1; - - if ((iHomeService = lp_servicenumber(HOMES_NAME)) < 0) - return -1; - - /* - * If this is a winbindd provided username, remove - * the domain component before adding the service. - * Log a warning if the "path=" parameter does not - * include any macros. - */ - - { - const char *p = strchr(service,*lp_winbind_separator()); - - /* We only want the 'user' part of the string */ - if (p) { - service = p + 1; - } - } - - if (!lp_add_home(service, iHomeService, username, homedir)) { - return -1; - } - - return lp_servicenumber(service); - -} - static int load_registry_service(const char *servicename) { struct registry_key *key; @@ -348,6 +310,47 @@ void load_registry_shares(void) return; } +/**************************************************************************** + Add a home service. Returns the new service number or -1 if fail. +****************************************************************************/ + +int add_home_service(const char *service, const char *username, const char *homedir) +{ + int iHomeService; + + if (!service || !homedir) + return -1; + + if ((iHomeService = lp_servicenumber(HOMES_NAME)) < 0) { + if ((iHomeService = load_registry_service(HOMES_NAME)) < 0) { + return -1; + } + } + + /* + * If this is a winbindd provided username, remove + * the domain component before adding the service. + * Log a warning if the "path=" parameter does not + * include any macros. + */ + + { + const char *p = strchr(service,*lp_winbind_separator()); + + /* We only want the 'user' part of the string */ + if (p) { + service = p + 1; + } + } + + if (!lp_add_home(service, iHomeService, username, homedir)) { + return -1; + } + + return lp_servicenumber(service); + +} + /** * Find a service entry. * @@ -364,7 +367,7 @@ int find_service(fstring service) /* now handle the special case of a home directory */ if (iService < 0) { - char *phome_dir = get_user_home_dir(service); + char *phome_dir = get_user_home_dir(talloc_tos(), service); if(!phome_dir) { /* @@ -372,7 +375,8 @@ int find_service(fstring service) * be a Windows to unix mapped user name. */ if(map_username(service)) - phome_dir = get_user_home_dir(service); + phome_dir = get_user_home_dir( + talloc_tos(), service); } DEBUG(3,("checking for home directory %s gave %s\n",service, @@ -385,7 +389,10 @@ int find_service(fstring service) if (iService < 0) { int iPrinterService; - if ((iPrinterService = lp_servicenumber(PRINTERS_NAME)) >= 0) { + if ((iPrinterService = lp_servicenumber(PRINTERS_NAME)) < 0) { + iPrinterService = load_registry_service(PRINTERS_NAME); + } + if (iPrinterService) { DEBUG(3,("checking whether %s is a valid printer name...\n", service)); if (pcap_printername_ok(service)) { DEBUG(3,("%s is a valid printer name\n", service)); @@ -744,11 +751,12 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser, *status = NT_STATUS_WRONG_PASSWORD; return NULL; } - pass = Get_Pwnam(user); + pass = Get_Pwnam_alloc(talloc_tos(), user); status2 = create_token_from_username(conn->mem_ctx, pass->pw_name, True, &conn->uid, &conn->gid, &found_username, &conn->nt_user_token); + TALLOC_FREE(pass); if (!NT_STATUS_IS_OK(status2)) { conn_free(conn); *status = status2; @@ -790,6 +798,8 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser, conn->case_preserve = lp_preservecase(snum); conn->short_case_preserve = lp_shortpreservecase(snum); + conn->encrypt_level = lp_smb_encrypt(snum); + conn->veto_list = NULL; conn->hide_list = NULL; conn->veto_oplock_list = NULL; @@ -1143,23 +1153,27 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser, #if SOFTLINK_OPTIMISATION /* resolve any soft links early if possible */ if (vfs_ChDir(conn,conn->connectpath) == 0) { - TALLOC_CTX *ctx = talloc_stackframe(); + TALLOC_CTX *ctx = talloc_tos(); char *s = vfs_GetWd(ctx,s); if (!s) { *status = map_nt_error_from_unix(errno); - TALLOC_FREE(ctx); goto err_root_exit; } if (!set_conn_connectpath(conn,s)) { *status = NT_STATUS_NO_MEMORY; - TALLOC_FREE(ctx); goto err_root_exit; } vfs_ChDir(conn,conn->connectpath); - TALLOC_FREE(ctx); } #endif + /* Figure out the characteristics of the underlying filesystem. This + * assumes that all the filesystem mounted withing a share path have + * the same characteristics, which is likely but not guaranteed. + */ + + conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn); + /* * Print out the 'connected as' stuff here as we need * to know the effective uid and gid we will be using diff --git a/source3/smbd/sesssetup.c b/source3/smbd/sesssetup.c index 8ca012ff24..aee8e498e9 100644 --- a/source3/smbd/sesssetup.c +++ b/source3/smbd/sesssetup.c @@ -118,8 +118,7 @@ static void sessionsetup_start_signing_engine( Send a security blob via a session setup reply. ****************************************************************************/ -static void reply_sesssetup_blob(connection_struct *conn, - struct smb_request *req, +static void reply_sesssetup_blob(struct smb_request *req, DATA_BLOB blob, NTSTATUS nt_status) { @@ -139,7 +138,7 @@ static void reply_sesssetup_blob(connection_struct *conn, } show_msg((char *)req->outbuf); - send_smb(smbd_server_fd(),(char *)req->outbuf); + srv_send_smb(smbd_server_fd(),(char *)req->outbuf,req->encrypted); TALLOC_FREE(req->outbuf); } @@ -247,8 +246,7 @@ static bool make_krb5_skew_error(DATA_BLOB *pblob_out) Reply to a session setup spnego negotiate packet for kerberos. ****************************************************************************/ -static void reply_spnego_kerberos(connection_struct *conn, - struct smb_request *req, +static void reply_spnego_kerberos(struct smb_request *req, DATA_BLOB *secblob, uint16 vuid, bool *p_invalidate_vuid) @@ -261,7 +259,7 @@ static void reply_spnego_kerberos(connection_struct *conn, fstring user; int sess_vuid = req->vuid; NTSTATUS ret = NT_STATUS_OK; - PAC_DATA *pac_data; + PAC_DATA *pac_data = NULL; DATA_BLOB ap_rep, ap_rep_wrapped, response; auth_serversupplied_info *server_info = NULL; DATA_BLOB session_key = data_blob_null; @@ -273,7 +271,6 @@ static void reply_spnego_kerberos(connection_struct *conn, PAC_LOGON_INFO *logon_info = NULL; ZERO_STRUCT(ticket); - ZERO_STRUCT(pac_data); ZERO_STRUCT(ap_rep); ZERO_STRUCT(ap_rep_wrapped); ZERO_STRUCT(response); @@ -539,7 +536,9 @@ static void reply_spnego_kerberos(connection_struct *conn, } } - server_info->was_mapped |= username_was_mapped; + if (username_was_mapped) { + server_info->was_mapped = username_was_mapped; + } /* we need to build the token for the user. make_server_info_guest() already does this */ @@ -605,7 +604,7 @@ static void reply_spnego_kerberos(connection_struct *conn, } response = spnego_gen_auth_response(&ap_rep_wrapped, ret, OID_KERBEROS5_OLD); - reply_sesssetup_blob(conn, req, response, ret); + reply_sesssetup_blob(req, response, ret); data_blob_free(&ap_rep); data_blob_free(&ap_rep_wrapped); @@ -623,8 +622,7 @@ static void reply_spnego_kerberos(connection_struct *conn, leg of the NTLM auth steps. ***************************************************************************/ -static void reply_spnego_ntlmssp(connection_struct *conn, - struct smb_request *req, +static void reply_spnego_ntlmssp(struct smb_request *req, uint16 vuid, AUTH_NTLMSSP_STATE **auth_ntlmssp_state, DATA_BLOB *ntlmssp_blob, NTSTATUS nt_status, @@ -693,7 +691,7 @@ static void reply_spnego_ntlmssp(connection_struct *conn, response = *ntlmssp_blob; } - reply_sesssetup_blob(conn, req, response, nt_status); + reply_sesssetup_blob(req, response, nt_status); if (wrap) { data_blob_free(&response); } @@ -756,8 +754,7 @@ NTSTATUS parse_spnego_mechanisms(DATA_BLOB blob_in, DATA_BLOB *pblob_out, Reply to a session setup spnego negotiate packet. ****************************************************************************/ -static void reply_spnego_negotiate(connection_struct *conn, - struct smb_request *req, +static void reply_spnego_negotiate(struct smb_request *req, uint16 vuid, DATA_BLOB blob1, AUTH_NTLMSSP_STATE **auth_ntlmssp_state) @@ -783,7 +780,7 @@ static void reply_spnego_negotiate(connection_struct *conn, if ( got_kerberos_mechanism && ((lp_security()==SEC_ADS) || lp_use_kerberos_keytab()) ) { bool destroy_vuid = True; - reply_spnego_kerberos(conn, req, &secblob, vuid, + reply_spnego_kerberos(req, &secblob, vuid, &destroy_vuid); data_blob_free(&secblob); if (destroy_vuid) { @@ -811,7 +808,7 @@ static void reply_spnego_negotiate(connection_struct *conn, data_blob_free(&secblob); - reply_spnego_ntlmssp(conn, req, vuid, auth_ntlmssp_state, + reply_spnego_ntlmssp(req, vuid, auth_ntlmssp_state, &chal, status, True); data_blob_free(&chal); @@ -824,8 +821,7 @@ static void reply_spnego_negotiate(connection_struct *conn, Reply to a session setup spnego auth packet. ****************************************************************************/ -static void reply_spnego_auth(connection_struct *conn, - struct smb_request *req, +static void reply_spnego_auth(struct smb_request *req, uint16 vuid, DATA_BLOB blob1, AUTH_NTLMSSP_STATE **auth_ntlmssp_state) @@ -860,7 +856,7 @@ static void reply_spnego_auth(connection_struct *conn, if ( got_krb5_mechanism && ((lp_security()==SEC_ADS) || lp_use_kerberos_keytab()) ) { bool destroy_vuid = True; - reply_spnego_kerberos(conn, req, &secblob, + reply_spnego_kerberos(req, &secblob, vuid, &destroy_vuid); data_blob_free(&secblob); data_blob_free(&auth); @@ -892,7 +888,7 @@ static void reply_spnego_auth(connection_struct *conn, data_blob_free(&auth); - reply_spnego_ntlmssp(conn, req, vuid, + reply_spnego_ntlmssp(req, vuid, auth_ntlmssp_state, &auth_reply, status, True); @@ -1104,8 +1100,7 @@ static NTSTATUS check_spnego_blob_complete(uint16 smbpid, uint16 vuid, conn POINTER CAN BE NULL HERE ! ****************************************************************************/ -static void reply_sesssetup_and_X_spnego(connection_struct *conn, - struct smb_request *req) +static void reply_sesssetup_and_X_spnego(struct smb_request *req) { uint8 *p; DATA_BLOB blob1; @@ -1225,7 +1220,7 @@ static void reply_sesssetup_and_X_spnego(connection_struct *conn, /* its a negTokenTarg packet */ - reply_spnego_negotiate(conn, req, vuid, blob1, + reply_spnego_negotiate(req, vuid, blob1, &vuser->auth_ntlmssp_state); data_blob_free(&blob1); return; @@ -1235,7 +1230,7 @@ static void reply_sesssetup_and_X_spnego(connection_struct *conn, /* its a auth packet */ - reply_spnego_auth(conn, req, vuid, blob1, + reply_spnego_auth(req, vuid, blob1, &vuser->auth_ntlmssp_state); data_blob_free(&blob1); return; @@ -1260,7 +1255,7 @@ static void reply_sesssetup_and_X_spnego(connection_struct *conn, data_blob_free(&blob1); - reply_spnego_ntlmssp(conn, req, vuid, + reply_spnego_ntlmssp(req, vuid, &vuser->auth_ntlmssp_state, &chal, status, False); data_blob_free(&chal); @@ -1326,7 +1321,7 @@ static void setup_new_vc_session(void) Reply to a session setup command. ****************************************************************************/ -void reply_sesssetup_and_X(connection_struct *conn, struct smb_request *req) +void reply_sesssetup_and_X(struct smb_request *req) { int sess_vuid; int smb_bufsize; @@ -1377,7 +1372,7 @@ void reply_sesssetup_and_X(connection_struct *conn, struct smb_request *req) setup_new_vc_session(); } - reply_sesssetup_and_X_spnego(conn, req); + reply_sesssetup_and_X_spnego(req); END_PROFILE(SMBsesssetupX); return; } diff --git a/source3/smbd/statcache.c b/source3/smbd/statcache.c index 8f1e008985..72fed008a2 100644 --- a/source3/smbd/statcache.c +++ b/source3/smbd/statcache.c @@ -26,8 +26,6 @@ Stat cache code used in unix_convert. *****************************************************************************/ -static TDB_CONTEXT *tdb_stat_cache; - /** * Add an entry into the stat cache. * @@ -45,10 +43,8 @@ void stat_cache_add( const char *full_orig_name, bool case_sensitive) { size_t translated_path_length; - TDB_DATA data_val; char *original_path; size_t original_path_length; - size_t sc_size = lp_max_stat_cache_size(); char saved_char; TALLOC_CTX *ctx = talloc_tos(); @@ -56,12 +52,6 @@ void stat_cache_add( const char *full_orig_name, return; } - if (sc_size && (tdb_map_size(tdb_stat_cache) > sc_size*1024)) { - reset_stat_cache(); - } - - ZERO_STRUCT(data_val); - /* * Don't cache trivial valid directory entries such as . and .. */ @@ -132,24 +122,20 @@ void stat_cache_add( const char *full_orig_name, saved_char = translated_path[translated_path_length]; translated_path[translated_path_length] = '\0'; - data_val.dsize = translated_path_length + 1; - data_val.dptr = (uint8 *)translated_path; - /* * New entry or replace old entry. */ - if (tdb_store_bystring(tdb_stat_cache, original_path, data_val, - TDB_REPLACE) != 0) { - DEBUG(0,("stat_cache_add: Error storing entry %s -> %s\n", - original_path, translated_path)); - } else { - DEBUG(5,("stat_cache_add: Added entry (%lx:size%x) %s -> %s\n", - (unsigned long)data_val.dptr, - (unsigned int)data_val.dsize, - original_path, - translated_path)); - } + memcache_add( + smbd_memcache(), STAT_CACHE, + data_blob_const(original_path, original_path_length), + data_blob_const(translated_path, translated_path_length + 1)); + + DEBUG(5,("stat_cache_add: Added entry (%lx:size %x) %s -> %s\n", + (unsigned long)translated_path, + (unsigned int)translated_path_length, + original_path, + translated_path)); translated_path[translated_path_length] = saved_char; TALLOC_FREE(original_path); @@ -186,7 +172,7 @@ bool stat_cache_lookup(connection_struct *conn, unsigned int num_components = 0; char *translated_path; size_t translated_path_length; - TDB_DATA data_val; + DATA_BLOB data_val; char *name; TALLOC_CTX *ctx = talloc_tos(); @@ -236,9 +222,12 @@ bool stat_cache_lookup(connection_struct *conn, while (1) { char *sp; - data_val = tdb_fetch_bystring(tdb_stat_cache, chk_name); + data_val = data_blob_null; - if (data_val.dptr != NULL && data_val.dsize != 0) { + if (memcache_lookup( + smbd_memcache(), STAT_CACHE, + data_blob_const(chk_name, strlen(chk_name)), + &data_val)) { break; } @@ -275,12 +264,11 @@ bool stat_cache_lookup(connection_struct *conn, } } - translated_path = talloc_strdup(ctx,(char *)data_val.dptr); + translated_path = talloc_strdup(ctx,(char *)data_val.data); if (!translated_path) { smb_panic("talloc failed"); } - translated_path_length = data_val.dsize - 1; - SAFE_FREE(data_val.dptr); + translated_path_length = data_val.length - 1; DEBUG(10,("stat_cache_lookup: lookup succeeded for name [%s] " "-> [%s]\n", chk_name, translated_path )); @@ -288,7 +276,8 @@ bool stat_cache_lookup(connection_struct *conn, if (SMB_VFS_STAT(conn, translated_path, pst) != 0) { /* Discard this entry - it doesn't exist in the filesystem. */ - tdb_delete_bystring(tdb_stat_cache, chk_name); + memcache_delete(smbd_memcache(), STAT_CACHE, + data_blob_const(chk_name, strlen(chk_name))); TALLOC_FREE(chk_name); TALLOC_FREE(translated_path); return False; @@ -366,7 +355,8 @@ void stat_cache_delete(const char *name) DEBUG(10,("stat_cache_delete: deleting name [%s] -> %s\n", lname, name )); - tdb_delete_bystring(tdb_stat_cache, lname); + memcache_delete(smbd_memcache(), STAT_CACHE, + data_blob_const(lname, talloc_get_size(lname)-1)); TALLOC_FREE(lname); } @@ -395,15 +385,7 @@ bool reset_stat_cache( void ) if (!lp_stat_cache()) return True; - if (tdb_stat_cache) { - tdb_close(tdb_stat_cache); - } + memcache_flush(smbd_memcache(), STAT_CACHE); - /* Create the in-memory tdb using our custom hash function. */ - tdb_stat_cache = tdb_open_ex("statcache", 1031, TDB_INTERNAL, - (O_RDWR|O_CREAT), 0644, NULL, fast_string_hash); - - if (!tdb_stat_cache) - return False; return True; } diff --git a/source3/smbd/statvfs.c b/source3/smbd/statvfs.c index f6663208ea..0e9a2c2ebe 100644 --- a/source3/smbd/statvfs.c +++ b/source3/smbd/statvfs.c @@ -3,6 +3,7 @@ VFS API's statvfs abstraction Copyright (C) Alexander Bokovoy 2005 Copyright (C) Steve French 2005 + Copyright (C) James Peach 2006 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -37,11 +38,93 @@ static int linux_statvfs(const char *path, vfs_statvfs_struct *statbuf) statbuf->TotalFileNodes = statvfs_buf.f_files; statbuf->FreeFileNodes = statvfs_buf.f_ffree; statbuf->FsIdentifier = statvfs_buf.f_fsid; + + /* Good defaults for Linux filesystems are case sensitive + * and case preserving. + */ + statbuf->FsCapabilities = + FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES; } return result; } #endif +#if defined(DARWINOS) + +#include <sys/attr.h> + +static int darwin_fs_capabilities(const char * path) +{ + int caps = 0; + vol_capabilities_attr_t *vcaps; + struct attrlist attrlist; + char attrbuf[sizeof(u_int32_t) + sizeof(vol_capabilities_attr_t)]; + +#define FORMAT_CAP(vinfo, cap) \ + ( ((vinfo)->valid[VOL_CAPABILITIES_FORMAT] & (cap)) && \ + ((vinfo)->capabilities[VOL_CAPABILITIES_FORMAT] & (cap)) ) + +#define INTERFACE_CAP(vinfo, cap) \ + ( ((vinfo)->valid[VOL_CAPABILITIES_INTERFACES] & (cap)) && \ + ((vinfo)->capabilities[VOL_CAPABILITIES_INTERFACES] & (cap)) ) + + ZERO_STRUCT(attrlist); + attrlist.bitmapcount = ATTR_BIT_MAP_COUNT; + attrlist.volattr = ATTR_VOL_CAPABILITIES; + + if (getattrlist(path, &attrlist, attrbuf, sizeof(attrbuf), 0) != 0) { + DEBUG(0, ("getattrlist for %s capabilities failed: %s\n", + path, strerror(errno))); + /* Return no capabilities on failure. */ + return 0; + } + + vcaps = + (vol_capabilities_attr_t *)(attrbuf + sizeof(u_int32_t)); + + if (FORMAT_CAP(vcaps, VOL_CAP_FMT_SPARSE_FILES)) { + caps |= FILE_SUPPORTS_SPARSE_FILES; + } + + if (FORMAT_CAP(vcaps, VOL_CAP_FMT_CASE_SENSITIVE)) { + caps |= FILE_CASE_SENSITIVE_SEARCH; + } + + if (FORMAT_CAP(vcaps, VOL_CAP_FMT_CASE_PRESERVING)) { + caps |= FILE_CASE_PRESERVED_NAMES; + } + + if (INTERFACE_CAP(vcaps, VOL_CAP_INT_EXTENDED_SECURITY)) { + caps |= FILE_PERSISTENT_ACLS; + } + + return caps; +} + +static int darwin_statvfs(const char *path, vfs_statvfs_struct *statbuf) +{ + struct statfs sbuf; + int ret; + + ret = statfs(path, &sbuf); + if (ret != 0) { + return ret; + } + + statbuf->OptimalTransferSize = sbuf.f_iosize; + statbuf->BlockSize = sbuf.f_bsize; + statbuf->TotalBlocks = sbuf.f_blocks; + statbuf->BlocksAvail = sbuf.f_bfree; + statbuf->UserBlocksAvail = sbuf.f_bavail; + statbuf->TotalFileNodes = sbuf.f_files; + statbuf->FreeFileNodes = sbuf.f_ffree; + statbuf->FsIdentifier = *(SMB_BIG_UINT *)(&sbuf.f_fsid); /* Ick. */ + statbuf->FsCapabilities = darwin_fs_capabilities(sbuf.f_mntonname); + + return 0; +} +#endif + /* sys_statvfs() is an abstraction layer over system-dependent statvfs()/statfs() for particular POSIX systems. Due to controversy of what is considered more important @@ -52,6 +135,8 @@ int sys_statvfs(const char *path, vfs_statvfs_struct *statbuf) { #if defined(LINUX) && defined(HAVE_FSID_INT) return linux_statvfs(path, statbuf); +#elif defined(DARWINOS) + return darwin_statvfs(path, statbuf); #else /* BB change this to return invalid level */ #ifdef EOPNOTSUPP diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c index e37f6ffbfd..1f7af82670 100644 --- a/source3/smbd/trans2.c +++ b/source3/smbd/trans2.c @@ -5,7 +5,7 @@ Copyright (C) Stefan (metze) Metzmacher 2003 Copyright (C) Volker Lendecke 2005-2007 Copyright (C) Steve French 2005 - Copyright (C) James Peach 2007 + Copyright (C) James Peach 2006-2007 Extensively modified by Andrew Tridgell, 1995 @@ -105,17 +105,22 @@ static bool samba_private_attr_name(const char *unix_ea_name) for (i = 0; prohibited_ea_names[i]; i++) { if (strequal( prohibited_ea_names[i], unix_ea_name)) - return True; + return true; } - return False; + if (StrnCaseCmp(unix_ea_name, SAMBA_XATTR_DOSSTREAM_PREFIX, + strlen(SAMBA_XATTR_DOSSTREAM_PREFIX)) == 0) { + return true; + } + return false; } /**************************************************************************** Get one EA value. Fill in a struct ea_struct. ****************************************************************************/ -static bool get_ea_value(TALLOC_CTX *mem_ctx, connection_struct *conn, files_struct *fsp, - const char *fname, char *ea_name, struct ea_struct *pea) +NTSTATUS get_ea_value(TALLOC_CTX *mem_ctx, connection_struct *conn, + files_struct *fsp, const char *fname, + const char *ea_name, struct ea_struct *pea) { /* Get the value of this xattr. Max size is 64k. */ size_t attr_size = 256; @@ -126,11 +131,11 @@ static bool get_ea_value(TALLOC_CTX *mem_ctx, connection_struct *conn, files_str val = TALLOC_REALLOC_ARRAY(mem_ctx, val, char, attr_size); if (!val) { - return False; + return NT_STATUS_NO_MEMORY; } if (fsp && fsp->fh->fd != -1) { - sizeret = SMB_VFS_FGETXATTR(fsp, fsp->fh->fd, ea_name, val, attr_size); + sizeret = SMB_VFS_FGETXATTR(fsp, ea_name, val, attr_size); } else { sizeret = SMB_VFS_GETXATTR(conn, fname, ea_name, val, attr_size); } @@ -141,7 +146,7 @@ static bool get_ea_value(TALLOC_CTX *mem_ctx, connection_struct *conn, files_str } if (sizeret == -1) { - return False; + return map_nt_error_from_unix(errno); } DEBUG(10,("get_ea_value: EA %s is of length %u\n", ea_name, (unsigned int)sizeret)); @@ -149,93 +154,192 @@ static bool get_ea_value(TALLOC_CTX *mem_ctx, connection_struct *conn, files_str pea->flags = 0; if (strnequal(ea_name, "user.", 5)) { - pea->name = &ea_name[5]; + pea->name = talloc_strdup(mem_ctx, &ea_name[5]); } else { - pea->name = ea_name; + 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 True; + return NT_STATUS_OK; } -/**************************************************************************** - Return a linked list of the total EA's. Plus the total size -****************************************************************************/ - -static struct ea_list *get_ea_list_from_file(TALLOC_CTX *mem_ctx, connection_struct *conn, files_struct *fsp, - const char *fname, size_t *pea_total_len) +NTSTATUS get_ea_names_from_file(TALLOC_CTX *mem_ctx, connection_struct *conn, + files_struct *fsp, const char *fname, + char ***pnames, size_t *pnum_names) { /* Get a list of all xattrs. Max namesize is 64k. */ size_t ea_namelist_size = 1024; - char *ea_namelist; + char *ea_namelist = NULL; + char *p; + char **names, **tmp; + size_t num_names; ssize_t sizeret; - int i; - struct ea_list *ea_list_head = NULL; - - *pea_total_len = 0; if (!lp_ea_support(SNUM(conn))) { - return NULL; + *pnames = NULL; + *pnum_names = 0; + return NT_STATUS_OK; } - for (i = 0, ea_namelist = TALLOC_ARRAY(mem_ctx, char, ea_namelist_size); i < 6; - ea_namelist = TALLOC_REALLOC_ARRAY(mem_ctx, ea_namelist, char, ea_namelist_size), i++) { + /* + * TALLOC the result early to get the talloc hierarchy right. + */ - if (!ea_namelist) { - return NULL; + names = TALLOC_ARRAY(mem_ctx, char *, 1); + if (names == NULL) { + DEBUG(0, ("talloc failed\n")); + return NT_STATUS_NO_MEMORY; + } + + while (ea_namelist_size <= 65536) { + + ea_namelist = TALLOC_REALLOC_ARRAY( + names, ea_namelist, char, ea_namelist_size); + if (ea_namelist == NULL) { + DEBUG(0, ("talloc failed\n")); + TALLOC_FREE(names); + return NT_STATUS_NO_MEMORY; } if (fsp && fsp->fh->fd != -1) { - sizeret = SMB_VFS_FLISTXATTR(fsp, fsp->fh->fd, ea_namelist, ea_namelist_size); + sizeret = SMB_VFS_FLISTXATTR(fsp, ea_namelist, + ea_namelist_size); } else { - sizeret = SMB_VFS_LISTXATTR(conn, fname, ea_namelist, ea_namelist_size); + sizeret = SMB_VFS_LISTXATTR(conn, fname, ea_namelist, + ea_namelist_size); } - if (sizeret == -1 && errno == ERANGE) { + if ((sizeret == -1) && (errno == ERANGE)) { ea_namelist_size *= 2; - } else { + } + else { break; } } - if (sizeret == -1) - return NULL; + if (sizeret == -1) { + TALLOC_FREE(names); + return map_nt_error_from_unix(errno); + } - DEBUG(10,("get_ea_list_from_file: ea_namelist size = %u\n", (unsigned int)sizeret )); + DEBUG(10, ("get_ea_list_from_file: ea_namelist size = %u\n", + (unsigned int)sizeret)); - if (sizeret) { - for (p = ea_namelist; p - ea_namelist < sizeret; p += strlen(p) + 1) { - struct ea_list *listp; + if (sizeret == 0) { + TALLOC_FREE(names); + *pnames = NULL; + *pnum_names = 0; + return NT_STATUS_OK; + } - if (strnequal(p, "system.", 7) || samba_private_attr_name(p)) - continue; + /* + * Ensure the result is 0-terminated + */ - listp = TALLOC_P(mem_ctx, struct ea_list); - if (!listp) - return NULL; + if (ea_namelist[sizeret-1] != '\0') { + TALLOC_FREE(names); + return NT_STATUS_INTERNAL_ERROR; + } - if (!get_ea_value(mem_ctx, conn, fsp, fname, p, &listp->ea)) { - return NULL; - } + /* + * count the names + */ + num_names = 0; - { - fstring dos_ea_name; - push_ascii_fstring(dos_ea_name, listp->ea.name); - *pea_total_len += 4 + strlen(dos_ea_name) + 1 + listp->ea.value.length; - DEBUG(10,("get_ea_list_from_file: total_len = %u, %s, val len = %u\n", - (unsigned int)*pea_total_len, dos_ea_name, - (unsigned int)listp->ea.value.length )); - } - DLIST_ADD_END(ea_list_head, listp, struct ea_list *); + for (p = ea_namelist; p - ea_namelist < sizeret; p += strlen(p)+1) { + num_names += 1; + } + + tmp = TALLOC_REALLOC_ARRAY(mem_ctx, names, char *, num_names); + if (tmp == NULL) { + DEBUG(0, ("talloc failed\n")); + TALLOC_FREE(names); + return NT_STATUS_NO_MEMORY; + } + + names = tmp; + num_names = 0; + + for (p = ea_namelist; p - ea_namelist < sizeret; p += strlen(p)+1) { + names[num_names++] = p; + } + + *pnames = names; + *pnum_names = num_names; + return NT_STATUS_OK; +} + +/**************************************************************************** + Return a linked list of the total EA's. Plus the total size +****************************************************************************/ + +static struct ea_list *get_ea_list_from_file(TALLOC_CTX *mem_ctx, connection_struct *conn, files_struct *fsp, + const char *fname, size_t *pea_total_len) +{ + /* Get a list of all xattrs. Max namesize is 64k. */ + size_t i, num_names; + char **names; + struct ea_list *ea_list_head = NULL; + NTSTATUS status; + + *pea_total_len = 0; + + if (!lp_ea_support(SNUM(conn))) { + return NULL; + } + + status = get_ea_names_from_file(talloc_tos(), conn, fsp, fname, + &names, &num_names); + + if (!NT_STATUS_IS_OK(status) || (num_names == 0)) { + return NULL; + } + + for (i=0; i<num_names; i++) { + struct ea_list *listp; + fstring dos_ea_name; + + if (strnequal(names[i], "system.", 7) + || samba_private_attr_name(names[i])) + continue; + + listp = TALLOC_P(mem_ctx, struct ea_list); + if (listp == NULL) { + return NULL; } - /* Add on 4 for total length. */ - if (*pea_total_len) { - *pea_total_len += 4; + + if (!NT_STATUS_IS_OK(get_ea_value(mem_ctx, conn, fsp, + fname, names[i], + &listp->ea))) { + return NULL; } + + push_ascii_fstring(dos_ea_name, listp->ea.name); + + *pea_total_len += + 4 + strlen(dos_ea_name) + 1 + listp->ea.value.length; + + DEBUG(10,("get_ea_list_from_file: total_len = %u, %s, val len " + "= %u\n", (unsigned int)*pea_total_len, dos_ea_name, + (unsigned int)listp->ea.value.length)); + + DLIST_ADD_END(ea_list_head, listp, struct ea_list *); + } - DEBUG(10,("get_ea_list_from_file: total_len = %u\n", (unsigned int)*pea_total_len)); + /* Add on 4 for total length. */ + if (*pea_total_len) { + *pea_total_len += 4; + } + + DEBUG(10, ("get_ea_list_from_file: total_len = %u\n", + (unsigned int)*pea_total_len)); + return ea_list_head; } @@ -297,9 +401,8 @@ static unsigned int estimate_ea_size(connection_struct *conn, files_struct *fsp, if (!lp_ea_support(SNUM(conn))) { return 0; } - mem_ctx = talloc_init("estimate_ea_size"); + mem_ctx = talloc_tos(); (void)get_ea_list_from_file(mem_ctx, conn, fsp, fname, &total_ea_len); - talloc_destroy(mem_ctx); return total_ea_len; } @@ -310,7 +413,7 @@ static unsigned int estimate_ea_size(connection_struct *conn, files_struct *fsp, static void canonicalize_ea_name(connection_struct *conn, files_struct *fsp, const char *fname, fstring unix_ea_name) { size_t total_ea_len; - TALLOC_CTX *mem_ctx = talloc_init("canonicalize_ea_name"); + TALLOC_CTX *mem_ctx = talloc_tos(); struct ea_list *ea_list = get_ea_list_from_file(mem_ctx, conn, fsp, fname, &total_ea_len); for (; ea_list; ea_list = ea_list->next) { @@ -321,7 +424,6 @@ static void canonicalize_ea_name(connection_struct *conn, files_struct *fsp, con break; } } - talloc_destroy(mem_ctx); } /**************************************************************************** @@ -355,7 +457,7 @@ NTSTATUS set_ea(connection_struct *conn, files_struct *fsp, const char *fname, s if (fsp && (fsp->fh->fd != -1)) { DEBUG(10,("set_ea: deleting ea name %s on file %s by file descriptor.\n", unix_ea_name, fsp->fsp_name)); - ret = SMB_VFS_FREMOVEXATTR(fsp, fsp->fh->fd, unix_ea_name); + ret = SMB_VFS_FREMOVEXATTR(fsp, unix_ea_name); } else { DEBUG(10,("set_ea: deleting ea name %s on file %s.\n", unix_ea_name, fname)); @@ -373,7 +475,7 @@ NTSTATUS set_ea(connection_struct *conn, files_struct *fsp, const char *fname, s if (fsp && (fsp->fh->fd != -1)) { DEBUG(10,("set_ea: setting ea name %s on file %s by file descriptor.\n", unix_ea_name, fsp->fsp_name)); - ret = SMB_VFS_FSETXATTR(fsp, fsp->fh->fd, unix_ea_name, + ret = SMB_VFS_FSETXATTR(fsp, unix_ea_name, ea_list->ea.value.data, ea_list->ea.value.length, 0); } else { DEBUG(10,("set_ea: setting ea name %s on file %s.\n", @@ -575,7 +677,8 @@ static struct ea_list *ea_list_union(struct ea_list *name_list, struct ea_list * HACK ! Always assumes smb_setup field is zero. ****************************************************************************/ -void send_trans2_replies(struct smb_request *req, +void send_trans2_replies(connection_struct *conn, + struct smb_request *req, const char *params, int paramsize, const char *pdata, @@ -737,8 +840,10 @@ void send_trans2_replies(struct smb_request *req, /* Send the packet */ show_msg((char *)req->outbuf); - if (!send_smb(smbd_server_fd(),(char *)req->outbuf)) - exit_server_cleanly("send_trans2_replies: send_smb failed."); + if (!srv_send_smb(smbd_server_fd(), + (char *)req->outbuf, + IS_CONN_ENCRYPTED(conn))) + exit_server_cleanly("send_trans2_replies: srv_send_smb failed."); TALLOC_FREE(req->outbuf); @@ -841,20 +946,6 @@ static void call_trans2open(connection_struct *conn, fname, (unsigned int)deny_mode, (unsigned int)open_attr, (unsigned int)open_ofun, open_size)); - /* XXXX we need to handle passed times, sattr and flags */ - - status = unix_convert(ctx, conn, fname, False, &fname, NULL, &sbuf); - if (!NT_STATUS_IS_OK(status)) { - reply_nterror(req, status); - return; - } - - status = check_name(conn, fname); - if (!NT_STATUS_IS_OK(status)) { - reply_nterror(req, status); - return; - } - if (open_ofun == 0) { reply_nterror(req, NT_STATUS_OBJECT_NAME_COLLISION); return; @@ -899,14 +990,22 @@ static void call_trans2open(connection_struct *conn, return; } - status = open_file_ntcreate(conn, req, fname, &sbuf, - access_mask, - share_mode, - create_disposition, - create_options, - open_attr, - oplock_request, - &smb_action, &fsp); + status = create_file(conn, /* conn */ + req, /* req */ + 0, /* root_dir_fid */ + fname, /* fname */ + access_mask, /* access_mask */ + share_mode, /* share_access */ + create_disposition, /* create_disposition*/ + create_options, /* create_options */ + open_attr, /* file_attributes */ + oplock_request, /* oplock_request */ + open_size, /* allocation_size */ + NULL, /* sd */ + ea_list, /* ea_list */ + &fsp, /* result */ + &smb_action, /* pinfo */ + &sbuf); /* psbuf */ if (!NT_STATUS_IS_OK(status)) { if (open_was_deferred(req->mid)) { @@ -927,41 +1026,6 @@ static void call_trans2open(connection_struct *conn, return; } - /* Save the requested allocation size. */ - /* Allocate space for the file if a size hint is supplied */ - if ((smb_action == FILE_WAS_CREATED) || (smb_action == FILE_WAS_OVERWRITTEN)) { - SMB_BIG_UINT allocation_size = (SMB_BIG_UINT)open_size; - if (allocation_size && (allocation_size > (SMB_BIG_UINT)size)) { - fsp->initial_allocation_size = smb_roundup(fsp->conn, allocation_size); - if (fsp->is_directory) { - close_file(fsp,ERROR_CLOSE); - /* Can't set allocation size on a directory. */ - reply_nterror(req, NT_STATUS_ACCESS_DENIED); - return; - } - if (vfs_allocate_file_space(fsp, fsp->initial_allocation_size) == -1) { - close_file(fsp,ERROR_CLOSE); - reply_nterror(req, NT_STATUS_DISK_FULL); - return; - } - - /* Adjust size here to return the right size in the reply. - Windows does it this way. */ - size = fsp->initial_allocation_size; - } else { - fsp->initial_allocation_size = smb_roundup(fsp->conn,(SMB_BIG_UINT)size); - } - } - - if (ea_list && smb_action == FILE_WAS_CREATED) { - status = set_ea(conn, fsp, fname, ea_list); - if (!NT_STATUS_IS_OK(status)) { - close_file(fsp,ERROR_CLOSE); - reply_nterror(req, status); - return; - } - } - /* Realloc the size of parameters and data we will return */ *pparams = (char *)SMB_REALLOC(*pparams, 30); if(*pparams == NULL ) { @@ -997,7 +1061,7 @@ static void call_trans2open(connection_struct *conn, } /* Send the required number of replies */ - send_trans2_replies(req, params, 30, *ppdata, 0, max_data_bytes); + send_trans2_replies(conn, req, params, 30, *ppdata, 0, max_data_bytes); } /********************************************************* @@ -1993,9 +2057,7 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd out_of_space = True; finished = False; } else { - TALLOC_CTX *sub_ctx = talloc_stackframe(); - - finished = !get_lanman2_dir_entry(sub_ctx, + finished = !get_lanman2_dir_entry(ctx, conn, req->flags2, mask,dirtype,info_level, @@ -2004,8 +2066,6 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd space_remaining, &out_of_space, &got_exact_match, &last_entry_off, ea_list); - - TALLOC_FREE(sub_ctx); } if (finished && out_of_space) @@ -2067,7 +2127,7 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd SSVAL(params,6,0); /* Never an EA error */ SSVAL(params,8,last_entry_off); - send_trans2_replies(req, params, 10, pdata, PTR_DIFF(p,pdata), + send_trans2_replies(conn, req, params, 10, pdata, PTR_DIFF(p,pdata), max_data_bytes); if ((! *directory) && dptr_path(dptr_num)) { @@ -2341,9 +2401,7 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd out_of_space = True; finished = False; } else { - TALLOC_CTX *sub_ctx = talloc_stackframe(); - - finished = !get_lanman2_dir_entry(sub_ctx, + finished = !get_lanman2_dir_entry(ctx, conn, req->flags2, mask,dirtype,info_level, @@ -2352,8 +2410,6 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd space_remaining, &out_of_space, &got_exact_match, &last_entry_off, ea_list); - - TALLOC_FREE(sub_ctx); } if (finished && out_of_space) @@ -2391,7 +2447,7 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd SSVAL(params,4,0); /* Never an EA error */ SSVAL(params,6,last_entry_off); - send_trans2_replies(req, params, 8, pdata, PTR_DIFF(p,pdata), + send_trans2_replies(conn, req, params, 8, pdata, PTR_DIFF(p,pdata), max_data_bytes); return; @@ -2403,6 +2459,41 @@ unsigned char *create_volume_objectid(connection_struct *conn, unsigned char obj return objid; } +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_GIT_COMMIT_TIME + unix_to_nt_time(&extended_info->samba_gitcommitdate, SAMBA_VERSION_GIT_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()); +} + /**************************************************************************** Reply to a TRANS2_QFSINFO (query filesystem info). ****************************************************************************/ @@ -2421,8 +2512,8 @@ static void call_trans2qfsinfo(connection_struct *conn, const char *vname = volume_label(SNUM(conn)); int snum = SNUM(conn); char *fstype = lp_fstype(SNUM(conn)); - int quota_flag = 0; - + uint32 additional_flags = 0; + if (total_params < 2) { reply_nterror(req, NT_STATUS_INVALID_PARAMETER); return; @@ -2430,6 +2521,27 @@ static void call_trans2qfsinfo(connection_struct *conn, info_level = SVAL(params,0); + if (IS_IPC(conn)) { + if (info_level != SMB_QUERY_CIFS_UNIX_INFO) { + DEBUG(0,("call_trans2qfsinfo: not an allowed " + "info level (0x%x) on IPC$.\n", + (unsigned int)info_level)); + reply_nterror(req, NT_STATUS_ACCESS_DENIED); + return; + } + } + + if (ENCRYPTION_REQUIRED(conn) && !req->encrypted) { + if (info_level != SMB_QUERY_CIFS_UNIX_INFO) { + DEBUG(0,("call_trans2qfsinfo: encryption required " + "and info level 0x%x sent.\n", + (unsigned int)info_level)); + exit_server_cleanly("encryption required " + "on connection"); + return; + } + } + DEBUG(3,("call_trans2qfsinfo: level = %d\n", info_level)); if(SMB_VFS_STAT(conn,".",&st)!=0) { @@ -2514,16 +2626,21 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)st.st_dev, (unsi case SMB_QUERY_FS_ATTRIBUTE_INFO: case SMB_FS_ATTRIBUTE_INFORMATION: - + additional_flags = 0; #if defined(HAVE_SYS_QUOTAS) - quota_flag = FILE_VOLUME_QUOTAS; + additional_flags |= FILE_VOLUME_QUOTAS; #endif + if(lp_nt_acl_support(SNUM(conn))) { + additional_flags |= FILE_PERSISTENT_ACLS; + } + + /* Capabilities are filled in at connection time through STATVFS call */ + additional_flags |= conn->fs_capabilities; + SIVAL(pdata,0,FILE_CASE_PRESERVED_NAMES|FILE_CASE_SENSITIVE_SEARCH| - (lp_nt_acl_support(SNUM(conn)) ? FILE_PERSISTENT_ACLS : 0)| - FILE_SUPPORTS_OBJECT_IDS| - FILE_UNICODE_ON_DISK| - quota_flag); /* FS ATTRIBUTES */ + FILE_SUPPORTS_OBJECT_IDS|FILE_UNICODE_ON_DISK| + additional_flags); /* FS ATTRIBUTES */ SIVAL(pdata,4,255); /* Max filename component length */ /* NOTE! the fstype must *not* be null terminated or win98 won't recognise it @@ -2715,7 +2832,14 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned case SMB_FS_OBJECTID_INFORMATION: { unsigned char objid[16]; + struct smb_extended_info extended_info; memcpy(pdata,create_volume_objectid(conn, objid),16); + samba_extended_info_version (&extended_info); + SIVAL(pdata,16,extended_info.samba_magic); + SIVAL(pdata,20,extended_info.samba_version); + SIVAL(pdata,24,extended_info.samba_subversion); + SBIG_UINT(pdata,28,extended_info.samba_gitcommitdate); + memcpy(pdata+36,extended_info.samba_version_string,28); data_len = 64; break; } @@ -2729,22 +2853,45 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned { bool large_write = lp_min_receive_file_size() && !srv_is_signing_active(); + bool large_read = !srv_is_signing_active(); + int encrypt_caps = 0; if (!lp_unix_extensions()) { reply_nterror(req, NT_STATUS_INVALID_LEVEL); return; } + + switch (conn->encrypt_level) { + case 0: + encrypt_caps = 0; + break; + case 1: + case Auto: + encrypt_caps = CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP; + break; + case Required: + encrypt_caps = CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP| + CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP; + large_write = false; + large_read = false; + break; + } + data_len = 12; SSVAL(pdata,0,CIFS_UNIX_MAJOR_VERSION); SSVAL(pdata,2,CIFS_UNIX_MINOR_VERSION); - /* We have POSIX ACLs, pathname and locking capability. */ + + /* We have POSIX ACLs, pathname, encryption, + * large read/write, and locking capability. */ + SBIG_UINT(pdata,4,((SMB_BIG_UINT)( CIFS_UNIX_POSIX_ACLS_CAP| CIFS_UNIX_POSIX_PATHNAMES_CAP| CIFS_UNIX_FCNTL_LOCKS_CAP| CIFS_UNIX_EXTATTR_CAP| CIFS_UNIX_POSIX_PATH_OPERATIONS_CAP| - CIFS_UNIX_LARGE_READ_CAP| + encrypt_caps| + (large_read ? CIFS_UNIX_LARGE_READ_CAP : 0) | (large_write ? CIFS_UNIX_LARGE_WRITE_CAP : 0)))); break; @@ -2864,8 +3011,8 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned */ for (i = 0, sid_bytes = 0; i < current_user.nt_user_token->num_sids; ++i) { - sid_bytes += - sid_size(¤t_user.nt_user_token->user_sids[i]); + sid_bytes += ndr_size_dom_sid( + ¤t_user.nt_user_token->user_sids[i], 0); } /* SID list byte count */ @@ -2885,8 +3032,8 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned /* SID list */ for (i = 0; i < current_user.nt_user_token->num_sids; ++i) { - int sid_len = - sid_size(¤t_user.nt_user_token->user_sids[i]); + int sid_len = ndr_size_dom_sid( + ¤t_user.nt_user_token->user_sids[i], 0); sid_linearize(pdata + data_len, sid_len, ¤t_user.nt_user_token->user_sids[i]); @@ -2913,7 +3060,7 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned } - send_trans2_replies(req, params, 0, pdata, data_len, + send_trans2_replies(conn, req, params, 0, pdata, data_len, max_data_bytes); DEBUG( 4, ( "%s info_level = %d\n", @@ -2948,6 +3095,28 @@ static void call_trans2setfsinfo(connection_struct *conn, info_level = SVAL(params,2); + if (IS_IPC(conn)) { + if (info_level != SMB_REQUEST_TRANSPORT_ENCRYPTION && + info_level != SMB_SET_CIFS_UNIX_INFO) { + DEBUG(0,("call_trans2setfsinfo: not an allowed " + "info level (0x%x) on IPC$.\n", + (unsigned int)info_level)); + reply_nterror(req, NT_STATUS_ACCESS_DENIED); + return; + } + } + + if (ENCRYPTION_REQUIRED(conn) && !req->encrypted) { + if (info_level != SMB_REQUEST_TRANSPORT_ENCRYPTION) { + DEBUG(0,("call_trans2setfsinfo: encryption required " + "and info level 0x%x sent.\n", + (unsigned int)info_level)); + exit_server_cleanly("encryption required " + "on connection"); + return; + } + } + switch(info_level) { case SMB_SET_CIFS_UNIX_INFO: { @@ -2998,6 +3167,62 @@ cap_low = 0x%x, cap_high = 0x%x\n", } break; } + + case SMB_REQUEST_TRANSPORT_ENCRYPTION: + { + NTSTATUS status; + size_t param_len = 0; + size_t data_len = total_data; + + if (!lp_unix_extensions()) { + reply_nterror( + req, + NT_STATUS_INVALID_LEVEL); + return; + } + + if (lp_smb_encrypt(SNUM(conn)) == false) { + reply_nterror( + req, + NT_STATUS_NOT_SUPPORTED); + return; + } + + DEBUG( 4,("call_trans2setfsinfo: " + "request transport encrption.\n")); + + status = srv_request_encryption_setup(conn, + (unsigned char **)ppdata, + &data_len, + (unsigned char **)pparams, + ¶m_len); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && + !NT_STATUS_IS_OK(status)) { + reply_nterror(req, status); + return; + } + + send_trans2_replies(conn, req, + *pparams, + param_len, + *ppdata, + data_len, + max_data_bytes); + + if (NT_STATUS_IS_OK(status)) { + /* Server-side transport + * encryption is now *on*. */ + status = srv_encryption_start(conn); + if (!NT_STATUS_IS_OK(status)) { + exit_server_cleanly( + "Failure in setting " + "up encrypted transport"); + } + } + return; + } + case SMB_FS_QUOTA_INFORMATION: { files_struct *fsp = NULL; @@ -3389,6 +3614,72 @@ static char *store_file_unix_basic_info2(connection_struct *conn, return pdata; } +static NTSTATUS marshall_stream_info(unsigned int num_streams, + const struct stream_struct *streams, + char *data, + unsigned int max_data_bytes, + unsigned int *data_size) +{ + unsigned int i; + unsigned int ofs = 0; + + for (i=0; i<num_streams; i++) { + unsigned int next_offset; + size_t namelen; + smb_ucs2_t *namebuf; + + namelen = push_ucs2_talloc(talloc_tos(), &namebuf, + streams[i].name); + + if ((namelen == (size_t)-1) || (namelen <= 2)) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* + * name_buf is now null-terminated, we need to marshall as not + * terminated + */ + + namelen -= 2; + + if (ofs + 24 + namelen > max_data_bytes) { + TALLOC_FREE(namebuf); + return NT_STATUS_BUFFER_TOO_SMALL; + } + + SIVAL(data, ofs+4, namelen); + SOFF_T(data, ofs+8, streams[i].size); + SOFF_T(data, ofs+16, streams[i].alloc_size); + memcpy(data+ofs+24, namebuf, namelen); + TALLOC_FREE(namebuf); + + next_offset = ofs + 24 + namelen; + + if (i == num_streams-1) { + SIVAL(data, ofs, 0); + } + else { + unsigned int align = ndr_align_size(next_offset, 8); + + if (next_offset + align > max_data_bytes) { + return NT_STATUS_BUFFER_TOO_SMALL; + } + + memset(data+next_offset, 0, align); + next_offset += align; + + SIVAL(data, ofs, next_offset - ofs); + ofs = next_offset; + } + + ofs = next_offset; + } + + *data_size = ofs; + + return NT_STATUS_OK; +} + /**************************************************************************** Reply to a TRANSACT2_QFILEINFO on a PIPE ! ****************************************************************************/ @@ -3454,7 +3745,7 @@ static void call_trans2qpipeinfo(connection_struct *conn, return; } - send_trans2_replies(req, params, param_size, *ppdata, data_size, + send_trans2_replies(conn, req, params, param_size, *ppdata, data_size, max_data_bytes); return; @@ -3579,7 +3870,7 @@ static void call_trans2qfilepathinfo(connection_struct *conn, return; } - if (SMB_VFS_FSTAT(fsp,fsp->fh->fd,&sbuf) != 0) { + if (SMB_VFS_FSTAT(fsp, &sbuf) != 0) { DEBUG(3,("fstat of fnum %d failed (%s)\n", fsp->fnum, strerror(errno))); reply_unixerror(req, ERRDOS, ERRbadfid); return; @@ -3664,17 +3955,6 @@ static void call_trans2qfilepathinfo(connection_struct *conn, } } - nlink = sbuf.st_nlink; - - if ((nlink > 0) && S_ISDIR(sbuf.st_mode)) { - /* NTFS does not seem to count ".." */ - nlink -= 1; - } - - if ((nlink > 0) && delete_pending) { - nlink -= 1; - } - if (INFO_LEVEL_IS_UNIX(info_level) && !lp_unix_extensions()) { reply_nterror(req, NT_STATUS_INVALID_LEVEL); return; @@ -3693,6 +3973,16 @@ static void call_trans2qfilepathinfo(connection_struct *conn, if (!mode) mode = FILE_ATTRIBUTE_NORMAL; + nlink = sbuf.st_nlink; + + if (nlink && (mode&aDIR)) { + nlink = 1; + } + + if ((nlink > 0) && delete_pending) { + nlink -= 1; + } + fullpathname = fname; if (!(mode & aDIR)) file_size = get_file_size(sbuf); @@ -4087,28 +4377,49 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd data_size = 4; break; -#if 0 /* - * NT4 server just returns "invalid query" to this - if we try to answer - * it then NTws gets a BSOD! (tridge). - * W2K seems to want this. JRA. + * NT4 server just returns "invalid query" to this - if we try + * to answer it then NTws gets a BSOD! (tridge). W2K seems to + * want this. JRA. + */ + /* The first statement above is false - verified using Thursby + * client against NT4 -- gcolley. */ case SMB_QUERY_FILE_STREAM_INFO: -#endif - case SMB_FILE_STREAM_INFORMATION: - DEBUG(10,("call_trans2qfilepathinfo: SMB_FILE_STREAM_INFORMATION\n")); - if (mode & aDIR) { - data_size = 0; - } else { - size_t byte_len = dos_PutUniCode(pdata+24,"::$DATA", (size_t)0xE, False); - SIVAL(pdata,0,0); /* ??? */ - SIVAL(pdata,4,byte_len); /* Byte length of unicode string ::$DATA */ - SOFF_T(pdata,8,file_size); - SOFF_T(pdata,16,allocation_size); - data_size = 24 + byte_len; + case SMB_FILE_STREAM_INFORMATION: { + unsigned int num_streams; + struct stream_struct *streams; + NTSTATUS status; + + DEBUG(10,("call_trans2qfilepathinfo: " + "SMB_FILE_STREAM_INFORMATION\n")); + + status = SMB_VFS_STREAMINFO( + conn, fsp, fname, talloc_tos(), + &num_streams, &streams); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("could not get stream info: %s\n", + nt_errstr(status))); + reply_nterror(req, status); + return; } - break; + status = marshall_stream_info(num_streams, streams, + pdata, max_data_bytes, + &data_size); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("marshall_stream_info failed: %s\n", + nt_errstr(status))); + reply_nterror(req, status); + return; + } + + TALLOC_FREE(streams); + + break; + } case SMB_QUERY_COMPRESSION_INFO: case SMB_FILE_COMPRESSION_INFORMATION: DEBUG(10,("call_trans2qfilepathinfo: SMB_FILE_COMPRESSION_INFORMATION\n")); @@ -4221,7 +4532,7 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd uint16 num_def_acls = 0; if (fsp && !fsp->is_directory && (fsp->fh->fd != -1)) { - file_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fsp->fh->fd); + file_acl = SMB_VFS_SYS_ACL_GET_FD(fsp); } else { file_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS); } @@ -4386,7 +4697,7 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd return; } - send_trans2_replies(req, params, param_size, *ppdata, data_size, + send_trans2_replies(conn, req, params, param_size, *ppdata, data_size, max_data_bytes); return; @@ -4675,17 +4986,12 @@ static NTSTATUS smb_info_set_ea(connection_struct *conn, return NT_STATUS_INVALID_PARAMETER; } - ctx = talloc_init("SMB_INFO_SET_EA"); - if (!ctx) { - return NT_STATUS_NO_MEMORY; - } + ctx = talloc_tos(); ea_list = read_ea_list(ctx, pdata + 4, total_data - 4); if (!ea_list) { - talloc_destroy(ctx); return NT_STATUS_INVALID_PARAMETER; } status = set_ea(conn, fsp, fname, ea_list); - talloc_destroy(ctx); return status; } @@ -5090,8 +5396,7 @@ static NTSTATUS smb_set_posix_acl(connection_struct *conn, ****************************************************************************/ static NTSTATUS smb_set_posix_lock(connection_struct *conn, - const uint8 *inbuf, - int length, + const struct smb_request *req, const char *pdata, int total_data, files_struct *fsp) @@ -5101,6 +5406,7 @@ static NTSTATUS smb_set_posix_lock(connection_struct *conn, uint32 lock_pid; bool blocking_lock = False; enum brl_type lock_type; + NTSTATUS status = NT_STATUS_OK; if (fsp == NULL || fsp->fh->fd == -1) { @@ -5188,7 +5494,7 @@ static NTSTATUS smb_set_posix_lock(connection_struct *conn, * onto the blocking lock queue. */ if(push_blocking_lock_request(br_lck, - (char *)inbuf, length, + req, fsp, -1, /* infinite timeout. */ 0, @@ -6124,7 +6430,7 @@ static NTSTATUS smb_posix_unlink(connection_struct *conn, * non-POSIX opens return SHARING_VIOLATION. */ - lck = get_share_mode_lock(NULL, fsp->file_id, NULL, NULL); + lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL); if (lck == NULL) { DEBUG(0, ("smb_posix_unlink: Could not get share mode " "lock for file %s\n", fsp->fsp_name)); @@ -6246,7 +6552,7 @@ static void call_trans2setfilepathinfo(connection_struct *conn, DEBUG(3,("call_trans2setfilepathinfo: Cancelling print job (%s)\n", fsp->fsp_name )); SSVAL(params,0,0); - send_trans2_replies(req, params, 2, + send_trans2_replies(conn, req, params, 2, *ppdata, 0, max_data_bytes); return; @@ -6262,7 +6568,7 @@ static void call_trans2setfilepathinfo(connection_struct *conn, return; } - if (SMB_VFS_FSTAT(fsp, fsp->fh->fd, &sbuf) != 0) { + if (SMB_VFS_FSTAT(fsp, &sbuf) != 0) { DEBUG(3,("call_trans2setfilepathinfo: fstat of fnum %d failed (%s)\n",fsp->fnum, strerror(errno))); reply_unixerror(req, ERRDOS, ERRbadfid); return; @@ -6536,8 +6842,7 @@ static void call_trans2setfilepathinfo(connection_struct *conn, reply_nterror(req, NT_STATUS_INVALID_LEVEL); return; } - status = smb_set_posix_lock(conn, req->inbuf, - smb_len(req->inbuf) + 4, + status = smb_set_posix_lock(conn, req, pdata, total_data, fsp); break; } @@ -6605,7 +6910,7 @@ static void call_trans2setfilepathinfo(connection_struct *conn, } SSVAL(params,0,0); - send_trans2_replies(req, params, 2, *ppdata, data_return_size, + send_trans2_replies(conn, req, params, 2, *ppdata, data_return_size, max_data_bytes); return; @@ -6697,7 +7002,7 @@ static void call_trans2mkdir(connection_struct *conn, struct smb_request *req, return; } - status = create_directory(conn, directory); + status = create_directory(conn, req, directory); if (!NT_STATUS_IS_OK(status)) { reply_nterror(req, status); @@ -6723,7 +7028,7 @@ static void call_trans2mkdir(connection_struct *conn, struct smb_request *req, SSVAL(params,0,0); - send_trans2_replies(req, params, 2, *ppdata, 0, max_data_bytes); + send_trans2_replies(conn, req, params, 2, *ppdata, 0, max_data_bytes); return; } @@ -6777,7 +7082,7 @@ static void call_trans2findnotifyfirst(connection_struct *conn, if(fnf_handle == 0) fnf_handle = 257; - send_trans2_replies(req, params, 6, *ppdata, 0, max_data_bytes); + send_trans2_replies(conn, req, params, 6, *ppdata, 0, max_data_bytes); return; } @@ -6808,7 +7113,7 @@ static void call_trans2findnotifynext(connection_struct *conn, SSVAL(params,0,0); /* No changes */ SSVAL(params,2,0); /* No EA errors */ - send_trans2_replies(req, params, 4, *ppdata, 0, max_data_bytes); + send_trans2_replies(conn, req, params, 4, *ppdata, 0, max_data_bytes); return; } @@ -6858,7 +7163,7 @@ static void call_trans2getdfsreferral(connection_struct *conn, SSVAL(req->inbuf, smb_flg2, SVAL(req->inbuf,smb_flg2) | FLAGS2_DFS_PATHNAMES); - send_trans2_replies(req,0,0,*ppdata,reply_size, max_data_bytes); + send_trans2_replies(conn, req,0,0,*ppdata,reply_size, max_data_bytes); return; } @@ -6905,7 +7210,7 @@ static void call_trans2ioctl(connection_struct *conn, srvstr_push(pdata, req->flags2, pdata+18, lp_servicename(SNUM(conn)), 13, STR_ASCII|STR_TERMINATE); /* Service name */ - send_trans2_replies(req, *pparams, 0, *ppdata, 32, + send_trans2_replies(conn, req, *pparams, 0, *ppdata, 32, max_data_bytes); return; } @@ -6918,7 +7223,7 @@ static void call_trans2ioctl(connection_struct *conn, Reply to a SMBfindclose (stop trans2 directory search). ****************************************************************************/ -void reply_findclose(connection_struct *conn, struct smb_request *req) +void reply_findclose(struct smb_request *req) { int dptr_num; @@ -6948,7 +7253,7 @@ void reply_findclose(connection_struct *conn, struct smb_request *req) Reply to a SMBfindnclose (stop FINDNOTIFYFIRST directory search). ****************************************************************************/ -void reply_findnclose(connection_struct *conn, struct smb_request *req) +void reply_findnclose(struct smb_request *req) { int dptr_num; @@ -6984,6 +7289,17 @@ static void handle_trans2(connection_struct *conn, struct smb_request *req, SSVAL(req->inbuf,smb_flg2,req->flags2); } + if (conn->encrypt_level == Required && !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; + } + } + /* Now we must call the relevant TRANS2 function */ switch(state->call) { case TRANSACT2_OPEN: @@ -7131,8 +7447,9 @@ static void handle_trans2(connection_struct *conn, struct smb_request *req, Reply to a SMBtrans2. ****************************************************************************/ -void reply_trans2(connection_struct *conn, struct smb_request *req) +void reply_trans2(struct smb_request *req) { + connection_struct *conn = req->conn; unsigned int dsoff; unsigned int dscnt; unsigned int psoff; @@ -7166,12 +7483,20 @@ void reply_trans2(connection_struct *conn, struct smb_request *req) return; } - if (IS_IPC(conn) && (tran_call != TRANSACT2_OPEN) - && (tran_call != TRANSACT2_GET_DFS_REFERRAL) - && (tran_call != TRANSACT2_QFILEINFO)) { - reply_doserror(req, ERRSRV, ERRaccess); - 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_doserror(req, ERRSRV, ERRaccess); + END_PROFILE(SMBtrans2); + return; + } } if ((state = TALLOC_P(conn->mem_ctx, struct trans_state)) == NULL) { @@ -7312,8 +7637,9 @@ void reply_trans2(connection_struct *conn, struct smb_request *req) Reply to a SMBtranss2 ****************************************************************************/ -void reply_transs2(connection_struct *conn, struct smb_request *req) +void reply_transs2(struct smb_request *req) { + connection_struct *conn = req->conn; unsigned int pcnt,poff,dcnt,doff,pdisp,ddisp; struct trans_state *state; int size; diff --git a/source3/smbd/utmp.c b/source3/smbd/utmp.c index e82bbea3b3..5931b2b1d0 100644 --- a/source3/smbd/utmp.c +++ b/source3/smbd/utmp.c @@ -223,7 +223,7 @@ static char *uw_pathname(TALLOC_CTX *ctx, } /* For u-files and non-explicit w-dir, look for "utmp dir" */ - if (!dirname == 0 || strlen(dirname) == 0) { + if ((dirname == NULL) || (strlen(dirname) == 0)) { dirname = talloc_strdup(ctx, lp_utmpdir()); if (!dirname) { return NULL; diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c index 628d2eec4b..33a3a43aa4 100644 --- a/source3/smbd/vfs.c +++ b/source3/smbd/vfs.c @@ -31,7 +31,7 @@ static_decl_vfs; struct vfs_init_function_entry { char *name; - vfs_op_tuple *vfs_op_tuples; + const vfs_op_tuple *vfs_op_tuples; struct vfs_init_function_entry *prev, *next; }; @@ -55,7 +55,7 @@ static struct vfs_init_function_entry *vfs_find_backend_entry(const char *name) return NULL; } -NTSTATUS smb_register_vfs(int version, const char *name, vfs_op_tuple *vfs_op_tuples) +NTSTATUS smb_register_vfs(int version, const char *name, const vfs_op_tuple *vfs_op_tuples) { struct vfs_init_function_entry *entry = backends; @@ -110,13 +110,13 @@ static inline void vfs_set_operation(struct vfs_ops * vfs, vfs_op_type which, bool vfs_init_custom(connection_struct *conn, const char *vfs_object) { - vfs_op_tuple *ops; + const vfs_op_tuple *ops; char *module_path = NULL; char *module_name = NULL; char *module_param = NULL, *p; int i; vfs_handle_struct *handle; - struct vfs_init_function_entry *entry; + const struct vfs_init_function_entry *entry; if (!conn||!vfs_object||!vfs_object[0]) { DEBUG(0,("vfs_init_custon() called with NULL pointer or emtpy vfs_object!\n")); @@ -263,19 +263,31 @@ void vfs_remove_fsp_extension(vfs_handle_struct *handle, files_struct *fsp) } } -void *vfs_fetch_fsp_extension(vfs_handle_struct *handle, files_struct *fsp) +void *vfs_memctx_fsp_extension(vfs_handle_struct *handle, files_struct *fsp) { struct vfs_fsp_data *head; for (head = fsp->vfs_extension; head; head = head->next) { if (head->owner == handle) { - return EXT_DATA_AREA(head); + return head; } } return NULL; } +void *vfs_fetch_fsp_extension(vfs_handle_struct *handle, files_struct *fsp) +{ + struct vfs_fsp_data *head; + + head = (struct vfs_fsp_data *)vfs_memctx_fsp_extension(handle, fsp); + if (head != NULL) { + return EXT_DATA_AREA(head); + } + + return NULL; +} + #undef EXT_DATA_AREA /***************************************************************** @@ -377,8 +389,8 @@ ssize_t vfs_read_data(files_struct *fsp, char *buf, size_t byte_count) while (total < byte_count) { - ssize_t ret = SMB_VFS_READ(fsp, fsp->fh->fd, buf + total, - byte_count - total); + ssize_t ret = SMB_VFS_READ(fsp, buf + total, + byte_count - total); if (ret == 0) return total; if (ret == -1) { @@ -399,7 +411,7 @@ ssize_t vfs_pread_data(files_struct *fsp, char *buf, while (total < byte_count) { - ssize_t ret = SMB_VFS_PREAD(fsp, fsp->fh->fd, buf + total, + ssize_t ret = SMB_VFS_PREAD(fsp, buf + total, byte_count - total, offset + total); if (ret == 0) return total; @@ -433,13 +445,12 @@ ssize_t vfs_write_data(struct smb_request *req, req->unread_bytes = 0; return SMB_VFS_RECVFILE(smbd_server_fd(), fsp, - fsp->fh->fd, (SMB_OFF_T)-1, N); } while (total < N) { - ret = SMB_VFS_WRITE(fsp,fsp->fh->fd,buffer + total,N - total); + ret = SMB_VFS_WRITE(fsp, buffer + total, N - total); if (ret == -1) return -1; @@ -467,14 +478,13 @@ ssize_t vfs_pwrite_data(struct smb_request *req, req->unread_bytes = 0; return SMB_VFS_RECVFILE(smbd_server_fd(), fsp, - fsp->fh->fd, offset, N); } while (total < N) { - ret = SMB_VFS_PWRITE(fsp, fsp->fh->fd, buffer + total, - N - total, offset + total); + ret = SMB_VFS_PWRITE(fsp, buffer + total, N - total, + offset + total); if (ret == -1) return -1; @@ -513,7 +523,7 @@ int vfs_allocate_file_space(files_struct *fsp, SMB_BIG_UINT len) return -1; } - ret = SMB_VFS_FSTAT(fsp,fsp->fh->fd,&st); + ret = SMB_VFS_FSTAT(fsp, &st); if (ret == -1) return ret; @@ -527,7 +537,7 @@ int vfs_allocate_file_space(files_struct *fsp, SMB_BIG_UINT len) fsp->fsp_name, (double)st.st_size )); flush_write_cache(fsp, SIZECHANGE_FLUSH); - if ((ret = SMB_VFS_FTRUNCATE(fsp, fsp->fh->fd, (SMB_OFF_T)len)) != -1) { + if ((ret = SMB_VFS_FTRUNCATE(fsp, (SMB_OFF_T)len)) != -1) { set_filelen_write_cache(fsp, len); } return ret; @@ -569,7 +579,7 @@ int vfs_set_filelen(files_struct *fsp, SMB_OFF_T len) release_level_2_oplocks_on_change(fsp); DEBUG(10,("vfs_set_filelen: ftruncate %s to len %.0f\n", fsp->fsp_name, (double)len)); flush_write_cache(fsp, SIZECHANGE_FLUSH); - if ((ret = SMB_VFS_FTRUNCATE(fsp, fsp->fh->fd, len)) != -1) { + if ((ret = SMB_VFS_FTRUNCATE(fsp, len)) != -1) { set_filelen_write_cache(fsp, len); notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED, FILE_NOTIFY_CHANGE_SIZE @@ -600,7 +610,7 @@ int vfs_fill_sparse(files_struct *fsp, SMB_OFF_T len) ssize_t pwrite_ret; release_level_2_oplocks_on_change(fsp); - ret = SMB_VFS_FSTAT(fsp,fsp->fh->fd,&st); + ret = SMB_VFS_FSTAT(fsp, &st); if (ret == -1) { return ret; } @@ -629,7 +639,7 @@ int vfs_fill_sparse(files_struct *fsp, SMB_OFF_T len) while (total < num_to_write) { size_t curr_write_size = MIN(SPARSE_BUF_WRITE_SIZE, (num_to_write - total)); - pwrite_ret = SMB_VFS_PWRITE(fsp, fsp->fh->fd, sparse_buf, curr_write_size, offset + total); + pwrite_ret = SMB_VFS_PWRITE(fsp, sparse_buf, curr_write_size, offset + total); if (pwrite_ret == -1) { DEBUG(10,("vfs_fill_sparse: SMB_VFS_PWRITE for file %s failed with error %s\n", fsp->fsp_name, strerror(errno) )); @@ -650,25 +660,24 @@ int vfs_fill_sparse(files_struct *fsp, SMB_OFF_T len) Transfer some data (n bytes) between two file_struct's. ****************************************************************************/ -static files_struct *in_fsp; -static files_struct *out_fsp; - -static ssize_t read_fn(int fd, void *buf, size_t len) +static ssize_t vfs_read_fn(void *file, void *buf, size_t len) { - return SMB_VFS_READ(in_fsp, fd, buf, len); + struct files_struct *fsp = (struct files_struct *)file; + + return SMB_VFS_READ(fsp, buf, len); } -static ssize_t write_fn(int fd, const void *buf, size_t len) +static ssize_t vfs_write_fn(void *file, const void *buf, size_t len) { - return SMB_VFS_WRITE(out_fsp, fd, buf, len); + struct files_struct *fsp = (struct files_struct *)file; + + return SMB_VFS_WRITE(fsp, buf, len); } SMB_OFF_T vfs_transfer_file(files_struct *in, files_struct *out, SMB_OFF_T n) { - in_fsp = in; - out_fsp = out; - - return transfer_file_internal(in_fsp->fh->fd, out_fsp->fh->fd, n, read_fn, write_fn); + return transfer_file_internal((void *)in, (void *)out, n, + vfs_read_fn, vfs_write_fn); } /******************************************************************* @@ -731,152 +740,98 @@ int vfs_ChDir(connection_struct *conn, const char *path) return(res); } -/* number of list structures for a caching GetWd function. */ -#define MAX_GETWDCACHE (50) - -static struct { - SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */ - SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */ - char *path; /* The pathname. */ - bool valid; -} ino_list[MAX_GETWDCACHE]; - -extern bool use_getwd_cache; - -/**************************************************************************** - Prompte a ptr (to make it recently used) -****************************************************************************/ - -static void array_promote(char *array,int elsize,int element) -{ - char *p; - if (element == 0) - return; - - p = (char *)SMB_MALLOC(elsize); - - if (!p) { - DEBUG(5,("array_promote: malloc fail\n")); - return; - } - - memcpy(p,array + element * elsize, elsize); - memmove(array + elsize,array,elsize*element); - memcpy(array,p,elsize); - SAFE_FREE(p); -} - /******************************************************************* Return the absolute current directory path - given a UNIX pathname. Note that this path is returned in DOS format, not UNIX format. Note this can be called with conn == NULL. ********************************************************************/ +struct getwd_cache_key { + SMB_DEV_T dev; + SMB_INO_T ino; +}; + char *vfs_GetWd(TALLOC_CTX *ctx, connection_struct *conn) { char s[PATH_MAX+1]; - static bool getwd_cache_init = False; SMB_STRUCT_STAT st, st2; - int i; - char *ret = NULL; + char *result; + DATA_BLOB cache_value; + struct getwd_cache_key key; *s = 0; - if (!use_getwd_cache) { - nocache: - ret = SMB_VFS_GETWD(conn,s); - if (!ret) { - DEBUG(0,("vfs_GetWd: SMB_VFS_GETWD call failed, " - "errno %s\n",strerror(errno))); - return NULL; - } - return talloc_strdup(ctx, ret); - } - - /* init the cache */ - if (!getwd_cache_init) { - getwd_cache_init = True; - for (i=0;i<MAX_GETWDCACHE;i++) { - string_set(&ino_list[i].path,""); - ino_list[i].valid = False; - } + if (!lp_getwd_cache()) { + goto nocache; } - /* Get the inode of the current directory, if this doesn't work we're - in trouble :-) */ + SET_STAT_INVALID(st); if (SMB_VFS_STAT(conn, ".",&st) == -1) { - /* Known to fail for root: the directory may be - * NFS-mounted and exported with root_squash (so has no root access). */ + /* + * Known to fail for root: the directory may be NFS-mounted + * and exported with root_squash (so has no root access). + */ DEBUG(1,("vfs_GetWd: couldn't stat \".\" error %s " - "(NFS problem ?)\n", - strerror(errno) )); + "(NFS problem ?)\n", strerror(errno) )); goto nocache; } + ZERO_STRUCT(key); /* unlikely, but possible padding */ + key.dev = st.st_dev; + key.ino = st.st_ino; - for (i=0; i<MAX_GETWDCACHE; i++) { - if (ino_list[i].valid) { - - /* If we have found an entry with a matching inode and dev number - then find the inode number for the directory in the cached string. - If this agrees with that returned by the stat for the current - directory then all is o.k. (but make sure it is a directory all - the same...) */ - - if (st.st_ino == ino_list[i].inode && st.st_dev == ino_list[i].dev) { - if (SMB_VFS_STAT(conn,ino_list[i].path,&st2) == 0) { - if (st.st_ino == st2.st_ino && st.st_dev == st2.st_dev && - (st2.st_mode & S_IFMT) == S_IFDIR) { + if (!memcache_lookup(smbd_memcache(), GETWD_CACHE, + data_blob_const(&key, sizeof(key)), + &cache_value)) { + goto nocache; + } - ret = talloc_strdup(ctx, - ino_list[i].path); + SMB_ASSERT((cache_value.length > 0) + && (cache_value.data[cache_value.length-1] == '\0')); - /* promote it for future use */ - array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i); - if (ret == NULL) { - errno = ENOMEM; - } - return ret; - } else { - /* If the inode is different then something's changed, - scrub the entry and start from scratch. */ - ino_list[i].valid = False; - } - } - } + if ((SMB_VFS_STAT(conn, (char *)cache_value.data, &st2) == 0) + && (st.st_dev == st2.st_dev) && (st.st_ino == st2.st_ino) + && (S_ISDIR(st.st_mode))) { + /* + * Ok, we're done + */ + result = talloc_strdup(ctx, (char *)cache_value.data); + if (result == NULL) { + errno = ENOMEM; } + return result; } - /* We don't have the information to hand so rely on traditional - * methods. The very slow getcwd, which spawns a process on some - * systems, or the not quite so bad getwd. */ + nocache: + + /* + * We don't have the information to hand so rely on traditional + * methods. The very slow getcwd, which spawns a process on some + * systems, or the not quite so bad getwd. + */ if (!SMB_VFS_GETWD(conn,s)) { - DEBUG(0,("vfs_GetWd: SMB_VFS_GETWD call failed, errno %s\n", - strerror(errno))); - return (NULL); + DEBUG(0, ("vfs_GetWd: SMB_VFS_GETWD call failed: %s\n", + strerror(errno))); + return NULL; } - ret = talloc_strdup(ctx,s); - - DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n", - s,(double)st.st_ino,(double)st.st_dev)); + if (lp_getwd_cache() && VALID_STAT(st)) { + ZERO_STRUCT(key); /* unlikely, but possible padding */ + key.dev = st.st_dev; + key.ino = st.st_ino; - /* add it to the cache */ - i = MAX_GETWDCACHE - 1; - string_set(&ino_list[i].path,s); - ino_list[i].dev = st.st_dev; - ino_list[i].inode = st.st_ino; - ino_list[i].valid = True; - - /* put it at the top of the list */ - array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i); + memcache_add(smbd_memcache(), GETWD_CACHE, + data_blob_const(&key, sizeof(key)), + data_blob_const(s, strlen(s)+1)); + } - if (ret == NULL) { + result = talloc_strdup(ctx, s); + if (result == NULL) { errno = ENOMEM; } - return ret; + return result; } /******************************************************************* @@ -911,14 +866,13 @@ NTSTATUS check_reduced_name(connection_struct *conn, const char *fname) return map_nt_error_from_unix(errno); case ENOENT: { - TALLOC_CTX *tmp_ctx = talloc_stackframe(); + TALLOC_CTX *ctx = talloc_tos(); char *tmp_fname = NULL; char *last_component = NULL; /* Last component didn't exist. Remove it and try and canonicalise the directory. */ - tmp_fname = talloc_strdup(tmp_ctx, fname); + tmp_fname = talloc_strdup(ctx, fname); if (!tmp_fname) { - TALLOC_FREE(tmp_ctx); return NT_STATUS_NO_MEMORY; } p = strrchr_m(tmp_fname, '/'); @@ -927,10 +881,9 @@ NTSTATUS check_reduced_name(connection_struct *conn, const char *fname) last_component = p; } else { last_component = tmp_fname; - tmp_fname = talloc_strdup(tmp_ctx, + tmp_fname = talloc_strdup(ctx, "."); if (!tmp_fname) { - TALLOC_FREE(tmp_ctx); return NT_STATUS_NO_MEMORY; } } @@ -942,15 +895,13 @@ NTSTATUS check_reduced_name(connection_struct *conn, const char *fname) #endif if (!resolved_name) { DEBUG(3,("reduce_name: couldn't get realpath for %s\n", fname)); - TALLOC_FREE(tmp_ctx); return map_nt_error_from_unix(errno); } - tmp_fname = talloc_asprintf(tmp_ctx, + tmp_fname = talloc_asprintf(ctx, "%s/%s", resolved_name, last_component); if (!tmp_fname) { - TALLOC_FREE(tmp_ctx); return NT_STATUS_NO_MEMORY; } #ifdef REALPATH_TAKES_NULL @@ -964,7 +915,6 @@ NTSTATUS check_reduced_name(connection_struct *conn, const char *fname) safe_strcpy(resolved_name_buf, tmp_fname, PATH_MAX); resolved_name = resolved_name_buf; #endif - TALLOC_FREE(tmp_ctx); break; } default: |