libcli/util Rename common map_nt_error_from_unix to avoid duplicate symbol
[metze/samba/wip.git] / libcli / auth / msrpc_parse.c
1 /* 
2    Unix SMB/CIFS implementation.
3    simple kerberos5/SPNEGO routines
4    Copyright (C) Andrew Tridgell 2001
5    Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
6    Copyright (C) Andrew Bartlett 2002-2003
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "libcli/auth/msrpc_parse.h"
24
25 /*
26   this is a tiny msrpc packet generator. I am only using this to
27   avoid tying this code to a particular varient of our rpc code. This
28   generator is not general enough for all our rpc needs, its just
29   enough for the spnego/ntlmssp code
30
31   format specifiers are:
32
33   U = unicode string (input is unix string)
34   a = address (input is char *unix_string)
35       (1 byte type, 1 byte length, unicode/ASCII string, all inline)
36   A = ASCII string (input is unix string)
37   B = data blob (pointer + length)
38   b = data blob in header (pointer + length)
39   D
40   d = word (4 bytes)
41   C = constant ascii string
42  */
43 NTSTATUS msrpc_gen(TALLOC_CTX *mem_ctx, 
44                DATA_BLOB *blob,
45                const char *format, ...)
46 {
47         int i, j;
48         bool ret;
49         va_list ap;
50         char *s;
51         uint8_t *b;
52         int head_size=0, data_size=0;
53         int head_ofs, data_ofs;
54         int *intargs;
55         size_t n;
56
57         DATA_BLOB *pointers;
58
59         pointers = talloc_array(mem_ctx, DATA_BLOB, strlen(format));
60         if (!pointers) {
61                 return NT_STATUS_NO_MEMORY;
62         }
63         intargs = talloc_array(pointers, int, strlen(format));
64         if (!intargs) {
65                 return NT_STATUS_NO_MEMORY;
66         }
67
68         /* first scan the format to work out the header and body size */
69         va_start(ap, format);
70         for (i=0; format[i]; i++) {
71                 switch (format[i]) {
72                 case 'U':
73                         s = va_arg(ap, char *);
74                         head_size += 8;
75                         ret = push_ucs2_talloc(
76                                 pointers,
77                                 (smb_ucs2_t **)(void *)&pointers[i].data,
78                                 s, &n);
79                         if (!ret) {
80                                 va_end(ap);
81                                 return map_nt_error_from_unix_common(errno);
82                         }
83                         pointers[i].length = n;
84                         pointers[i].length -= 2;
85                         data_size += pointers[i].length;
86                         break;
87                 case 'A':
88                         s = va_arg(ap, char *);
89                         head_size += 8;
90                         ret = push_ascii_talloc(
91                                 pointers, (char **)(void *)&pointers[i].data,
92                                 s, &n);
93                         if (!ret) {
94                                 va_end(ap);
95                                 return map_nt_error_from_unix_common(errno);
96                         }
97                         pointers[i].length = n;
98                         pointers[i].length -= 1;
99                         data_size += pointers[i].length;
100                         break;
101                 case 'a':
102                         j = va_arg(ap, int);
103                         intargs[i] = j;
104                         s = va_arg(ap, char *);
105                         ret = push_ucs2_talloc(
106                                 pointers,
107                                 (smb_ucs2_t **)(void *)&pointers[i].data,
108                                 s, &n);
109                         if (!ret) {
110                                 va_end(ap);
111                                 return map_nt_error_from_unix_common(errno);
112                         }
113                         pointers[i].length = n;
114                         pointers[i].length -= 2;
115                         data_size += pointers[i].length + 4;
116                         break;
117                 case 'B':
118                         b = va_arg(ap, uint8_t *);
119                         head_size += 8;
120                         pointers[i].data = b;
121                         pointers[i].length = va_arg(ap, int);
122                         data_size += pointers[i].length;
123                         break;
124                 case 'b':
125                         b = va_arg(ap, uint8_t *);
126                         pointers[i].data = b;
127                         pointers[i].length = va_arg(ap, int);
128                         head_size += pointers[i].length;
129                         break;
130                 case 'd':
131                         j = va_arg(ap, int);
132                         intargs[i] = j;
133                         head_size += 4;
134                         break;
135                 case 'C':
136                         s = va_arg(ap, char *);
137                         pointers[i].data = (uint8_t *)s;
138                         pointers[i].length = strlen(s)+1;
139                         head_size += pointers[i].length;
140                         break;
141                 default:
142                         va_end(ap);
143                         return NT_STATUS_INVALID_PARAMETER;
144                 }
145         }
146         va_end(ap);
147
148         if (head_size + data_size == 0) {
149                 return NT_STATUS_INVALID_PARAMETER;
150         }
151
152         /* allocate the space, then scan the format again to fill in the values */
153         *blob = data_blob_talloc(mem_ctx, NULL, head_size + data_size);
154         if (!blob->data) {
155                 return NT_STATUS_NO_MEMORY;
156         }
157         head_ofs = 0;
158         data_ofs = head_size;
159
160         va_start(ap, format);
161         for (i=0; format[i]; i++) {
162                 switch (format[i]) {
163                 case 'U':
164                 case 'A':
165                 case 'B':
166                         n = pointers[i].length;
167                         SSVAL(blob->data, head_ofs, n); head_ofs += 2;
168                         SSVAL(blob->data, head_ofs, n); head_ofs += 2;
169                         SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
170                         if (pointers[i].data && n) /* don't follow null pointers... */
171                                 memcpy(blob->data+data_ofs, pointers[i].data, n);
172                         data_ofs += n;
173                         break;
174                 case 'a':
175                         j = intargs[i];
176                         SSVAL(blob->data, data_ofs, j); data_ofs += 2;
177
178                         n = pointers[i].length;
179                         SSVAL(blob->data, data_ofs, n); data_ofs += 2;
180                         if (n >= 0) {
181                                 memcpy(blob->data+data_ofs, pointers[i].data, n);
182                         }
183                         data_ofs += n;
184                         break;
185                 case 'd':
186                         j = intargs[i];
187                         SIVAL(blob->data, head_ofs, j); 
188                         head_ofs += 4;
189                         break;
190                 case 'b':
191                         n = pointers[i].length;
192                         if (pointers[i].data && n) {
193                                 /* don't follow null pointers... */
194                                 memcpy(blob->data + head_ofs, pointers[i].data, n);
195                         }
196                         head_ofs += n;
197                         break;
198                 case 'C':
199                         n = pointers[i].length;
200                         memcpy(blob->data + head_ofs, pointers[i].data, n);
201                         head_ofs += n;
202                         break;
203                 default:
204                         va_end(ap);
205                         return NT_STATUS_INVALID_PARAMETER;
206                 }
207         }
208         va_end(ap);
209         
210         talloc_free(pointers);
211
212         return NT_STATUS_OK;
213 }
214
215
216 /* a helpful macro to avoid running over the end of our blob */
217 #define NEED_DATA(amount) \
218 if ((head_ofs + amount) > blob->length) { \
219         va_end(ap); \
220         return false; \
221 }
222
223 /**
224   this is a tiny msrpc packet parser. This the the partner of msrpc_gen
225
226   format specifiers are:
227
228   U = unicode string (output is unix string)
229   A = ascii string
230   B = data blob
231   b = data blob in header
232   d = word (4 bytes)
233   C = constant ascii string
234  */
235
236 bool msrpc_parse(TALLOC_CTX *mem_ctx, 
237                  const DATA_BLOB *blob,
238                  const char *format, ...)
239 {
240         int i;
241         va_list ap;
242         char **ps, *s;
243         DATA_BLOB *b;
244         size_t head_ofs = 0;
245         uint16_t len1, len2;
246         uint32_t ptr;
247         uint32_t *v;
248         size_t p_len = 1024;
249         char *p = talloc_array(mem_ctx, char, p_len);
250         bool ret = true;
251
252         if (!p) {
253                 return false;
254         }
255
256         va_start(ap, format);
257         for (i=0; format[i]; i++) {
258                 switch (format[i]) {
259                 case 'U':
260                         NEED_DATA(8);
261                         len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
262                         len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
263                         ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
264
265                         ps = va_arg(ap, char **);
266                         if (len1 == 0 && len2 == 0) {
267                                 *ps = (char *)discard_const("");
268                         } else {
269                                 /* make sure its in the right format - be strict */
270                                 if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) {
271                                         ret = false;
272                                         goto cleanup;
273                                 }
274                                 if (len1 & 1) {
275                                         /* if odd length and unicode */
276                                         ret = false;
277                                         goto cleanup;
278                                 }
279                                 if (blob->data + ptr < (uint8_t *)(uintptr_t)ptr ||
280                                                 blob->data + ptr < blob->data) {
281                                         ret = false;
282                                         goto cleanup;
283                                 }
284
285                                 if (0 < len1) {
286                                         size_t pull_len;
287                                         if (!convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX, 
288                                                                    blob->data + ptr, len1, 
289                                                                    ps, &pull_len)) {
290                                                 ret = false;
291                                                 goto cleanup;
292                                         }
293                                 } else {
294                                         (*ps) = (char *)discard_const("");
295                                 }
296                         }
297                         break;
298                 case 'A':
299                         NEED_DATA(8);
300                         len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
301                         len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
302                         ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
303
304                         ps = (char **)va_arg(ap, char **);
305                         /* make sure its in the right format - be strict */
306                         if (len1 == 0 && len2 == 0) {
307                                 *ps = (char *)discard_const("");
308                         } else {
309                                 if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) {
310                                         ret = false;
311                                         goto cleanup;
312                                 }
313
314                                 if (blob->data + ptr < (uint8_t *)(uintptr_t)ptr ||
315                                                 blob->data + ptr < blob->data) {
316                                         ret = false;
317                                         goto cleanup;
318                                 }
319
320                                 if (0 < len1) {
321                                         size_t pull_len;
322
323                                         if (!convert_string_talloc(mem_ctx, CH_DOS, CH_UNIX, 
324                                                                    blob->data + ptr, len1, 
325                                                                    ps, &pull_len)) {
326                                                 ret = false;
327                                                 goto cleanup;
328                                         }
329                                 } else {
330                                         (*ps) = (char *)discard_const("");
331                                 }
332                         }
333                         break;
334                 case 'B':
335                         NEED_DATA(8);
336                         len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
337                         len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
338                         ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
339
340                         b = (DATA_BLOB *)va_arg(ap, void *);
341                         if (len1 == 0 && len2 == 0) {
342                                 *b = data_blob_talloc(mem_ctx, NULL, 0);
343                         } else {
344                                 /* make sure its in the right format - be strict */
345                                 if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) {
346                                         ret = false;
347                                         goto cleanup;
348                                 }
349
350                                 if (blob->data + ptr < (uint8_t *)(uintptr_t)ptr ||
351                                                 blob->data + ptr < blob->data) {
352                                         ret = false;
353                                         goto cleanup;
354                                 }
355
356                                 *b = data_blob_talloc(mem_ctx, blob->data + ptr, len1);
357                         }
358                         break;
359                 case 'b':
360                         b = (DATA_BLOB *)va_arg(ap, void *);
361                         len1 = va_arg(ap, unsigned int);
362                         /* make sure its in the right format - be strict */
363                         NEED_DATA(len1);
364                         if (blob->data + head_ofs < (uint8_t *)head_ofs ||
365                                         blob->data + head_ofs < blob->data) {
366                                 ret = false;
367                                 goto cleanup;
368                         }
369
370                         *b = data_blob_talloc(mem_ctx, blob->data + head_ofs, len1);
371                         head_ofs += len1;
372                         break;
373                 case 'd':
374                         v = va_arg(ap, uint32_t *);
375                         NEED_DATA(4);
376                         *v = IVAL(blob->data, head_ofs); head_ofs += 4;
377                         break;
378                 case 'C':
379                         s = va_arg(ap, char *);
380
381                         if (blob->data + head_ofs < (uint8_t *)head_ofs ||
382                                         blob->data + head_ofs < blob->data ||
383                             (head_ofs + (strlen(s) + 1)) > blob->length) {
384                                 ret = false;
385                                 goto cleanup;
386                         }
387
388                         if (memcmp(blob->data + head_ofs, s, strlen(s)+1) != 0) {
389                                 ret = false;
390                                 goto cleanup;
391                         }
392                         head_ofs += (strlen(s) + 1);
393
394                         break;
395                 }
396         }
397
398 cleanup:
399         va_end(ap);
400         talloc_free(p);
401         return ret;
402 }