/src/samba/source4/libcli/raw/raweas.c
Line | Count | Source |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | parsing of EA (extended attribute) lists |
4 | | Copyright (C) Andrew Tridgell 2003 |
5 | | |
6 | | This program is free software; you can redistribute it and/or modify |
7 | | it under the terms of the GNU General Public License as published by |
8 | | the Free Software Foundation; either version 3 of the License, or |
9 | | (at your option) any later version. |
10 | | |
11 | | This program is distributed in the hope that it will be useful, |
12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | GNU General Public License for more details. |
15 | | |
16 | | You should have received a copy of the GNU General Public License |
17 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
18 | | */ |
19 | | |
20 | | #include "includes.h" |
21 | | #include "libcli/raw/libcliraw.h" |
22 | | #include "libcli/raw/raw_proto.h" |
23 | | |
24 | | /* |
25 | | work out how many bytes on the wire a ea list will consume. |
26 | | This assumes the names are strict ascii, which should be a |
27 | | reasonable assumption |
28 | | */ |
29 | | size_t ea_list_size(unsigned int num_eas, struct ea_struct *eas) |
30 | 0 | { |
31 | 0 | unsigned int total = 4; |
32 | 0 | int i; |
33 | 0 | for (i=0;i<num_eas;i++) { |
34 | 0 | total += 4 + strlen(eas[i].name.s)+1 + eas[i].value.length; |
35 | 0 | } |
36 | 0 | return total; |
37 | 0 | } |
38 | | |
39 | | /* |
40 | | work out how many bytes on the wire a ea name list will consume. |
41 | | */ |
42 | | static unsigned int ea_name_list_size(unsigned int num_names, struct ea_name *eas) |
43 | 0 | { |
44 | 0 | unsigned int total = 4; |
45 | 0 | int i; |
46 | 0 | for (i=0;i<num_names;i++) { |
47 | 0 | total += 1 + strlen(eas[i].name.s) + 1; |
48 | 0 | } |
49 | 0 | return total; |
50 | 0 | } |
51 | | |
52 | | /* |
53 | | work out how many bytes on the wire a chained ea list will consume. |
54 | | This assumes the names are strict ascii, which should be a |
55 | | reasonable assumption |
56 | | */ |
57 | | size_t ea_list_size_chained(unsigned int num_eas, struct ea_struct *eas, unsigned alignment) |
58 | 0 | { |
59 | 0 | unsigned int total = 0; |
60 | 0 | int i; |
61 | 0 | for (i=0;i<num_eas;i++) { |
62 | 0 | unsigned int len = 8 + strlen(eas[i].name.s)+1 + eas[i].value.length; |
63 | 0 | len = (len + (alignment-1)) & ~(alignment-1); |
64 | 0 | total += len; |
65 | 0 | } |
66 | 0 | return total; |
67 | 0 | } |
68 | | |
69 | | /* |
70 | | put a ea_list into a pre-allocated buffer - buffer must be at least |
71 | | of size ea_list_size() |
72 | | */ |
73 | | void ea_put_list(uint8_t *data, unsigned int num_eas, struct ea_struct *eas) |
74 | 0 | { |
75 | 0 | int i; |
76 | 0 | uint32_t ea_size; |
77 | |
|
78 | 0 | ea_size = ea_list_size(num_eas, eas); |
79 | |
|
80 | 0 | SIVAL(data, 0, ea_size); |
81 | 0 | data += 4; |
82 | |
|
83 | 0 | for (i=0;i<num_eas;i++) { |
84 | 0 | unsigned int nlen = strlen(eas[i].name.s); |
85 | 0 | SCVAL(data, 0, eas[i].flags); |
86 | 0 | SCVAL(data, 1, nlen); |
87 | 0 | SSVAL(data, 2, eas[i].value.length); |
88 | 0 | memcpy(data+4, eas[i].name.s, nlen+1); |
89 | 0 | if (eas[i].value.length > 0) { |
90 | 0 | memcpy(data + 4 + nlen + 1, |
91 | 0 | eas[i].value.data, |
92 | 0 | eas[i].value.length); |
93 | 0 | } |
94 | 0 | data += 4+nlen+1+eas[i].value.length; |
95 | 0 | } |
96 | 0 | } |
97 | | |
98 | | |
99 | | /* |
100 | | put a chained ea_list into a pre-allocated buffer - buffer must be |
101 | | at least of size ea_list_size() |
102 | | */ |
103 | | void ea_put_list_chained(uint8_t *data, unsigned int num_eas, struct ea_struct *eas, |
104 | | unsigned alignment) |
105 | 0 | { |
106 | 0 | int i; |
107 | |
|
108 | 0 | for (i=0;i<num_eas;i++) { |
109 | 0 | unsigned int nlen = strlen(eas[i].name.s); |
110 | 0 | uint32_t len = 8+nlen+1+eas[i].value.length; |
111 | 0 | unsigned int pad = ((len + (alignment-1)) & ~(alignment-1)) - len; |
112 | 0 | if (i == num_eas-1) { |
113 | 0 | SIVAL(data, 0, 0); |
114 | 0 | } else { |
115 | 0 | SIVAL(data, 0, len+pad); |
116 | 0 | } |
117 | 0 | SCVAL(data, 4, eas[i].flags); |
118 | 0 | SCVAL(data, 5, nlen); |
119 | 0 | SSVAL(data, 6, eas[i].value.length); |
120 | 0 | memcpy(data+8, eas[i].name.s, nlen+1); |
121 | 0 | memcpy(data+8+nlen+1, eas[i].value.data, eas[i].value.length); |
122 | 0 | memset(data+len, 0, pad); |
123 | 0 | data += len + pad; |
124 | 0 | } |
125 | 0 | } |
126 | | |
127 | | |
128 | | /* |
129 | | pull a ea_struct from a buffer. Return the number of bytes consumed |
130 | | */ |
131 | | unsigned int ea_pull_struct(const DATA_BLOB *blob, |
132 | | TALLOC_CTX *mem_ctx, |
133 | | struct ea_struct *ea) |
134 | 0 | { |
135 | 0 | uint8_t nlen; |
136 | 0 | uint16_t vlen; |
137 | |
|
138 | 0 | ZERO_STRUCTP(ea); |
139 | |
|
140 | 0 | if (blob->length < 6) { |
141 | 0 | return 0; |
142 | 0 | } |
143 | | |
144 | 0 | ea->flags = CVAL(blob->data, 0); |
145 | 0 | nlen = CVAL(blob->data, 1); |
146 | 0 | vlen = SVAL(blob->data, 2); |
147 | |
|
148 | 0 | if (nlen+1+vlen > blob->length-4) { |
149 | 0 | return 0; |
150 | 0 | } |
151 | | |
152 | 0 | ea->name.s = talloc_strndup(mem_ctx, (const char *)(blob->data+4), nlen); |
153 | 0 | ea->name.private_length = nlen; |
154 | 0 | ea->value = data_blob_talloc(mem_ctx, NULL, vlen+1); |
155 | 0 | if (!ea->value.data) return 0; |
156 | 0 | if (vlen) { |
157 | 0 | memcpy(ea->value.data, blob->data+4+nlen+1, vlen); |
158 | 0 | } |
159 | 0 | ea->value.data[vlen] = 0; |
160 | 0 | ea->value.length--; |
161 | |
|
162 | 0 | return 4 + nlen+1 + vlen; |
163 | 0 | } |
164 | | |
165 | | |
166 | | /* |
167 | | pull a ea_list from a buffer |
168 | | */ |
169 | | NTSTATUS ea_pull_list(const DATA_BLOB *blob, |
170 | | TALLOC_CTX *mem_ctx, |
171 | | unsigned int *num_eas, struct ea_struct **eas) |
172 | 0 | { |
173 | 0 | int n; |
174 | 0 | uint32_t ea_size, ofs; |
175 | |
|
176 | 0 | if (blob->length < 4) { |
177 | 0 | return NT_STATUS_INFO_LENGTH_MISMATCH; |
178 | 0 | } |
179 | | |
180 | 0 | ea_size = IVAL(blob->data, 0); |
181 | 0 | if (ea_size > blob->length) { |
182 | 0 | return NT_STATUS_INVALID_PARAMETER; |
183 | 0 | } |
184 | | |
185 | 0 | ofs = 4; |
186 | 0 | n = 0; |
187 | 0 | *num_eas = 0; |
188 | 0 | *eas = NULL; |
189 | |
|
190 | 0 | while (ofs < ea_size) { |
191 | 0 | unsigned int len; |
192 | 0 | DATA_BLOB blob2; |
193 | |
|
194 | 0 | blob2.data = blob->data + ofs; |
195 | 0 | blob2.length = ea_size - ofs; |
196 | |
|
197 | 0 | *eas = talloc_realloc(mem_ctx, *eas, struct ea_struct, n+1); |
198 | 0 | if (! *eas) return NT_STATUS_NO_MEMORY; |
199 | | |
200 | 0 | len = ea_pull_struct(&blob2, mem_ctx, &(*eas)[n]); |
201 | 0 | if (len == 0) { |
202 | 0 | return NT_STATUS_INVALID_PARAMETER; |
203 | 0 | } |
204 | | |
205 | 0 | ofs += len; |
206 | 0 | n++; |
207 | 0 | } |
208 | | |
209 | 0 | *num_eas = n; |
210 | |
|
211 | 0 | return NT_STATUS_OK; |
212 | 0 | } |
213 | | |
214 | | |
215 | | /* |
216 | | pull a chained ea_list from a buffer |
217 | | */ |
218 | | NTSTATUS ea_pull_list_chained(const DATA_BLOB *blob, |
219 | | TALLOC_CTX *mem_ctx, |
220 | | unsigned int *num_eas, struct ea_struct **eas) |
221 | 0 | { |
222 | 0 | int n; |
223 | 0 | uint32_t ofs; |
224 | |
|
225 | 0 | if (blob->length < 4) { |
226 | 0 | return NT_STATUS_INFO_LENGTH_MISMATCH; |
227 | 0 | } |
228 | | |
229 | 0 | ofs = 0; |
230 | 0 | n = 0; |
231 | 0 | *num_eas = 0; |
232 | 0 | *eas = NULL; |
233 | |
|
234 | 0 | while (ofs < blob->length) { |
235 | 0 | unsigned int len; |
236 | 0 | DATA_BLOB blob2; |
237 | 0 | uint32_t next_ofs = IVAL(blob->data, ofs); |
238 | |
|
239 | 0 | blob2.data = blob->data + ofs + 4; |
240 | 0 | blob2.length = blob->length - (ofs + 4); |
241 | |
|
242 | 0 | *eas = talloc_realloc(mem_ctx, *eas, struct ea_struct, n+1); |
243 | 0 | if (! *eas) return NT_STATUS_NO_MEMORY; |
244 | | |
245 | 0 | len = ea_pull_struct(&blob2, mem_ctx, &(*eas)[n]); |
246 | 0 | if (len == 0) { |
247 | 0 | return NT_STATUS_INVALID_PARAMETER; |
248 | 0 | } |
249 | | |
250 | 0 | if (ofs + next_ofs < ofs) { |
251 | 0 | return NT_STATUS_INVALID_PARAMETER; |
252 | 0 | } |
253 | | |
254 | 0 | ofs += next_ofs; |
255 | 0 | if (ofs+4 > blob->length || ofs+4 < ofs) { |
256 | 0 | return NT_STATUS_INVALID_PARAMETER; |
257 | 0 | } |
258 | 0 | n++; |
259 | 0 | if (next_ofs == 0) break; |
260 | 0 | } |
261 | | |
262 | 0 | *num_eas = n; |
263 | |
|
264 | 0 | return NT_STATUS_OK; |
265 | 0 | } |
266 | | |
267 | | |
268 | | /* |
269 | | pull a ea_name from a buffer. Return the number of bytes consumed |
270 | | */ |
271 | | static unsigned int ea_pull_name(const DATA_BLOB *blob, |
272 | | TALLOC_CTX *mem_ctx, |
273 | | struct ea_name *ea) |
274 | 0 | { |
275 | 0 | uint8_t nlen; |
276 | |
|
277 | 0 | if (blob->length < 2) { |
278 | 0 | return 0; |
279 | 0 | } |
280 | | |
281 | 0 | nlen = CVAL(blob->data, 0); |
282 | |
|
283 | 0 | if (nlen+2 > blob->length) { |
284 | 0 | return 0; |
285 | 0 | } |
286 | | |
287 | 0 | ea->name.s = talloc_strndup(mem_ctx, (const char *)(blob->data+1), nlen); |
288 | 0 | ea->name.private_length = nlen; |
289 | |
|
290 | 0 | return nlen+2; |
291 | 0 | } |
292 | | |
293 | | |
294 | | /* |
295 | | pull a ea_name list from a buffer |
296 | | */ |
297 | | NTSTATUS ea_pull_name_list(const DATA_BLOB *blob, |
298 | | TALLOC_CTX *mem_ctx, |
299 | | unsigned int *num_names, struct ea_name **ea_names) |
300 | 0 | { |
301 | 0 | int n; |
302 | 0 | uint32_t ea_size, ofs; |
303 | |
|
304 | 0 | if (blob->length < 4) { |
305 | 0 | return NT_STATUS_INFO_LENGTH_MISMATCH; |
306 | 0 | } |
307 | | |
308 | 0 | ea_size = IVAL(blob->data, 0); |
309 | 0 | if (ea_size > blob->length) { |
310 | 0 | return NT_STATUS_INVALID_PARAMETER; |
311 | 0 | } |
312 | | |
313 | 0 | ofs = 4; |
314 | 0 | n = 0; |
315 | 0 | *num_names = 0; |
316 | 0 | *ea_names = NULL; |
317 | |
|
318 | 0 | while (ofs < ea_size) { |
319 | 0 | unsigned int len; |
320 | 0 | DATA_BLOB blob2; |
321 | |
|
322 | 0 | blob2.data = blob->data + ofs; |
323 | 0 | blob2.length = ea_size - ofs; |
324 | |
|
325 | 0 | *ea_names = talloc_realloc(mem_ctx, *ea_names, struct ea_name, n+1); |
326 | 0 | if (! *ea_names) return NT_STATUS_NO_MEMORY; |
327 | | |
328 | 0 | len = ea_pull_name(&blob2, mem_ctx, &(*ea_names)[n]); |
329 | 0 | if (len == 0) { |
330 | 0 | return NT_STATUS_INVALID_PARAMETER; |
331 | 0 | } |
332 | | |
333 | 0 | ofs += len; |
334 | 0 | n++; |
335 | 0 | } |
336 | | |
337 | 0 | *num_names = n; |
338 | |
|
339 | 0 | return NT_STATUS_OK; |
340 | 0 | } |
341 | | |
342 | | |
343 | | /* |
344 | | put a ea_name list into a data blob |
345 | | */ |
346 | | bool ea_push_name_list(TALLOC_CTX *mem_ctx, |
347 | | DATA_BLOB *data, unsigned int num_names, struct ea_name *eas) |
348 | 0 | { |
349 | 0 | int i; |
350 | 0 | uint32_t ea_size; |
351 | 0 | uint32_t off; |
352 | |
|
353 | 0 | ea_size = ea_name_list_size(num_names, eas); |
354 | |
|
355 | 0 | *data = data_blob_talloc(mem_ctx, NULL, ea_size); |
356 | 0 | if (data->data == NULL) { |
357 | 0 | return false; |
358 | 0 | } |
359 | | |
360 | 0 | SIVAL(data->data, 0, ea_size); |
361 | 0 | off = 4; |
362 | |
|
363 | 0 | for (i=0;i<num_names;i++) { |
364 | 0 | unsigned int nlen = strlen(eas[i].name.s); |
365 | 0 | SCVAL(data->data, off, nlen); |
366 | 0 | memcpy(data->data+off+1, eas[i].name.s, nlen+1); |
367 | 0 | off += 1+nlen+1; |
368 | 0 | } |
369 | |
|
370 | | return true; |
371 | 0 | } |