Fix bug #10010 - Missing integer wrap protection in EA list reading can cause server...
[metze/samba/wip.git] / source4 / libcli / raw / raweas.c
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 {
31         unsigned int total = 4;
32         int i;
33         for (i=0;i<num_eas;i++) {
34                 total += 4 + strlen(eas[i].name.s)+1 + eas[i].value.length;
35         }
36         return total;
37 }
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 {
44         unsigned int total = 4;
45         int i;
46         for (i=0;i<num_names;i++) {
47                 total += 1 + strlen(eas[i].name.s) + 1;
48         }
49         return total;
50 }
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 {
59         unsigned int total = 0;
60         int i;
61         for (i=0;i<num_eas;i++) {
62                 unsigned int len = 8 + strlen(eas[i].name.s)+1 + eas[i].value.length;
63                 len = (len + (alignment-1)) & ~(alignment-1);
64                 total += len;
65         }
66         return total;
67 }
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 {
75         int i;
76         uint32_t ea_size;
77
78         ea_size = ea_list_size(num_eas, eas);
79
80         SIVAL(data, 0, ea_size);
81         data += 4;
82
83         for (i=0;i<num_eas;i++) {
84                 unsigned int nlen = strlen(eas[i].name.s);
85                 SCVAL(data, 0, eas[i].flags);
86                 SCVAL(data, 1, nlen);
87                 SSVAL(data, 2, eas[i].value.length);
88                 memcpy(data+4, eas[i].name.s, nlen+1);
89                 memcpy(data+4+nlen+1, eas[i].value.data, eas[i].value.length);
90                 data += 4+nlen+1+eas[i].value.length;
91         }
92 }
93
94
95 /*
96   put a chained ea_list into a pre-allocated buffer - buffer must be
97   at least of size ea_list_size()
98 */
99 void ea_put_list_chained(uint8_t *data, unsigned int num_eas, struct ea_struct *eas,
100                          unsigned alignment)
101 {
102         int i;
103
104         for (i=0;i<num_eas;i++) {
105                 unsigned int nlen = strlen(eas[i].name.s);
106                 uint32_t len = 8+nlen+1+eas[i].value.length;
107                 unsigned int pad = ((len + (alignment-1)) & ~(alignment-1)) - len;
108                 if (i == num_eas-1) {
109                         SIVAL(data, 0, 0);
110                 } else {
111                         SIVAL(data, 0, len+pad);
112                 }
113                 SCVAL(data, 4, eas[i].flags);
114                 SCVAL(data, 5, nlen);
115                 SSVAL(data, 6, eas[i].value.length);
116                 memcpy(data+8, eas[i].name.s, nlen+1);
117                 memcpy(data+8+nlen+1, eas[i].value.data, eas[i].value.length);
118                 memset(data+len, 0, pad);
119                 data += len + pad;
120         }
121 }
122
123
124 /*
125   pull a ea_struct from a buffer. Return the number of bytes consumed
126 */
127 unsigned int ea_pull_struct(const DATA_BLOB *blob,
128                       TALLOC_CTX *mem_ctx,
129                       struct ea_struct *ea)
130 {
131         uint8_t nlen;
132         uint16_t vlen;
133
134         ZERO_STRUCTP(ea);
135
136         if (blob->length < 6) {
137                 return 0;
138         }
139
140         ea->flags = CVAL(blob->data, 0);
141         nlen = CVAL(blob->data, 1);
142         vlen = SVAL(blob->data, 2);
143
144         if (nlen+1+vlen > blob->length-4) {
145                 return 0;
146         }
147
148         ea->name.s = talloc_strndup(mem_ctx, (const char *)(blob->data+4), nlen);
149         ea->name.private_length = nlen;
150         ea->value = data_blob_talloc(mem_ctx, NULL, vlen+1);
151         if (!ea->value.data) return 0;
152         if (vlen) {
153                 memcpy(ea->value.data, blob->data+4+nlen+1, vlen);
154         }
155         ea->value.data[vlen] = 0;
156         ea->value.length--;
157
158         return 4 + nlen+1 + vlen;
159 }
160
161
162 /*
163   pull a ea_list from a buffer
164 */
165 NTSTATUS ea_pull_list(const DATA_BLOB *blob, 
166                       TALLOC_CTX *mem_ctx,
167                       unsigned int *num_eas, struct ea_struct **eas)
168 {
169         int n;
170         uint32_t ea_size, ofs;
171
172         if (blob->length < 4) {
173                 return NT_STATUS_INFO_LENGTH_MISMATCH;
174         }
175
176         ea_size = IVAL(blob->data, 0);
177         if (ea_size > blob->length) {
178                 return NT_STATUS_INVALID_PARAMETER;
179         }
180
181         ofs = 4;
182         n = 0;
183         *num_eas = 0;
184         *eas = NULL;
185
186         while (ofs < ea_size) {
187                 unsigned int len;
188                 DATA_BLOB blob2;
189
190                 blob2.data = blob->data + ofs;
191                 blob2.length = ea_size - ofs;
192
193                 *eas = talloc_realloc(mem_ctx, *eas, struct ea_struct, n+1);
194                 if (! *eas) return NT_STATUS_NO_MEMORY;
195
196                 len = ea_pull_struct(&blob2, mem_ctx, &(*eas)[n]);
197                 if (len == 0) {
198                         return NT_STATUS_INVALID_PARAMETER;
199                 }
200
201                 ofs += len;
202                 n++;
203         }
204
205         *num_eas = n;
206
207         return NT_STATUS_OK;
208 }
209
210
211 /*
212   pull a chained ea_list from a buffer
213 */
214 NTSTATUS ea_pull_list_chained(const DATA_BLOB *blob, 
215                               TALLOC_CTX *mem_ctx,
216                               unsigned int *num_eas, struct ea_struct **eas)
217 {
218         int n;
219         uint32_t ofs;
220
221         if (blob->length < 4) {
222                 return NT_STATUS_INFO_LENGTH_MISMATCH;
223         }
224
225         ofs = 0;
226         n = 0;
227         *num_eas = 0;
228         *eas = NULL;
229
230         while (ofs < blob->length) {
231                 unsigned int len;
232                 DATA_BLOB blob2;
233                 uint32_t next_ofs = IVAL(blob->data, ofs);
234
235                 blob2.data = blob->data + ofs + 4;
236                 blob2.length = blob->length - (ofs + 4);
237
238                 *eas = talloc_realloc(mem_ctx, *eas, struct ea_struct, n+1);
239                 if (! *eas) return NT_STATUS_NO_MEMORY;
240
241                 len = ea_pull_struct(&blob2, mem_ctx, &(*eas)[n]);
242                 if (len == 0) {
243                         return NT_STATUS_INVALID_PARAMETER;
244                 }
245
246                 if (ofs + next_ofs < ofs) {
247                         return NT_STATUS_INVALID_PARAMETER;
248                 }
249
250                 ofs += next_ofs;
251                 if (ofs+4 > blob->length || ofs+4 < ofs) {
252                         return NT_STATUS_INVALID_PARAMETER;
253                 }
254                 n++;
255                 if (next_ofs == 0) break;
256         }
257
258         *num_eas = n;
259
260         return NT_STATUS_OK;
261 }
262
263
264 /*
265   pull a ea_name from a buffer. Return the number of bytes consumed
266 */
267 static unsigned int ea_pull_name(const DATA_BLOB *blob,
268                            TALLOC_CTX *mem_ctx,
269                            struct ea_name *ea)
270 {
271         uint8_t nlen;
272
273         if (blob->length < 2) {
274                 return 0;
275         }
276
277         nlen = CVAL(blob->data, 0);
278
279         if (nlen+2 > blob->length) {
280                 return 0;
281         }
282
283         ea->name.s = talloc_strndup(mem_ctx, (const char *)(blob->data+1), nlen);
284         ea->name.private_length = nlen;
285
286         return nlen+2;
287 }
288
289
290 /*
291   pull a ea_name list from a buffer
292 */
293 NTSTATUS ea_pull_name_list(const DATA_BLOB *blob, 
294                            TALLOC_CTX *mem_ctx,
295                            unsigned int *num_names, struct ea_name **ea_names)
296 {
297         int n;
298         uint32_t ea_size, ofs;
299
300         if (blob->length < 4) {
301                 return NT_STATUS_INFO_LENGTH_MISMATCH;
302         }
303
304         ea_size = IVAL(blob->data, 0);
305         if (ea_size > blob->length) {
306                 return NT_STATUS_INVALID_PARAMETER;
307         }
308
309         ofs = 4;
310         n = 0;
311         *num_names = 0;
312         *ea_names = NULL;
313
314         while (ofs < ea_size) {
315                 unsigned int len;
316                 DATA_BLOB blob2;
317
318                 blob2.data = blob->data + ofs;
319                 blob2.length = ea_size - ofs;
320
321                 *ea_names = talloc_realloc(mem_ctx, *ea_names, struct ea_name, n+1);
322                 if (! *ea_names) return NT_STATUS_NO_MEMORY;
323
324                 len = ea_pull_name(&blob2, mem_ctx, &(*ea_names)[n]);
325                 if (len == 0) {
326                         return NT_STATUS_INVALID_PARAMETER;
327                 }
328
329                 ofs += len;
330                 n++;
331         }
332
333         *num_names = n;
334
335         return NT_STATUS_OK;
336 }
337
338
339 /*
340   put a ea_name list into a data blob
341 */
342 bool ea_push_name_list(TALLOC_CTX *mem_ctx,
343                        DATA_BLOB *data, unsigned int num_names, struct ea_name *eas)
344 {
345         int i;
346         uint32_t ea_size;
347         uint32_t off;
348
349         ea_size = ea_name_list_size(num_names, eas);
350
351         *data = data_blob_talloc(mem_ctx, NULL, ea_size);
352         if (data->data == NULL) {
353                 return false;
354         }
355
356         SIVAL(data->data, 0, ea_size);
357         off = 4;
358
359         for (i=0;i<num_names;i++) {
360                 unsigned int nlen = strlen(eas[i].name.s);
361                 SCVAL(data->data, off, nlen);
362                 memcpy(data->data+off+1, eas[i].name.s, nlen+1);
363                 off += 1+nlen+1;
364         }
365
366         return true;
367 }