s3: smbd: Add a dirfsp parameter to smbd_calculate_access_mask().
[amitay/samba.git] / source3 / smbd / smb2_create.c
index 41bfe9ccd8898e277924529f98c4d1bf4e0d584b..0418ca34f95086f67159b0946941c34c03a46c16 100644 (file)
@@ -29,6 +29,9 @@
 #include "../lib/util/tevent_ntstatus.h"
 #include "messages.h"
 
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_SMB2
+
 int map_smb2_oplock_levels_to_samba(uint8_t in_oplock_level)
 {
        switch(in_oplock_level) {
@@ -65,6 +68,44 @@ static uint8_t map_samba_oplock_levels_to_smb2(int oplock_type)
        }
 }
 
+/*
+ MS-FSA 2.1.5.1 Server Requests an Open of a File
+ Trailing '/' or '\\' checker.
+ Must be done before the filename parser removes any
+ trailing characters. If we decide to add this to SMB1
+ NTCreate processing we can make this public.
+
+ Note this is Windows pathname processing only. When
+ POSIX pathnames are added to SMB2 this will not apply.
+*/
+
+static NTSTATUS windows_name_trailing_check(const char *name,
+                       uint32_t create_options)
+{
+       size_t name_len = strlen(name);
+       char trail_c;
+
+       if (name_len <= 1) {
+               return NT_STATUS_OK;
+       }
+
+       trail_c = name[name_len-1];
+
+       /*
+        * Trailing '/' is always invalid.
+        */
+       if (trail_c == '/') {
+               return NT_STATUS_OBJECT_NAME_INVALID;
+       }
+
+       if (create_options & FILE_NON_DIRECTORY_FILE) {
+               if (trail_c == '\\') {
+                       return NT_STATUS_OBJECT_NAME_INVALID;
+               }
+       }
+       return NT_STATUS_OK;
+}
+
 static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
                        struct tevent_context *ev,
                        struct smbd_smb2_request *smb2req,
@@ -331,18 +372,18 @@ static void smbd_smb2_request_create_done(struct tevent_req *tsubreq)
        SCVAL(outbody.data, 0x03, 0);           /* reserved */
        SIVAL(outbody.data, 0x04,
              out_create_action);               /* create action */
