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