diff options
-rw-r--r-- | source4/include/smb_interfaces.h | 21 | ||||
-rw-r--r-- | source4/include/trans2.h | 1 | ||||
-rw-r--r-- | source4/libcli/raw/raweas.c | 116 | ||||
-rw-r--r-- | source4/libcli/raw/rawfileinfo.c | 64 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_qfileinfo.c | 58 | ||||
-rw-r--r-- | source4/smb_server/trans2.c | 27 | ||||
-rw-r--r-- | source4/torture/torture_util.c | 73 |
7 files changed, 310 insertions, 50 deletions
diff --git a/source4/include/smb_interfaces.h b/source4/include/smb_interfaces.h index d5e43633db..9916bf2385 100644 --- a/source4/include/smb_interfaces.h +++ b/source4/include/smb_interfaces.h @@ -313,6 +313,7 @@ enum smb_fileinfo_level { RAW_FILEINFO_SEC_DESC, /* NT_TRANSACT_QUERY_SECURITY_DESC */ RAW_FILEINFO_STANDARD = SMB_QFILEINFO_STANDARD, RAW_FILEINFO_EA_SIZE = SMB_QFILEINFO_EA_SIZE, + RAW_FILEINFO_EA_LIST = SMB_QFILEINFO_EA_LIST, RAW_FILEINFO_ALL_EAS = SMB_QFILEINFO_ALL_EAS, RAW_FILEINFO_IS_NAME_VALID = SMB_QFILEINFO_IS_NAME_VALID, RAW_FILEINFO_BASIC_INFO = SMB_QFILEINFO_BASIC_INFO, @@ -443,16 +444,30 @@ union smb_fileinfo { } out; } ea_size; - /* trans2 RAW_FILEINFO_ALL_EAS interface */ + /* trans2 RAW_FILEINFO_EA_LIST interface */ struct { enum smb_fileinfo_level level; - union smb_fileinfo_in in; + union smb_fileinfo_in file; + + struct { + uint_t num_names; + struct ea_name { + WIRE_STRING name; + } *ea_names; + } in; struct smb_ea_list { - /* the ea_size is implied by the list */ uint_t num_eas; struct ea_struct *eas; } out; + } ea_list; + + /* trans2 RAW_FILEINFO_ALL_EAS interface */ + struct { + enum smb_fileinfo_level level; + union smb_fileinfo_in in; + + struct smb_ea_list out; } all_eas; /* trans2 qpathinfo RAW_FILEINFO_IS_NAME_VALID interface diff --git a/source4/include/trans2.h b/source4/include/trans2.h index 9d934deace..9afb9d4c2d 100644 --- a/source4/include/trans2.h +++ b/source4/include/trans2.h @@ -126,6 +126,7 @@ Found 8 aliased levels */ #define SMB_QFILEINFO_STANDARD 1 #define SMB_QFILEINFO_EA_SIZE 2 +#define SMB_QFILEINFO_EA_LIST 3 #define SMB_QFILEINFO_ALL_EAS 4 #define SMB_QFILEINFO_IS_NAME_VALID 6 /* only for QPATHINFO */ #define SMB_QFILEINFO_BASIC_INFO 0x101 diff --git a/source4/libcli/raw/raweas.c b/source4/libcli/raw/raweas.c index 14d4557995..ec8bacdf64 100644 --- a/source4/libcli/raw/raweas.c +++ b/source4/libcli/raw/raweas.c @@ -37,6 +37,19 @@ uint_t ea_list_size(uint_t num_eas, struct ea_struct *eas) } /* + work out how many bytes on the wire a ea name list will consume. +*/ +static uint_t ea_name_list_size(uint_t num_names, struct ea_name *eas) +{ + uint_t total = 4; + int i; + for (i=0;i<num_names;i++) { + total += 1 + strlen(eas[i].name.s) + 1; + } + return total; +} + +/* work out how many bytes on the wire a chained ea list will consume. This assumes the names are strict ascii, which should be a reasonable assumption @@ -242,4 +255,107 @@ NTSTATUS ea_pull_list_chained(const DATA_BLOB *blob, } +/* + pull a ea_name from a buffer. Return the number of bytes consumed +*/ +static uint_t ea_pull_name(const DATA_BLOB *blob, + TALLOC_CTX *mem_ctx, + struct ea_name *ea) +{ + uint8_t nlen; + + if (blob->length < 2) { + return 0; + } + + nlen = CVAL(blob->data, 0); + + if (nlen+2 > blob->length) { + return 0; + } + + ea->name.s = talloc_strndup(mem_ctx, (const char *)(blob->data+1), nlen); + ea->name.private_length = nlen; + + return nlen+2; +} + + +/* + pull a ea_name list from a buffer +*/ +NTSTATUS ea_pull_name_list(const DATA_BLOB *blob, + TALLOC_CTX *mem_ctx, + uint_t *num_names, struct ea_name **ea_names) +{ + int n; + uint32_t ea_size, ofs; + + if (blob->length < 4) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + + ea_size = IVAL(blob->data, 0); + if (ea_size > blob->length) { + return NT_STATUS_INVALID_PARAMETER; + } + + ofs = 4; + n = 0; + *num_names = 0; + *ea_names = NULL; + + while (ofs < ea_size) { + uint_t len; + DATA_BLOB blob2; + + blob2.data = blob->data + ofs; + blob2.length = ea_size - ofs; + + *ea_names = talloc_realloc_p(mem_ctx, *ea_names, struct ea_name, n+1); + if (! *ea_names) return NT_STATUS_NO_MEMORY; + + len = ea_pull_name(&blob2, mem_ctx, &(*ea_names)[n]); + if (len == 0) { + return NT_STATUS_INVALID_PARAMETER; + } + + ofs += len; + n++; + } + + *num_names = n; + + return NT_STATUS_OK; +} + + +/* + put a ea_name list into a data blob +*/ +BOOL ea_push_name_list(TALLOC_CTX *mem_ctx, + DATA_BLOB *data, uint_t num_names, struct ea_name *eas) +{ + int i; + uint32_t ea_size; + uint32_t off; + + ea_size = ea_name_list_size(num_names, eas); + + *data = data_blob_talloc(mem_ctx, NULL, ea_size); + if (data->data == NULL) { + return False; + } + + SIVAL(data->data, 0, ea_size); + off = 4; + for (i=0;i<num_names;i++) { + uint_t nlen = strlen(eas[i].name.s); + SCVAL(data->data, off, nlen); + memcpy(data->data+off+1, eas[i].name.s, nlen+1); + off += 1+nlen+1; + } + + return True; +} diff --git a/source4/libcli/raw/rawfileinfo.c b/source4/libcli/raw/rawfileinfo.c index 6f875f51a7..8f694a23d8 100644 --- a/source4/libcli/raw/rawfileinfo.c +++ b/source4/libcli/raw/rawfileinfo.c @@ -79,6 +79,12 @@ static NTSTATUS smb_raw_info_backend(struct smbcli_session *session, parms->ea_size.out.ea_size = IVAL(blob->data, 22); return NT_STATUS_OK; + case RAW_FILEINFO_EA_LIST: + FINFO_CHECK_MIN_SIZE(4); + return ea_pull_list(blob, mem_ctx, + &parms->ea_list.out.num_eas, + &parms->ea_list.out.eas); + case RAW_FILEINFO_ALL_EAS: FINFO_CHECK_MIN_SIZE(4); return ea_pull_list(blob, mem_ctx, @@ -280,7 +286,9 @@ static NTSTATUS smb_raw_info_backend(struct smbcli_session *session, Very raw query file info - returns param/data blobs - (async send) ****************************************************************************/ static struct smbcli_request *smb_raw_fileinfo_blob_send(struct smbcli_tree *tree, - uint16_t fnum, uint16_t info_level) + uint16_t fnum, + uint16_t info_level, + DATA_BLOB data) { struct smb_trans2 tp; uint16_t setup = TRANSACT2_QFILEINFO; @@ -291,7 +299,7 @@ static struct smbcli_request *smb_raw_fileinfo_blob_send(struct smbcli_tree *tre tp.in.flags = 0; tp.in.timeout = 0; tp.in.setup_count = 1; - tp.in.data = data_blob(NULL, 0); + tp.in.data = data; tp.in.max_param = 2; tp.in.max_data = smb_raw_max_trans_data(tree, 2); tp.in.setup = &setup; @@ -332,8 +340,9 @@ static NTSTATUS smb_raw_fileinfo_blob_recv(struct smbcli_request *req, Very raw query path info - returns param/data blobs (async send) ****************************************************************************/ static struct smbcli_request *smb_raw_pathinfo_blob_send(struct smbcli_tree *tree, - const char *fname, - uint16_t info_level) + const char *fname, + uint16_t info_level, + DATA_BLOB data) { struct smb_trans2 tp; uint16_t setup = TRANSACT2_QPATHINFO; @@ -344,7 +353,7 @@ static struct smbcli_request *smb_raw_pathinfo_blob_send(struct smbcli_tree *tre tp.in.flags = 0; tp.in.timeout = 0; tp.in.setup_count = 1; - tp.in.data = data_blob(NULL, 0); + tp.in.data = data; tp.in.max_param = 2; tp.in.max_data = smb_raw_max_trans_data(tree, 2); tp.in.setup = &setup; @@ -463,6 +472,9 @@ failed: struct smbcli_request *smb_raw_fileinfo_send(struct smbcli_tree *tree, union smb_fileinfo *parms) { + DATA_BLOB data; + struct smbcli_request *req; + /* pass off the non-trans2 level to specialised functions */ if (parms->generic.level == RAW_FILEINFO_GETATTRE) { return smb_raw_getattrE_send(tree, parms); @@ -474,9 +486,24 @@ struct smbcli_request *smb_raw_fileinfo_send(struct smbcli_tree *tree, return NULL; } - return smb_raw_fileinfo_blob_send(tree, - parms->generic.in.fnum, - parms->generic.level); + data = data_blob(NULL, 0); + + if (parms->generic.level == RAW_FILEINFO_EA_LIST) { + if (!ea_push_name_list(tree, + &data, + parms->ea_list.in.num_names, + parms->ea_list.in.ea_names)) { + return NULL; + } + } + + req = smb_raw_fileinfo_blob_send(tree, + parms->generic.in.fnum, + parms->generic.level, data); + + data_blob_free(&data); + + return req; } /**************************************************************************** @@ -525,6 +552,9 @@ NTSTATUS smb_raw_fileinfo(struct smbcli_tree *tree, struct smbcli_request *smb_raw_pathinfo_send(struct smbcli_tree *tree, union smb_fileinfo *parms) { + DATA_BLOB data; + struct smbcli_request *req; + if (parms->generic.level == RAW_FILEINFO_GETATTR) { return smb_raw_getattr_send(tree, parms); } @@ -532,8 +562,22 @@ struct smbcli_request *smb_raw_pathinfo_send(struct smbcli_tree *tree, return NULL; } - return smb_raw_pathinfo_blob_send(tree, parms->generic.in.fname, - parms->generic.level); + data = data_blob(NULL, 0); + + if (parms->generic.level == RAW_FILEINFO_EA_LIST) { + if (!ea_push_name_list(tree, + &data, + parms->ea_list.in.num_names, + parms->ea_list.in.ea_names)) { + return NULL; + } + } + + req = smb_raw_pathinfo_blob_send(tree, parms->generic.in.fname, + parms->generic.level, data); + data_blob_free(&data); + + return req; } /**************************************************************************** diff --git a/source4/ntvfs/posix/pvfs_qfileinfo.c b/source4/ntvfs/posix/pvfs_qfileinfo.c index fe53b0a415..ae55ad5e98 100644 --- a/source4/ntvfs/posix/pvfs_qfileinfo.c +++ b/source4/ntvfs/posix/pvfs_qfileinfo.c @@ -25,6 +25,45 @@ #include "librpc/gen_ndr/ndr_xattr.h" /* + reply to a RAW_FILEINFO_EA_LIST call +*/ +static NTSTATUS pvfs_query_ea_list(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, + struct pvfs_filename *name, int fd, + uint_t num_names, + struct ea_name *names, + struct smb_ea_list *eas) +{ + NTSTATUS status; + int i; + struct xattr_DosEAs *ealist = talloc_p(mem_ctx, struct xattr_DosEAs); + + ZERO_STRUCTP(eas); + status = pvfs_doseas_load(pvfs, name, fd, ealist); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + eas->eas = talloc_array_p(mem_ctx, struct ea_struct, num_names); + if (eas->eas == NULL) { + return NT_STATUS_NO_MEMORY; + } + eas->num_eas = num_names; + for (i=0;i<num_names;i++) { + int j; + eas->eas[i].flags = 0; + eas->eas[i].name.s = names[i].name.s; + eas->eas[i].value = data_blob(NULL, 0); + for (j=0;j<ealist->num_eas;j++) { + if (StrCaseCmp(eas->eas[i].name.s, + ealist->eas[j].name) == 0) { + eas->eas[i].value = ealist->eas[j].value; + break; + } + } + } + return NT_STATUS_OK; +} + +/* reply to a RAW_FILEINFO_ALL_EAS call */ static NTSTATUS pvfs_query_all_eas(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, @@ -40,15 +79,16 @@ static NTSTATUS pvfs_query_all_eas(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, if (!NT_STATUS_IS_OK(status)) { return status; } - eas->num_eas = ealist->num_eas; - eas->eas = talloc_array_p(mem_ctx, struct ea_struct, eas->num_eas); + eas->eas = talloc_array_p(mem_ctx, struct ea_struct, ealist->num_eas); if (eas->eas == NULL) { return NT_STATUS_NO_MEMORY; } - for (i=0;i<eas->num_eas;i++) { - eas->eas[i].flags = 0; - eas->eas[i].name.s = ealist->eas[i].name; - eas->eas[i].value = ealist->eas[i].value; + eas->num_eas = 0; + for (i=0;i<ealist->num_eas;i++) { + eas->eas[eas->num_eas].flags = 0; + eas->eas[eas->num_eas].name.s = ealist->eas[i].name; + eas->eas[eas->num_eas].value = ealist->eas[i].value; + eas->num_eas++; } return NT_STATUS_OK; } @@ -91,6 +131,12 @@ static NTSTATUS pvfs_map_fileinfo(struct pvfs_state *pvfs, info->ea_size.out.ea_size = name->dos.ea_size; return NT_STATUS_OK; + case RAW_FILEINFO_EA_LIST: + return pvfs_query_ea_list(pvfs, req, name, fd, + info->ea_list.in.num_names, + info->ea_list.in.ea_names, + &info->ea_list.out); + case RAW_FILEINFO_ALL_EAS: return pvfs_query_all_eas(pvfs, req, name, fd, &info->all_eas.out); diff --git a/source4/smb_server/trans2.c b/source4/smb_server/trans2.c index 3ca9bafcfa..0c827c92a1 100644 --- a/source4/smb_server/trans2.c +++ b/source4/smb_server/trans2.c @@ -614,6 +614,15 @@ static NTSTATUS trans2_fileinfo_fill(struct smbsrv_request *req, struct smb_tran st->alignment_information.out.alignment_requirement); return NT_STATUS_OK; + case RAW_FILEINFO_EA_LIST: + list_size = ea_list_size(st->ea_list.out.num_eas, + st->ea_list.out.eas); + trans2_setup_reply(req, trans, 2, list_size, 0); + SSVAL(trans->out.params.data, 0, 0); + ea_put_list(trans->out.data.data, + st->ea_list.out.num_eas, st->ea_list.out.eas); + return NT_STATUS_OK; + case RAW_FILEINFO_ALL_EAS: list_size = ea_list_size(st->all_eas.out.num_eas, st->all_eas.out.eas); @@ -753,6 +762,15 @@ static NTSTATUS trans2_qpathinfo(struct smbsrv_request *req, struct smb_trans2 * return NT_STATUS_INVALID_LEVEL; } + if (st.generic.level == RAW_FILEINFO_EA_LIST) { + status = ea_pull_name_list(&trans->in.data, req, + &st.ea_list.in.num_names, + &st.ea_list.in.ea_names); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + /* call the backend */ status = ntvfs_qpathinfo(req, &st); if (!NT_STATUS_IS_OK(status)) { @@ -789,6 +807,15 @@ static NTSTATUS trans2_qfileinfo(struct smbsrv_request *req, struct smb_trans2 * return NT_STATUS_INVALID_LEVEL; } + if (st.generic.level == RAW_FILEINFO_EA_LIST) { + status = ea_pull_name_list(&trans->in.data, req, + &st.ea_list.in.num_names, + &st.ea_list.in.ea_names); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + /* call the backend */ status = ntvfs_qfileinfo(req, &st); if (!NT_STATUS_IS_OK(status)) { diff --git a/source4/torture/torture_util.c b/source4/torture/torture_util.c index d96a285b6f..de93d6731f 100644 --- a/source4/torture/torture_util.c +++ b/source4/torture/torture_util.c @@ -362,11 +362,15 @@ NTSTATUS torture_check_ea(struct smbcli_state *cli, { union smb_fileinfo info; NTSTATUS status; - int i; + struct ea_name ea; TALLOC_CTX *mem_ctx = talloc(cli, 0); - info.all_eas.level = RAW_FILEINFO_ALL_EAS; - info.all_eas.in.fname = fname; + info.ea_list.level = RAW_FILEINFO_EA_LIST; + info.ea_list.file.fname = fname; + info.ea_list.in.num_names = 1; + info.ea_list.in.ea_names = &ea; + + ea.name.s = eaname; status = smb_raw_pathinfo(cli->tree, mem_ctx, &info); if (!NT_STATUS_IS_OK(status)) { @@ -374,38 +378,45 @@ NTSTATUS torture_check_ea(struct smbcli_state *cli, return status; } - for (i=0;i<info.all_eas.out.num_eas;i++) { - if (StrCaseCmp(eaname, info.all_eas.out.eas[i].name.s) == 0) { - if (value == NULL) { - printf("attr '%s' should not be present\n", eaname); - talloc_free(mem_ctx); - return NT_STATUS_EA_CORRUPT_ERROR; - } - if (strlen(value) == info.all_eas.out.eas[i].value.length && - memcmp(value, - info.all_eas.out.eas[i].value.data, - info.all_eas.out.eas[i].value.length) == 0) { - talloc_free(mem_ctx); - return NT_STATUS_OK; - } else { - printf("attr '%s' has wrong value '%*.*s'\n", - eaname, - info.all_eas.out.eas[i].value.length, - info.all_eas.out.eas[i].value.length, - info.all_eas.out.eas[i].value.data); - talloc_free(mem_ctx); - return NT_STATUS_EA_CORRUPT_ERROR; - } - } + if (info.ea_list.out.num_eas != 1) { + printf("Expected 1 ea in ea_list\n"); + talloc_free(mem_ctx); + return NT_STATUS_EA_CORRUPT_ERROR; } - talloc_free(mem_ctx); + if (StrCaseCmp(eaname, info.ea_list.out.eas[0].name.s) != 0) { + printf("Expected ea '%s' not '%s' in ea_list\n", + eaname, info.ea_list.out.eas[0].name.s); + talloc_free(mem_ctx); + return NT_STATUS_EA_CORRUPT_ERROR; + } + + if (value == NULL) { + if (info.ea_list.out.eas[0].value.length != 0) { + printf("Expected zero length ea for %s\n", eaname); + talloc_free(mem_ctx); + return NT_STATUS_EA_CORRUPT_ERROR; + } + talloc_free(mem_ctx); + return NT_STATUS_OK; + } - if (value != NULL) { - printf("attr '%s' not found\n", eaname); - return NT_STATUS_NONEXISTENT_EA_ENTRY; + if (strlen(value) == info.ea_list.out.eas[0].value.length && + memcmp(value, info.ea_list.out.eas[0].value.data, + info.ea_list.out.eas[0].value.length) == 0) { + talloc_free(mem_ctx); + return NT_STATUS_OK; } - return NT_STATUS_OK; + printf("Expected value '%s' not '%*.*s' for ea %s\n", + value, + info.ea_list.out.eas[0].value.length, + info.ea_list.out.eas[0].value.length, + info.ea_list.out.eas[0].value.data, + eaname); + + talloc_free(mem_ctx); + + return NT_STATUS_EA_CORRUPT_ERROR; } |