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