-       put_long_date_timespec(conn->ts_res,
+       put_long_date_full_timespec(conn->ts_res,
              (char *)outbody.data + 0x08,
-             out_creation_ts);                 /* creation time */
-       put_long_date_timespec(conn->ts_res,
+             &out_creation_ts);                /* creation time */
+       put_long_date_full_timespec(conn->ts_res,
              (char *)outbody.data + 0x10,
-             out_last_access_ts);              /* last access time */
-       put_long_date_timespec(conn->ts_res,
+             &out_last_access_ts);             /* last access time */
+       put_long_date_full_timespec(conn->ts_res,
              (char *)outbody.data + 0x18,
-             out_last_write_ts);               /* last write time */
-       put_long_date_timespec(conn->ts_res,
+             &out_last_write_ts);              /* last write time */
+       put_long_date_full_timespec(conn->ts_res,
              (char *)outbody.data + 0x20,
-             out_change_ts);                   /* change time */
+             &out_change_ts);                  /* change time */
        SBVAL(outbody.data, 0x28,
              out_allocation_size);             /* allocation size */
        SBVAL(outbody.data, 0x30,
@@ -378,6 +419,7 @@ static NTSTATUS smbd_smb2_create_durable_lease_check(struct smb_request *smb1req
        const char *requested_filename, const struct files_struct *fsp,
        const struct smb2_lease *lease_ptr)
 {
+       char *filename = NULL;
        struct smb_filename *smb_fname = NULL;
        uint32_t ucf_flags;
        NTSTATUS status;
@@ -404,10 +446,23 @@ static NTSTATUS smbd_smb2_create_durable_lease_check(struct smb_request *smb1req
                return NT_STATUS_OBJECT_NAME_NOT_FOUND;
        }
 
+       filename = talloc_strdup(talloc_tos(), requested_filename);
+       if (filename == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /* This also converts '\' to '/' */
+       status = check_path_syntax(filename);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(filename);
+               return status;
+       }
+
        ucf_flags = filename_create_ucf_flags(smb1req, FILE_OPEN);
        status = filename_convert(talloc_tos(), fsp->conn,
-                                 requested_filename, ucf_flags,
-                                 NULL, &smb_fname);
+                                 filename, ucf_flags,
+                                 NULL, NULL, &smb_fname);
+       TALLOC_FREE(filename);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(10, ("filename_convert returned %s\n",
                           nt_errstr(status)));
@@ -459,6 +514,8 @@ struct smbd_smb2_create_state {
        ssize_t lease_len;
        bool need_replay_cache;
        struct smbXsrv_open *op;
+       time_t twrp_time;
+       time_t *twrp_timep;
 
        struct smb2_create_blob *dhnc;
        struct smb2_create_blob *dh2c;
@@ -494,6 +551,14 @@ static NTSTATUS smbd_smb2_create_fetch_create_ctx(
        struct smbd_smb2_create_state *state = tevent_req_data(
                req, struct smbd_smb2_create_state);
 
+       /*
+        * For now, remove the posix create context from the wire. We
+        * are using it inside smbd and will properly use it once
+        * smb3.11 unix extensions will be done. So in the future we
+        * will remove it only if unix extensions are not negotiated.
+        */
+       smb2_create_blob_remove(in_context_blobs, SMB2_CREATE_TAG_POSIX);
+
        state->dhnq = smb2_create_blob_find(in_context_blobs,
                                            SMB2_CREATE_TAG_DHNQ);
        state->dhnc = smb2_create_blob_find(in_context_blobs,
@@ -646,14 +711,12 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
        state->smb1req = smb1req;
 
        if (smb2req->subreq == NULL) {
-               DEBUG(10,("smbd_smb2_create: name[%s]\n",
-                       in_name));
+               DBG_DEBUG("name [%s]\n", in_name);
        } else {
                struct smbd_smb2_create_state *old_state = tevent_req_data(
                        smb2req->subreq, struct smbd_smb2_create_state);
 
-               DEBUG(10,("smbd_smb2_create_send: reentrant for file %s\n",
-                       in_name ));
+               DBG_DEBUG("reentrant for file %s\n", in_name);
 
                state->id = old_state->id;
                state->request_time = old_state->request_time;
@@ -741,12 +804,19 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
                return req;
        }
 
+       /* Check for trailing slash specific directory handling. */
+       status = windows_name_trailing_check(state->fname, in_create_options);
+       if (!NT_STATUS_IS_OK(status)) {
+               tevent_req_nterror(req, status);
+               return tevent_req_post(req, state->ev);
+       }
+
        smbd_smb2_create_before_exec(req);
        if (!tevent_req_is_in_progress(req)) {
                return tevent_req_post(req, state->ev);
        }
 
-       DEBUG(10, ("smbd_smb2_create_send: open execution phase\n"));
+       DBG_DEBUG("open execution phase\n");
 
        /*
         * For the backend file open procedure, there are
@@ -779,16 +849,14 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
                                               now,
                                               &state->op);
                if (!NT_STATUS_IS_OK(status)) {
-                       DEBUG(3, ("smbd_smb2_create_send: "
-                                 "smb2srv_open_recreate failed: %s\n",
-                                 nt_errstr(status)));
+                       DBG_NOTICE("smb2srv_open_recreate failed: %s\n",
+                                  nt_errstr(status));
                        tevent_req_nterror(req, status);
                        return tevent_req_post(req, state->ev);
                }
 
-               DEBUG(10, ("smb2_create_send: %s to recreate the "
-                          "smb2srv_open struct for a durable handle.\n",
-                          state->op->global->durable ? "succeeded" : "failed"));
+               DBG_DEBUG("%s to recreate durable handle\n",
+                         state->op->global->durable ? "succeeded" : "failed");
 
                if (!state->op->global->durable) {
                        talloc_free(state->op);
@@ -809,17 +877,16 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 
                        return_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
 
-                       DEBUG(3, ("smbd_smb2_create_send: "
-                                 "durable_reconnect failed: %s => %s\n",
-                                 nt_errstr(status),
-                                 nt_errstr(return_status)));
+                       DBG_NOTICE("durable_reconnect failed: %s => %s\n",
+                                  nt_errstr(status),
+                                  nt_errstr(return_status));
 
                        tevent_req_nterror(req, return_status);
                        return tevent_req_post(req, state->ev);
                }
 
-               DEBUG(10, ("result->oplock_type=%u, lease_ptr==%p\n",
-                          (unsigned)state->result->oplock_type, state->lease_ptr));
+               DBG_DEBUG("oplock_type=%u, lease_ptr==%p\n",
+                         (unsigned)state->result->oplock_type, state->lease_ptr);
 
                status = smbd_smb2_create_durable_lease_check(
                        smb1req, state->fname, state->result, state->lease_ptr);
@@ -879,6 +946,7 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
                                  smb1req->conn,
                                  state->fname,
                                  ucf_flags,
+                                 state->twrp_timep,
                                  NULL, /* ppath_contains_wcards */
                                  &smb_fname);
        if (!NT_STATUS_IS_OK(status)) {
@@ -1166,9 +1234,6 @@ static void smbd_smb2_create_before_exec(struct tevent_req *req)
 
        if (state->twrp != NULL) {
                NTTIME nttime;
-               time_t t;
-               struct tm *tm;
-               char *tmpname = state->fname;
 
                if (state->twrp->data.length != 8) {
                        tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
@@ -1176,27 +1241,9 @@ static void smbd_smb2_create_before_exec(struct tevent_req *req)
                }
 
                nttime = BVAL(state->twrp->data.data, 0);
-               t = nt_time_to_unix(nttime);
-               tm = gmtime(&t);
+               state->twrp_time = nt_time_to_unix(nttime);
+               state->twrp_timep = &state->twrp_time;
 
-               state->fname = talloc_asprintf(
-                       state,
-                       "%s\\@GMT-%04u.%02u.%02u-%02u.%02u.%02u",
-                       state->fname,
-                       tm->tm_year + 1900,
-                       tm->tm_mon + 1,
-                       tm->tm_mday,
-                       tm->tm_hour,
-                       tm->tm_min,
-                       tm->tm_sec);
-               if (tevent_req_nomem(state->fname, req)) {
-                       return;
-               }
-               TALLOC_FREE(tmpname);
-               /*
-                * Tell filename_create_ucf_flags() this
-                * is an @GMT path.
-                */
                smb1req->flags2 |= FLAGS2_REPARSE_PATH;
        }
 
@@ -1264,8 +1311,6 @@ static void smbd_smb2_create_before_exec(struct tevent_req *req)
                        }
                }
        }
-
-       return;
 }
 
 static void smbd_smb2_create_after_exec(struct tevent_req *req)
