Same fix as bug 8989 - Samba 3.5.x (and probably all other versions of Samba) does...
[samba.git] / source3 / smbd / trans2.c
index fb4d46b76bf39c58eedaa433e4d7991de2d839d4..602280d9c18c2809a1126e73ded06ce982c45928 100644 (file)
@@ -849,12 +849,6 @@ void send_trans2_replies(connection_struct *conn,
                reply_outbuf(req, 10, total_sent_thistime + alignment_offset
                             + data_alignment_offset);
 
-               /*
-                * We might have SMBtrans2s in req which was transferred to
-                * the outbuf, fix that.
-                */
-               SCVAL(req->outbuf, smb_com, SMBtrans2);
-
                /* Set total params and data to be sent */
                SSVAL(req->outbuf,smb_tprcnt,paramsize);
                SSVAL(req->outbuf,smb_tdrcnt,datasize);
@@ -1068,7 +1062,8 @@ static void call_trans2open(connection_struct *conn,
                goto out;
        }
 
-       if (!map_open_params_to_ntcreate(smb_fname, deny_mode, open_ofun,
+       if (!map_open_params_to_ntcreate(smb_fname->base_name, deny_mode,
+                                        open_ofun,
                                         &access_mask, &share_mode,
                                         &create_disposition,
                                         &create_options)) {
@@ -2232,6 +2227,7 @@ static void call_trans2findfirst(connection_struct *conn,
        TALLOC_CTX *ctx = talloc_tos();
        struct dptr_struct *dirptr = NULL;
        struct smbd_server_connection *sconn = smbd_server_conn;
+       uint32_t ucf_flags = (UCF_SAVE_LCOMP | UCF_ALWAYS_ALLOW_WCARD_LCOMP);
 
        if (total_params < 13) {
                reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
@@ -2275,6 +2271,7 @@ close_if_end = %d requires_resume_key = %d level = 0x%x, max_data_bytes = %d\n",
                                reply_nterror(req, NT_STATUS_INVALID_LEVEL);
                                goto out;
                        }
+                       ucf_flags |= UCF_UNIX_NAME_LOOKUP;
                        break;
                default:
                        reply_nterror(req, NT_STATUS_INVALID_LEVEL);
@@ -2292,8 +2289,7 @@ close_if_end = %d requires_resume_key = %d level = 0x%x, max_data_bytes = %d\n",
        ntstatus = filename_convert(ctx, conn,
                                    req->flags2 & FLAGS2_DFS_PATHNAMES,
                                    directory,
-                                   (UCF_SAVE_LCOMP |
-                                       UCF_ALWAYS_ALLOW_WCARD_LCOMP),
+                                   ucf_flags,
                                    &mask_contains_wcard,
                                    &smb_dname);
        if (!NT_STATUS_IS_OK(ntstatus)) {
@@ -3156,7 +3152,8 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned
                        fsp.fnum = -1;
 
                        /* access check */
-                       if (conn->server_info->utok.uid != sec_initial_uid()) {
+                       if (conn->server_info->utok.uid != sec_initial_uid() &&
+                                       !conn->admin_user) {
                                DEBUG(0,("set_user_quota: access_denied "
                                         "service [%s] user [%s]\n",
                                         lp_servicename(SNUM(conn)),
@@ -3643,7 +3640,7 @@ cap_low = 0x%x, cap_high = 0x%x\n",
                                ZERO_STRUCT(quotas);
 
                                /* access check */
-                               if ((conn->server_info->utok.uid != sec_initial_uid())
+                               if (((conn->server_info->utok.uid != sec_initial_uid()) && !conn->admin_user)
                                    ||!CAN_WRITE(conn)) {
                                        DEBUG(0,("set_user_quota: access_denied service [%s] user [%s]\n",
                                                 lp_servicename(SNUM(conn)),
@@ -5087,6 +5084,7 @@ static void call_trans2qfilepathinfo(connection_struct *conn,
 
        } else {
                char *fname = NULL;
+               uint32_t ucf_flags = 0;
 
                /* qpathinfo */
                if (total_params < 7) {
@@ -5098,9 +5096,16 @@ static void call_trans2qfilepathinfo(connection_struct *conn,
 
                DEBUG(3,("call_trans2qfilepathinfo: TRANSACT2_QPATHINFO: level = %d\n", info_level));
 
-               if (INFO_LEVEL_IS_UNIX(info_level) && !lp_unix_extensions()) {
-                       reply_nterror(req, NT_STATUS_INVALID_LEVEL);
-                       return;
+               if (INFO_LEVEL_IS_UNIX(info_level)) {
+                       if (!lp_unix_extensions()) {
+                               reply_nterror(req, NT_STATUS_INVALID_LEVEL);
+                               return;
+                       }
+                       if (info_level == SMB_QUERY_FILE_UNIX_BASIC ||
+                                       info_level == SMB_QUERY_FILE_UNIX_INFO2 ||
+                                       info_level == SMB_QUERY_FILE_UNIX_LINK) {
+                               ucf_flags |= UCF_UNIX_NAME_LOOKUP;
+                       }
                }
 
                srvstr_get_path(req, params, req->flags2, &fname, &params[6],
@@ -5115,7 +5120,7 @@ static void call_trans2qfilepathinfo(connection_struct *conn,
                                        conn,
                                        req->flags2 & FLAGS2_DFS_PATHNAMES,
                                        fname,
-                                       0,
+                                       ucf_flags,
                                        NULL,
                                        &smb_fname);
                if (!NT_STATUS_IS_OK(status)) {
@@ -6594,6 +6599,7 @@ static NTSTATUS smb_set_file_unix_basic(connection_struct *conn,
        files_struct *all_fsps = NULL;
        bool modify_mtime = true;
        struct file_id id;
+       struct smb_filename *smb_fname_tmp = NULL;
        SMB_STRUCT_STAT sbuf;
 
        ZERO_STRUCT(ft);
@@ -6646,7 +6652,6 @@ static NTSTATUS smb_set_file_unix_basic(connection_struct *conn,
        sbuf = smb_fname->st;
 
        if (!VALID_STAT(sbuf)) {
-               struct smb_filename *smb_fname_tmp = NULL;
                /*
                 * The only valid use of this is to create character and block
                 * devices, and named pipes. This is deprecated (IMHO) and 
@@ -6675,7 +6680,7 @@ static NTSTATUS smb_set_file_unix_basic(connection_struct *conn,
                }
 
                sbuf = smb_fname_tmp->st;
-               TALLOC_FREE(smb_fname_tmp);
+               smb_fname = smb_fname_tmp;
 
                /* Ensure we don't try and change anything else. */
                raw_unixmode = SMB_MODE_NO_CHANGE;
@@ -7044,18 +7049,55 @@ static NTSTATUS smb_posix_open(connection_struct *conn,
 
        wire_open_mode &= ~SMB_ACCMODE;
 
-       if((wire_open_mode & (SMB_O_CREAT | SMB_O_EXCL)) == (SMB_O_CREAT | SMB_O_EXCL)) {
-               create_disp = FILE_CREATE;
-       } else if((wire_open_mode & (SMB_O_CREAT | SMB_O_TRUNC)) == (SMB_O_CREAT | SMB_O_TRUNC)) {
-               create_disp = FILE_OVERWRITE_IF;
-       } else if((wire_open_mode & SMB_O_CREAT) == SMB_O_CREAT) {
-               create_disp = FILE_OPEN_IF;
-       } else if ((wire_open_mode & (SMB_O_CREAT | SMB_O_EXCL | SMB_O_TRUNC)) == 0) {
-               create_disp = FILE_OPEN;
-       } else {
-               DEBUG(5,("smb_posix_open: invalid create mode 0x%x\n",
-                       (unsigned int)wire_open_mode ));
-               return NT_STATUS_INVALID_PARAMETER;
+       /* First take care of O_CREAT|O_EXCL interactions. */
+       switch (wire_open_mode & (SMB_O_CREAT | SMB_O_EXCL)) {
+               case (SMB_O_CREAT | SMB_O_EXCL):
+                       /* File exists fail. File not exist create. */
+                       create_disp = FILE_CREATE;
+                       break;
+               case SMB_O_CREAT:
+                       /* File exists open. File not exist create. */
+                       create_disp = FILE_OPEN_IF;
+                       break;
+               case 0:
+                       /* File exists open. File not exist fail. */
+                       create_disp = FILE_OPEN;
+                       break;
+               case SMB_O_EXCL:
+                       /* O_EXCL on its own without O_CREAT is undefined. */
+               default:
+                       DEBUG(5,("smb_posix_open: invalid create mode 0x%x\n",
+                               (unsigned int)wire_open_mode ));
+                       return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       /* Next factor in the effects of O_TRUNC. */
+       wire_open_mode &= ~(SMB_O_CREAT | SMB_O_EXCL);
+
+       if (wire_open_mode & SMB_O_TRUNC) {
+               switch (create_disp) {
+                       case FILE_CREATE:
+                               /* (SMB_O_CREAT | SMB_O_EXCL | O_TRUNC) */
+                               /* Leave create_disp alone as
+                                  (O_CREAT|O_EXCL|O_TRUNC) == (O_CREAT|O_EXCL)
+                               */
+                               /* File exists fail. File not exist create. */
+                               break;
+                       case FILE_OPEN_IF:
+                               /* SMB_O_CREAT | SMB_O_TRUNC */
+                               /* File exists overwrite. File not exist create. */
+                               create_disp = FILE_OVERWRITE_IF;
+                               break;
+                       case FILE_OPEN:
+                               /* SMB_O_TRUNC */
+                               /* File exists overwrite. File not exist fail. */
+                               create_disp = FILE_OVERWRITE;
+                               break;
+                       default:
+                               /* Cannot get here. */
+                               smb_panic("smb_posix_open: logic error");
+                               return NT_STATUS_INVALID_PARAMETER;
+               }
        }
 
        raw_unixmode = IVAL(pdata,8);
@@ -7665,6 +7707,7 @@ static void call_trans2setfilepathinfo(connection_struct *conn,
                }
        } else {
                char *fname = NULL;
+               uint32_t ucf_flags = 0;
 
                /* set path info */
                if (total_params < 7) {
@@ -7681,10 +7724,17 @@ static void call_trans2setfilepathinfo(connection_struct *conn,
                        return;
                }
 
+               if (info_level == SMB_SET_FILE_UNIX_BASIC ||
+                               info_level == SMB_SET_FILE_UNIX_INFO2 ||
+                               info_level == SMB_FILE_RENAME_INFORMATION ||
+                               info_level == SMB_POSIX_PATH_UNLINK) {
+                       ucf_flags |= UCF_UNIX_NAME_LOOKUP;
+               }
+
                status = filename_convert(req, conn,
                                         req->flags2 & FLAGS2_DFS_PATHNAMES,
                                         fname,
-                                        0,
+                                        ucf_flags,
                                         NULL,
                                         &smb_fname);
                if (!NT_STATUS_IS_OK(status)) {
@@ -8506,6 +8556,15 @@ void reply_transs2(struct smb_request *req)
 
        show_msg((char *)req->inbuf);
 
+       /* Windows clients expect all replies to
+          a transact secondary (SMBtranss2 0x33)
+          to have a command code of transact
+          (SMBtrans2 0x32). See bug #8989
+          and also [MS-CIFS] section 2.2.4.47.2
+          for details.
+       */
+       req->cmd = SMBtrans2;
+
        if (req->wct < 8) {
                reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
                END_PROFILE(SMBtranss2);