CVE-2015-7560: s3: smbd: Refuse to set EA's on a symlink.
[samba.git] / source3 / smbd / trans2.c
index 6186e5ce3f6ba9e99abc5551237d8586d1e7ddcf..103e601072e19e96a16437812f01e2dbbdd1c1d9 100644 (file)
@@ -24,6 +24,7 @@
 */
 
 #include "includes.h"
+#include "ntioctl.h"
 #include "system/filesys.h"
 #include "version.h"
 #include "smbd/smbd.h"
@@ -38,6 +39,7 @@
 #include "smbprofile.h"
 #include "rpc_server/srv_pipe_hnd.h"
 #include "printing.h"
+#include "lib/util_ea.h"
 
 #define DIR_ENTRY_SAFETY_MARGIN 4096
 
@@ -51,6 +53,34 @@ static char *store_file_unix_basic_info2(connection_struct *conn,
                                files_struct *fsp,
                                const SMB_STRUCT_STAT *psbuf);
 
+/****************************************************************************
+ Check if an open file handle or pathname is a symlink.
+****************************************************************************/
+
+static NTSTATUS refuse_symlink(connection_struct *conn,
+                       const files_struct *fsp,
+                       const char *name)
+{
+       SMB_STRUCT_STAT sbuf;
+       const SMB_STRUCT_STAT *pst = NULL;
+
+       if (fsp) {
+               pst = &fsp->fsp_name->st;
+       } else {
+               int ret = vfs_stat_smb_basename(conn,
+                               name,
+                               &sbuf);
+               if (ret == -1) {
+                       return map_nt_error_from_unix(errno);
+               }
+               pst = &sbuf;
+       }
+       if (S_ISLNK(pst->st_ex_mode)) {
+               return NT_STATUS_ACCESS_DENIED;
+       }
+       return NT_STATUS_OK;
+}
+
 /********************************************************************
  The canonical "check access" based on object handle or path function.
 ********************************************************************/
