s3:smbd: Add functions calc_max_read_pdu()/calc_read_size() to work out the length...
[obnox/samba/samba-obnox.git] / source3 / smbd / reply.c
index 954938cbe30a96e16d6fd35c83fdbb7c8270dffc..8b500c5387cce56959bfc12ef93083d795847986 100644 (file)
@@ -41,6 +41,8 @@
 #include "auth.h"
 #include "smbprofile.h"
 #include "../lib/tsocket/tsocket.h"
+#include "lib/tevent_wait.h"
+#include "libcli/smb/smb_signing.h"
 
 /****************************************************************************
  Ensure we check the path in *exactly* the same way as W2K for a findfirst/findnext
@@ -588,6 +590,19 @@ void reply_special(struct smbd_server_connection *sconn, char *inbuf, size_t inb
                set_local_machine_name(name1, True);
                set_remote_machine_name(name2, True);
 
+               if (is_ipaddress(sconn->remote_hostname)) {
+                       char *p = discard_const_p(char, sconn->remote_hostname);
+
+                       talloc_free(p);
+
+                       sconn->remote_hostname = talloc_strdup(sconn,
+                                               get_remote_machine_name());
+                       if (sconn->remote_hostname == NULL) {
+                               exit_server_cleanly("could not copy remote name");
+                       }
+                       sconn->conn->remote_hostname = sconn->remote_hostname;
+               }
+
                DEBUG(2,("netbios connect: local=%s remote=%s, name type = %x\n",
                         get_local_machine_name(), get_remote_machine_name(),
                         name_type2));
@@ -651,6 +666,7 @@ void reply_tcon(struct smb_request *req)
        const char *p;
        TALLOC_CTX *ctx = talloc_tos();
        struct smbd_server_connection *sconn = req->sconn;
+       NTTIME now = timeval_to_nttime(&req->request_time);
 
        START_PROFILE(SMBtcon);
 
@@ -680,7 +696,7 @@ void reply_tcon(struct smb_request *req)
                service = service_buf;
        }
 
-       conn = make_connection(sconn,service,dev,
+       conn = make_connection(sconn, now, service, dev,
                               req->vuid,&nt_status);
        req->conn = conn;
 
@@ -720,7 +736,11 @@ void reply_tcon_and_X(struct smb_request *req)
        int passlen;
        char *path = NULL;
        const char *p, *q;
-       uint16 tcon_flags;
+       uint16_t tcon_flags;
+       struct smbXsrv_session *session = NULL;
+       NTTIME now = timeval_to_nttime(&req->request_time);
+       bool session_key_updated = false;
+       uint16_t optional_support = 0;
        struct smbd_server_connection *sconn = req->sconn;
 
        START_PROFILE(SMBtconX);
@@ -735,7 +755,7 @@ void reply_tcon_and_X(struct smb_request *req)
        tcon_flags = SVAL(req->vwv+2, 0);
 
        /* we might have to close an old one */
