s3:smbd: allow status code in smbd_do_qfsinfo() to be set by information class handler
[metze/samba/wip.git] / source3 / smbd / trans2.c
index a1b69fcfb41b7d886dbaf74c0dafd4311d517156..76176620800e2fcd06840e7f4ba87a1c77c7740d 100644 (file)
@@ -345,6 +345,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;
@@ -623,6 +632,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) {
@@ -878,6 +896,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 +937,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 +1075,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 +1253,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 +1342,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);
 }
@@ -2516,6 +2564,11 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd
                }
        }
 
+       if (max_data_bytes + DIR_ENTRY_SAFETY_MARGIN < max_data_bytes) {
+               reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               goto out;
+       }
+
        *ppdata = (char *)SMB_REALLOC(
                *ppdata, max_data_bytes + DIR_ENTRY_SAFETY_MARGIN);
        if(*ppdata == NULL ) {
@@ -2658,7 +2711,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)) {
@@ -2845,6 +2898,11 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd
                }
        }
 
+       if (max_data_bytes + DIR_ENTRY_SAFETY_MARGIN < max_data_bytes) {
+               reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               return;
+       }
+
        *ppdata = (char *)SMB_REALLOC(
                *ppdata, max_data_bytes + DIR_ENTRY_SAFETY_MARGIN);
        if(*ppdata == NULL) {
@@ -3009,7 +3067,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;
@@ -3074,6 +3132,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 = ".";
@@ -3102,6 +3161,10 @@ NTSTATUS smbd_do_qfsinfo(connection_struct *conn,
 
        st = smb_fname.st;
 
+       if (max_data_bytes + DIR_ENTRY_SAFETY_MARGIN < max_data_bytes) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
        *ppdata = (char *)SMB_REALLOC(
                *ppdata, max_data_bytes + DIR_ENTRY_SAFETY_MARGIN);
        if (*ppdata == NULL) {
@@ -3605,7 +3668,7 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned
        }
 
        *ret_data_len = data_len;
-       return NT_STATUS_OK;
+       return status;
 }
 
 /****************************************************************************
@@ -3653,7 +3716,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 +3872,7 @@ static void call_trans2setfsinfo(connection_struct *conn,
                                }
 
                                send_trans2_replies(conn, req,
+                                               NT_STATUS_OK,
                                                *pparams,
                                                param_len,
                                                *ppdata,
@@ -4319,6 +4383,10 @@ static void call_trans2qpipeinfo(connection_struct *conn,
        }
        params = *pparams;
        SSVAL(params,0,0);
+       if (max_data_bytes + DIR_ENTRY_SAFETY_MARGIN < max_data_bytes) {
+               reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               return;
+       }
        data_size = max_data_bytes + DIR_ENTRY_SAFETY_MARGIN;
        *ppdata = (char *)SMB_REALLOC(*ppdata, data_size); 
        if (*ppdata == NULL ) {
@@ -4341,7 +4409,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;
@@ -4400,6 +4468,10 @@ NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn,
                nlink -= 1;
        }
 
+       if (max_data_bytes + DIR_ENTRY_SAFETY_MARGIN < max_data_bytes) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
        data_size = max_data_bytes + DIR_ENTRY_SAFETY_MARGIN;
        *ppdata = (char *)SMB_REALLOC(*ppdata, data_size); 
        if (*ppdata == NULL) {
@@ -5543,7 +5615,7 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd
                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;
@@ -6194,10 +6266,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;
                }
 
@@ -6363,10 +6436,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;
                }
 
@@ -6434,11 +6508,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;
                        }
                }
@@ -8105,7 +8178,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;
@@ -8228,11 +8301,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;
@@ -8357,7 +8439,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);
@@ -8412,7 +8494,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;
 }
@@ -8443,7 +8525,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;
 }
@@ -8493,7 +8575,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;
 }
@@ -8542,7 +8624,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;
        }