@@ -1282,21 +1327,25 @@ static void smbd_smb2_create_after_exec(struct tevent_req *req)
        DEBUG(10, ("smbd_smb2_create_send: "
                   "response construction phase\n"));
 
+       state->out_file_attributes = dos_mode(state->result->conn,
+                                             state->result->fsp_name);
+
        if (state->mxac != NULL) {
                NTTIME last_write_time;
 
-               last_write_time = unix_timespec_to_nt_time(
-                       state->result->fsp_name->st.st_ex_mtime);
+               last_write_time = full_timespec_to_nt_time(
+                       &state->result->fsp_name->st.st_ex_mtime);
                if (last_write_time != state->max_access_time) {
                        uint8_t p[8];
                        uint32_t max_access_granted;
                        DATA_BLOB blob = data_blob_const(p, sizeof(p));
 
                        status = smbd_calculate_access_mask(smb1req->conn,
-                                                           state->result->fsp_name,
-                                                           false,
-                                                           SEC_FLAG_MAXIMUM_ALLOWED,
-                                                           &max_access_granted);
+                                       smb1req->conn->cwd_fsp,
+                                       state->result->fsp_name,
+                                       false,
+                                       SEC_FLAG_MAXIMUM_ALLOWED,
+                                       &max_access_granted);
 
                        SIVAL(p, 0, NT_STATUS_V(status));
                        SIVAL(p, 4, max_access_granted);
@@ -1397,8 +1446,9 @@ static void smbd_smb2_create_after_exec(struct tevent_req *req)
 
        if (state->qfid != NULL) {
                uint8_t p[32];
-               uint64_t file_index = get_FileIndex(state->result->conn,
-                                                   &state->result->fsp_name->st);
+               uint64_t file_id = SMB_VFS_FS_FILE_ID(
+                       state->result->conn,
+                       &state->result->fsp_name->st);
                DATA_BLOB blob = data_blob_const(p, sizeof(p));
 
                ZERO_STRUCT(p);
@@ -1407,7 +1457,7 @@ static void smbd_smb2_create_after_exec(struct tevent_req *req)
                   the MS plugfest. The first 8 bytes are the "volume index"
                   == inode, the second 8 bytes are the "volume id",
                   == dev. This will be updated in the SMB2 doc. */
-               SBVAL(p, 0, file_index);
+               SBVAL(p, 0, file_id);
                SIVAL(p, 8, state->result->fsp_name->st.st_ex_dev);/* FileIndexHigh */
 
                status = smb2_create_blob_add(state->out_context_blobs,
@@ -1479,8 +1529,6 @@ static void smbd_smb2_create_finish(struct tevent_req *req)
                state->out_create_action = state->info;
        }
        result->op->create_action = state->out_create_action;
-       state->out_file_attributes = dos_mode(result->conn,
-                                          result->fsp_name);
 
        state->out_creation_ts = get_create_timespec(smb1req->conn,
                                        result, result->fsp_name);
@@ -1510,7 +1558,6 @@ static void smbd_smb2_create_finish(struct tevent_req *req)
 
        tevent_req_done(req);
        tevent_req_post(req, state->ev);
-       return;
 }
 
 static NTSTATUS smbd_smb2_create_recv(struct tevent_req *req,