sync 3.0 into HEAD for the last time
[samba.git] / source / libsmb / 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 2 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, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.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 BOOL unicode, 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(DATA_BLOB *blob,
44                const char *format, ...)
45 {
46         int i, n;
47         va_list ap;
48         char *s;
49         uint8 *b;
50         int head_size=0, data_size=0;
51         int head_ofs, data_ofs;
52         BOOL unicode;
53
54         /* first scan the format to work out the header and body size */
55         va_start(ap, format);
56         for (i=0; format[i]; i++) {
57                 switch (format[i]) {
58                 case 'U':
59                         s = va_arg(ap, char *);
60                         head_size += 8;
61                         data_size += str_charnum(s) * 2;
62                         break;
63                 case 'A':
64                         s = va_arg(ap, char *);
65                         head_size += 8;
66                         data_size += str_ascii_charnum(s);
67                         break;
68                 case 'a':
69                         unicode = va_arg(ap, BOOL);
70                         n = va_arg(ap, int);
71                         s = va_arg(ap, char *);
72                         if (unicode) {
73                                 data_size += (str_charnum(s) * 2) + 4;
74                         } else {
75                                 data_size += (str_ascii_charnum(s)) + 4;
76                         }
77                         break;
78                 case 'B':
79                         b = va_arg(ap, uint8 *);
80                         head_size += 8;
81                         data_size += va_arg(ap, int);
82                         break;
83                 case 'b':
84                         b = va_arg(ap, uint8 *);
85                         head_size += va_arg(ap, int);
86                         break;
87                 case 'd':
88                         n = va_arg(ap, int);
89                         head_size += 4;
90                         break;
91                 case 'C':
92                         s = va_arg(ap, char *);
93                         head_size += str_charnum(s) + 1;
94                         break;
95                 }
96         }
97         va_end(ap);
98
99         /* allocate the space, then scan the format again to fill in the values */
100         *blob = data_blob(NULL, head_size + data_size);
101
102         head_ofs = 0;
103         data_ofs = head_size;
104
105         va_start(ap, format);
106         for (i=0; format[i]; i++) {
107                 switch (format[i]) {
108                 case 'U':
109                         s = va_arg(ap, char *);
110                         n = str_charnum(s);
111                         SSVAL(blob->data, head_ofs, n*2); head_ofs += 2;
112                         SSVAL(blob->data, head_ofs, n*2); head_ofs += 2;
113                         SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
114                         push_string(NULL, blob->data+data_ofs, s, n*2, STR_UNICODE|STR_NOALIGN);
115                         data_ofs += n*2;
116                         break;
117                 case 'A':
118                         s = va_arg(ap, char *);
119                         n = str_ascii_charnum(s);
120                         SSVAL(blob->data, head_ofs, n); head_ofs += 2;
121                         SSVAL(blob->data, head_ofs, n); head_ofs += 2;
122                         SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
123                         push_string(NULL, blob->data+data_ofs, s, n, STR_ASCII|STR_NOALIGN);
124                         data_ofs += n;
125                         break;
126                 case 'a':
127                         unicode = va_arg(ap, BOOL);
128                         n = va_arg(ap, int);
129                         SSVAL(blob->data, data_ofs, n); data_ofs += 2;
130                         s = va_arg(ap, char *);
131                         if (unicode) {
132                                 n = str_charnum(s);
133                                 SSVAL(blob->data, data_ofs, n*2); data_ofs += 2;
134                                 if (0 < n) {
135                                         push_string(NULL, blob->data+data_ofs, s, n*2,
136                                                     STR_UNICODE|STR_NOALIGN);
137                                 }
138                                 data_ofs += n*2;
139                         } else {
140                                 n = str_ascii_charnum(s);
141                                 SSVAL(blob->data, data_ofs, n); data_ofs += 2;
142                                 if (0 < n) {
143                                         push_string(NULL, blob->data+data_ofs, s, n,
144                                                     STR_ASCII|STR_NOALIGN);
145                                 }
146                                 data_ofs += n;
147                         }
148                         break;
149
150                 case 'B':
151                         b = va_arg(ap, uint8 *);
152                         n = va_arg(ap, int);
153                         SSVAL(blob->data, head_ofs, n); head_ofs += 2;
154                         SSVAL(blob->data, head_ofs, n); head_ofs += 2;
155                         SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
156                         if (n && b) /* don't follow null pointers... */
157                                 memcpy(blob->data+data_ofs, b, n);
158                         data_ofs += n;
159                         break;
160                 case 'd':
161                         n = va_arg(ap, int);
162                         SIVAL(blob->data, head_ofs, n); head_ofs += 4;
163                         break;
164                 case 'b':
165                         b = va_arg(ap, uint8 *);
166                         n = va_arg(ap, int);
167                         memcpy(blob->data + head_ofs, b, n);
168                         head_ofs += n;
169                         break;
170                 case 'C':
171                         s = va_arg(ap, char *);
172                         head_ofs += push_string(NULL, blob->data+head_ofs, s, -1, 
173                                                 STR_ASCII|STR_TERMINATE);
174                         break;
175                 }
176         }
177         va_end(ap);
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(const DATA_BLOB *blob,
203                  const char *format, ...)
204 {
205         int i;
206         va_list ap;
207         char **ps, *s;
208         DATA_BLOB *b;
209         size_t head_ofs = 0;
210         uint16 len1, len2;
211         uint32 ptr;
212         uint32 *v;
213         pstring p;
214
215         va_start(ap, format);
216         for (i=0; format[i]; i++) {
217                 switch (format[i]) {
218                 case 'U':
219                         NEED_DATA(8);
220                         len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
221                         len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
222                         ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
223
224                         ps = va_arg(ap, char **);
225                         if (len1 == 0 && len2 == 0) {
226                                 *ps = smb_xstrdup("");
227                         } else {
228                                 /* make sure its in the right format - be strict */
229                                 if (len1 != len2 || ptr + len1 > blob->length) {
230                                         return False;
231                                 }
232                                 if (len1 & 1) {
233                                         /* if odd length and unicode */
234                                         return False;
235                                 }
236                                 
237                                 if (0 < len1) {
238                                         pull_string(NULL, p, blob->data + ptr, sizeof(p), 
239                                                     len1, 
240                                                     STR_UNICODE|STR_NOALIGN);
241                                         (*ps) = smb_xstrdup(p);
242                                 } else {
243                                         (*ps) = smb_xstrdup("");
244                                 }
245                         }
246                         break;
247                 case 'A':
248                         NEED_DATA(8);
249                         len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
250                         len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
251                         ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
252
253                         ps = va_arg(ap, char **);
254                         /* make sure its in the right format - be strict */
255                         if (len1 == 0 && len2 == 0) {
256                                 *ps = smb_xstrdup("");
257                         } else {
258                                 if (len1 != len2 || ptr + len1 > blob->length) {
259                                         return False;
260                                 }
261                                 
262                                 if (0 < len1) {
263                                         pull_string(NULL, p, blob->data + ptr, sizeof(p), 
264                                                     len1, 
265                                                     STR_ASCII|STR_NOALIGN);
266                                         (*ps) = smb_xstrdup(p);
267                                 } else {
268                                         (*ps) = smb_xstrdup("");
269                                 }
270                         }
271                         break;
272                 case 'B':
273                         NEED_DATA(8);
274                         len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
275                         len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
276                         ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
277
278                         b = (DATA_BLOB *)va_arg(ap, void *);
279                         if (len1 == 0 && len2 == 0) {
280                                 *b = data_blob(NULL, 0);
281                         } else {
282                                 /* make sure its in the right format - be strict */
283                                 if (len1 != len2 || ptr + len1 > blob->length) {
284                                         return False;
285                                 }
286                                 *b = data_blob(blob->data + ptr, len1);
287                         }
288                         break;
289                 case 'b':
290                         b = (DATA_BLOB *)va_arg(ap, void *);
291                         len1 = va_arg(ap, unsigned);
292                         /* make sure its in the right format - be strict */
293                         NEED_DATA(len1);
294                         *b = data_blob(blob->data + head_ofs, len1);
295                         head_ofs += len1;
296                         break;
297                 case 'd':
298                         v = va_arg(ap, uint32 *);
299                         NEED_DATA(4);
300                         *v = IVAL(blob->data, head_ofs); head_ofs += 4;
301                         break;
302                 case 'C':
303                         s = va_arg(ap, char *);
304                         head_ofs += pull_string(NULL, p, blob->data+head_ofs, sizeof(p), 
305                                                 blob->length - head_ofs, 
306                                                 STR_ASCII|STR_TERMINATE);
307                         if (strcmp(s, p) != 0) {
308                                 return False;
309                         }
310                         break;
311                 }
312         }
313         va_end(ap);
314
315         return True;
316 }
317