diff options
author | Karolin Seeger <kseeger@samba.org> | 2008-02-29 10:44:38 +0100 |
---|---|---|
committer | Karolin Seeger <kseeger@samba.org> | 2008-02-29 10:44:38 +0100 |
commit | c4fbe2846231a6b322c1094c6a1dbf93b7305768 (patch) | |
tree | 09eb77a294f4acda131b41fe4c9bec4ed175eb16 /source3/smbd | |
parent | 1a6415fc77c708b87c8e2ce6e7828f486ffc87ac (diff) | |
parent | 695b6662abe64a40061bfa05ede12173fc4b1945 (diff) | |
download | samba-c4fbe2846231a6b322c1094c6a1dbf93b7305768.tar.gz samba-c4fbe2846231a6b322c1094c6a1dbf93b7305768.tar.bz2 samba-c4fbe2846231a6b322c1094c6a1dbf93b7305768.zip |
Merge commit 'origin/v3-2-test' into v3-2-stable
Conflicts:
WHATSNEW.txt
(This used to be commit a390bcf9403df4cf4d5eef42b35ebccbe253882e)
Diffstat (limited to 'source3/smbd')
-rw-r--r-- | source3/smbd/aio.c | 51 | ||||
-rw-r--r-- | source3/smbd/chgpasswd.c | 31 | ||||
-rw-r--r-- | source3/smbd/close.c | 115 | ||||
-rw-r--r-- | source3/smbd/connection.c | 9 | ||||
-rw-r--r-- | source3/smbd/dmapi.c | 266 | ||||
-rw-r--r-- | source3/smbd/dosmode.c | 55 | ||||
-rw-r--r-- | source3/smbd/filename.c | 123 | ||||
-rw-r--r-- | source3/smbd/lanman.c | 2 | ||||
-rw-r--r-- | source3/smbd/map_username.c | 6 | ||||
-rw-r--r-- | source3/smbd/msdfs.c | 5 | ||||
-rw-r--r-- | source3/smbd/notify_inotify.c | 8 | ||||
-rw-r--r-- | source3/smbd/nttrans.c | 13 | ||||
-rw-r--r-- | source3/smbd/open.c | 227 | ||||
-rw-r--r-- | source3/smbd/oplock_irix.c | 2 | ||||
-rw-r--r-- | source3/smbd/oplock_linux.c | 22 | ||||
-rw-r--r-- | source3/smbd/password.c | 31 | ||||
-rw-r--r-- | source3/smbd/posix_acls.c | 223 | ||||
-rw-r--r-- | source3/smbd/process.c | 317 | ||||
-rw-r--r-- | source3/smbd/reply.c | 98 | ||||
-rw-r--r-- | source3/smbd/seal.c | 20 | ||||
-rw-r--r-- | source3/smbd/server.c | 76 | ||||
-rw-r--r-- | source3/smbd/service.c | 94 | ||||
-rw-r--r-- | source3/smbd/sesssetup.c | 144 | ||||
-rw-r--r-- | source3/smbd/trans2.c | 427 | ||||
-rw-r--r-- | source3/smbd/utmp.c | 2 |
25 files changed, 1591 insertions, 776 deletions
diff --git a/source3/smbd/aio.c b/source3/smbd/aio.c index 9c25f69c97..2889e3c13f 100644 --- a/source3/smbd/aio.c +++ b/source3/smbd/aio.c @@ -141,21 +141,27 @@ static struct aio_extra *find_aio_ex(uint16 mid) We can have these many aio buffers in flight. *****************************************************************************/ -#define AIO_PENDING_SIZE 10 +static int aio_pending_size; static sig_atomic_t signals_received; static int outstanding_aio_calls; -static uint16 aio_pending_array[AIO_PENDING_SIZE]; +static uint16 *aio_pending_array; /**************************************************************************** Signal handler when an aio request completes. *****************************************************************************/ -static void signal_handler(int sig, siginfo_t *info, void *unused) +void aio_request_done(uint16_t mid) { - if (signals_received < AIO_PENDING_SIZE) { - aio_pending_array[signals_received] = info->si_value.sival_int; + if (signals_received < aio_pending_size) { + aio_pending_array[signals_received] = mid; signals_received++; - } /* Else signal is lost. */ + } + /* Else signal is lost. */ +} + +static void signal_handler(int sig, siginfo_t *info, void *unused) +{ + aio_request_done(info->si_value.sival_int); sys_select_signal(RT_SIGNAL_AIO); } @@ -176,6 +182,10 @@ void initialize_async_io_handler(void) { struct sigaction act; + aio_pending_size = lp_maxmux(); + aio_pending_array = SMB_MALLOC_ARRAY(uint16, aio_pending_size); + SMB_ASSERT(aio_pending_array != NULL); + ZERO_STRUCT(act); act.sa_sigaction = signal_handler; act.sa_flags = SA_SIGINFO; @@ -202,7 +212,14 @@ 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 (fsp->base_fsp != NULL) { + /* No AIO on streams yet */ + DEBUG(10, ("AIO on streams not yet supported\n")); + return false; + } + + 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", @@ -218,7 +235,7 @@ bool schedule_aio_read_and_X(connection_struct *conn, return False; } - if (outstanding_aio_calls >= AIO_PENDING_SIZE) { + if (outstanding_aio_calls >= aio_pending_size) { DEBUG(10,("schedule_aio_read_and_X: Already have %d aio " "activities outstanding.\n", outstanding_aio_calls )); @@ -284,7 +301,14 @@ 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 (fsp->base_fsp != NULL) { + /* No AIO on streams yet */ + DEBUG(10, ("AIO on streams not yet supported\n")); + return false; + } + + 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", @@ -300,7 +324,7 @@ bool schedule_aio_write_and_X(connection_struct *conn, return False; } - if (outstanding_aio_calls >= AIO_PENDING_SIZE) { + if (outstanding_aio_calls >= aio_pending_size) { DEBUG(3,("schedule_aio_write_and_X: Already have %d aio " "activities outstanding.\n", outstanding_aio_calls )); @@ -349,6 +373,8 @@ bool schedule_aio_write_and_X(connection_struct *conn, return False; } + release_level_2_oplocks_on_change(fsp); + if (!write_through && !lp_syncalways(SNUM(fsp->conn)) && fsp->aio_write_behind) { /* Lie to the client and immediately claim we finished the @@ -419,6 +445,9 @@ static int handle_aio_read_complete(struct aio_extra *aio_ex) SSVAL(outbuf,smb_vwv7,((nread >> 16) & 1)); SSVAL(smb_buf(outbuf),-2,nread); + aio_ex->fsp->fh->pos = aio_ex->acb.aio_offset + nread; + aio_ex->fsp->fh->position_information = aio_ex->fsp->fh->pos; + DEBUG( 3, ( "handle_aio_read_complete file %s max=%d " "nread=%d\n", aio_ex->fsp->fsp_name, @@ -520,6 +549,8 @@ static int handle_aio_write_complete(struct aio_extra *aio_ex) DEBUG(5,("handle_aio_write: sync_file for %s returned %s\n", fsp->fsp_name, nt_errstr(status) )); } + + aio_ex->fsp->fh->pos = aio_ex->acb.aio_offset + nwritten; } show_msg(outbuf); diff --git a/source3/smbd/chgpasswd.c b/source3/smbd/chgpasswd.c index fb228f9e2a..e7ab60d22f 100644 --- a/source3/smbd/chgpasswd.c +++ b/source3/smbd/chgpasswd.c @@ -239,7 +239,8 @@ static int dochild(int master, const char *slavedev, const struct passwd *pass, static int expect(int master, char *issue, char *expected) { char buffer[1024]; - int attempts, timeout, nread, len; + int attempts, timeout, nread; + size_t len; bool match = False; for (attempts = 0; attempts < 2; attempts++) { @@ -248,7 +249,8 @@ static int expect(int master, char *issue, char *expected) DEBUG(100, ("expect: sending [%s]\n", issue)); if ((len = sys_write(master, issue, strlen(issue))) != strlen(issue)) { - DEBUG(2,("expect: (short) write returned %d\n", len )); + DEBUG(2,("expect: (short) write returned %d\n", + (int)len )); return False; } } @@ -261,9 +263,16 @@ static int expect(int master, char *issue, char *expected) nread = 0; buffer[nread] = 0; - while ((len = read_socket_with_timeout(master, buffer + nread, 1, - sizeof(buffer) - nread - 1, - timeout, NULL)) > 0) { + while (True) { + NTSTATUS status; + status = read_socket_with_timeout( + master, buffer + nread, 1, + sizeof(buffer) - nread - 1, + timeout, &len); + + if (!NT_STATUS_IS_OK(status)) { + break; + } nread += len; buffer[nread] = 0; @@ -1092,7 +1101,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 +1115,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 +1128,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 +1138,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,7 +1146,7 @@ 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; } @@ -1158,7 +1167,7 @@ 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; diff --git a/source3/smbd/close.c b/source3/smbd/close.c index 4c385d7611..4bd23a35fc 100644 --- a/source3/smbd/close.c +++ b/source3/smbd/close.c @@ -156,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. ****************************************************************************/ @@ -305,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 @@ -570,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; } - return close_normal_file(fsp, close_type); + + 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 status; } diff --git a/source3/smbd/connection.c b/source3/smbd/connection.c index 016c8adb1b..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. ****************************************************************************/ diff --git a/source3/smbd/dmapi.c b/source3/smbd/dmapi.c index 05e9138ea9..fab0d5f9ef 100644 --- a/source3/smbd/dmapi.c +++ b/source3/smbd/dmapi.c @@ -25,9 +25,9 @@ #ifndef USE_DMAPI -int dmapi_init_session(void) { return -1; } uint32 dmapi_file_flags(const char * const path) { return 0; } bool dmapi_have_session(void) { return False; } +const void * dmapi_get_current_session(void) { return NULL; } #else /* USE_DMAPI */ @@ -46,100 +46,77 @@ 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; +static unsigned session_num; -/* Initialise the DMAPI interface. Make sure that we only end up initialising - * once per process to avoid resource leaks across different DMAPI - * implementations. - */ -static int init_dmapi_service(void) -{ - static pid_t lastpid; - - pid_t mypid; - - mypid = sys_getpid(); - if (mypid != lastpid) { - char *version; - - lastpid = mypid; - if (dm_init_service(&version) < 0) { - return -1; - } - - DEBUG(0, ("Initializing DMAPI: %s\n", version)); - } - - return 0; -} - -bool dmapi_have_session(void) -{ - return dmapi_session != DM_NO_SESSION; -} - -static dm_sessid_t *realloc_session_list(dm_sessid_t * sessions, int count) -{ - dm_sessid_t *nsessions; - - nsessions = TALLOC_REALLOC_ARRAY(NULL, sessions, dm_sessid_t, count); - if (nsessions == NULL) { - TALLOC_FREE(sessions); - return NULL; - } - - return nsessions; -} - -/* Initialise DMAPI session. The session is persistant kernel state, so it - * might already exist, in which case we merely want to reconnect to it. This - * function should be called as root. - */ -int dmapi_init_session(void) +/* + Initialise DMAPI session. The session is persistant kernel state, + so it might already exist, in which case we merely want to + reconnect to it. This function should be called as root. +*/ +static int dmapi_init_session(void) { char buf[DM_SESSION_INFO_LEN]; size_t buflen; - - uint nsessions = 10; + uint nsessions = 5; dm_sessid_t *sessions = NULL; + char *version; + char *session_name; + TALLOC_CTX *tmp_ctx = talloc_new(NULL); int i, err; - /* If we aren't root, something in the following will fail due to lack - * of privileges. Aborting seems a little extreme. - */ - SMB_WARN(getuid() == 0, "dmapi_init_session must be called as root"); + if (session_num == 0) { + session_name = DMAPI_SESSION_NAME; + } else { + session_name = talloc_asprintf(tmp_ctx, "%s%u", DMAPI_SESSION_NAME, + session_num); + } - dmapi_session = DM_NO_SESSION; - if (init_dmapi_service() < 0) { + if (session_name == NULL) { + DEBUG(0,("Out of memory in dmapi_init_session\n")); + talloc_free(tmp_ctx); return -1; } + -retry: - - if ((sessions = realloc_session_list(sessions, nsessions)) == NULL) { + if (dm_init_service(&version) < 0) { + DEBUG(0, ("dm_init_service failed - disabling DMAPI\n")); + talloc_free(tmp_ctx); return -1; } - err = dm_getall_sessions(nsessions, sessions, &nsessions); - if (err < 0) { - if (errno == E2BIG) { - nsessions *= 2; - goto retry; + ZERO_STRUCT(buf); + + /* Fetch kernel DMAPI sessions until we get any of them */ + do { + dm_sessid_t *new_sessions; + nsessions *= 2; + new_sessions = TALLOC_REALLOC_ARRAY(tmp_ctx, sessions, + dm_sessid_t, nsessions); + if (new_sessions == NULL) { + talloc_free(tmp_ctx); + return -1; } + sessions = new_sessions; + err = dm_getall_sessions(nsessions, sessions, &nsessions); + } while (err == -1 && errno == E2BIG); + + if (err == -1) { DEBUGADD(DMAPI_TRACE, ("failed to retrieve DMAPI sessions: %s\n", strerror(errno))); - TALLOC_FREE(sessions); + talloc_free(tmp_ctx); return -1; } + /* Look through existing kernel DMAPI sessions to find out ours */ for (i = 0; i < nsessions; ++i) { 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]; + if (err == 0 && strcmp(session_name, buf) == 0) { + samba_dmapi_session = sessions[i]; DEBUGADD(DMAPI_TRACE, ("attached to existing DMAPI session " "named '%s'\n", buf)); @@ -147,100 +124,123 @@ 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, + 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; + talloc_free(tmp_ctx); return -1; } - DEBUGADD(DMAPI_TRACE, - ("created new DMAPI session named '%s'\n", - DMAPI_SESSION_NAME)); + DEBUG(0, ("created new DMAPI session named '%s' for %s\n", + session_name, version)); + } + + if (samba_dmapi_session != DM_NO_SESSION) { + set_effective_capability(DMAPI_ACCESS_CAPABILITY); } - /* Note that we never end the DMAPI session. This enables child - * processes to continue to use the session after we exit. It also lets - * you run a second Samba server on different ports without any - * conflict. + /* + Note that we never end the DMAPI session. It gets re-used if possiblie. + DMAPI session is a kernel resource that is usually lives until server reboot + and doesn't get destroed when an application finishes. + + However, we free list of references to DMAPI sessions we've got from the kernel + as it is not needed anymore once we have found (or created) our session. */ + talloc_free(tmp_ctx); return 0; } -/* Reattach to an existing dmapi session. Called from service processes that - * might not be running as root. - */ -static int reattach_dmapi_session(void) +/* + Return a pointer to our DMAPI session, if available. + This assumes that you have called dmapi_have_session() first. +*/ +const void *dmapi_get_current_session(void) { - char buf[DM_SESSION_INFO_LEN]; - size_t buflen; - - if (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; - unbecome_root(); - return -1; - } + if (samba_dmapi_session == DM_NO_SESSION) { + return NULL; + } - if (dm_query_session(dmapi_session, sizeof(buf), - buf, &buflen) < 0) { - /* Session is stale. Disable DMAPI. */ - dmapi_session = DM_NO_SESSION; - unbecome_root(); - return -1; - } + return (void *)&samba_dmapi_session; +} + +/* + dmapi_have_session() must be the first DMAPI call you make in Samba. It will + initialize DMAPI, if available, and tell you if you can get a DMAPI session. + This should be called in the client-specific child process. +*/ - set_effective_capability(DMAPI_ACCESS_CAPABILITY); +bool dmapi_have_session(void) +{ + static bool initialized; + if (!initialized) { + initialized = true; - DEBUG(DMAPI_TRACE, ("reattached DMAPI session\n")); + become_root(); + dmapi_init_session(); unbecome_root(); + } - return 0; + return samba_dmapi_session != DM_NO_SESSION; } -uint32 dmapi_file_flags(const char * const path) +/* + only call this when you get back an EINVAL error indicating that the + session you are using is invalid. This destroys the existing session + and creates a new one. + */ +bool dmapi_new_session(void) { - static int attached = 0; + if (dmapi_have_session()) { + /* try to destroy the old one - this may not succeed */ + dm_destroy_session(samba_dmapi_session); + } + samba_dmapi_session = DM_NO_SESSION; + become_root(); + session_num++; + dmapi_init_session(); + unbecome_root(); + return samba_dmapi_session != DM_NO_SESSION; +} +/* + This is default implementation of dmapi_file_flags() that is + called from VFS is_offline() call to know whether file is offline. + For GPFS-specific version see modules/vfs_tsmsm.c. It might be + that approach on quering existence of a specific attribute that + is used in vfs_tsmsm.c will work with other DMAPI-based HSM + implementations as well. +*/ +uint32 dmapi_file_flags(const char * const path) +{ int err; dm_eventset_t events = {0}; uint nevents; - void *dm_handle; - size_t dm_handle_len; + dm_sessid_t dmapi_session; + const void *dmapi_session_ptr; + void *dm_handle = NULL; + size_t dm_handle_len = 0; - uint32 flags = 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_ptr = dmapi_get_current_session(); + if (dmapi_session_ptr == NULL) { + return 0; + } + + dmapi_session = *(dm_sessid_t *)dmapi_session_ptr; + if (dmapi_session == DM_NO_SESSION) { + return 0; } /* AIX has DMAPI but no POSIX capablities support. In this case, @@ -292,8 +292,7 @@ uint32 dmapi_file_flags(const char * const path) * interested in trapping read events is that part of the file is * offline. */ - DEBUG(DMAPI_TRACE, ("DMAPI event list for %s is %#llx\n", - path, events)); + DEBUG(DMAPI_TRACE, ("DMAPI event list for %s\n", path)); if (DMEV_ISSET(DM_EVENT_READ, events)) { flags = FILE_ATTRIBUTE_OFFLINE; } @@ -313,4 +312,5 @@ done: return flags; } + #endif /* USE_DMAPI */ diff --git a/source3/smbd/dosmode.c b/source3/smbd/dosmode.c index d3813f9b41..a2e617c117 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,10 +418,11 @@ 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; + uint32_t old_mode; /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */ - dosmode &= SAMBA_ATTRIBUTES_MASK; + dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE); DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, fname)); @@ -458,7 +445,24 @@ int file_set_dosmode(connection_struct *conn, const char *fname, else dosmode &= ~aDIR; - if (dos_mode(conn,fname,st) == dosmode) { + old_mode = dos_mode(conn,fname,st); + + if (dosmode & FILE_ATTRIBUTE_OFFLINE) { + if (!(old_mode & 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)); + } + } + } + + dosmode &= ~FILE_ATTRIBUTE_OFFLINE; + old_mode &= ~FILE_ATTRIBUTE_OFFLINE; + + if (old_mode == dosmode) { st->st_mode = unixmode; return(0); } @@ -505,10 +509,11 @@ 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) { + 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; diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c index be4960cab8..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 @@ -653,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; @@ -823,3 +859,90 @@ static bool scan_directory(connection_struct *conn, const char *path, 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/lanman.c b/source3/smbd/lanman.c index 5a6df1f139..18e6bf9f7b 100644 --- a/source3/smbd/lanman.c +++ b/source3/smbd/lanman.c @@ -3175,7 +3175,7 @@ static bool api_NetWkstaGetInfo(connection_struct *conn,uint16 vuid, where: - usri11_name specifies the user name for which information is retireved + usri11_name specifies the user name for which information is retrieved usri11_pad aligns the next data structure element to a word boundary diff --git a/source3/smbd/map_username.c b/source3/smbd/map_username.c index 7290f70547..7536758bcb 100644 --- a/source3/smbd/map_username.c +++ b/source3/smbd/map_username.c @@ -178,7 +178,7 @@ bool map_username(fstring user) /* skip lines like 'user = ' */ - dosuserlist = str_list_make(dosname, NULL); + dosuserlist = str_list_make(talloc_tos(), dosname, NULL); if (!dosuserlist) { DEBUG(0,("Bad username map entry. Unable to build user list. Ignoring.\n")); continue; @@ -193,13 +193,13 @@ bool map_username(fstring user) fstrcpy( user, unixname ); if ( return_if_mapped ) { - str_list_free (&dosuserlist); + TALLOC_FREE(dosuserlist); x_fclose(f); return True; } } - str_list_free (&dosuserlist); + TALLOC_FREE(dosuserlist); } x_fclose(f); 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/notify_inotify.c b/source3/smbd/notify_inotify.c index 73cee440d4..fa0f0ed51d 100644 --- a/source3/smbd/notify_inotify.c +++ b/source3/smbd/notify_inotify.c @@ -29,10 +29,12 @@ #include <asm/types.h> #endif +#ifndef HAVE_INOTIFY_INIT + #include <linux/inotify.h> #include <asm/unistd.h> -#ifndef HAVE_INOTIFY_INIT + /* glibc doesn't define these functions yet (as of March 2006) */ @@ -50,6 +52,10 @@ static int inotify_rm_watch(int fd, int wd) { return syscall(__NR_inotify_rm_watch, fd, wd); } +#else + +#include <sys/inotify.h> + #endif diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c index e8df732ea2..5293ca5347 100644 --- a/source3/smbd/nttrans.c +++ b/source3/smbd/nttrans.c @@ -271,6 +271,9 @@ void send_nt_replies(connection_struct *conn, /**************************************************************************** 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) @@ -555,7 +558,7 @@ void reply_ntcreate_and_X(struct smb_request *req) } file_len = sbuf.st_size; - fattr = dos_mode(conn,fname,&sbuf); + fattr = dos_mode(conn,fsp->fsp_name,&sbuf); if (fattr == 0) { fattr = FILE_ATTRIBUTE_NORMAL; } @@ -623,7 +626,7 @@ void reply_ntcreate_and_X(struct smb_request *req) uint32 perms = 0; p += 25; if (fsp->is_directory - || can_write_to_file(conn, fname, &sbuf)) { + || can_write_to_file(conn, fsp->fsp_name, &sbuf)) { perms = FILE_GENERIC_ALL; } else { perms = FILE_GENERIC_READ|FILE_EXECUTE; @@ -1004,7 +1007,7 @@ static void call_nt_transact_create(connection_struct *conn, } file_len = sbuf.st_size; - fattr = dos_mode(conn,fname,&sbuf); + fattr = dos_mode(conn,fsp->fsp_name,&sbuf); if (fattr == 0) { fattr = FILE_ATTRIBUTE_NORMAL; } @@ -1072,7 +1075,7 @@ static void call_nt_transact_create(connection_struct *conn, uint32 perms = 0; p += 25; if (fsp->is_directory - || can_write_to_file(conn, fname, &sbuf)) { + || can_write_to_file(conn, fsp->fsp_name, &sbuf)) { perms = FILE_GENERIC_ALL; } else { perms = FILE_GENERIC_READ|FILE_EXECUTE; @@ -1080,7 +1083,7 @@ static void call_nt_transact_create(connection_struct *conn, SIVAL(p,0,perms); } - DEBUG(5,("call_nt_transact_create: open name = %s\n", fname)); + DEBUG(5,("call_nt_transact_create: open name = %s\n", fsp->fsp_name)); /* Send the required number of replies */ send_nt_replies(conn, req, NT_STATUS_OK, params, param_len, *ppdata, 0); diff --git a/source3/smbd/open.c b/source3/smbd/open.c index 9d48bcc98b..cc78503379 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -1842,7 +1842,9 @@ 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)) { @@ -2104,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; } @@ -2444,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 */ @@ -2466,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; @@ -2495,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 @@ -2528,12 +2657,62 @@ 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, ("create_file_unixpath: " + "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; @@ -2662,7 +2841,17 @@ NTSTATUS create_file_unixpath(connection_struct *conn, } } - DEBUG(10, ("create_file: info=%d\n", info)); + DEBUG(10, ("create_file_unixpath: 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) { @@ -2679,12 +2868,16 @@ NTSTATUS create_file_unixpath(connection_struct *conn, return NT_STATUS_OK; fail: - DEBUG(10, ("create_file: %s\n", nt_errstr(status))); + DEBUG(10, ("create_file_unixpath: %s\n", nt_errstr(status))); if (fsp != NULL) { close_file(fsp, ERROR_CLOSE); fsp = NULL; } + if (base_fsp != NULL) { + close_file(base_fsp, ERROR_CLOSE); + base_fsp = NULL; + } return status; } @@ -2812,19 +3005,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 @@ -2843,8 +3035,7 @@ NTSTATUS create_file(connection_struct *conn, goto fail; } - SET_STAT_INVALID(sbuf); - + ZERO_STRUCT(sbuf); goto done; } } @@ -2889,6 +3080,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); diff --git a/source3/smbd/oplock_irix.c b/source3/smbd/oplock_irix.c index a4ea63bc0a..788cd04c17 100644 --- a/source3/smbd/oplock_irix.c +++ b/source3/smbd/oplock_irix.c @@ -121,7 +121,6 @@ static files_struct *irix_oplock_receive_message(fd_set *fds) DEBUG(0,("irix_oplock_receive_message: read of kernel " "notification failed. Error was %s.\n", strerror(errno) )); - set_smb_read_error(get_srv_read_error(), SMB_READ_ERROR); return NULL; } @@ -141,7 +140,6 @@ static files_struct *irix_oplock_receive_message(fd_set *fds) */ return NULL; } - set_smb_read_error(get_srv_read_error(), SMB_READ_ERROR); return NULL; } diff --git a/source3/smbd/oplock_linux.c b/source3/smbd/oplock_linux.c index 05021b6c74..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) { diff --git a/source3/smbd/password.c b/source3/smbd/password.c index 6b517c3d86..80eba562c5 100644 --- a/source3/smbd/password.c +++ b/source3/smbd/password.c @@ -121,6 +121,10 @@ void invalidate_vuid(uint16 vuid) data_blob_free(&vuser->session_key); + if (vuser->auth_ntlmssp_state) { + auth_ntlmssp_end(&vuser->auth_ntlmssp_state); + } + DLIST_REMOVE(validated_users, vuser); /* clear the vuid from the 'cache' on each connection, and @@ -582,7 +586,7 @@ static bool user_ok(const char *user, int snum) ret = True; if (lp_invalid_users(snum)) { - str_list_copy(&invalid, lp_invalid_users(snum)); + str_list_copy(talloc_tos(), &invalid, lp_invalid_users(snum)); if (invalid && str_list_substitute(invalid, "%S", lp_servicename(snum))) { @@ -595,11 +599,10 @@ static bool user_ok(const char *user, int snum) } } } - if (invalid) - str_list_free (&invalid); + TALLOC_FREE(invalid); if (ret && lp_valid_users(snum)) { - str_list_copy(&valid, lp_valid_users(snum)); + str_list_copy(talloc_tos(), &valid, lp_valid_users(snum)); if ( valid && str_list_substitute(valid, "%S", lp_servicename(snum)) ) { @@ -611,17 +614,17 @@ static bool user_ok(const char *user, int snum) } } } - if (valid) - str_list_free (&valid); + TALLOC_FREE(valid); if (ret && lp_onlyuser(snum)) { - char **user_list = str_list_make (lp_username(snum), NULL); + char **user_list = str_list_make( + talloc_tos(), lp_username(snum), NULL); if (user_list && str_list_substitute(user_list, "%S", lp_servicename(snum))) { ret = user_in_list(user, (const char **)user_list); } - if (user_list) str_list_free (&user_list); + TALLOC_FREE(user_list); } return(ret); @@ -759,6 +762,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 +772,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 +797,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 +812,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) { diff --git a/source3/smbd/posix_acls.c b/source3/smbd/posix_acls.c index 5f18615f66..f60329a039 100644 --- a/source3/smbd/posix_acls.c +++ b/source3/smbd/posix_acls.c @@ -1408,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); - + } } } @@ -1474,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). @@ -3202,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; - enum security_ace_type 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, @@ -3270,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); @@ -3285,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, @@ -3318,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]); @@ -3342,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; @@ -3413,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 )); @@ -3435,6 +3444,8 @@ 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; /* * Unpack the user/group/world id's. @@ -3449,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 )); @@ -3487,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); @@ -3630,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; } diff --git a/source3/smbd/process.c b/source3/smbd/process.c index a5bdb96650..68bec7830a 100644 --- a/source3/smbd/process.c +++ b/source3/smbd/process.c @@ -20,12 +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; - /* * Size of data we can send to client. Set * by the client for all protocols above CORE. @@ -45,11 +41,6 @@ extern int max_send; /* Accessor function for smb_read_error for smbd functions. */ -enum smb_read_errors *get_srv_read_error(void) -{ - return &smb_read_error; -} - /**************************************************************************** Send an smb to a fd. ****************************************************************************/ @@ -130,50 +121,20 @@ static bool valid_packet_size(size_t len) DEBUG(0,("Invalid packet length! (%lu bytes).\n", (unsigned long)len)); if (len > BUFFER_SIZE + (SAFETY_MARGIN/2)) { - - /* - * Correct fix. smb_read_error may have already been - * set. Only set it here if not already set. Global - * variables still suck :-). JRA. - */ - - cond_set_smb_read_error(get_srv_read_error(), - SMB_READ_ERROR); return false; } } return true; } -static ssize_t read_packet_remainder(int fd, - char *buffer, - unsigned int timeout, - ssize_t len) +static NTSTATUS read_packet_remainder(int fd, char *buffer, + unsigned int timeout, ssize_t len) { - ssize_t ret; - - if(len <= 0) { - 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()); - } - - if (ret != len) { - cond_set_smb_read_error(get_srv_read_error(), - SMB_READ_ERROR); - return -1; + if (len <= 0) { + return NT_STATUS_OK; } - return len; + return read_socket_with_timeout(fd, buffer, len, len, timeout, NULL); } /**************************************************************************** @@ -192,39 +153,29 @@ static ssize_t read_packet_remainder(int fd, (2*14) + /* word count (including bcc) */ \ 1 /* pad byte */) -static ssize_t receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx, - const char lenbuf[4], - int fd, - char **buffer, - unsigned int timeout, - size_t *p_unread) +static NTSTATUS receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx, + const char lenbuf[4], + int fd, char **buffer, + unsigned int timeout, + size_t *p_unread, + size_t *len_ret) { /* Size of a WRITEX call (+4 byte len). */ char writeX_header[4 + STANDARD_WRITE_AND_X_HEADER_SIZE]; ssize_t len = smb_len_large(lenbuf); /* Could be a UNIX large writeX. */ ssize_t toread; - ssize_t ret; + NTSTATUS status; 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()); - } + status = read_socket_with_timeout( + fd, writeX_header + 4, + STANDARD_WRITE_AND_X_HEADER_SIZE, + STANDARD_WRITE_AND_X_HEADER_SIZE, + timeout, NULL); - if (ret != STANDARD_WRITE_AND_X_HEADER_SIZE) { - cond_set_smb_read_error(get_srv_read_error(), - SMB_READ_ERROR); - return -1; + if (!NT_STATUS_IS_OK(status)) { + return status; } /* @@ -263,19 +214,17 @@ static ssize_t receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx, if (*buffer == NULL) { DEBUG(0, ("Could not allocate inbuf of length %d\n", (int)sizeof(writeX_header))); - cond_set_smb_read_error(get_srv_read_error(), - SMB_READ_ERROR); - return -1; + return NT_STATUS_NO_MEMORY; } /* Work out the remaining bytes. */ *p_unread = len - STANDARD_WRITE_AND_X_HEADER_SIZE; - - return newlen + 4; + *len_ret = newlen + 4; + return NT_STATUS_OK; } if (!valid_packet_size(len)) { - return -1; + return NT_STATUS_INVALID_PARAMETER; } /* @@ -288,9 +237,7 @@ static ssize_t receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx, if (*buffer == NULL) { DEBUG(0, ("Could not allocate inbuf of length %d\n", (int)len+4)); - cond_set_smb_read_error(get_srv_read_error(), - SMB_READ_ERROR); - return -1; + return NT_STATUS_NO_MEMORY; } /* Copy in what we already read. */ @@ -300,44 +247,34 @@ static ssize_t receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx, toread = len - STANDARD_WRITE_AND_X_HEADER_SIZE; if(toread > 0) { - ret = read_packet_remainder(fd, - (*buffer) + 4 + STANDARD_WRITE_AND_X_HEADER_SIZE, - timeout, - toread); - if (ret != toread) { - return -1; + status = read_packet_remainder( + fd, (*buffer) + 4 + STANDARD_WRITE_AND_X_HEADER_SIZE, + timeout, toread); + + if (!NT_STATUS_IS_OK(status)) { + return status; } } - return len + 4; + *len_ret = len + 4; + return NT_STATUS_OK; } -static ssize_t receive_smb_raw_talloc(TALLOC_CTX *mem_ctx, - int fd, - char **buffer, - unsigned int timeout, - size_t *p_unread) +static NTSTATUS receive_smb_raw_talloc(TALLOC_CTX *mem_ctx, int fd, + char **buffer, unsigned int timeout, + size_t *p_unread, size_t *plen) { char lenbuf[4]; - ssize_t len,ret; + size_t len; int min_recv_size = lp_min_receive_file_size(); + NTSTATUS status; - set_smb_read_error(get_srv_read_error(),SMB_READ_OK); *p_unread = 0; - len = read_smb_length_return_keepalive(fd, lenbuf, - timeout, get_srv_read_error()); - if (len < 0) { - DEBUG(10,("receive_smb_raw: length < 0!\n")); - - /* - * Correct fix. smb_read_error may have already been - * set. Only set it here if not already set. Global - * variables still suck :-). JRA. - */ - - cond_set_smb_read_error(get_srv_read_error(),SMB_READ_ERROR); - return -1; + status = read_smb_length_return_keepalive(fd, lenbuf, timeout, &len); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("receive_smb_raw: %s\n", nt_errstr(status))); + return status; } if (CVAL(lenbuf,0) == 0 && @@ -345,16 +282,18 @@ static ssize_t receive_smb_raw_talloc(TALLOC_CTX *mem_ctx, smb_len_large(lenbuf) > min_recv_size && /* Could be a UNIX large writeX. */ !srv_is_signing_active()) { - return receive_smb_raw_talloc_partial_read(mem_ctx, - lenbuf, - fd, - buffer, - timeout, - p_unread); + status = receive_smb_raw_talloc_partial_read( + mem_ctx, lenbuf, fd, buffer, timeout, p_unread, &len); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("receive_smb_raw: %s\n", + nt_errstr(status))); + return status; + } } if (!valid_packet_size(len)) { - return -1; + return NT_STATUS_INVALID_PARAMETER; } /* @@ -366,46 +305,43 @@ static ssize_t receive_smb_raw_talloc(TALLOC_CTX *mem_ctx, if (*buffer == NULL) { DEBUG(0, ("Could not allocate inbuf of length %d\n", (int)len+4)); - cond_set_smb_read_error(get_srv_read_error(),SMB_READ_ERROR); - return -1; + return NT_STATUS_NO_MEMORY; } memcpy(*buffer, lenbuf, sizeof(lenbuf)); - ret = read_packet_remainder(fd, (*buffer)+4, timeout, len); - if (ret != len) { - return -1; + status = read_packet_remainder(fd, (*buffer)+4, timeout, len); + if (!NT_STATUS_IS_OK(status)) { + return status; } - return len + 4; + *plen = len + 4; + return NT_STATUS_OK; } -static ssize_t receive_smb_talloc(TALLOC_CTX *mem_ctx, - int fd, - char **buffer, - unsigned int timeout, - size_t *p_unread, - bool *p_encrypted) +static NTSTATUS receive_smb_talloc(TALLOC_CTX *mem_ctx, int fd, + char **buffer, unsigned int timeout, + size_t *p_unread, bool *p_encrypted, + size_t *p_len) { - ssize_t len; + size_t len = 0; + NTSTATUS status; *p_encrypted = false; - len = receive_smb_raw_talloc(mem_ctx, fd, buffer, timeout, p_unread); - - if (len < 0) { - return -1; + status = receive_smb_raw_talloc(mem_ctx, fd, buffer, timeout, + p_unread, &len); + if (!NT_STATUS_IS_OK(status)) { + return status; } if (is_encrypted_packet((uint8_t *)*buffer)) { - NTSTATUS status = srv_decrypt_buffer(*buffer); + 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; + return status; } *p_encrypted = true; } @@ -414,11 +350,11 @@ static ssize_t receive_smb_talloc(TALLOC_CTX *mem_ctx, if (!srv_check_sign_mac(*buffer, true)) { DEBUG(0, ("receive_smb: SMB Signature verification failed on " "incoming packet!\n")); - cond_set_smb_read_error(get_srv_read_error(),SMB_READ_BAD_SIG); - return -1; + return NT_STATUS_INVALID_NETWORK_RESPONSE; } - return len; + *p_len = len; + return NT_STATUS_OK; } /* @@ -775,21 +711,18 @@ static int select_on_fd(int fd, int maxfd, fd_set *fds) The timeout is in milliseconds ****************************************************************************/ -static bool receive_message_or_smb(TALLOC_CTX *mem_ctx, - char **buffer, - size_t *buffer_len, - int timeout, - size_t *p_unread, - bool *p_encrypted) +static NTSTATUS receive_message_or_smb(TALLOC_CTX *mem_ctx, char **buffer, + size_t *buffer_len, int timeout, + size_t *p_unread, bool *p_encrypted) { fd_set r_fds, w_fds; int selrtn; struct timeval to; int maxfd = 0; - ssize_t len; + size_t len = 0; + NTSTATUS status; *p_unread = 0; - set_smb_read_error(get_srv_read_error(),SMB_READ_OK); again: @@ -843,8 +776,7 @@ static bool receive_message_or_smb(TALLOC_CTX *mem_ctx, msg->buf.length); if (*buffer == NULL) { DEBUG(0, ("talloc failed\n")); - set_smb_read_error(get_srv_read_error(),SMB_READ_ERROR); - return False; + return NT_STATUS_NO_MEMORY; } *buffer_len = msg->buf.length; *p_encrypted = msg->encrypted; @@ -852,7 +784,7 @@ static bool receive_message_or_smb(TALLOC_CTX *mem_ctx, /* We leave this message on the queue so the open code can know this is a retry. */ DEBUG(5,("receive_message_or_smb: returning deferred open smb message.\n")); - return True; + return NT_STATUS_OK; } } @@ -938,14 +870,12 @@ static bool receive_message_or_smb(TALLOC_CTX *mem_ctx, /* Check if error */ if (selrtn == -1) { /* something is wrong. Maybe the socket is dead? */ - set_smb_read_error(get_srv_read_error(),SMB_READ_ERROR); - return False; + return map_nt_error_from_unix(errno); } /* Did we timeout ? */ if (selrtn == 0) { - set_smb_read_error(get_srv_read_error(),SMB_READ_TIMEOUT); - return False; + return NT_STATUS_IO_TIMEOUT; } /* @@ -964,16 +894,16 @@ 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, p_encrypted); + status = receive_smb_talloc(mem_ctx, smbd_server_fd(), buffer, 0, + p_unread, p_encrypted, &len); - if (len == -1) { - return False; + if (!NT_STATUS_IS_OK(status)) { + return status; } - *buffer_len = (size_t)len; + *buffer_len = len; - return True; + return NT_STATUS_OK; } /* @@ -1951,27 +1881,11 @@ void check_reload(time_t t) Process any timeout housekeeping. Return False if the caller should exit. ****************************************************************************/ -static bool timeout_processing(int *select_timeout, +static void timeout_processing(int *select_timeout, time_t *last_timeout_processing_time) { time_t t; - if (*get_srv_read_error() == SMB_READ_EOF) { - DEBUG(3,("timeout_processing: End of file from client (client has disconnected).\n")); - return false; - } - - if (*get_srv_read_error() == SMB_READ_ERROR) { - DEBUG(3,("timeout_processing: receive_smb error (%s) Exiting\n", - strerror(errno))); - return false; - } - - if (*get_srv_read_error() == SMB_READ_BAD_SIG) { - DEBUG(3,("timeout_processing: receive_smb error bad smb signature. Exiting\n")); - return false; - } - *last_timeout_processing_time = t = time(NULL); /* become root again if waiting */ @@ -2001,14 +1915,14 @@ static bool timeout_processing(int *select_timeout, if (secrets_lock_trust_account_password(lp_workgroup(), True) == False) { DEBUG(0,("process: unable to lock the machine account password for \ machine %s in domain %s.\n", global_myname(), lp_workgroup() )); - return True; + return; } if(!secrets_fetch_trust_account_password(lp_workgroup(), trust_passwd_hash, &lct, NULL)) { DEBUG(0,("process: unable to read the machine account password for \ machine %s in domain %s.\n", global_myname(), lp_workgroup())); secrets_lock_trust_account_password(lp_workgroup(), False); - return True; + return; } /* @@ -2018,7 +1932,7 @@ machine %s in domain %s.\n", global_myname(), lp_workgroup())); if(t < lct + lp_machine_password_timeout()) { global_machine_password_needs_changing = False; secrets_lock_trust_account_password(lp_workgroup(), False); - return True; + return; } /* always just contact the PDC here */ @@ -2050,7 +1964,7 @@ machine %s in domain %s.\n", global_myname(), lp_workgroup())); *select_timeout = setup_select_timeout(); - return True; + return; } /**************************************************************************** @@ -2068,8 +1982,8 @@ void smbd_process(void) while (True) { int select_timeout = setup_select_timeout(); int num_echos; - char *inbuf; - size_t inbuf_len; + char *inbuf = NULL; + size_t inbuf_len = 0; bool encrypted = false; TALLOC_CTX *frame = talloc_stackframe_pool(8192); @@ -2077,21 +1991,35 @@ void smbd_process(void) /* Did someone ask for immediate checks on things like blocking locks ? */ if (select_timeout == 0) { - if(!timeout_processing(&select_timeout, - &last_timeout_processing_time)) - return; + timeout_processing(&select_timeout, + &last_timeout_processing_time); num_smbs = 0; /* Reset smb counter. */ } run_events(smbd_event_context(), 0, NULL, NULL); - 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; + while (True) { + NTSTATUS status; + + status = receive_message_or_smb( + talloc_tos(), &inbuf, &inbuf_len, + select_timeout, &unread_bytes, &encrypted); + + if (NT_STATUS_IS_OK(status)) { + break; + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + timeout_processing( + &select_timeout, + &last_timeout_processing_time); + continue; + } + + DEBUG(3, ("receive_message_or_smb failed: %s, " + "exiting\n", nt_errstr(status))); + return; + num_smbs = 0; /* Reset smb counter. */ } @@ -2112,8 +2040,8 @@ void smbd_process(void) TALLOC_FREE(inbuf); if (smb_echo_count != num_echos) { - if(!timeout_processing( &select_timeout, &last_timeout_processing_time)) - return; + timeout_processing(&select_timeout, + &last_timeout_processing_time); num_smbs = 0; /* Reset smb counter. */ } @@ -2129,10 +2057,9 @@ void smbd_process(void) if ((num_smbs % 200) == 0) { time_t new_check_time = time(NULL); if(new_check_time - last_timeout_processing_time >= (select_timeout/1000)) { - if(!timeout_processing( - &select_timeout, - &last_timeout_processing_time)) - return; + timeout_processing( + &select_timeout, + &last_timeout_processing_time); num_smbs = 0; /* Reset smb counter. */ last_timeout_processing_time = new_check_time; /* Reset time. */ } diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index e2316ef120..818ff319e4 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; } @@ -1109,6 +1110,12 @@ void reply_setatr(struct smb_request *req) mode = SVAL(req->inbuf,smb_vwv0); mtime = srv_make_unix_date3(req->inbuf+smb_vwv1); + if (!set_filetime(conn,fname,convert_time_t_to_timespec(mtime))) { + reply_unixerror(req, ERRDOS, ERRnoaccess); + END_PROFILE(SMBsetatr); + return; + } + if (mode != FILE_ATTRIBUTE_NORMAL) { if (VALID_STAT_OF_DIR(sbuf)) mode |= aDIR; @@ -1122,12 +1129,6 @@ void reply_setatr(struct smb_request *req) } } - if (!set_filetime(conn,fname,convert_time_t_to_timespec(mtime))) { - reply_unixerror(req, ERRDOS, ERRnoaccess); - END_PROFILE(SMBsetatr); - return; - } - reply_outbuf(req, 0, 0); DEBUG( 3, ( "setatr name=%s mode=%d\n", fname, mode ) ); @@ -1638,11 +1639,11 @@ void reply_open(struct smb_request *req) } size = sbuf.st_size; - fattr = dos_mode(conn,fname,&sbuf); + fattr = dos_mode(conn,fsp->fsp_name,&sbuf); mtime = sbuf.st_mtime; if (fattr & aDIR) { - DEBUG(3,("attempt to open a directory %s\n",fname)); + DEBUG(3,("attempt to open a directory %s\n",fsp->fsp_name)); close_file(fsp,ERROR_CLOSE); reply_doserror(req, ERRDOS,ERRnoaccess); END_PROFILE(SMBopen); @@ -1801,7 +1802,7 @@ void reply_open_and_X(struct smb_request *req) sbuf.st_size = get_allocation_size(conn,fsp,&sbuf); } - fattr = dos_mode(conn,fname,&sbuf); + fattr = dos_mode(conn,fsp->fsp_name,&sbuf); mtime = sbuf.st_mtime; if (fattr & aDIR) { close_file(fsp,ERROR_CLOSE); @@ -1984,7 +1985,7 @@ void reply_mknew(struct smb_request *req) } ts[0] = get_atimespec(&sbuf); /* atime. */ - file_ntimes(conn, fname, ts); + file_ntimes(conn, fsp->fsp_name, ts); reply_outbuf(req, 1, 0); SSVAL(req->outbuf,smb_vwv0,fsp->fnum); @@ -1999,9 +2000,9 @@ void reply_mknew(struct smb_request *req) CVAL(req->outbuf,smb_flg)|CORE_OPLOCK_GRANTED); } - DEBUG( 2, ( "reply_mknew: file %s\n", fname ) ); + DEBUG( 2, ( "reply_mknew: file %s\n", fsp->fsp_name ) ); DEBUG( 3, ( "reply_mknew %s fd=%d dmode=0x%x\n", - fname, fsp->fh->fd, (unsigned int)fattr ) ); + fsp->fsp_name, fsp->fh->fd, (unsigned int)fattr ) ); END_PROFILE(SMBcreate); return; @@ -2124,9 +2125,9 @@ void reply_ctemp(struct smb_request *req) SSVAL(req->outbuf,smb_vwv0,fsp->fnum); /* the returned filename is relative to the directory */ - s = strrchr_m(fname, '/'); + s = strrchr_m(fsp->fsp_name, '/'); if (!s) { - s = fname; + s = fsp->fsp_name; } else { s++; } @@ -2153,9 +2154,9 @@ void reply_ctemp(struct smb_request *req) CVAL(req->outbuf,smb_flg)|CORE_OPLOCK_GRANTED); } - DEBUG( 2, ( "reply_ctemp: created temp file %s\n", fname ) ); - DEBUG( 3, ( "reply_ctemp %s fd=%d umode=0%o\n", fname, fsp->fh->fd, - (unsigned int)sbuf.st_mode ) ); + DEBUG( 2, ( "reply_ctemp: created temp file %s\n", fsp->fsp_name ) ); + DEBUG( 3, ( "reply_ctemp %s fd=%d umode=0%o\n", fsp->fsp_name, + fsp->fh->fd, (unsigned int)sbuf.st_mode ) ); END_PROFILE(SMBctemp); return; @@ -2289,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", @@ -2460,7 +2469,7 @@ NTSTATUS unlink_internals(connection_struct *conn, struct smb_request *req, } count++; - DEBUG(3,("unlink_internals: succesful unlink [%s]\n", + DEBUG(3,("unlink_internals: successful unlink [%s]\n", fname)); TALLOC_FREE(fname); @@ -3329,8 +3338,8 @@ void reply_read_and_X(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; } @@ -3362,7 +3371,6 @@ void error_to_writebrawerr(struct smb_request *req) void reply_writebraw(struct smb_request *req) { connection_struct *conn = req->conn; - int outsize = 0; char *buf = NULL; ssize_t nwritten=0; ssize_t total_written=0; @@ -3472,8 +3480,7 @@ void reply_writebraw(struct smb_request *req) * it to send more bytes */ memcpy(buf, req->inbuf, smb_size); - outsize = srv_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); @@ -3485,18 +3492,12 @@ void reply_writebraw(struct smb_request *req) } /* Now read the raw data into the buffer and write it */ - if (read_smb_length(smbd_server_fd(),buf, - SMB_SECONDARY_WAIT, get_srv_read_error()) == -1) { + status = read_smb_length(smbd_server_fd(), buf, SMB_SECONDARY_WAIT, + &numtowrite); + if (!NT_STATUS_IS_OK(status)) { exit_server_cleanly("secondary writebraw failed"); } - /* - * Even though this is not an smb message, - * smb_len returns the generic length of a packet. - */ - - numtowrite = smb_len(buf); - /* Set up outbuf to return the correct size */ reply_outbuf(req, 1, 0); @@ -3515,11 +3516,12 @@ void reply_writebraw(struct smb_request *req) (int)tcount,(int)nwritten,(int)numtowrite)); } - if (read_data(smbd_server_fd(), buf+4, numtowrite,get_srv_read_error()) - != numtowrite ) { + status = read_data(smbd_server_fd(), buf+4, numtowrite); + + if (!NT_STATUS_IS_OK(status)) { DEBUG(0,("reply_writebraw: Oversize secondary write " - "raw read failed (%s). Terminating\n", - strerror(errno) )); + "raw read failed (%s). Terminating\n", + nt_errstr(status))); exit_server_cleanly("secondary writebraw failed"); } @@ -4001,13 +4003,13 @@ void reply_write_and_X(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); } diff --git a/source3/smbd/seal.c b/source3/smbd/seal.c index ea017e08d8..d4394e5bca 100644 --- a/source3/smbd/seal.c +++ b/source3/smbd/seal.c @@ -496,12 +496,15 @@ static NTSTATUS srv_enc_spnego_negotiate(connection_struct *conn, srv_free_encryption_context(&partial_srv_trans_enc_ctx); + if (got_kerberos_mechanism) { #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 +#else + /* Currently we don't SPNEGO negotiate + * back to NTLMSSP as we do in sessionsetupX. We should... */ + return NT_STATUS_LOGON_FAILURE; #endif - { + } else { status = srv_enc_ntlm_negotiate(ppdata, p_data_size, secblob, true); } @@ -558,7 +561,16 @@ static NTSTATUS srv_enc_spnego_ntlm_auth(connection_struct *conn, 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); + /* From RFC4178. + * + * supportedMech + * + * This field SHALL only be present in the first reply from the + * target. + * So set mechOID to NULL here. + */ + + response = spnego_gen_auth_response(&auth_reply, status, NULL); data_blob_free(&auth_reply); if (NT_STATUS_IS_OK(status)) { diff --git a/source3/smbd/server.c b/source3/smbd/server.c index 8371d17f10..7bf11abfbd 100644 --- a/source3/smbd/server.c +++ b/source3/smbd/server.c @@ -268,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; @@ -560,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); } } @@ -603,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)) { @@ -906,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. ****************************************************************************/ @@ -929,7 +988,8 @@ static bool init_structs(void ) init_dptrs(); - secrets_init(); + if (!secrets_init()) + return False; return True; } @@ -1254,12 +1314,6 @@ extern void build_options(bool screen); if ( is_daemon && !interactive ) start_background_queue(); - /* Always attempt to initialize DMAPI. We will only use it later if - * lp_dmapi_support is set on the share, but we need a single global - * session to work with. - */ - dmapi_init_session(); - if (!open_sockets_smbd(is_daemon, interactive, ports)) exit(1); @@ -1305,6 +1359,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 2588a66b8b..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. * @@ -386,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)); @@ -1165,16 +1171,8 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser, * assumes that all the filesystem mounted withing a share path have * the same characteristics, which is likely but not guaranteed. */ - { - vfs_statvfs_struct svfs; - - conn->fs_capabilities = - FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES; - if (SMB_VFS_STATVFS(conn, conn->connectpath, &svfs) == 0) { - conn->fs_capabilities = svfs.FsCapabilities; - } - } + conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn); /* * Print out the 'connected as' stuff here as we need diff --git a/source3/smbd/sesssetup.c b/source3/smbd/sesssetup.c index aee8e498e9..9baa02977a 100644 --- a/source3/smbd/sesssetup.c +++ b/source3/smbd/sesssetup.c @@ -259,7 +259,7 @@ static void reply_spnego_kerberos(struct smb_request *req, fstring user; int sess_vuid = req->vuid; NTSTATUS ret = NT_STATUS_OK; - PAC_DATA *pac_data = NULL; + struct 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; @@ -268,7 +268,7 @@ static void reply_spnego_kerberos(struct smb_request *req, fstring real_username; bool map_domainuser_to_guest = False; bool username_was_mapped; - PAC_LOGON_INFO *logon_info = NULL; + struct PAC_LOGON_INFO *logon_info = NULL; ZERO_STRUCT(ticket); ZERO_STRUCT(ap_rep); @@ -394,10 +394,9 @@ static void reply_spnego_kerberos(struct smb_request *req, domain = p+1; - if (logon_info && logon_info->info3.hdr_logon_dom.uni_str_len) { - unistr2_to_ascii(netbios_domain_name, - &logon_info->info3.uni_logon_dom, - sizeof(netbios_domain_name)); + if (logon_info && logon_info->info3.base.domain.string) { + fstrcpy(netbios_domain_name, + logon_info->info3.base.domain.string); domain = netbios_domain_name; DEBUG(10, ("Mapped to [%s] (using PAC)\n", domain)); @@ -410,30 +409,24 @@ static void reply_spnego_kerberos(struct smb_request *req, name. And even w2k3 does use ntlmssp if you for example connect to an ip address. */ - struct winbindd_request wb_request; - struct winbindd_response wb_response; - NSS_STATUS wb_result; - - ZERO_STRUCT(wb_request); - ZERO_STRUCT(wb_response); + wbcErr wbc_status; + struct wbcDomainInfo *info = NULL; DEBUG(10, ("Mapping [%s] to short name\n", domain)); - fstrcpy(wb_request.domain_name, domain); - - wb_result = winbindd_request_response(WINBINDD_DOMAIN_INFO, - &wb_request, &wb_response); + wbc_status = wbcDomainInfo(domain, &info); - if (wb_result == NSS_STATUS_SUCCESS) { + if (WBC_ERROR_IS_OK(wbc_status)) { fstrcpy(netbios_domain_name, - wb_response.data.domain_info.name); - domain = netbios_domain_name; + info->short_name); + wbcFreeMemory(info); + domain = netbios_domain_name; DEBUG(10, ("Mapped to [%s] (using Winbind)\n", domain)); } else { - DEBUG(3, ("Could not find short name -- winbind " - "not running?\n")); + DEBUG(3, ("Could not find short name: %s\n", + wbcErrorString(wbc_status))); } } @@ -626,6 +619,7 @@ static void reply_spnego_ntlmssp(struct smb_request *req, uint16 vuid, AUTH_NTLMSSP_STATE **auth_ntlmssp_state, DATA_BLOB *ntlmssp_blob, NTSTATUS nt_status, + const char *OID, bool wrap) { DATA_BLOB response; @@ -686,7 +680,7 @@ static void reply_spnego_ntlmssp(struct smb_request *req, if (wrap) { response = spnego_gen_auth_response(ntlmssp_blob, - nt_status, OID_NTLMSSP); + nt_status, OID); } else { response = *ntlmssp_blob; } @@ -751,6 +745,28 @@ NTSTATUS parse_spnego_mechanisms(DATA_BLOB blob_in, DATA_BLOB *pblob_out, } /**************************************************************************** + Fall back from krb5 to NTLMSSP. +****************************************************************************/ + +static void reply_spnego_downgrade_to_ntlmssp(struct smb_request *req, + uint16 vuid) +{ + DATA_BLOB response; + + reply_outbuf(req, 4, 0); + SSVAL(req->outbuf,smb_uid,vuid); + + DEBUG(3,("reply_spnego_downgrade_to_ntlmssp: Got krb5 ticket in SPNEGO " + "but set to downgrade to NTLMSSP\n")); + + response = spnego_gen_auth_response(NULL, + NT_STATUS_MORE_PROCESSING_REQUIRED, + OID_NTLMSSP); + reply_sesssetup_blob(req, response, NT_STATUS_MORE_PROCESSING_REQUIRED); + data_blob_free(&response); +} + +/**************************************************************************** Reply to a session setup spnego negotiate packet. ****************************************************************************/ @@ -795,6 +811,15 @@ static void reply_spnego_negotiate(struct smb_request *req, auth_ntlmssp_end(auth_ntlmssp_state); } + if (got_kerberos_mechanism) { + data_blob_free(&secblob); + /* The mechtoken is a krb5 ticket, but + * we need to fall back to NTLM. */ + reply_spnego_downgrade_to_ntlmssp(req, + vuid); + return; + } + status = auth_ntlmssp_start(auth_ntlmssp_state); if (!NT_STATUS_IS_OK(status)) { /* Kill the intermediate vuid */ @@ -809,7 +834,7 @@ static void reply_spnego_negotiate(struct smb_request *req, data_blob_free(&secblob); reply_spnego_ntlmssp(req, vuid, auth_ntlmssp_state, - &chal, status, True); + &chal, status, OID_NTLMSSP, true); data_blob_free(&chal); @@ -829,7 +854,7 @@ static void reply_spnego_auth(struct smb_request *req, DATA_BLOB auth = data_blob_null; DATA_BLOB auth_reply = data_blob_null; DATA_BLOB secblob = data_blob_null; - NTSTATUS status = NT_STATUS_INVALID_PARAMETER; + NTSTATUS status = NT_STATUS_LOGON_FAILURE; if (!spnego_parse_auth(blob1, &auth)) { #if 0 @@ -839,7 +864,7 @@ static void reply_spnego_auth(struct smb_request *req, invalidate_vuid(vuid); reply_nterror(req, nt_status_squash( - NT_STATUS_INVALID_PARAMETER)); + NT_STATUS_LOGON_FAILURE)); return; } @@ -849,24 +874,43 @@ static void reply_spnego_auth(struct smb_request *req, bool got_krb5_mechanism = False; status = parse_spnego_mechanisms(auth, &secblob, &got_krb5_mechanism); - if (NT_STATUS_IS_OK(status)) { - DEBUG(3,("reply_spnego_auth: Got secblob of size %lu\n", - (unsigned long)secblob.length)); + + if (!NT_STATUS_IS_OK(status)) { + /* Kill the intermediate vuid */ + invalidate_vuid(vuid); + reply_nterror(req, nt_status_squash(status)); + return; + } + + DEBUG(3,("reply_spnego_auth: Got secblob of size %lu\n", + (unsigned long)secblob.length)); #ifdef HAVE_KRB5 - if ( got_krb5_mechanism && ((lp_security()==SEC_ADS) || - lp_use_kerberos_keytab()) ) { - bool destroy_vuid = True; - reply_spnego_kerberos(req, &secblob, - vuid, &destroy_vuid); - data_blob_free(&secblob); - data_blob_free(&auth); - if (destroy_vuid) { - /* Kill the intermediate vuid */ - invalidate_vuid(vuid); - } - return; + if ( got_krb5_mechanism && ((lp_security()==SEC_ADS) || + lp_use_kerberos_keytab()) ) { + bool destroy_vuid = True; + reply_spnego_kerberos(req, &secblob, + vuid, &destroy_vuid); + data_blob_free(&secblob); + data_blob_free(&auth); + if (destroy_vuid) { + /* Kill the intermediate vuid */ + invalidate_vuid(vuid); } + return; + } #endif + /* Can't blunder into NTLMSSP auth if we have + * a krb5 ticket. */ + + if (got_krb5_mechanism) { + /* Kill the intermediate vuid */ + invalidate_vuid(vuid); + DEBUG(3,("reply_spnego_auth: network " + "misconfiguration, client sent us a " + "krb5 ticket and kerberos security " + "not enabled")); + reply_nterror(req, nt_status_squash( + NT_STATUS_LOGON_FAILURE)); } } @@ -874,13 +918,13 @@ static void reply_spnego_auth(struct smb_request *req, data_blob_free(&secblob); if (!*auth_ntlmssp_state) { - /* Kill the intermediate vuid */ - invalidate_vuid(vuid); - - /* auth before negotiatiate? */ - reply_nterror(req, nt_status_squash( - NT_STATUS_INVALID_PARAMETER)); - return; + status = auth_ntlmssp_start(auth_ntlmssp_state); + if (!NT_STATUS_IS_OK(status)) { + /* Kill the intermediate vuid */ + invalidate_vuid(vuid); + reply_nterror(req, nt_status_squash(status)); + return; + } } status = auth_ntlmssp_update(*auth_ntlmssp_state, @@ -888,9 +932,11 @@ static void reply_spnego_auth(struct smb_request *req, data_blob_free(&auth); + /* Don't send the mechid as we've already sent this (RFC4178). */ + reply_spnego_ntlmssp(req, vuid, auth_ntlmssp_state, - &auth_reply, status, True); + &auth_reply, status, NULL, true); data_blob_free(&auth_reply); @@ -1257,7 +1303,7 @@ static void reply_sesssetup_and_X_spnego(struct smb_request *req) reply_spnego_ntlmssp(req, vuid, &vuser->auth_ntlmssp_state, - &chal, status, False); + &chal, status, OID_NTLMSSP, false); data_blob_free(&chal); return; } diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c index bf6802f2a6..dc908846b3 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,7 +131,7 @@ 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) { @@ -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, 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 *); + + } + + /* 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)); + DEBUG(10, ("get_ea_list_from_file: total_len = %u\n", + (unsigned int)*pea_total_len)); + return ea_list_head; } @@ -913,7 +1017,7 @@ static void call_trans2open(connection_struct *conn, } size = get_file_size(sbuf); - fattr = dos_mode(conn,fname,&sbuf); + fattr = dos_mode(conn,fsp->fsp_name,&sbuf); mtime = sbuf.st_mtime; inode = sbuf.st_ino; if (fattr & aDIR) { @@ -950,7 +1054,7 @@ static void call_trans2open(connection_struct *conn, SIVAL(params,20,inode); SSVAL(params,24,0); /* Padding. */ if (flags & 8) { - uint32 ea_size = estimate_ea_size(conn, fsp, fname); + uint32 ea_size = estimate_ea_size(conn, fsp, fsp->fsp_name); SIVAL(params, 26, ea_size); } else { SIVAL(params, 26, 0); @@ -2355,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). ****************************************************************************/ @@ -2373,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; @@ -2487,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 @@ -2688,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; } @@ -3463,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 ! ****************************************************************************/ @@ -3738,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; @@ -3767,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); @@ -4161,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")); 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; |