xref: /titanic_50/usr/src/lib/smbsrv/libmlrpc/common/ndr_ops.c (revision dc20a3024900c47dd2ee44b9707e6df38f7d62a5)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * MLRPC server-side NDR stream (PDU) operations. Stream operations
30  * should return TRUE (non-zero) on success or FALSE (zero or a null
31  * pointer) on failure. When an operation returns FALSE, including
32  * mlndo_malloc() returning NULL, it should set the mlnds->error to
33  * indicate what went wrong.
34  *
35  * When available, the relevant ndr_reference is passed to the
36  * operation but keep in mind that it may be a null pointer.
37  *
38  * Functions mlndo_get_pdu(), mlndo_put_pdu(), and mlndo_pad_pdu()
39  * must never grow the PDU data. A request for out-of-bounds data is
40  * an error. The swap_bytes flag is 1 if NDR knows that the byte-
41  * order in the PDU is different from the local system.
42  */
43 
44 #include <sys/types.h>
45 #include <stdarg.h>
46 #include <ctype.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <strings.h>
50 #include <string.h>
51 #include <assert.h>
52 
53 #include <smbsrv/libsmb.h>
54 #include <smbsrv/mlrpc.h>
55 #include <smbsrv/ndr.h>
56 #include <smbsrv/ntstatus.h>
57 
58 #define	NDOBUFSZ		128
59 
60 #define	NDR_PDU_BLOCK_SIZE	(4*1024)
61 #define	NDR_PDU_BLOCK_MASK	(NDR_PDU_BLOCK_SIZE - 1)
62 #define	NDR_PDU_ALIGN(N) \
63 	(((N) + NDR_PDU_BLOCK_SIZE) & ~NDR_PDU_BLOCK_MASK)
64 #define	NDR_PDU_MAX_SIZE		(64*1024*1024)
65 
66 static char *mlndo_malloc(struct mlndr_stream *, unsigned,
67     struct ndr_reference *);
68 static int mlndo_free(struct mlndr_stream *, char *, struct ndr_reference *);
69 static int mlndo_grow_pdu(struct mlndr_stream *, unsigned long,
70     struct ndr_reference *);
71 static int mlndo_pad_pdu(struct mlndr_stream *, unsigned long, unsigned long,
72     struct ndr_reference *);
73 static int mlndo_get_pdu(struct mlndr_stream *, unsigned long, unsigned long,
74     char *, int, struct ndr_reference *);
75 static int mlndo_put_pdu(struct mlndr_stream *, unsigned long, unsigned long,
76     char *, int, struct ndr_reference *);
77 static void mlndo_tattle(struct mlndr_stream *, char *, struct ndr_reference *);
78 static void mlndo_tattle_error(struct mlndr_stream *, struct ndr_reference *);
79 static int mlndo_reset(struct mlndr_stream *);
80 static void mlndo_destruct(struct mlndr_stream *);
81 static void mlndo_hexfmt(uint8_t *, int, int, char *, int);
82 
83 /*
84  * The mlndr stream operations table.
85  */
86 static struct mlndr_stream_ops mlnds_ops = {
87     mlndo_malloc,
88     mlndo_free,
89     mlndo_grow_pdu,
90     mlndo_pad_pdu,
91     mlndo_get_pdu,
92     mlndo_put_pdu,
93     mlndo_tattle,
94     mlndo_tattle_error,
95     mlndo_reset,
96     mlndo_destruct
97 };
98 
99 /*
100  * mlnds_bswap
101  *
102  * Copies len bytes from src to dst such that dst contains the bytes
103  * from src in reverse order.
104  *
105  * We expect to be dealing with bytes, words, dwords etc. So the
106  * length must be non-zero and a power of 2.
107  */
108 void
109 mlnds_bswap(void *srcbuf, void *dstbuf, size_t len)
110 {
111 	uint8_t *src = (uint8_t *)srcbuf;
112 	uint8_t *dst = (uint8_t *)dstbuf;
113 
114 	if ((len != 0) && ((len & (len - 1)) == 0)) {
115 		src += len;
116 
117 		while (len--)
118 			*dst++ = *(--src);
119 	}
120 }
121 
122 /*
123  * mlnds_initialize
124  *
125  * Initialize a stream. Sets up the PDU parameters and assigns the stream
126  * operations and the reference to the heap. An external heap is provided
127  * to the stream, rather than each stream creating its own heap.
128  */
129 int
130 mlnds_initialize(struct mlndr_stream *mlnds, unsigned pdu_size_hint,
131     int composite_op, mlrpc_heap_t *heap)
132 {
133 	unsigned size;
134 
135 	assert(mlnds);
136 	assert(heap);
137 
138 	bzero(mlnds, sizeof (*mlnds));
139 
140 	if (pdu_size_hint > NDR_PDU_MAX_SIZE)
141 		return (0);
142 
143 	size = (pdu_size_hint == 0) ? NDR_PDU_BLOCK_SIZE : pdu_size_hint;
144 	mlnds->pdu_base_addr = malloc(size);
145 	assert(mlnds->pdu_base_addr);
146 
147 	mlnds->pdu_max_size = size;
148 	mlnds->pdu_size = 0;
149 	mlnds->pdu_base_offset = (unsigned long)mlnds->pdu_base_addr;
150 
151 	mlnds->mlndo = &mlnds_ops;
152 	mlnds->heap = (struct mlrpc_heap *)heap;
153 
154 	mlnds->m_op = composite_op & 0x0F;
155 	mlnds->dir  = composite_op & 0xF0;
156 
157 	mlnds->outer_queue_tailp = &mlnds->outer_queue_head;
158 	return (1);
159 }
160 
161 /*
162  * mlnds_destruct
163  *
164  * Destroy a stream. This is an external interface to provide access to
165  * the stream's destruct operation.
166  */
167 void
168 mlnds_destruct(struct mlndr_stream *mlnds)
169 {
170 	MLNDS_DESTRUCT(mlnds);
171 }
172 
173 /*
174  * mlndo_malloc
175  *
176  * Allocate memory from the stream heap.
177  */
178 /*ARGSUSED*/
179 static char *
180 mlndo_malloc(struct mlndr_stream *mlnds, unsigned len,
181     struct ndr_reference *ref)
182 {
183 	return (mlrpc_heap_malloc((mlrpc_heap_t *)mlnds->heap, len));
184 }
185 
186 /*
187  * mlndo_free
188  *
189  * Always succeeds: cannot free individual stream allocations.
190  */
191 /*ARGSUSED*/
192 static int
193 mlndo_free(struct mlndr_stream *mlnds, char *p, struct ndr_reference *ref)
194 {
195 	return (1);
196 }
197 
198 /*
199  * mlndo_grow_pdu
200  *
201  * This is the only place that should change the size of the PDU. If the
202  * desired offset is beyond the current PDU size, we realloc the PDU
203  * buffer to accommodate the request. For efficiency, the PDU is always
204  * extended to a NDR_PDU_BLOCK_SIZE boundary. Requests to grow the PDU
205  * beyond NDR_PDU_MAX_SIZE are rejected.
206  *
207  * Returns 1 to indicate success. Otherwise 0 to indicate failure.
208  */
209 static int
210 mlndo_grow_pdu(struct mlndr_stream *mlnds, unsigned long want_end_offset,
211     struct ndr_reference *ref)
212 {
213 	unsigned char *pdu_addr;
214 	unsigned pdu_max_size;
215 
216 	mlndo_printf(mlnds, ref, "grow %d", want_end_offset);
217 
218 	pdu_max_size = mlnds->pdu_max_size;
219 
220 	if (want_end_offset > pdu_max_size) {
221 		pdu_max_size = NDR_PDU_ALIGN(want_end_offset);
222 
223 		if (pdu_max_size >= NDR_PDU_MAX_SIZE)
224 			return (0);
225 
226 		pdu_addr = realloc(mlnds->pdu_base_addr, pdu_max_size);
227 		if (pdu_addr == 0)
228 			return (0);
229 
230 		mlnds->pdu_max_size = pdu_max_size;
231 		mlnds->pdu_base_addr = pdu_addr;
232 		mlnds->pdu_base_offset = (unsigned long)pdu_addr;
233 	}
234 
235 	mlnds->pdu_size = want_end_offset;
236 	return (1);
237 }
238 
239 static int
240 mlndo_pad_pdu(struct mlndr_stream *mlnds, unsigned long pdu_offset,
241     unsigned long n_bytes, struct ndr_reference *ref)
242 {
243 	unsigned char *data;
244 
245 	data = (unsigned char *)mlnds->pdu_base_offset;
246 	data += pdu_offset;
247 
248 	mlndo_printf(mlnds, ref, "pad %d@%-3d", n_bytes, pdu_offset);
249 
250 	bzero(data, n_bytes);
251 	return (1);
252 }
253 
254 /*
255  * mlndo_get_pdu
256  *
257  * The swap flag is 1 if NDR knows that the byte-order in the PDU
258  * is different from the local system.
259  *
260  * Returns 1 on success or 0 to indicate failure.
261  */
262 static int
263 mlndo_get_pdu(struct mlndr_stream *mlnds, unsigned long pdu_offset,
264     unsigned long n_bytes, char *buf, int swap_bytes,
265     struct ndr_reference *ref)
266 {
267 	unsigned char *data;
268 	char hexbuf[NDOBUFSZ];
269 
270 	data = (unsigned char *)mlnds->pdu_base_offset;
271 	data += pdu_offset;
272 
273 	mlndo_hexfmt(data, n_bytes, swap_bytes, hexbuf, NDOBUFSZ);
274 
275 	mlndo_printf(mlnds, ref, "get %d@%-3d = %s",
276 	    n_bytes, pdu_offset, hexbuf);
277 
278 	if (!swap_bytes)
279 		bcopy(data, buf, n_bytes);
280 	else
281 		mlnds_bswap(data, (unsigned char *)buf, n_bytes);
282 
283 	return (1);
284 }
285 
286 /*
287  * mlndo_put_pdu
288  *
289  * This is a receiver makes right protocol. So we do not need
290  * to be concerned about the byte-order of an outgoing PDU.
291  */
292 /*ARGSUSED*/
293 static int
294 mlndo_put_pdu(struct mlndr_stream *mlnds, unsigned long pdu_offset,
295     unsigned long n_bytes, char *buf, int swap_bytes,
296     struct ndr_reference *ref)
297 {
298 	unsigned char *data;
299 	char hexbuf[NDOBUFSZ];
300 
301 	data = (unsigned char *)mlnds->pdu_base_offset;
302 	data += pdu_offset;
303 
304 	mlndo_hexfmt((uint8_t *)buf, n_bytes, 0, hexbuf, NDOBUFSZ);
305 
306 	mlndo_printf(mlnds, ref, "put %d@%-3d = %s",
307 	    n_bytes, pdu_offset, hexbuf);
308 
309 	bcopy(buf, data, n_bytes);
310 	return (1);
311 }
312 
313 static void
314 mlndo_tattle(struct mlndr_stream *mlnds, char *what,
315     struct ndr_reference *ref)
316 {
317 	mlndo_printf(mlnds, ref, what);
318 }
319 
320 static void
321 mlndo_tattle_error(struct mlndr_stream *mlnds, struct ndr_reference *ref)
322 {
323 	unsigned char *data;
324 	char hexbuf[NDOBUFSZ];
325 
326 	data = (unsigned char *)mlnds->pdu_base_offset;
327 	if (ref)
328 		data += ref->pdu_offset;
329 	else
330 		data += mlnds->pdu_scan_offset;
331 
332 	mlndo_hexfmt(data, 16, 0, hexbuf, NDOBUFSZ);
333 
334 	mlndo_printf(mlnds, ref, "ERROR=%d REF=%d OFFSET=%d SIZE=%d/%d",
335 	    mlnds->error, mlnds->error_ref, mlnds->pdu_scan_offset,
336 	    mlnds->pdu_size, mlnds->pdu_max_size);
337 	mlndo_printf(mlnds, ref, "      %s", hexbuf);
338 }
339 
340 /*
341  * mlndo_reset
342  *
343  * Reset a stream: zap the outer_queue. We don't need to tamper
344  * with the stream heap: it's handled externally to the stream.
345  */
346 static int
347 mlndo_reset(struct mlndr_stream *mlnds)
348 {
349 	mlndo_printf(mlnds, 0, "reset");
350 
351 	mlnds->pdu_size = 0;
352 	mlnds->pdu_scan_offset = 0;
353 	mlnds->outer_queue_head = 0;
354 	mlnds->outer_current = 0;
355 	mlnds->outer_queue_tailp = &mlnds->outer_queue_head;
356 
357 	return (1);
358 }
359 
360 /*
361  * mlndo_destruct
362  *
363  * Destruct a stream: zap the outer_queue. Currently, this operation isn't
364  * required because the heap management (creation/destruction) is external
365  * to the stream but it provides a place-holder for stream cleanup.
366  */
367 static void
368 mlndo_destruct(struct mlndr_stream *mlnds)
369 {
370 	mlndo_printf(mlnds, 0, "destruct");
371 
372 	if (mlnds->pdu_base_addr != 0) {
373 		free(mlnds->pdu_base_addr);
374 		mlnds->pdu_base_addr = 0;
375 		mlnds->pdu_base_offset = 0;
376 	}
377 
378 	mlnds->outer_queue_head = 0;
379 	mlnds->outer_current = 0;
380 	mlnds->outer_queue_tailp = &mlnds->outer_queue_head;
381 }
382 
383 /*
384  * Printf style formatting for NDR operations.
385  */
386 void
387 mlndo_printf(struct mlndr_stream *mlnds, struct ndr_reference *ref,
388     const char *fmt, ...)
389 {
390 	va_list ap;
391 	char buf[NDOBUFSZ];
392 
393 	va_start(ap, fmt);
394 	(void) vsnprintf(buf, NDOBUFSZ, fmt, ap);
395 	va_end(ap);
396 
397 	if (mlnds)
398 		mlndo_fmt(mlnds, ref, buf);
399 	else
400 		mlndo_trace(buf);
401 }
402 
403 /*
404  * Main output formatter for NDR operations.
405  *
406  *	UI 03 ... rpc_vers           get 1@0   =    5 {05}
407  *	UI 03 ... rpc_vers_minor     get 1@1   =    0 {00}
408  *
409  *	U       Marshalling flag (M=marshal, U=unmarshal)
410  *	I       Direction flag (I=in, O=out)
411  *	...     Field name
412  *	get     PDU operation (get or put)
413  *	1@0	Bytes @ offset (i.e. 1 byte at offset 0)
414  *	{05}    Value
415  */
416 void
417 mlndo_fmt(struct mlndr_stream *mlnds, struct ndr_reference *ref, char *note)
418 {
419 	struct ndr_reference *p;
420 	int			indent;
421 	char			ref_name[NDOBUFSZ];
422 	char			buf[NDOBUFSZ];
423 	int			m_op_c = '?', dir_c = '?';
424 
425 	switch (mlnds->m_op) {
426 	case 0:				m_op_c = '-';	break;
427 	case NDR_M_OP_MARSHALL:		m_op_c = 'M';	break;
428 	case NDR_M_OP_UNMARSHALL:	m_op_c = 'U';	break;
429 	default:			m_op_c = '?';	break;
430 	}
431 
432 	switch (mlnds->dir) {
433 	case 0:				dir_c = '-';	break;
434 	case NDR_DIR_IN:		dir_c = 'I';	break;
435 	case NDR_DIR_OUT:		dir_c = 'O';	break;
436 	default:			dir_c = '?';	break;
437 	}
438 
439 	for (indent = 0, p = ref; p; p = p->enclosing)
440 		indent++;
441 
442 	if (ref && ref->name) {
443 		if (*ref->name == '[' && ref->enclosing) {
444 			indent--;
445 			(void) snprintf(ref_name, NDOBUFSZ, "%s%s",
446 			    ref->enclosing->name, ref->name);
447 		} else {
448 			(void) strlcpy(ref_name, ref->name, NDOBUFSZ);
449 		}
450 	} else {
451 		(void) strlcpy(ref_name, "----", NDOBUFSZ);
452 	}
453 
454 	(void) snprintf(buf, NDOBUFSZ, "%c%c %02d %-.*s %-*s  %s",
455 	    m_op_c, dir_c, indent, indent,
456 	    "....+....+....+....+....+....",
457 	    20 - indent, ref_name, note);
458 
459 	mlndo_trace(buf);
460 }
461 
462 /*ARGSUSED*/
463 void
464 mlndo_trace(const char *s)
465 {
466 	/*
467 	 * Temporary fbt for dtrace until user space sdt enabled.
468 	 */
469 }
470 
471 /*
472  * Format data as hex bytes (limit is 10 bytes):
473  *
474  *	1188689424 {10 f6 d9 46}
475  *
476  * If the input data is greater than 10 bytes, an ellipsis will
477  * be inserted before the closing brace.
478  */
479 static void
480 mlndo_hexfmt(uint8_t *data, int size, int swap_bytes, char *buf, int len)
481 {
482 	char *p = buf;
483 	int interp = 1;
484 	uint32_t c;
485 	int n;
486 	int i;
487 
488 	n = (size > 10) ? 10 : size;
489 	if (n > len-1)
490 		n = len-1;
491 
492 	switch (size) {
493 	case 1:
494 		c = *(uint8_t *)data;
495 		break;
496 	case 2:
497 		if (swap_bytes == 0) /*LINTED E_BAD_PTR_CAST_ALIGN*/
498 			c = *(uint16_t *)data;
499 		else
500 			c = (data[0] << 8) | data[1];
501 		break;
502 	case 4:
503 		if (swap_bytes == 0) { /*LINTED E_BAD_PTR_CAST_ALIGN*/
504 			c = *(uint32_t *)data;
505 		} else {
506 			c = (data[0] << 24) | (data[1] << 16)
507 			    | (data[2] << 8) | data[3];
508 		}
509 		break;
510 	default:
511 		c = 0;
512 		interp = 0;
513 		break;
514 	}
515 
516 	if (interp)
517 		p += sprintf(p, "%4u {", c);
518 	else
519 		p += sprintf(p, " {");
520 
521 	p += sprintf(p, "%02x", data[0]);
522 	for (i = 1; i < n; i++)
523 		p += sprintf(p, " %02x", data[i]);
524 	if (size > 10)
525 		p += sprintf(p, " ...}");
526 	else
527 		p += sprintf(p, "}");
528 
529 	/*
530 	 * Show c if it's a printable character or wide-char.
531 	 */
532 	if (size < 4 && isprint((uint8_t)c))
533 		(void) sprintf(p, " %c", (uint8_t)c);
534 }
535