Rework trivial msrpc parser to use convert_string_talloc()
[kai/samba.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 "auth/ntlmssp/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 bool 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         intargs = talloc_array(pointers, int, strlen(format));
61
62         /* first scan the format to work out the header and body size */
63         va_start(ap, format);
64         for (i=0; format[i]; i++) {
65                 switch (format[i]) {
66                 case 'U':
67                         s = va_arg(ap, char *);
68                         head_size += 8;
69                         ret = push_ucs2_talloc(pointers, (smb_ucs2_t **)&pointers[i].data, 
70                                                                    s, &n);
71                         if (!ret) {
72                                 return false;
73                         }
74                         pointers[i].length = n;
75                         pointers[i].length -= 2;
76                         data_size += pointers[i].length;
77                         break;
78                 case 'A':
79                         s = va_arg(ap, char *);
80                         head_size += 8;
81                         ret = push_ascii_talloc(pointers, (char **)&pointers[i].data, s,
82                                                                         &n);
83                         if (!ret) {
84                                 return false;
85                         }
86                         pointers[i].length = n;
87                         pointers[i].length -= 1;
88                         data_size += pointers[i].length;
89                         break;
90                 case 'a':
91                         j = va_arg(ap, int);
92                         intargs[i] = j;
93                         s = va_arg(ap, char *);
94                         ret = push_ucs2_talloc(pointers, (smb_ucs2_t **)&pointers[i].data, 
95                                                                    s, &n);
96                         if (!ret) {
97                                 return false;
98                         }
99                         pointers[i].length = n;
100                         pointers[i].length -= 2;
101                         data_size += pointers[i].length + 4;
102                         break;
103                 case 'B':
104                         b = va_arg(ap, uint8_t *);
105                         head_size += 8;
106                         pointers[i].data = b;
107                         pointers[i].length = va_arg(ap, int);
108                         data_size += pointers[i].length;
109                         break;
110                 case 'b':
111                         b = va_arg(ap, uint8_t *);
112                         pointers[i].data = b;
113                         pointers[i].length = va_arg(ap, int);
114                         head_size += pointers[i].length;
115                         break;
116                 case 'd':
117                         j = va_arg(ap, int);
118                         intargs[i] = j;
119                         head_size += 4;
120                         break;
121                 case 'C':
122                         s = va_arg(ap, char *);
123                         pointers[i].data = (uint8_t *)s;
124                         pointers[i].length = strlen(s)+1;
125                         head_size += pointers[i].length;
126                         break;
127                 }
128         }
129         va_end(ap);
130
131         /* allocate the space, then scan the format again to fill in the values */
132         *blob = data_blob_talloc(mem_ctx, NULL, head_size + data_size);
133
134         head_ofs = 0;
135         data_ofs = head_size;
136
137         va_start(ap, format);
138         for (i=0; format[i]; i++) {
139                 switch (format[i]) {
140                 case 'U':
141                 case 'A':
142                 case 'B':
143                         n = pointers[i].length;
144                         SSVAL(blob->data, head_ofs, n); head_ofs += 2;
145                         SSVAL(blob->data, head_ofs, n); head_ofs += 2;
146                         SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
147                         if (pointers[i].data && n) /* don't follow null pointers... */
148                                 memcpy(blob->data+data_ofs, pointers[i].data, n);
149                         data_ofs += n;
150                         break;
151                 case 'a':
152                         j = intargs[i];
153                         SSVAL(blob->data, data_ofs, j); data_ofs += 2;
154
155                         n = pointers[i].length;
156                         SSVAL(blob->data, data_ofs, n); data_ofs += 2;
157                         if (n >= 0) {
158                                 memcpy(blob->data+data_ofs, pointers[i].data, n);
159                         }
160                         data_ofs += n;
161                         break;
162                 case 'd':
163                         j = intargs[i];
164                         SIVAL(blob->data, head_ofs, j); 
165                         head_ofs += 4;
166                         break;
167                 case 'b':
168                         n = pointers[i].length;
169                         memcpy(blob->data + head_ofs, pointers[i].data, n);
170                         head_ofs += n;
171                         break;
172                 case 'C':
173                         n = pointers[i].length;
174                         memcpy(blob->data + head_ofs, pointers[i].data, n);
175                         head_ofs += n;
176                         break;
177                 }
178         }
179         va_end(ap);
180         
181         talloc_free(pointers);
182
183         return true;
184 }
185
186
187 /* a helpful macro to avoid running over the end of our blob */
188 #define NEED_DATA(amount) \
189 if ((head_ofs + amount) > blob->length) { \
190         return false; \
191 }
192
193 /**
194   this is a tiny msrpc packet parser. This the the partner of msrpc_gen
195
196   format specifiers are:
197
198   U = unicode string (output is unix string)
199   A = ascii string
200   B = data blob
201   b = data blob in header
202   d = word (4 bytes)
203   C = constant ascii string
204  */
205
206 bool msrpc_parse(TALLOC_CTX *mem_ctx, 
207                  const DATA_BLOB *blob,
208                  const char *format, ...)
209 {
210         int i;
211         va_list ap;
212         char **ps, *s;
213         DATA_BLOB *b;
214         size_t head_ofs = 0;
215         uint16_t len1, len2;
216         uint32_t ptr;
217         uint32_t *v;
218         size_t p_len = 1024;
219         char *p = talloc_array(mem_ctx, char, p_len);
220         bool ret = true;
221
222         va_start(ap, format);
223         for (i=0; format[i]; i++) {
224                 switch (format[i]) {
225                 case 'U':
226                         NEED_DATA(8);
227                         len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
228                         len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
229                         ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
230
231                         ps = va_arg(ap, char **);
232                         if (len1 == 0 && len2 == 0) {
233                                 *ps = discard_const("");
234                         } else {
235                                 /* make sure its in the right format - be strict */
236                                 if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) {
237                                         ret = false;
238                                         goto cleanup;
239                                 }
240                                 if (len1 & 1) {
241                                         /* if odd length and unicode */
242                                         ret = false;
243                                         goto cleanup;
244                                 }
245                                 if (blob->data + ptr < (uint8_t *)(uintptr_t)ptr ||
246                                                 blob->data + ptr < blob->data) {
247                                         ret = false;
248                                         goto cleanup;
249                                 }
250
251                                 if (0 < len1) {
252                                         size_t pull_len;
253                                         if (!convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX, 
254                                                                    blob->data + ptr, len1, 
255                                                                    ps, &pull_len, false)) {
256                                                 ret = false;
257                                                 goto cleanup;
258                                         }
259                                 } else {
260                                         (*ps) = discard_const("");
261                                 }
262                         }
263                         break;
264                 case 'A':
265                         NEED_DATA(8);
266                         len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
267                         len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
268                         ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
269
270                         ps = (char **)va_arg(ap, char **);
271                         /* make sure its in the right format - be strict */
272                         if (len1 == 0 && len2 == 0) {
273                                 *ps = discard_const("");
274                         } else {
275                                 if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) {
276                                         ret = false;
277                                         goto cleanup;
278                                 }
279
280                                 if (blob->data + ptr < (uint8_t *)(uintptr_t)ptr ||
281                                                 blob->data + ptr < blob->data) {
282                                         ret = false;
283                                         goto cleanup;
284                                 }
285
286                                 if (0 < len1) {
287                                         size_t pull_len;
288
289                                         if (!convert_string_talloc(mem_ctx, CH_DOS, CH_UNIX, 
290                                                                    blob->data + ptr, len1, 
291                                                                    ps, &pull_len, false)) {
292                                                 ret = false;
293                                                 goto cleanup;
294                                         }
295                                 } else {
296                                         (*ps) = discard_const("");
297                                 }
298                         }
299                         break;
300                 case 'B':
301                         NEED_DATA(8);
302                         len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
303                         len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
304                         ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
305
306                         b = (DATA_BLOB *)va_arg(ap, void *);
307                         if (len1 == 0 && len2 == 0) {
308                                 *b = data_blob_talloc(mem_ctx, NULL, 0);
309                         } else {
310                                 /* make sure its in the right format - be strict */
311                                 if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) {
312                                         ret = false;
313                                         goto cleanup;
314                                 }
315
316                                 if (blob->data + ptr < (uint8_t *)(uintptr_t)ptr ||
317                                                 blob->data + ptr < blob->data) {
318                                         ret = false;
319                                         goto cleanup;
320                                 }
321
322                                 *b = data_blob_talloc(mem_ctx, blob->data + ptr, len1);
323                         }
324                         break;
325                 case 'b':
326                         b = (DATA_BLOB *)va_arg(ap, void *);
327                         len1 = va_arg(ap, uint_t);
328                         /* make sure its in the right format - be strict */
329                         NEED_DATA(len1);
330                         if (blob->data + head_ofs < (uint8_t *)head_ofs ||
331                                         blob->data + head_ofs < blob->data) {
332                                 ret = false;
333                                 goto cleanup;
334                         }
335
336                         *b = data_blob_talloc(mem_ctx, blob->data + head_ofs, len1);
337                         head_ofs += len1;
338                         break;
339                 case 'd':
340                         v = va_arg(ap, uint32_t *);
341                         NEED_DATA(4);
342                         *v = IVAL(blob->data, head_ofs); head_ofs += 4;
343                         break;
344                 case 'C':
345                         s = va_arg(ap, char *);
346
347                         if (blob->data + head_ofs < (uint8_t *)head_ofs ||
348                                         blob->data + head_ofs < blob->data ||
349                             (head_ofs + (strlen(s) + 1)) > blob->length) {
350                                 ret = false;
351                                 goto cleanup;
352                         }
353
354                         if (memcmp(blob->data + head_ofs, s, strlen(s)+1) != 0) {
355                                 ret = false;
356                                 goto cleanup;
357                         }
358                         head_ofs += (strlen(s) + 1);
359
360                         break;
361                 }
362         }
363
364 cleanup:
365         va_end(ap);
366         talloc_free(p);
367         return ret;
368 }