-       if ((tcon_flags & 0x1) && conn) {
+       if ((tcon_flags & TCONX_FLAG_DISCONNECT_TID) && conn) {
                struct smbXsrv_tcon *tcon;
                NTSTATUS status;
 
@@ -811,11 +831,99 @@ void reply_tcon_and_X(struct smb_request *req)
 
        DEBUG(4,("Client requested device type [%s] for share [%s]\n", client_devicetype, service));
 
-       conn = make_connection(sconn, service, client_devicetype,
+       nt_status = smb1srv_session_lookup(req->sconn->conn,
+                                          req->vuid, now, &session);
+       if (NT_STATUS_EQUAL(nt_status, NT_STATUS_USER_SESSION_DELETED)) {
+               reply_force_doserror(req, ERRSRV, ERRbaduid);
+               END_PROFILE(SMBtconX);
+               return;
+       }
+       if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NETWORK_SESSION_EXPIRED)) {
+               reply_nterror(req, nt_status);
+               END_PROFILE(SMBtconX);
+               return;
+       }
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               reply_nterror(req, NT_STATUS_INVALID_HANDLE);
+               END_PROFILE(SMBtconX);
+               return;
+       }
+
+       if (session->global->auth_session_info == NULL) {
+               reply_nterror(req, NT_STATUS_INVALID_HANDLE);
+               END_PROFILE(SMBtconX);
+               return;
+       }
+
+       /*
+        * If there is no application key defined yet
+        * we create one.
+        *
+        * This means we setup the application key on the
+        * first tcon that happens via the given session.
+        *
+        * Once the application key is defined, it does not
+        * change any more.
+        */
+       if (session->global->application_key.length == 0 &&
+           session->global->signing_key.length > 0)
+       {
+               struct smbXsrv_session *x = session;
+               struct auth_session_info *session_info =
+                       session->global->auth_session_info;
+               uint8_t session_key[16];
+
+               ZERO_STRUCT(session_key);
+               memcpy(session_key, x->global->signing_key.data,
+                      MIN(x->global->signing_key.length, sizeof(session_key)));
+
+               /*
+                * The application key is truncated/padded to 16 bytes
+                */
+               x->global->application_key = data_blob_talloc(x->global,
+                                                            session_key,
+                                                            sizeof(session_key));
+               ZERO_STRUCT(session_key);
+               if (x->global->application_key.data == NULL) {
+                       reply_nterror(req, NT_STATUS_NO_MEMORY);
+                       END_PROFILE(SMBtconX);
+                       return;
+               }
+
+               if (tcon_flags & TCONX_FLAG_EXTENDED_SIGNATURES) {
+                       smb_key_derivation(x->global->application_key.data,
+                                          x->global->application_key.length,
+                                          x->global->application_key.data);
+                       optional_support |= SMB_EXTENDED_SIGNATURES;
+               }
+
+               /*
+                * Place the application key into the session_info
+                */
+               data_blob_clear_free(&session_info->session_key);
+               session_info->session_key = data_blob_dup_talloc(session_info,
+                                               x->global->application_key);
+               if (session_info->session_key.data == NULL) {
+                       data_blob_clear_free(&x->global->application_key);
+                       reply_nterror(req, NT_STATUS_NO_MEMORY);
+                       END_PROFILE(SMBtconX);
+                       return;
+               }
+               session_key_updated = true;
+       }
+
+       conn = make_connection(sconn, now, service, client_devicetype,
                               req->vuid, &nt_status);
        req->conn =conn;
 
        if (!conn) {
+               if (session_key_updated) {
+                       struct smbXsrv_session *x = session;
+                       struct auth_session_info *session_info =
+                               session->global->auth_session_info;
+                       data_blob_clear_free(&x->global->application_key);
+                       data_blob_clear_free(&session_info->session_key);
+               }
                reply_nterror(req, nt_status);
                END_PROFILE(SMBtconX);
                return;
@@ -871,15 +979,17 @@ void reply_tcon_and_X(struct smb_request *req)
 
                /* what does setting this bit do? It is set by NT4 and
                   may affect the ability to autorun mounted cdroms */
-               SSVAL(req->outbuf, smb_vwv2, SMB_SUPPORT_SEARCH_BITS|
-                     (lp_csc_policy(SNUM(conn)) << 2));
+               optional_support |= SMB_SUPPORT_SEARCH_BITS;
+               optional_support |=
+                       (lp_csc_policy(SNUM(conn)) << SMB_CSC_POLICY_SHIFT);
 
                if (lp_msdfs_root(SNUM(conn)) && lp_host_msdfs()) {
                        DEBUG(2,("Serving %s as a Dfs root\n",
                                 lp_servicename(ctx, SNUM(conn)) ));
-                       SSVAL(req->outbuf, smb_vwv2,
-                             SMB_SHARE_IN_DFS | SVAL(req->outbuf, smb_vwv2));
+                       optional_support |= SMB_SHARE_IN_DFS;
                }
+
+               SSVAL(req->outbuf, smb_vwv2, optional_support);
        }
 
        SSVAL(req->outbuf, smb_vwv0, 0xff); /* andx chain ends */