@@ -206,12 +236,22 @@ NTSTATUS get_ea_names_from_file(TALLOC_CTX *mem_ctx, connection_struct *conn,
        char **names, **tmp;
        size_t num_names;
        ssize_t sizeret = -1;
+       NTSTATUS status;
+
+       if (pnames) {
+               *pnames = NULL;
+       }
+       *pnum_names = 0;
 
        if (!lp_ea_support(SNUM(conn))) {
-               if (pnames) {
-                       *pnames = NULL;
-               }
-               *pnum_names = 0;
+               return NT_STATUS_OK;
+       }
+
+       status = refuse_symlink(conn, fsp, fname);
+       if (!NT_STATUS_IS_OK(status)) {
+               /*
+                * Just return no EA's on a symlink.
+                */
                return NT_STATUS_OK;
        }
 
@@ -261,10 +301,6 @@ NTSTATUS get_ea_names_from_file(TALLOC_CTX *mem_ctx, connection_struct *conn,
 
        if (sizeret == 0) {
                TALLOC_FREE(names);
-               if (pnames) {
-                       *pnames = NULL;
-               }
-               *pnum_names = 0;
                return NT_STATUS_OK;
        }
 
@@ -345,6 +381,15 @@ static NTSTATUS get_ea_list_from_file_path(TALLOC_CTX *mem_ctx, connection_struc
                    || samba_private_attr_name(names[i]))
                        continue;
 
+               /*
+                * Filter out any underlying POSIX EA names
+                * that a Windows client can't handle.
+                */
+               if (!lp_posix_pathnames() &&
+                               is_invalid_windows_ea_name(names[i])) {
+                       continue;
+               }
+
                listp = talloc(mem_ctx, struct ea_list);
                if (listp == NULL) {
                        return NT_STATUS_NO_MEMORY;
@@ -613,6 +658,11 @@ NTSTATUS set_ea(connection_struct *conn, files_struct *fsp,
                return NT_STATUS_EAS_NOT_SUPPORTED;
        }
 
+       status = refuse_symlink(conn, fsp, smb_fname->base_name);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
        status = check_access(conn, fsp, smb_fname, FILE_WRITE_EA);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
@@ -623,6 +673,15 @@ NTSTATUS set_ea(connection_struct *conn, files_struct *fsp,
                return NT_STATUS_INVALID_PARAMETER;
        }
 
+       /*
+        * Filter out invalid Windows EA names - before
+        * we set *any* of them.
+        */
+
+       if (ea_list_has_invalid_name(ea_list)) {
+               return STATUS_INVALID_EA_NAME;
+       }
+
        fname = smb_fname->base_name;
 
        for (;ea_list; ea_list = ea_list->next) {
@@ -730,67 +789,6 @@ static struct ea_list *read_ea_name_list(TALLOC_CTX *ctx, const char *pdata, siz
        return ea_list_head;
 }
 
-/****************************************************************************
- Read one EA list entry from the buffer.
-****************************************************************************/
-
-struct ea_list *read_ea_list_entry(TALLOC_CTX *ctx, const char *pdata, size_t data_size, size_t *pbytes_used)
-{
-       struct ea_list *eal = talloc_zero(ctx, struct ea_list);
-       uint16 val_len;
-       unsigned int namelen;
-       size_t converted_size;
-
-       if (!eal) {
-               return NULL;
-       }
-
-       if (data_size < 6) {
-               return NULL;
-       }
-
-       eal->ea.flags = CVAL(pdata,0);
-       namelen = CVAL(pdata,1);
-       val_len = SVAL(pdata,2);
-
-       if (4 + namelen + 1 + val_len > data_size) {
-               return NULL;
-       }
-
-       /* Ensure the name is null terminated. */
-       if (pdata[namelen + 4] != '\0') {
-               return NULL;
-       }
-       if (!pull_ascii_talloc(ctx, &eal->ea.name, pdata + 4, &converted_size)) {
-               DEBUG(0,("read_ea_list_entry: pull_ascii_talloc failed: %s",
-                        strerror(errno)));
-       }
-       if (!eal->ea.name) {
-               return NULL;
-       }
-
-       eal->ea.value = data_blob_talloc(eal, NULL, (size_t)val_len + 1);
-       if (!eal->ea.value.data) {
-               return NULL;
-       }
-
-       memcpy(eal->ea.value.data, pdata + 4 + namelen + 1, val_len);
-
-       /* Ensure we're null terminated just in case we print the value. */
-       eal->ea.value.data[val_len] = '\0';
-       /* But don't count the null. */
-       eal->ea.value.length--;
-
-       if (pbytes_used) {
-               *pbytes_used = 4 + namelen + 1 + val_len;
-       }
-
-       DEBUG(10,("read_ea_list_entry: read ea name %s\n", eal->ea.name));
-       dump_data(10, eal->ea.value.data, eal->ea.value.length);
-
-       return eal;
-}
-
 /****************************************************************************
  Read a list of EA names and data from an incoming data buffer. Create an ea_list with them.
 ****************************************************************************/
@@ -878,6 +876,7 @@ static struct ea_list *ea_list_union(struct ea_list *name_list, struct ea_list *
 
 void send_trans2_replies(connection_struct *conn,
                        struct smb_request *req,
+                       NTSTATUS status,
                         const char *params,
                         int paramsize,
                         const char *pdata,
@@ -918,6 +917,14 @@ void send_trans2_replies(connection_struct *conn,
 
        if(params_to_send == 0 && data_to_send == 0) {
                reply_outbuf(req, 10, 0);
+               if (NT_STATUS_V(status)) {
+                       uint8_t eclass;
+                       uint32_t ecode;
+                       ntstatus_to_dos(status, &eclass, &ecode);
+                       error_packet_set((char *)req->outbuf,
+                                       eclass, ecode, status,
+                                       __LINE__,__FILE__);
+               }
                show_msg((char *)req->outbuf);
                if (!srv_send_smb(sconn,
                                (char *)req->outbuf,
@@ -1048,6 +1055,13 @@ void send_trans2_replies(connection_struct *conn,
                                         ERRDOS,ERRbufferoverflow,
                                         STATUS_BUFFER_OVERFLOW,
                                         __LINE__,__FILE__);
+               } else if (NT_STATUS_V(status)) {
+                       uint8_t eclass;
+                       uint32_t ecode;
+                       ntstatus_to_dos(status, &eclass, &ecode);
+                       error_packet_set((char *)req->outbuf,
+                                       eclass, ecode, status,
+                                       __LINE__,__FILE__);
                }
 
                /* Send the packet */
@@ -1219,6 +1233,20 @@ static void call_trans2open(connection_struct *conn,
                        reply_nterror(req, NT_STATUS_EAS_NOT_SUPPORTED);
                        goto out;
                }
+
+               if (ea_list_has_invalid_name(ea_list)) {
+                       int param_len = 30;
+                       *pparams = (char *)SMB_REALLOC(*pparams, param_len);
+                       if(*pparams == NULL ) {
+                               reply_nterror(req, NT_STATUS_NO_MEMORY);
+                               goto out;
+                       }
+                       params = *pparams;
+                       memset(params, '\0', param_len);
+                       send_trans2_replies(conn, req, STATUS_INVALID_EA_NAME,
+                               params, param_len, NULL, 0, max_data_bytes);
+                       goto out;
+               }
        }
 
        status = SMB_VFS_CREATE_FILE(
@@ -1294,7 +1322,7 @@ static void call_trans2open(connection_struct *conn,
        }
 
        /* Send the required number of replies */
-       send_trans2_replies(conn, req, params, 30, *ppdata, 0, max_data_bytes);
+       send_trans2_replies(conn, req, NT_STATUS_OK, params, 30, *ppdata, 0, max_data_bytes);
  out:
        TALLOC_FREE(smb_fname);
 }
@@ -1403,20 +1431,22 @@ static NTSTATUS unix_perms_from_wire( connection_struct *conn,
        ret |= ((perms & UNIX_SET_UID ) ? S_ISUID : 0);
 #endif
 
-       switch (ptype) {
-       case PERM_NEW_FILE:
-       case PERM_EXISTING_FILE:
-               /* Apply mode mask */
+       if (ptype == PERM_NEW_FILE) {
+               /*
+                * "create mask"/"force create mode" are
+                * only applied to new files, not existing ones.
+                */
                ret &= lp_create_mask(SNUM(conn));
                /* Add in force bits */
                ret |= lp_force_create_mode(SNUM(conn));
-               break;
-       case PERM_NEW_DIR:
-       case PERM_EXISTING_DIR:
+       } else if (ptype == PERM_NEW_DIR) {
+               /*
+                * "directory mask"/"force directory mode" are
+                * only applied to new directories, not existing ones.
+                */
                ret &= lp_dir_mask(SNUM(conn));
                /* Add in force bits */
                ret |= lp_force_dir_mode(SNUM(conn));
-               break;
        }
 
        *ret_perms = ret;
@@ -1829,12 +1859,14 @@ static bool smbd_marshall_dir_entry(TALLOC_CTX *ctx,
                SOFF_T(p,0,allocation_size); p += 8;
                SIVAL(p,0,mode); p += 4;
                q = p; p += 4; /* q is placeholder for name length. */
-               {
+               if (mode & FILE_ATTRIBUTE_REPARSE_POINT) {
+                       SIVAL(p, 0, IO_REPARSE_TAG_DFS);
+               } else {
                        unsigned int ea_size = estimate_ea_size(conn, NULL,
                                                                smb_fname);
                        SIVAL(p,0,ea_size); /* Extended attributes */
-                       p += 4;
                }
+               p += 4;
                /* Clear the short name buffer. This is
                 * IMPORTANT as not doing so will trigger
                 * a Win2k client bug. JRA.
@@ -2006,12 +2038,14 @@ static bool smbd_marshall_dir_entry(TALLOC_CTX *ctx,
                SOFF_T(p,0,allocation_size); p += 8;
                SIVAL(p,0,mode); p += 4;
                q = p; p += 4; /* q is placeholder for name length. */
-               {
+               if (mode & FILE_ATTRIBUTE_REPARSE_POINT) {
+                       SIVAL(p, 0, IO_REPARSE_TAG_DFS);
+               } else {
                        unsigned int ea_size = estimate_ea_size(conn, NULL,
                                                                smb_fname);
                        SIVAL(p,0,ea_size); /* Extended attributes */
-                       p +=4;
                }
+               p += 4;
                SIVAL(p,0,0); p += 4; /* Unknown - reserved ? */
                SBVAL(p,0,file_index); p += 8;
                len = srvstr_push(base_data, flags2, p,
@@ -2052,12 +2086,14 @@ static bool smbd_marshall_dir_entry(TALLOC_CTX *ctx,
                SOFF_T(p,0,allocation_size); p += 8;
                SIVAL(p,0,mode); p += 4;
                q = p; p += 4; /* q is placeholder for name length */
-               {
+               if (mode & FILE_ATTRIBUTE_REPARSE_POINT) {
+                       SIVAL(p, 0, IO_REPARSE_TAG_DFS);
+               } else {
                        unsigned int ea_size = estimate_ea_size(conn, NULL,
                                                                smb_fname);
                        SIVAL(p,0,ea_size); /* Extended attributes */
-                       p +=4;
                }
+               p += 4;
                /* Clear the short name buffer. This is
                 * IMPORTANT as not doing so will trigger
                 * a Win2k client bug. JRA.
@@ -2366,6 +2402,7 @@ static void call_trans2findfirst(connection_struct *conn,
        struct smbd_server_connection *sconn = req->sconn;
        uint32_t ucf_flags = (UCF_SAVE_LCOMP | UCF_ALWAYS_ALLOW_WCARD_LCOMP);
        bool backup_priv = false;
+       bool as_root = false;
 
        if (total_params < 13) {
                reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
@@ -2431,6 +2468,7 @@ close_if_end = %d requires_resume_key = %d backup_priv = %d level = 0x%x, max_da
 
        if (backup_priv) {
                become_root();
+               as_root = true;
                ntstatus = filename_convert_with_privilege(ctx,
                                conn,
                                req,
@@ -2658,7 +2696,7 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd
        SSVAL(params,6,0); /* Never an EA error */
        SSVAL(params,8,last_entry_off);
 
-       send_trans2_replies(conn, req, params, 10, pdata, PTR_DIFF(p,pdata),
+       send_trans2_replies(conn, req, NT_STATUS_OK, params, 10, pdata, PTR_DIFF(p,pdata),
                            max_data_bytes);
 
        if ((! *directory) && dptr_path(sconn, dptr_num)) {
@@ -2686,7 +2724,7 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd
        }
  out:
 
-       if (backup_priv) {
+       if (as_root) {
                unbecome_root();
        }
 
@@ -2740,6 +2778,7 @@ static void call_trans2findnext(connection_struct *conn,
        struct dptr_struct *dirptr;
        struct smbd_server_connection *sconn = req->sconn;
        bool backup_priv = false; 
+       bool as_root = false;
 
        if (total_params < 13) {
                reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
@@ -2908,6 +2947,7 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd
 
        if (backup_priv) {
                become_root();
+               as_root = true;
        }
 
        /*
@@ -2999,7 +3039,7 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd
                dptr_close(sconn, &dptr_num); /* This frees up the saved mask */
        }
 
-       if (backup_priv) {
+       if (as_root) {
                unbecome_root();
        }
 
@@ -3009,7 +3049,7 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd
        SSVAL(params,4,0); /* Never an EA error */
        SSVAL(params,6,last_entry_off);
 
-       send_trans2_replies(conn, req, params, 8, pdata, PTR_DIFF(p,pdata),
+       send_trans2_replies(conn, req, NT_STATUS_OK, params, 8, pdata, PTR_DIFF(p,pdata),
                            max_data_bytes);
 
        return;
@@ -3061,6 +3101,7 @@ NTSTATUS smbd_do_qfsinfo(connection_struct *conn,
                         uint16_t info_level,
                         uint16_t flags2,
                         unsigned int max_data_bytes,
+                        size_t *fixed_portion,
                         struct smb_filename *fname,
                         char **ppdata,
                         int *ret_data_len)
@@ -3074,6 +3115,7 @@ NTSTATUS smbd_do_qfsinfo(connection_struct *conn,
        uint32 additional_flags = 0;
        struct smb_filename smb_fname;
        SMB_STRUCT_STAT st;
+       NTSTATUS status = NT_STATUS_OK;
 
        if (fname == NULL || fname->base_name == NULL) {
                filename = ".";
@@ -3112,6 +3154,8 @@ NTSTATUS smbd_do_qfsinfo(connection_struct *conn,
        memset((char *)pdata,'\0',max_data_bytes + DIR_ENTRY_SAFETY_MARGIN);
        end_data = pdata + max_data_bytes + DIR_ENTRY_SAFETY_MARGIN - 1;
 
+       *fixed_portion = 0;
+
        switch (info_level) {
                case SMB_INFO_ALLOCATION:
                {
@@ -3204,6 +3248,13 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)st.st_ex_dev, (u
                                          STR_UNICODE);
                        SIVAL(pdata,8,len);
                        data_len = 12 + len;
+                       if (max_data_bytes >= 16 && data_len > max_data_bytes) {
+                               /* the client only requested a portion of the
+                                  file system name */
+                               data_len = max_data_bytes;
+                               status = STATUS_BUFFER_OVERFLOW;
+                       }
+                       *fixed_portion = 16;
                        break;
 
                case SMB_QUERY_FS_LABEL_INFO:
@@ -3234,6 +3285,13 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)st.st_ex_dev, (u
                        DEBUG(5,("smbd_do_qfsinfo : SMB_QUERY_FS_VOLUME_INFO namelen = %d, vol=%s serv=%s\n",
                                (int)strlen(vname),vname,
                                lp_servicename(talloc_tos(), snum)));
+                       if (max_data_bytes >= 24 && data_len > max_data_bytes) {
+                               /* the client only requested a portion of the
+                                  volume label */
+                               data_len = max_data_bytes;
+                               status = STATUS_BUFFER_OVERFLOW;
+                       }
+                       *fixed_portion = 24;
                        break;
 
                case SMB_QUERY_FS_SIZE_INFO:
@@ -3266,6 +3324,7 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned
                        SBIG_UINT(pdata,8,dfree);
                        SIVAL(pdata,16,sectors_per_unit);
                        SIVAL(pdata,20,bytes_per_sector);
+                       *fixed_portion = 24;
                        break;
                }
 
@@ -3299,6 +3358,7 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned
                        SBIG_UINT(pdata,16,dfree); /* Actual available allocation units. */
                        SIVAL(pdata,24,sectors_per_unit); /* Sectors per allocation unit. */
                        SIVAL(pdata,28,bytes_per_sector); /* Bytes per sector. */
+                       *fixed_portion = 32;
                        break;
                }
 
@@ -3313,6 +3373,7 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned
                        data_len = 8;
                        SIVAL(pdata,0,FILE_DEVICE_DISK); /* dev type */
                        SIVAL(pdata,4,characteristics);
+                       *fixed_portion = 8;
                        break;
                }
 
@@ -3425,6 +3486,7 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned
                        case SMB_SIGNING_OFF:
                                encrypt_caps = 0;
                                break;
+                       case SMB_SIGNING_DESIRED:
                        case SMB_SIGNING_IF_REQUIRED:
                        case SMB_SIGNING_DEFAULT:
                                encrypt_caps = CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP;
@@ -3605,7 +3667,7 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned
        }
 
        *ret_data_len = data_len;
-       return NT_STATUS_OK;
+       return status;
 }
 
 /****************************************************************************
@@ -3621,6 +3683,7 @@ static void call_trans2qfsinfo(connection_struct *conn,
        char *params = *pparams;
        uint16_t info_level;
        int data_len = 0;
+       size_t fixed_portion;
        NTSTATUS status;
 
        if (total_params < 2) {
@@ -3646,6 +3709,7 @@ static void call_trans2qfsinfo(connection_struct *conn,
                                 info_level,
                                 req->flags2,
                                 max_data_bytes,
+                                &fixed_portion,
                                 NULL,
                                 ppdata, &data_len);
        if (!NT_STATUS_IS_OK(status)) {
@@ -3653,7 +3717,7 @@ static void call_trans2qfsinfo(connection_struct *conn,
                return;
        }
 
-       send_trans2_replies(conn, req, params, 0, *ppdata, data_len,
+       send_trans2_replies(conn, req, NT_STATUS_OK, params, 0, *ppdata, data_len,
                            max_data_bytes);
 
        DEBUG( 4, ( "%s info_level = %d\n",
@@ -3809,6 +3873,7 @@ static void call_trans2setfsinfo(connection_struct *conn,
                                }
 
                                send_trans2_replies(conn, req,
+                                               NT_STATUS_OK,
                                                *pparams,
                                                param_len,
                                                *ppdata,
@@ -4208,6 +4273,10 @@ static NTSTATUS marshall_stream_info(unsigned int num_streams,
        unsigned int i;
        unsigned int ofs = 0;
 
+       if (max_data_bytes < 32) {
+               return NT_STATUS_INFO_LENGTH_MISMATCH;
+       }
+
        for (i = 0; i < num_streams; i++) {
                unsigned int next_offset;
                size_t namelen;
@@ -4341,7 +4410,7 @@ static void call_trans2qpipeinfo(connection_struct *conn,
                        return;
        }
 
-       send_trans2_replies(conn, req, params, param_size, *ppdata, data_size,
+       send_trans2_replies(conn, req, NT_STATUS_OK, params, param_size, *ppdata, data_size,
                            max_data_bytes);
 
        return;
@@ -4359,6 +4428,7 @@ NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn,
                               char *lock_data,
                               uint16_t flags2,
                               unsigned int max_data_bytes,
+                              size_t *fixed_portion,
                               char **ppdata,
                               unsigned int *pdata_size)
 {
@@ -4495,6 +4565,8 @@ NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn,
           BasicFileInformationTest. -tpot */
        file_index = get_FileIndex(conn, psbuf);
 
+       *fixed_portion = 0;
+
        switch (info_level) {
                case SMB_INFO_STANDARD:
                        DEBUG(10,("smbd_do_qfilepathinfo: SMB_INFO_STANDARD\n"));
@@ -4641,6 +4713,7 @@ NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn,
                        DEBUG(5,("write: %s ", ctime(&mtime)));
                        DEBUG(5,("change: %s ", ctime(&c_time)));
                        DEBUG(5,("mode: %x\n", mode));
+                       *fixed_portion = data_size;
                        break;
 
                case SMB_FILE_STANDARD_INFORMATION:
@@ -4654,6 +4727,7 @@ NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn,
                        SCVAL(pdata,20,delete_pending?1:0);
                        SCVAL(pdata,21,(mode&FILE_ATTRIBUTE_DIRECTORY)?1:0);
                        SSVAL(pdata,22,0); /* Padding. */
+                       *fixed_portion = 24;
                        break;
 
                case SMB_FILE_EA_INFORMATION:
@@ -4663,6 +4737,7 @@ NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn,
                            estimate_ea_size(conn, fsp, smb_fname);
                        DEBUG(10,("smbd_do_qfilepathinfo: SMB_FILE_EA_INFORMATION\n"));
                        data_size = 4;
+                       *fixed_portion = 4;
                        SIVAL(pdata,0,ea_size);
                        break;
                }
@@ -4684,6 +4759,7 @@ NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn,
                                          STR_UNICODE);
                        data_size = 4 + len;
                        SIVAL(pdata,0,len);
+                       *fixed_portion = 8;
                        break;
                }
 
@@ -4747,6 +4823,7 @@ NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn,
                        SIVAL(pdata,0,len);
                        pdata += 4 + len;
                        data_size = PTR_DIFF(pdata,(*ppdata));
+                       *fixed_portion = 10;
                        break;
                }
 
@@ -4784,6 +4861,7 @@ NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn,
                        SIVAL(pdata,0,len);
                        pdata += 4 + len;
                        data_size = PTR_DIFF(pdata,(*ppdata));
+                       *fixed_portion = 104;
                        break;
                }
                case SMB_FILE_INTERNAL_INFORMATION:
@@ -4791,12 +4869,14 @@ NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn,
                        DEBUG(10,("smbd_do_qfilepathinfo: SMB_FILE_INTERNAL_INFORMATION\n"));
                        SBVAL(pdata, 0, file_index);
                        data_size = 8;
+                       *fixed_portion = 8;
                        break;
 
                case SMB_FILE_ACCESS_INFORMATION:
                        DEBUG(10,("smbd_do_qfilepathinfo: SMB_FILE_ACCESS_INFORMATION\n"));
                        SIVAL(pdata, 0, access_mask);
                        data_size = 4;
+                       *fixed_portion = 4;
                        break;
 
                case SMB_FILE_NAME_INFORMATION:
@@ -4814,24 +4894,28 @@ NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn,
                        DEBUG(10,("smbd_do_qfilepathinfo: SMB_FILE_DISPOSITION_INFORMATION\n"));
                        data_size = 1;
                        SCVAL(pdata,0,delete_pending);
+                       *fixed_portion = 1;
                        break;
 
                case SMB_FILE_POSITION_INFORMATION:
                        DEBUG(10,("smbd_do_qfilepathinfo: SMB_FILE_POSITION_INFORMATION\n"));
                        data_size = 8;
                        SOFF_T(pdata,0,pos);
+                       *fixed_portion = 8;
                        break;
 
                case SMB_FILE_MODE_INFORMATION:
                        DEBUG(10,("smbd_do_qfilepathinfo: SMB_FILE_MODE_INFORMATION\n"));
                        SIVAL(pdata,0,mode);
                        data_size = 4;
+                       *fixed_portion = 4;
                        break;
 
                case SMB_FILE_ALIGNMENT_INFORMATION:
                        DEBUG(10,("smbd_do_qfilepathinfo: SMB_FILE_ALIGNMENT_INFORMATION\n"));
                        SIVAL(pdata,0,0); /* No alignment needed. */
                        data_size = 4;
+                       *fixed_portion = 4;
                        break;
 
                /*
@@ -4876,6 +4960,8 @@ NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn,
 
                        TALLOC_FREE(streams);
 
+                       *fixed_portion = 32;
+
                        break;
                }
                case SMB_QUERY_COMPRESSION_INFO:
@@ -4885,6 +4971,7 @@ NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn,
                        SIVAL(pdata,8,0); /* ??? */
                        SIVAL(pdata,12,0); /* ??? */
                        data_size = 16;
+                       *fixed_portion = 16;
                        break;
 
                case SMB_FILE_NETWORK_OPEN_INFORMATION:
@@ -4898,6 +4985,7 @@ NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn,
                        SIVAL(pdata,48,mode);
                        SIVAL(pdata,52,0); /* ??? */
                        data_size = 56;
+                       *fixed_portion = 56;
                        break;
 
                case SMB_FILE_ATTRIBUTE_TAG_INFORMATION:
@@ -4905,6 +4993,7 @@ NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn,
                        SIVAL(pdata,0,mode);
                        SIVAL(pdata,4,0);
                        data_size = 8;
+                       *fixed_portion = 8;
                        break;
 
                /*
@@ -4980,6 +5069,13 @@ NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn,
                                uint16 num_file_acls = 0;
                                uint16 num_def_acls = 0;
 
+                               status = refuse_symlink(conn,
+                                               fsp,
+                                               smb_fname->base_name);
+                               if (!NT_STATUS_IS_OK(status)) {
+                                       return status;
+                               }
+
                                if (fsp && fsp->fh->fd != -1) {
                                        file_acl = SMB_VFS_SYS_ACL_GET_FD(fsp,
                                                talloc_tos());
@@ -5178,6 +5274,7 @@ static void call_trans2qfilepathinfo(connection_struct *conn,
        struct ea_list *ea_list = NULL;
        int lock_data_count = 0;
        char *lock_data = NULL;
+       size_t fixed_portion;
        NTSTATUS status = NT_STATUS_OK;
 
        if (!params) {
@@ -5537,13 +5634,18 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd
                                       ea_list,
                                       lock_data_count, lock_data,
                                       req->flags2, max_data_bytes,
+                                      &fixed_portion,
                                       ppdata, &data_size);
        if (!NT_STATUS_IS_OK(status)) {
                reply_nterror(req, status);
                return;
        }
+       if (fixed_portion > max_data_bytes) {
+               reply_nterror(req, NT_STATUS_INFO_LENGTH_MISMATCH);
+               return;
+       }
 
-       send_trans2_replies(conn, req, params, param_size, *ppdata, data_size,
+       send_trans2_replies(conn, req, NT_STATUS_OK, params, param_size, *ppdata, data_size,
                            max_data_bytes);
 
        return;
@@ -5721,7 +5823,7 @@ static NTSTATUS smb_set_file_dosmode(connection_struct *conn,
                                     const struct smb_filename *smb_fname,
                                     uint32 dosmode)
 {
-       struct smb_filename *smb_fname_base = NULL;
+       struct smb_filename *smb_fname_base;
        NTSTATUS status;
 
        if (!VALID_STAT(smb_fname->st)) {
@@ -5729,11 +5831,10 @@ static NTSTATUS smb_set_file_dosmode(connection_struct *conn,
        }
 
        /* Always operate on the base_name, even if a stream was passed in. */
-       status = create_synthetic_smb_fname(talloc_tos(), smb_fname->base_name,
-                                           NULL, &smb_fname->st,
-                                           &smb_fname_base);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
+       smb_fname_base = synthetic_smb_fname(
+               talloc_tos(), smb_fname->base_name, NULL, &smb_fname->st);
+       if (smb_fname_base == NULL) {
+               return NT_STATUS_NO_MEMORY;
        }
 
        if (dosmode) {
@@ -6195,10 +6296,11 @@ static NTSTATUS smb2_file_rename_information(connection_struct *conn,
                }
 
                /* Create an smb_fname to call rename_internals_fsp() with. */
-               status = create_synthetic_smb_fname(talloc_tos(),
-                   fsp->base_fsp->fsp_name->base_name, newname, NULL,
-                   &smb_fname_dst);
-               if (!NT_STATUS_IS_OK(status)) {
+               smb_fname_dst = synthetic_smb_fname(
+                       talloc_tos(), fsp->base_fsp->fsp_name->base_name,
+                       newname, NULL);
+               if (smb_fname_dst == NULL) {
+                       status = NT_STATUS_NO_MEMORY;
                        goto out;
                }
 
@@ -6364,10 +6466,11 @@ static NTSTATUS smb_file_rename_information(connection_struct *conn,
                }
 
                /* Create an smb_fname to call rename_internals_fsp() with. */
-               status = create_synthetic_smb_fname(talloc_tos(),
-                   fsp->base_fsp->fsp_name->base_name, newname, NULL,
-                   &smb_fname_dst);
-               if (!NT_STATUS_IS_OK(status)) {
+               smb_fname_dst = synthetic_smb_fname(
+                       talloc_tos(), fsp->base_fsp->fsp_name->base_name,
+                       newname, NULL);
+               if (smb_fname_dst == NULL) {
+                       status = NT_STATUS_NO_MEMORY;
                        goto out;
                }
 
@@ -6435,11 +6538,10 @@ static NTSTATUS smb_file_rename_information(connection_struct *conn,
                                goto out;
                        }
                        /* Create an smb_fname to call rename_internals_fsp() */
-                       status = create_synthetic_smb_fname(ctx,
-                                                           base_name, NULL,
-                                                           NULL,
-                                                           &smb_fname_dst);
-                       if (!NT_STATUS_IS_OK(status)) {
+                       smb_fname_dst = synthetic_smb_fname(
+                               ctx, base_name, NULL, NULL);
+                       if (smb_fname_dst == NULL) {
+                               status = NT_STATUS_NO_MEMORY;
                                goto out;
                        }
                }
@@ -6483,6 +6585,7 @@ static NTSTATUS smb_set_posix_acl(connection_struct *conn,
        uint16 num_def_acls;
        bool valid_file_acls = True;
        bool valid_def_acls = True;
+       NTSTATUS status;
 
        if (total_data < SMB_POSIX_ACL_HEADER_SIZE) {
                return NT_STATUS_INVALID_PARAMETER;
@@ -6510,6 +6613,11 @@ static NTSTATUS smb_set_posix_acl(connection_struct *conn,
                return NT_STATUS_INVALID_PARAMETER;
        }
 
+       status = refuse_symlink(conn, fsp, smb_fname->base_name);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
        DEBUG(10,("smb_set_posix_acl: file %s num_file_acls = %u, num_def_acls = %u\n",
                smb_fname ? smb_fname_str_dbg(smb_fname) : fsp_str_dbg(fsp),
                (unsigned int)num_file_acls,
@@ -7093,11 +7201,18 @@ static NTSTATUS smb_set_file_unix_basic(connection_struct *conn,
         */
 
        if (raw_unixmode != SMB_MODE_NO_CHANGE) {
+               int ret;
+
                DEBUG(10,("smb_set_file_unix_basic: SMB_SET_FILE_UNIX_BASIC "
                          "setting mode 0%o for file %s\n",
                          (unsigned int)unixmode,
                          smb_fname_str_dbg(smb_fname)));
-               if (SMB_VFS_CHMOD(conn, smb_fname->base_name, unixmode) != 0) {
+               if (fsp && fsp->fh->fd != -1) {
+                       ret = SMB_VFS_FCHMOD(fsp, unixmode);
+               } else {
+                       ret = SMB_VFS_CHMOD(conn, smb_fname->base_name, unixmode);
+               }
+               if (ret != 0) {
                        return map_nt_error_from_unix(errno);
                }
        }
@@ -7115,12 +7230,15 @@ static NTSTATUS smb_set_file_unix_basic(connection_struct *conn,
                          (unsigned int)set_owner,
                          smb_fname_str_dbg(smb_fname)));
 
-               if (S_ISLNK(sbuf.st_ex_mode)) {
+               if (fsp && fsp->fh->fd != -1) {
+                       ret = SMB_VFS_FCHOWN(fsp, set_owner, (gid_t)-1);
+               } else {
+                       /*
+                        * UNIX extensions calls must always operate
+                        * on symlinks.
+                        */
                        ret = SMB_VFS_LCHOWN(conn, smb_fname->base_name,
                                             set_owner, (gid_t)-1);
-               } else {
-                       ret = SMB_VFS_CHOWN(conn, smb_fname->base_name,
-                                           set_owner, (gid_t)-1);
                }
 
                if (ret != 0) {
@@ -7138,12 +7256,23 @@ static NTSTATUS smb_set_file_unix_basic(connection_struct *conn,
 
        if ((set_grp != (uid_t)SMB_GID_NO_CHANGE) &&
            (sbuf.st_ex_gid != set_grp)) {
+               int ret;
+
                DEBUG(10,("smb_set_file_unix_basic: SMB_SET_FILE_UNIX_BASIC "
                          "changing group %u for file %s\n",
                          (unsigned int)set_owner,
                          smb_fname_str_dbg(smb_fname)));
-               if (SMB_VFS_CHOWN(conn, smb_fname->base_name, (uid_t)-1,
-                                 set_grp) != 0) {
+               if (fsp && fsp->fh->fd != -1) {
+                       ret = SMB_VFS_FCHOWN(fsp, set_owner, (gid_t)-1);
+               } else {
+                       /*
+                        * UNIX extensions calls must always operate
+                        * on symlinks.
+                        */
+                       ret = SMB_VFS_LCHOWN(conn, smb_fname->base_name, (uid_t)-1,
+                                 set_grp);
+               }
+               if (ret != 0) {
                        status = map_nt_error_from_unix(errno);
                        if (delete_on_fail) {
                                SMB_VFS_UNLINK(conn, smb_fname);
@@ -8106,7 +8235,7 @@ static void call_trans2setfilepathinfo(connection_struct *conn,
                                         fsp_str_dbg(fsp)));
 
                                SSVAL(params,0,0);
-                               send_trans2_replies(conn, req, params, 2,
+                               send_trans2_replies(conn, req, NT_STATUS_OK, params, 2,
                                                    *ppdata, 0,
                                                    max_data_bytes);
                                return;
@@ -8229,11 +8358,20 @@ static void call_trans2setfilepathinfo(connection_struct *conn,
                        return;
                }
 
-               reply_nterror(req, status);
+               /*
+                * Invalid EA name needs to return 2 param bytes,
+                * not a zero-length error packet.
+                */
+               if (NT_STATUS_EQUAL(status, STATUS_INVALID_EA_NAME)) {
+                       send_trans2_replies(conn, req, status, params, 2, NULL, 0,
+                                       max_data_bytes);
+               } else {
+                       reply_nterror(req, status);
+               }
                return;
        }
 
-       send_trans2_replies(conn, req, params, 2, *ppdata, data_return_size,
+       send_trans2_replies(conn, req, NT_STATUS_OK, params, 2, *ppdata, data_return_size,
                            max_data_bytes);
 
        return;
@@ -8358,7 +8496,7 @@ static void call_trans2mkdir(connection_struct *conn, struct smb_request *req,
 
        SSVAL(params,0,0);
 
-       send_trans2_replies(conn, req, params, 2, *ppdata, 0, max_data_bytes);
+       send_trans2_replies(conn, req, NT_STATUS_OK, params, 2, *ppdata, 0, max_data_bytes);
 
  out:
        TALLOC_FREE(smb_dname);
@@ -8413,7 +8551,7 @@ static void call_trans2findnotifyfirst(connection_struct *conn,
        if(fnf_handle == 0)
                fnf_handle = 257;
 
-       send_trans2_replies(conn, req, params, 6, *ppdata, 0, max_data_bytes);
+       send_trans2_replies(conn, req, NT_STATUS_OK, params, 6, *ppdata, 0, max_data_bytes);
 
        return;
 }
@@ -8444,7 +8582,7 @@ static void call_trans2findnotifynext(connection_struct *conn,
        SSVAL(params,0,0); /* No changes */
        SSVAL(params,2,0); /* No EA errors */
 
-       send_trans2_replies(conn, req, params, 4, *ppdata, 0, max_data_bytes);
+       send_trans2_replies(conn, req, NT_STATUS_OK, params, 4, *ppdata, 0, max_data_bytes);
 
        return;
 }
@@ -8494,7 +8632,7 @@ static void call_trans2getdfsreferral(connection_struct *conn,
 
        SSVAL((discard_const_p(uint8_t, req->inbuf)), smb_flg2,
              SVAL(req->inbuf,smb_flg2) | FLAGS2_DFS_PATHNAMES);
-       send_trans2_replies(conn, req,0,0,*ppdata,reply_size, max_data_bytes);
+       send_trans2_replies(conn, req, NT_STATUS_OK, 0,0,*ppdata,reply_size, max_data_bytes);
 
        return;
 }
@@ -8543,7 +8681,7 @@ static void call_trans2ioctl(connection_struct *conn,
                srvstr_push(pdata, req->flags2, pdata+18,
                            lp_servicename(talloc_tos(), SNUM(conn)), 13,
                            STR_ASCII|STR_TERMINATE); /* Service name */
-               send_trans2_replies(conn, req, *pparams, 0, *ppdata, 32,
+               send_trans2_replies(conn, req, NT_STATUS_OK, *pparams, 0, *ppdata, 32,
                                    max_data_bytes);
                return;
        }