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