@@ -3184,8 +3294,7 @@ void reply_readbraw(struct smb_request *req)
 
        START_PROFILE(SMBreadbraw);
 
-       if (srv_is_signing_active(sconn) ||
-           is_encrypted_packet(sconn, req->inbuf)) {
+       if (srv_is_signing_active(sconn) || req->encrypted) {
                exit_server_cleanly("reply_readbraw: SMB signing/sealing is active - "
                        "raw reads/writes are disallowed.");
        }
@@ -3588,7 +3697,7 @@ static void send_file_readX(connection_struct *conn, struct smb_request *req,
         */
 
        if (!req_is_in_chain(req) &&
-           !is_encrypted_packet(req->sconn, req->inbuf) &&
+           !req->encrypted &&
            (fsp->base_fsp == NULL) &&
            (fsp->wcp == NULL) &&
            lp_use_sendfile(SNUM(conn), req->sconn->smb1.signing_state) ) {
@@ -3738,17 +3847,95 @@ nosendfile_read:
        return;
 }
 
+/****************************************************************************
+ Work out how much space we have for a read return.
+****************************************************************************/
+
+static size_t calc_max_read_pdu(const struct smb_request *req)
+{
+       if (req->sconn->conn->protocol < PROTOCOL_NT1) {
+               return req->sconn->smb1.sessions.max_send;
+       }
+
+       if (!lp_large_readwrite()) {
+               return req->sconn->smb1.sessions.max_send;
+       }
+
+       if (req_is_in_chain(req)) {
+               return req->sconn->smb1.sessions.max_send;
+       }
+
+       if (req->encrypted) {
+               /*
+                * Don't take encrypted traffic up to the
+                * limit. There are padding considerations
+                * that make that tricky.
+                */
+               return req->sconn->smb1.sessions.max_send;
+       }
+
+       if (srv_is_signing_active(req->sconn)) {
+               return 0x1FFFF;
+       }
+
+       if (!lp_unix_extensions()) {
+               return 0x1FFFF;
+       }
+
+       /*
+        * We can do ultra-large POSIX reads.
+        */
+       return 0xFFFFFF;
+}
+
+/****************************************************************************
+ Calculate how big a read can be. Copes with all clients. It's always
+ safe to return a short read - Windows does this.
+****************************************************************************/
+
+static size_t calc_read_size(const struct smb_request *req,
+                            size_t upper_size,
+                            size_t lower_size)
+{
+       size_t max_pdu = calc_max_read_pdu(req);
+       size_t total_size = 0;
+       size_t hdr_len = MIN_SMB_SIZE + VWV(12);
+       size_t max_len = max_pdu - hdr_len;
+
+       /*
+        * Windows explicitly ignores upper size of 0xFFFF.
+        * See [MS-SMB].pdf <26> Section 2.2.4.2.1:
+        * We must do the same as these will never fit even in
+        * an extended size NetBIOS packet.
+        */
+       if (upper_size == 0xFFFF) {
+               upper_size = 0;
+       }
+
+       if (req->sconn->conn->protocol < PROTOCOL_NT1) {
+               upper_size = 0;
+       }
+
+       total_size = ((upper_size<<16) | lower_size);
+
+       /*
+        * LARGE_READX test shows it's always safe to return
+        * a short read. Windows does so.
+        */
+       return MIN(total_size, max_len);
+}
+
 /****************************************************************************
  Reply to a read and X.
 ****************************************************************************/
 
 void reply_read_and_X(struct smb_request *req)
 {
-       struct smbd_server_connection *sconn = req->sconn;
        connection_struct *conn = req->conn;
        files_struct *fsp;
        off_t startpos;
        size_t smb_maxcnt;
+       size_t upper_size;
        bool big_readX = False;
 #if 0
        size_t smb_mincnt = SVAL(req->vwv+6, 0);
@@ -3783,40 +3970,15 @@ void reply_read_and_X(struct smb_request *req)
                return;
        }
 
-       if ((sconn->smb1.unix_info.client_cap_low & CIFS_UNIX_LARGE_READ_CAP) ||
-           (get_remote_arch() == RA_SAMBA)) {
+       upper_size = SVAL(req->vwv+7, 0);
+       smb_maxcnt = calc_read_size(req, upper_size, smb_maxcnt);
+       if (smb_maxcnt > (0x1FFFF - (MIN_SMB_SIZE + VWV(12)))) {
                /*
-                * This is Samba only behavior (up to Samba 3.6)!
-                *
-                * Windows 2008 R2 ignores the upper_size,
-                * so we do unless unix extentions are active
-                * or "smbclient" is talking to us.
+                * This is a heuristic to avoid keeping large
+                * outgoing buffers around over long-lived aio
+                * requests.
                 */
-               size_t upper_size = SVAL(req->vwv+7, 0);
-               smb_maxcnt |= (upper_size<<16);
-               if (upper_size > 1) {
-                       /* Can't do this on a chained packet. */
-                       if ((CVAL(req->vwv+0, 0) != 0xFF)) {
-                               reply_nterror(req, NT_STATUS_NOT_SUPPORTED);
-                               END_PROFILE(SMBreadX);
-                               return;
-                       }
-                       /* We currently don't do this on signed or sealed data. */
-                       if (srv_is_signing_active(req->sconn) ||
-                           is_encrypted_packet(req->sconn, req->inbuf)) {
-                               reply_nterror(req, NT_STATUS_NOT_SUPPORTED);
-                               END_PROFILE(SMBreadX);
-                               return;
-                       }
-                       /* Is there room in the reply for this data ? */
-                       if (smb_maxcnt > (0xFFFFFF - (smb_size -4 + 12*2)))  {
-                               reply_nterror(req,
-                                             NT_STATUS_INVALID_PARAMETER);
-                               END_PROFILE(SMBreadX);
-                               return;
-                       }
-                       big_readX = True;
-               }
+               big_readX = True;
        }
 
        if (req->wct == 12) {
@@ -4812,6 +4974,13 @@ void reply_exit(struct smb_request *req)
        return;
 }
 
+struct reply_close_state {
+       files_struct *fsp;
+       struct smb_request *smbreq;
+};
+
+static void do_smb1_close(struct tevent_req *req);
+
 void reply_close(struct smb_request *req)
 {
        connection_struct *conn = req->conn;
@@ -4853,6 +5022,39 @@ void reply_close(struct smb_request *req)
                set_close_write_time(fsp, convert_time_t_to_timespec(t));
        }
 
+       if (fsp->num_aio_requests != 0) {
+
+               struct reply_close_state *state;
+
+               DEBUG(10, ("closing with aio %u requests pending\n",
+                          fsp->num_aio_requests));
+
+               /*
+                * We depend on the aio_extra destructor to take care of this
+                * close request once fsp->num_aio_request drops to 0.
+                */
+
+               fsp->deferred_close = tevent_wait_send(
+                       fsp, fsp->conn->sconn->ev_ctx);
+               if (fsp->deferred_close == NULL) {
+                       status = NT_STATUS_NO_MEMORY;
+                       goto done;
+               }
+
+               state = talloc(fsp, struct reply_close_state);
+               if (state == NULL) {
+                       TALLOC_FREE(fsp->deferred_close);
+                       status = NT_STATUS_NO_MEMORY;
+                       goto done;
+               }
+               state->fsp = fsp;
+               state->smbreq = talloc_move(fsp, &req);
+               tevent_req_set_callback(fsp->deferred_close, do_smb1_close,
+                                       state);
+               END_PROFILE(SMBclose);
+               return;
+       }
+
        /*
         * close_file() returns the unix errno if an error was detected on
         * close - normally this is due to a disk full error. If not then it
@@ -4860,7 +5062,7 @@ void reply_close(struct smb_request *req)
         */
 
        status = close_file(req, fsp, NORMAL_CLOSE);
-
+done:
        if (!NT_STATUS_IS_OK(status)) {
                reply_nterror(req, status);
                END_PROFILE(SMBclose);
@@ -4872,6 +5074,49 @@ void reply_close(struct smb_request *req)
        return;
 }
 
+static void do_smb1_close(struct tevent_req *req)
+{
+       struct reply_close_state *state = tevent_req_callback_data(
+               req, struct reply_close_state);
+       struct smb_request *smbreq;
+       NTSTATUS status;
+       int ret;
+
+       ret = tevent_wait_recv(req);
+       TALLOC_FREE(req);
+       if (ret != 0) {
+               DEBUG(10, ("tevent_wait_recv returned %s\n",
+                          strerror(ret)));
+               /*
+                * Continue anyway, this should never happen
+                */
+       }
+
+       /*
+        * fsp->smb2_close_request right now is a talloc grandchild of
+        * fsp. When we close_file(fsp), it would go with it. No chance to
+        * reply...
+        */
+       smbreq = talloc_move(talloc_tos(), &state->smbreq);
+
+       status = close_file(smbreq, state->fsp, NORMAL_CLOSE);
+       if (NT_STATUS_IS_OK(status)) {
+               reply_outbuf(smbreq, 0, 0);
+       } else {
+               reply_nterror(smbreq, status);
+       }
+       if (!srv_send_smb(smbreq->sconn,
+                       (char *)smbreq->outbuf,
+                       true,
+                       smbreq->seqnum+1,
+                       IS_CONN_ENCRYPTED(smbreq->conn)||smbreq->encrypted,
+                       NULL)) {
+               exit_server_cleanly("handle_aio_read_complete: srv_send_smb "
+                                   "failed.");
+       }
+       TALLOC_FREE(smbreq);
+}
+
 /****************************************************************************
  Reply to a writeclose (Core+ protocol).
 ****************************************************************************/
@@ -6204,7 +6449,8 @@ NTSTATUS rename_internals_fsp(connection_struct *conn,
                          "%s -> %s\n", smb_fname_str_dbg(fsp->fsp_name),
                          smb_fname_str_dbg(smb_fname_dst)));
 
-               if (!lp_posix_pathnames() &&
+               if (!fsp->is_directory &&
+                   !lp_posix_pathnames() &&
                    (lp_map_archive(SNUM(conn)) ||
                    lp_store_dos_attributes(SNUM(conn)))) {
                        /* We must set the archive bit on the newly
@@ -6292,6 +6538,7 @@ NTSTATUS rename_internals(TALLOC_CTX *ctx,
        long offset = 0;
        int create_options = 0;
        bool posix_pathnames = lp_posix_pathnames();
+       int rc;
 
        /*
         * Split the old name into directory and last component
@@ -6384,9 +6631,13 @@ NTSTATUS rename_internals(TALLOC_CTX *ctx,
 
                ZERO_STRUCT(smb_fname_src->st);
                if (posix_pathnames) {
-                       SMB_VFS_LSTAT(conn, smb_fname_src);
+                       rc = SMB_VFS_LSTAT(conn, smb_fname_src);
                } else {
-                       SMB_VFS_STAT(conn, smb_fname_src);
+                       rc = SMB_VFS_STAT(conn, smb_fname_src);
+               }
+               if (rc == -1) {
+                       status = map_nt_error_from_unix_common(errno);
+                       goto out;
                }
 
                if (S_ISDIR(smb_fname_src->st.st_ex_mode)) {