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 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright 2012 Milan Jurik. All rights reserved.
25 */
26
27 /*
28 * Network Data Representation (NDR) is a compatible subset of the DCE RPC
29 * and MSRPC NDR. NDR is used to move parameters consisting of
30 * complicated trees of data constructs between an RPC client and server.
31 */
32
33 #include <sys/byteorder.h>
34 #include <strings.h>
35 #include <assert.h>
36 #include <string.h>
37 #include <stdlib.h>
38
39 #include <smbsrv/libsmb.h>
40 #include <smbsrv/string.h>
41 #include <smbsrv/libmlrpc.h>
42
43 #define NDR_STRING_MAX 4096
44
45 #define NDR_IS_UNION(T) \
46 (((T)->type_flags & NDR_F_TYPEOP_MASK) == NDR_F_UNION)
47 #define NDR_IS_STRING(T) \
48 (((T)->type_flags & NDR_F_TYPEOP_MASK) == NDR_F_STRING)
49
50 extern ndr_typeinfo_t ndt_s_wchar;
51
52 /*
53 * The following synopsis describes the terms TOP-MOST, OUTER and INNER.
54 *
55 * Each parameter (call arguments and return values) is a TOP-MOST item.
56 * A TOP-MOST item consists of one or more OUTER items. An OUTER item
57 * consists of one or more INNER items. There are important differences
58 * between each kind, which, primarily, have to do with the allocation
59 * of memory to contain data structures and the order of processing.
60 *
61 * This is most easily demonstrated with a short example.
62 * Consider these structures:
63 *
64 * struct top_param {
65 * long level;
66 * struct list * head;
67 * long count;
68 * };
69 *
70 * struct list {
71 * struct list * next;
72 * char * str; // a string
73 * };
74 *
75 * Now, consider an instance tree like this:
76 *
77 * +---------+ +-------+ +-------+
78 * |top_param| +--->|list #1| +--->|list #2|
79 * +---------+ | +-------+ | +-------+
80 * | level | | | next ----+ | next --->(NULL)
81 * | head ----+ | str -->"foo" | str -->"bar"
82 * | count | | flag | | flag |
83 * +---------+ +-------+ +-------+
84 *
85 * The DCE(MS)/RPC Stub Data encoding for the tree is the following.
86 * The vertical bars (|) indicate OUTER construct boundaries.
87 *
88 * +-----+----------------------+----------------------+-----+-----+-----+
89 * |level|#1.next #1.str #1.flag|#2.next #2.str #2.flag|"bar"|"foo"|count|
90 * +-----+----------------------+----------------------+-----+-----+-----+
91 * level |<----------------------- head -------------------------->|count
92 * TOP TOP TOP
93 *
94 * Here's what to notice:
95 *
96 * - The members of the TOP-MOST construct are scattered through the Stub
97 * Data in the order they occur. This example shows a TOP-MOST construct
98 * consisting of atomic types (pointers and integers). A construct
99 * (struct) within the TOP-MOST construct would be contiguous and not
100 * scattered.
101 *
102 * - The members of OUTER constructs are contiguous, which allows for
103 * non-copied relocated (fixed-up) data structures at the packet's
104 * destination. We don't do fix-ups here. The pointers within the
105 * OUTER constructs are processed depth-first in the order that they
106 * occur. If they were processed breadth first, the sequence would
107 * be #1,"foo",#2,"bar". This is tricky because OUTER constructs may
108 * be variable length, and pointers are often encountered before the
109 * size(s) is known.
110 *
111 * - The INNER constructs are simply the members of an OUTER construct.
112 *
113 * For comparison, consider how ONC RPC would handle the same tree of
114 * data. ONC requires very little buffering, while DCE requires enough
115 * buffer space for the entire message. ONC does atom-by-atom depth-first
116 * (de)serialization and copy, while DCE allows for constructs to be
117 * "fixed-up" (relocated) in place at the destination. The packet data
118 * for the same tree processed by ONC RPC would look like this:
119 *
120 * +---------------------------------------------------------------------+
121 * |level #1.next #2.next #2.str "bar" #2.flag #1.str "foo" #1.flag count|
122 * +---------------------------------------------------------------------+
123 * TOP #1 #2 #2 bar #2 #1 foo #1 TOP
124 *
125 * More details about each TOP-MOST, OUTER, and INNER constructs appear
126 * throughout this source file near where such constructs are processed.
127 *
128 * NDR_REFERENCE
129 *
130 * The primary object for NDR is the ndr_ref_t.
131 *
132 * An ndr reference indicates the local datum (i.e. native "C" data
133 * format), and the element within the Stub Data (contained within the
134 * RPC PDU (protocol data unit). An ndr reference also indicates,
135 * largely as a debugging aid, something about the type of the
136 * element/datum, and the enclosing construct for the element. The
137 * ndr reference's are typically allocated on the stack as locals,
138 * and the chain of ndr-reference.enclosing references is in reverse
139 * order of the call graph.
140 *
141 * The ndr-reference.datum is a pointer to the local memory that
142 * contains/receives the value. The ndr-reference.pdu_offset indicates
143 * where in the Stub Data the value is to be stored/retrieved.
144 *
145 * The ndr-reference also contains various parameters to the NDR
146 * process, such as ndr-reference.size_is, which indicates the size
147 * of variable length data, or ndr-reference.switch_is, which
148 * indicates the arm of a union to use.
149 *
150 * QUEUE OF OUTER REFERENCES
151 *
152 * Some OUTER constructs are variable size. Sometimes (often) we don't
153 * know the size of the OUTER construct until after pointers have been
154 * encountered. Hence, we can not begin processing the referent of the
155 * pointer until after the referring OUTER construct is completely
156 * processed, i.e. we don't know where to find/put the referent in the
157 * Stub Data until we know the size of all its predecessors.
158 *
159 * This is managed using the queue of OUTER references. The queue is
160 * anchored in ndr_stream.outer_queue_head. At any time,
161 * ndr_stream.outer_queue_tailp indicates where to put the
162 * ndr-reference for the next encountered pointer.
163 *
164 * Refer to the example above as we illustrate the queue here. In these
165 * illustrations, the queue entries are not the data structures themselves.
166 * Rather, they are ndr-reference entries which **refer** to the data
167 * structures in both the PDU and local memory.
168 *
169 * During some point in the processing, the queue looks like this:
170 *
171 * outer_current -------v
172 * outer_queue_head --> list#1 --0
173 * outer_queue_tailp ---------&
174 *
175 * When the pointer #1.next is encountered, and entry is added to the
176 * queue,
177 *
178 * outer_current -------v
179 * outer_queue_head --> list#1 --> list#2 --0
180 * outer_queue_tailp --------------------&
181 *
182 * and the members of #1 continue to be processed, which encounters
183 * #1.str:
184 *
185 * outer_current -------v
186 * outer_queue_head --> list#1 --> list#2 --> "foo" --0
187 * outer_queue_tailp ------------------------------&
188 *
189 * Upon the completion of list#1, the processing continues by moving to
190 * ndr_stream.outer_current->next, and the tail is set to this outer member:
191 *
192 * outer_current ------------------v
193 * outer_queue_head --> list#1 --> list#2 --> "foo" --0
194 * outer_queue_tailp --------------------&
195 *
196 * Space for list#2 is allocated, either in the Stub Data or of local
197 * memory. When #2.next is encountered, it is found to be the null
198 * pointer and no reference is added to the queue. When #2.str is
199 * encountered, it is found to be valid, and a reference is added:
200 *
201 * outer_current ------------------v
202 * outer_queue_head --> list#1 --> list#2 --> "bar" --> "foo" --0
203 * outer_queue_tailp ------------------------------&
204 *
205 * Processing continues in a similar fashion with the string "bar",
206 * which is variable-length. At this point, memory for "bar" may be
207 * malloc()ed during NDR_M_OP_UNMARSHALL:
208 *
209 * outer_current -----------------------------v
210 * outer_queue_head --> list#1 --> list#2 --> "bar" --> "foo" --0
211 * outer_queue_tailp ------------------------------&
212 *
213 * And finishes on string "foo". Notice that because "bar" is a
214 * variable length string, and we don't know the PDU offset for "foo"
215 * until we reach this point.
216 *
217 * When the queue is drained (current->next==0), processing continues
218 * with the next TOP-MOST member.
219 *
220 * The queue of OUTER constructs manages the variable-length semantics
221 * of OUTER constructs and satisfies the depth-first requirement.
222 * We allow the queue to linger until the entire TOP-MOST structure is
223 * processed as an aid to debugging.
224 */
225
226 static ndr_ref_t *ndr_enter_outer_queue(ndr_ref_t *);
227 extern int ndr__ulong(ndr_ref_t *);
228
229 /*
230 * TOP-MOST ELEMENTS
231 *
232 * This is fundamentally the first OUTER construct of the parameter,
233 * possibly followed by more OUTER constructs due to pointers. The
234 * datum (local memory) for TOP-MOST constructs (structs) is allocated
235 * by the caller of NDR.
236 *
237 * After the element is transferred, the outer_queue is drained.
238 *
239 * All we have to do is add an entry to the outer_queue for this
240 * top-most member, and commence the outer_queue processing.
241 */
242 int
ndo_process(ndr_stream_t * nds,ndr_typeinfo_t * ti,char * datum)243 ndo_process(ndr_stream_t *nds, ndr_typeinfo_t *ti, char *datum)
244 {
245 ndr_ref_t myref;
246
247 bzero(&myref, sizeof (myref));
248 myref.stream = nds;
249 myref.datum = datum;
250 myref.name = "PROCESS";
251 myref.ti = ti;
252
253 return (ndr_topmost(&myref));
254 }
255
256 int
ndo_operation(ndr_stream_t * nds,ndr_typeinfo_t * ti,int opnum,char * datum)257 ndo_operation(ndr_stream_t *nds, ndr_typeinfo_t *ti, int opnum, char *datum)
258 {
259 ndr_ref_t myref;
260
261 bzero(&myref, sizeof (myref));
262 myref.stream = nds;
263 myref.datum = datum;
264 myref.name = "OPERATION";
265 myref.ti = ti;
266 myref.inner_flags = NDR_F_SWITCH_IS;
267 myref.switch_is = opnum;
268
269 if (ti->type_flags != NDR_F_INTERFACE) {
270 NDR_SET_ERROR(&myref, NDR_ERR_NOT_AN_INTERFACE);
271 return (0);
272 }
273
274 return ((*ti->ndr_func)(&myref));
275 }
276
277 int
ndr_params(ndr_ref_t * params_ref)278 ndr_params(ndr_ref_t *params_ref)
279 {
280 ndr_typeinfo_t *ti = params_ref->ti;
281
282 if (ti->type_flags == NDR_F_OPERATION)
283 return (*ti->ndr_func) (params_ref);
284 else
285 return (ndr_topmost(params_ref));
286 }
287
288 int
ndr_topmost(ndr_ref_t * top_ref)289 ndr_topmost(ndr_ref_t *top_ref)
290 {
291 ndr_stream_t *nds;
292 ndr_typeinfo_t *ti;
293 ndr_ref_t *outer_ref = 0;
294 int is_varlen;
295 int is_string;
296 int error;
297 int rc;
298 unsigned n_fixed;
299 int params;
300
301 assert(top_ref);
302 assert(top_ref->stream);
303 assert(top_ref->ti);
304
305 nds = top_ref->stream;
306 ti = top_ref->ti;
307
308 is_varlen = ti->pdu_size_variable_part;
309 is_string = NDR_IS_STRING(ti);
310
311 assert(nds->outer_queue_tailp && !*nds->outer_queue_tailp);
312 assert(!nds->outer_current);
313
314 params = top_ref->inner_flags & NDR_F_PARAMS_MASK;
315
316 switch (params) {
317 case NDR_F_NONE:
318 case NDR_F_SWITCH_IS:
319 if (is_string || is_varlen) {
320 error = NDR_ERR_TOPMOST_VARLEN_ILLEGAL;
321 NDR_SET_ERROR(outer_ref, error);
322 return (0);
323 }
324 n_fixed = ti->pdu_size_fixed_part;
325 break;
326
327 case NDR_F_SIZE_IS:
328 error = NDR_ERR_TOPMOST_VARLEN_ILLEGAL;
329 NDR_SET_ERROR(outer_ref, error);
330 return (0);
331
332 case NDR_F_DIMENSION_IS:
333 if (is_varlen) {
334 error = NDR_ERR_ARRAY_VARLEN_ILLEGAL;
335 NDR_SET_ERROR(outer_ref, error);
336 return (0);
337 }
338 n_fixed = ti->pdu_size_fixed_part * top_ref->dimension_is;
339 break;
340
341 case NDR_F_IS_POINTER:
342 case NDR_F_IS_POINTER+NDR_F_SIZE_IS:
343 n_fixed = 4;
344 break;
345
346 case NDR_F_IS_REFERENCE:
347 case NDR_F_IS_REFERENCE+NDR_F_SIZE_IS:
348 n_fixed = 0;
349 break;
350
351 default:
352 error = NDR_ERR_OUTER_PARAMS_BAD;
353 NDR_SET_ERROR(outer_ref, error);
354 return (0);
355 }
356
357 outer_ref = ndr_enter_outer_queue(top_ref);
358 if (!outer_ref)
359 return (0); /* error already set */
360
361 /*
362 * Hand-craft the first OUTER construct and directly call
363 * ndr_inner(). Then, run the outer_queue. We do this
364 * because ndr_outer() wants to malloc() memory for
365 * the construct, and we already have the memory.
366 */
367
368 /* move the flags, etc, around again, undoes enter_outer_queue() */
369 outer_ref->inner_flags = top_ref->inner_flags;
370 outer_ref->outer_flags = 0;
371 outer_ref->datum = top_ref->datum;
372
373 /* All outer constructs start on a mod4 (longword) boundary */
374 if (!ndr_outer_align(outer_ref))
375 return (0); /* error already set */
376
377 /* Regardless of what it is, this is where it starts */
378 outer_ref->pdu_offset = nds->pdu_scan_offset;
379
380 rc = ndr_outer_grow(outer_ref, n_fixed);
381 if (!rc)
382 return (0); /* error already set */
383
384 outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_fixed;
385
386 /* set-up outer_current, as though run_outer_queue() was doing it */
387 nds->outer_current = outer_ref;
388 nds->outer_queue_tailp = &nds->outer_current->next;
389 nds->pdu_scan_offset = outer_ref->pdu_end_offset;
390
391 /* do the topmost member */
392 rc = ndr_inner(outer_ref);
393 if (!rc)
394 return (0); /* error already set */
395
396 nds->pdu_scan_offset = outer_ref->pdu_end_offset;
397
398 /* advance, as though run_outer_queue() was doing it */
399 nds->outer_current = nds->outer_current->next;
400 return (ndr_run_outer_queue(nds));
401 }
402
403 static ndr_ref_t *
ndr_enter_outer_queue(ndr_ref_t * arg_ref)404 ndr_enter_outer_queue(ndr_ref_t *arg_ref)
405 {
406 ndr_stream_t *nds = arg_ref->stream;
407 ndr_ref_t *outer_ref;
408
409 /*LINTED E_BAD_PTR_CAST_ALIGN*/
410 outer_ref = (ndr_ref_t *)NDS_MALLOC(nds, sizeof (*outer_ref), arg_ref);
411 if (!outer_ref) {
412 NDR_SET_ERROR(arg_ref, NDR_ERR_MALLOC_FAILED);
413 return (0);
414 }
415
416 *outer_ref = *arg_ref;
417
418 /* move advice in inner_flags to outer_flags */
419 outer_ref->outer_flags = arg_ref->inner_flags & NDR_F_PARAMS_MASK;
420 outer_ref->inner_flags = 0;
421 outer_ref->enclosing = nds->outer_current;
422 outer_ref->backptr = 0;
423 outer_ref->datum = 0;
424
425 assert(nds->outer_queue_tailp);
426
427 outer_ref->next = *nds->outer_queue_tailp;
428 *nds->outer_queue_tailp = outer_ref;
429 nds->outer_queue_tailp = &outer_ref->next;
430 return (outer_ref);
431 }
432
433 int
ndr_run_outer_queue(ndr_stream_t * nds)434 ndr_run_outer_queue(ndr_stream_t *nds)
435 {
436 while (nds->outer_current) {
437 nds->outer_queue_tailp = &nds->outer_current->next;
438
439 if (!ndr_outer(nds->outer_current))
440 return (0);
441
442 nds->outer_current = nds->outer_current->next;
443 }
444
445 return (1);
446 }
447
448 /*
449 * OUTER CONSTRUCTS
450 *
451 * OUTER constructs are where the real work is, which stems from the
452 * variable-length potential.
453 *
454 * DCE(MS)/RPC VARIABLE LENGTH -- CONFORMANT, VARYING, VARYING/CONFORMANT
455 *
456 * DCE(MS)/RPC provides for three forms of variable length: CONFORMANT,
457 * VARYING, and VARYING/CONFORMANT.
458 *
459 * What makes this so tough is that the variable-length array may be well
460 * encapsulated within the outer construct. Further, because DCE(MS)/RPC
461 * tries to keep the constructs contiguous in the data stream, the sizing
462 * information precedes the entire OUTER construct. The sizing information
463 * must be used at the appropriate time, which can be after many, many,
464 * many fixed-length elements. During IDL type analysis, we know in
465 * advance constructs that encapsulate variable-length constructs. So,
466 * we know when we have a sizing header and when we don't. The actual
467 * semantics of the header are largely deferred.
468 *
469 * Currently, VARYING constructs are not implemented but they are described
470 * here in case they have to be implemented in the future. Similarly,
471 * DCE(MS)/RPC provides for multi-dimensional arrays, which are currently
472 * not implemented. Only one-dimensional, variable-length arrays are
473 * supported.
474 *
475 * CONFORMANT CONSTRUCTS -- VARIABLE LENGTH ARRAYS START THE SHOW
476 *
477 * All variable-length values are arrays. These arrays may be embedded
478 * well within another construct. However, a variable-length construct
479 * may ONLY appear as the last member of an enclosing construct. Example:
480 *
481 * struct credentials {
482 * ulong uid, gid;
483 * ulong n_gids;
484 * [size_is(n_gids)]
485 * ulong gids[*]; // variable-length.
486 * };
487 *
488 * CONFORMANT constructs have a dynamic size in local memory and in the
489 * PDU. The CONFORMANT quality is indicated by the [size_is()] advice.
490 * CONFORMANT constructs have the following header:
491 *
492 * struct conformant_header {
493 * ulong size_is;
494 * };
495 *
496 * (Multi-dimensional CONFORMANT arrays have a similar header for each
497 * dimension - not implemented).
498 *
499 * Example CONFORMANT construct:
500 *
501 * struct user {
502 * char * name;
503 * struct credentials cred; // see above
504 * };
505 *
506 * Consider the data tree:
507 *
508 * +--------+
509 * | user |
510 * +--------+
511 * | name ----> "fred" (the string is a different OUTER)
512 * | uid |
513 * | gid |
514 * | n_gids | for example, 3
515 * | gids[0]|
516 * | gids[1]|
517 * | gids[2]|
518 * +--------+
519 *
520 * The OUTER construct in the Stub Data would be:
521 *
522 * +---+---------+---------------------------------------------+
523 * |pad|size_is=3 name uid gid n_gids=3 gids[0] gids[1] gids[2]|
524 * +---+---------+---------------------------------------------+
525 * szing hdr|user |<-------------- user.cred ------------>|
526 * |<--- fixed-size ---->|<----- conformant ---->|
527 *
528 * The ndr_typeinfo for struct user will have:
529 * pdu_fixed_size_part = 16 four long words (name uid gid n_gids)
530 * pdu_variable_size_part = 4 per element, sizeof gids[0]
531 *
532 * VARYING CONSTRUCTS -- NOT IMPLEMENTED
533 *
534 * VARYING constructs have the following header:
535 *
536 * struct varying_header {
537 * ulong first_is;
538 * ulong length_is;
539 * };
540 *
541 * This indicates which interval of an array is significant.
542 * Non-intersecting elements of the array are undefined and usually
543 * zero-filled. The first_is parameter for C arrays is always 0 for
544 * the first element.
545 *
546 * N.B. Constructs may contain one CONFORMANT element, which is always
547 * last, but may contain many VARYING elements, which can be anywhere.
548 *
549 * VARYING CONFORMANT constructs have the sizing headers arranged like
550 * this:
551 *
552 * struct conformant_header all_conformant[N_CONFORMANT_DIM];
553 * struct varying_header all_varying[N_VARYING_ELEMS_AND_DIMS];
554 *
555 * The sizing header is immediately followed by the values for the
556 * construct. Again, we don't support more than one dimension and
557 * we don't support VARYING constructs at this time.
558 *
559 * A good example of a VARYING/CONFORMANT data structure is the UNIX
560 * directory entry:
561 *
562 * struct dirent {
563 * ushort reclen;
564 * ushort namlen;
565 * ulong inum;
566 * [size_is(reclen-8) length_is(namlen+1)] // -(2+2+4), +1 for NUL
567 * uchar name[*];
568 * };
569 *
570 *
571 * STRINGS ARE A SPECIAL CASE
572 *
573 * Strings are handled specially. MS/RPC uses VARYING/CONFORMANT structures
574 * for strings. This is a simple one-dimensional variable-length array,
575 * typically with its last element all zeroes. We handle strings with the
576 * header:
577 *
578 * struct string_header {
579 * ulong size_is;
580 * ulong first_is; // always 0
581 * ulong length_is; // always same as size_is
582 * };
583 *
584 * If general support for VARYING and VARYING/CONFORMANT mechanisms is
585 * implemented, we probably won't need the strings special case.
586 */
587 int
ndr_outer(ndr_ref_t * outer_ref)588 ndr_outer(ndr_ref_t *outer_ref)
589 {
590 ndr_stream_t *nds = outer_ref->stream;
591 ndr_typeinfo_t *ti = outer_ref->ti;
592 int is_varlen = ti->pdu_size_variable_part;
593 int is_union = NDR_IS_UNION(ti);
594 int is_string = NDR_IS_STRING(ti);
595 int error = NDR_ERR_OUTER_PARAMS_BAD;
596 int params;
597
598 params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
599
600 NDR_TATTLE(outer_ref, "--OUTER--");
601
602 /* All outer constructs start on a mod4 (longword) boundary */
603 if (!ndr_outer_align(outer_ref))
604 return (0); /* error already set */
605
606 /* Regardless of what it is, this is where it starts */
607 outer_ref->pdu_offset = nds->pdu_scan_offset;
608
609 if (is_union) {
610 error = NDR_ERR_OUTER_UNION_ILLEGAL;
611 NDR_SET_ERROR(outer_ref, error);
612 return (0);
613 }
614
615 switch (params) {
616 case NDR_F_NONE:
617 if (is_string)
618 return (ndr_outer_string(outer_ref));
619 if (is_varlen)
620 return (ndr_outer_conformant_construct(outer_ref));
621
622 return (ndr_outer_fixed(outer_ref));
623
624 case NDR_F_SIZE_IS:
625 case NDR_F_DIMENSION_IS:
626 case NDR_F_IS_POINTER+NDR_F_SIZE_IS:
627 case NDR_F_IS_REFERENCE+NDR_F_SIZE_IS:
628 if (is_varlen) {
629 error = NDR_ERR_ARRAY_VARLEN_ILLEGAL;
630 break;
631 }
632
633 if (params & NDR_F_SIZE_IS)
634 return (ndr_outer_conformant_array(outer_ref));
635 else
636 return (ndr_outer_fixed_array(outer_ref));
637
638 default:
639 error = NDR_ERR_OUTER_PARAMS_BAD;
640 break;
641 }
642
643 /*
644 * If we get here, something is wrong. Most likely,
645 * the params flags do not match.
646 */
647 NDR_SET_ERROR(outer_ref, error);
648 return (0);
649 }
650
651 int
ndr_outer_fixed(ndr_ref_t * outer_ref)652 ndr_outer_fixed(ndr_ref_t *outer_ref)
653 {
654 ndr_stream_t *nds = outer_ref->stream;
655 ndr_typeinfo_t *ti = outer_ref->ti;
656 ndr_ref_t myref;
657 char *valp = NULL;
658 int is_varlen = ti->pdu_size_variable_part;
659 int is_union = NDR_IS_UNION(ti);
660 int is_string = NDR_IS_STRING(ti);
661 int rc;
662 unsigned n_hdr;
663 unsigned n_fixed;
664 unsigned n_variable;
665 unsigned n_alloc;
666 unsigned n_pdu_total;
667 int params;
668
669 params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
670
671 assert(!is_varlen && !is_string && !is_union);
672 assert(params == NDR_F_NONE);
673
674 /* no header for this */
675 n_hdr = 0;
676
677 /* fixed part -- exactly one of these */
678 n_fixed = ti->pdu_size_fixed_part;
679 assert(n_fixed > 0);
680
681 /* variable part -- exactly none of these */
682 n_variable = 0;
683
684 /* sum them up to determine the PDU space required */
685 n_pdu_total = n_hdr + n_fixed + n_variable;
686
687 /* similar sum to determine how much local memory is required */
688 n_alloc = n_fixed + n_variable;
689
690 rc = ndr_outer_grow(outer_ref, n_pdu_total);
691 if (!rc)
692 return (rc); /* error already set */
693
694 switch (nds->m_op) {
695 case NDR_M_OP_MARSHALL:
696 valp = outer_ref->datum;
697 if (!valp) {
698 NDR_SET_ERROR(outer_ref, NDR_ERR_OUTER_PARAMS_BAD);
699 return (0);
700 }
701 if (outer_ref->backptr)
702 assert(valp == *outer_ref->backptr);
703 break;
704
705 case NDR_M_OP_UNMARSHALL:
706 valp = NDS_MALLOC(nds, n_alloc, outer_ref);
707 if (!valp) {
708 NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED);
709 return (0);
710 }
711 if (outer_ref->backptr)
712 *outer_ref->backptr = valp;
713 outer_ref->datum = valp;
714 break;
715
716 default:
717 NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
718 return (0);
719 }
720
721 bzero(&myref, sizeof (myref));
722 myref.stream = nds;
723 myref.enclosing = outer_ref;
724 myref.ti = outer_ref->ti;
725 myref.datum = outer_ref->datum;
726 myref.name = "FIXED-VALUE";
727 myref.outer_flags = NDR_F_NONE;
728 myref.inner_flags = NDR_F_NONE;
729
730 myref.pdu_offset = outer_ref->pdu_offset;
731 outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total;
732
733 rc = ndr_inner(&myref);
734 if (!rc)
735 return (rc); /* error already set */
736
737 nds->pdu_scan_offset = outer_ref->pdu_end_offset;
738 return (1);
739 }
740
741 int
ndr_outer_fixed_array(ndr_ref_t * outer_ref)742 ndr_outer_fixed_array(ndr_ref_t *outer_ref)
743 {
744 ndr_stream_t *nds = outer_ref->stream;
745 ndr_typeinfo_t *ti = outer_ref->ti;
746 ndr_ref_t myref;
747 char *valp = NULL;
748 int is_varlen = ti->pdu_size_variable_part;
749 int is_union = NDR_IS_UNION(ti);
750 int is_string = NDR_IS_STRING(ti);
751 int rc;
752 unsigned n_hdr;
753 unsigned n_fixed;
754 unsigned n_variable;
755 unsigned n_alloc;
756 unsigned n_pdu_total;
757 int params;
758
759 params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
760
761 assert(!is_varlen && !is_string && !is_union);
762 assert(params == NDR_F_DIMENSION_IS);
763
764 /* no header for this */
765 n_hdr = 0;
766
767 /* fixed part -- exactly dimension_is of these */
768 n_fixed = ti->pdu_size_fixed_part * outer_ref->dimension_is;
769 assert(n_fixed > 0);
770
771 /* variable part -- exactly none of these */
772 n_variable = 0;
773
774 /* sum them up to determine the PDU space required */
775 n_pdu_total = n_hdr + n_fixed + n_variable;
776
777 /* similar sum to determine how much local memory is required */
778 n_alloc = n_fixed + n_variable;
779
780 rc = ndr_outer_grow(outer_ref, n_pdu_total);
781 if (!rc)
782 return (rc); /* error already set */
783
784 switch (nds->m_op) {
785 case NDR_M_OP_MARSHALL:
786 valp = outer_ref->datum;
787 if (!valp) {
788 NDR_SET_ERROR(outer_ref, NDR_ERR_OUTER_PARAMS_BAD);
789 return (0);
790 }
791 if (outer_ref->backptr)
792 assert(valp == *outer_ref->backptr);
793 break;
794
795 case NDR_M_OP_UNMARSHALL:
796 valp = NDS_MALLOC(nds, n_alloc, outer_ref);
797 if (!valp) {
798 NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED);
799 return (0);
800 }
801 if (outer_ref->backptr)
802 *outer_ref->backptr = valp;
803 outer_ref->datum = valp;
804 break;
805
806 default:
807 NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
808 return (0);
809 }
810
811 bzero(&myref, sizeof (myref));
812 myref.stream = nds;
813 myref.enclosing = outer_ref;
814 myref.ti = outer_ref->ti;
815 myref.datum = outer_ref->datum;
816 myref.name = "FIXED-ARRAY";
817 myref.outer_flags = NDR_F_NONE;
818 myref.inner_flags = NDR_F_DIMENSION_IS;
819 myref.dimension_is = outer_ref->dimension_is;
820
821 myref.pdu_offset = outer_ref->pdu_offset;
822 outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total;
823
824 rc = ndr_inner(&myref);
825 if (!rc)
826 return (rc); /* error already set */
827
828 nds->pdu_scan_offset = outer_ref->pdu_end_offset;
829 return (1);
830 }
831
832 int
ndr_outer_conformant_array(ndr_ref_t * outer_ref)833 ndr_outer_conformant_array(ndr_ref_t *outer_ref)
834 {
835 ndr_stream_t *nds = outer_ref->stream;
836 ndr_typeinfo_t *ti = outer_ref->ti;
837 ndr_ref_t myref;
838 char *valp = NULL;
839 int is_varlen = ti->pdu_size_variable_part;
840 int is_union = NDR_IS_UNION(ti);
841 int is_string = NDR_IS_STRING(ti);
842 unsigned long size_is;
843 int rc;
844 unsigned n_hdr;
845 unsigned n_fixed;
846 unsigned n_variable;
847 unsigned n_alloc;
848 unsigned n_pdu_total;
849 unsigned n_ptr_offset;
850 int params;
851
852 params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
853
854 assert(!is_varlen && !is_string && !is_union);
855 assert(params & NDR_F_SIZE_IS);
856
857 /* conformant header for this */
858 n_hdr = 4;
859
860 /* fixed part -- exactly none of these */
861 n_fixed = 0;
862
863 /* variable part -- exactly size_of of these */
864 /* notice that it is the **fixed** size of the ti */
865 n_variable = ti->pdu_size_fixed_part * outer_ref->size_is;
866
867 /* sum them up to determine the PDU space required */
868 n_pdu_total = n_hdr + n_fixed + n_variable;
869
870 /* similar sum to determine how much local memory is required */
871 n_alloc = n_fixed + n_variable;
872
873 rc = ndr_outer_grow(outer_ref, n_pdu_total);
874 if (!rc)
875 return (rc); /* error already set */
876
877 switch (nds->m_op) {
878 case NDR_M_OP_MARSHALL:
879 size_is = outer_ref->size_is;
880 rc = ndr_outer_poke_sizing(outer_ref, 0, &size_is);
881 if (!rc)
882 return (0); /* error already set */
883
884 valp = outer_ref->datum;
885 if (!valp) {
886 NDR_SET_ERROR(outer_ref, NDR_ERR_OUTER_PARAMS_BAD);
887 return (0);
888 }
889 if (outer_ref->backptr)
890 assert(valp == *outer_ref->backptr);
891 n_ptr_offset = 4;
892 break;
893
894 case NDR_M_OP_UNMARSHALL:
895 if (params & NDR_F_IS_REFERENCE) {
896 size_is = outer_ref->size_is;
897 n_ptr_offset = 0;
898 } else {
899 /* NDR_F_IS_POINTER */
900 rc = ndr_outer_peek_sizing(outer_ref, 0, &size_is);
901 if (!rc)
902 return (0); /* error already set */
903
904 if (size_is != outer_ref->size_is) {
905 NDR_SET_ERROR(outer_ref,
906 NDR_ERR_SIZE_IS_MISMATCH_PDU);
907 return (0);
908 }
909
910 n_ptr_offset = 4;
911 }
912
913 if (size_is > 0) {
914 valp = NDS_MALLOC(nds, n_alloc, outer_ref);
915 if (!valp) {
916 NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED);
917 return (0);
918 }
919 }
920
921 if (outer_ref->backptr)
922 *outer_ref->backptr = valp;
923 outer_ref->datum = valp;
924 break;
925
926 default:
927 NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
928 return (0);
929 }
930
931 outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total;
932 outer_ref->type_flags = NDR_F_NONE;
933 outer_ref->inner_flags = NDR_F_NONE;
934
935 if (size_is > 0) {
936 bzero(&myref, sizeof (myref));
937 myref.stream = nds;
938 myref.enclosing = outer_ref;
939 myref.ti = outer_ref->ti;
940 myref.datum = outer_ref->datum;
941 myref.name = "CONFORMANT-ARRAY";
942 myref.outer_flags = NDR_F_NONE;
943 myref.inner_flags = NDR_F_SIZE_IS;
944 myref.size_is = outer_ref->size_is;
945
946 myref.inner_flags = NDR_F_DIMENSION_IS; /* convenient */
947 myref.dimension_is = outer_ref->size_is; /* convenient */
948
949 myref.pdu_offset = outer_ref->pdu_offset + n_ptr_offset;
950
951 rc = ndr_inner(&myref);
952 if (!rc)
953 return (rc); /* error already set */
954 }
955
956 nds->pdu_scan_offset = outer_ref->pdu_end_offset;
957 return (1);
958 }
959
960 int
ndr_outer_conformant_construct(ndr_ref_t * outer_ref)961 ndr_outer_conformant_construct(ndr_ref_t *outer_ref)
962 {
963 ndr_stream_t *nds = outer_ref->stream;
964 ndr_typeinfo_t *ti = outer_ref->ti;
965 ndr_ref_t myref;
966 char *valp = NULL;
967 int is_varlen = ti->pdu_size_variable_part;
968 int is_union = NDR_IS_UNION(ti);
969 int is_string = NDR_IS_STRING(ti);
970 unsigned long size_is;
971 int rc;
972 unsigned n_hdr;
973 unsigned n_fixed;
974 unsigned n_variable;
975 unsigned n_alloc;
976 unsigned n_pdu_total;
977 int params;
978
979 params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
980
981 assert(is_varlen && !is_string && !is_union);
982 assert(params == NDR_F_NONE);
983
984 /* conformant header for this */
985 n_hdr = 4;
986
987 /* fixed part -- exactly one of these */
988 n_fixed = ti->pdu_size_fixed_part;
989
990 /* variable part -- exactly size_of of these */
991 n_variable = 0; /* 0 for the moment */
992
993 /* sum them up to determine the PDU space required */
994 n_pdu_total = n_hdr + n_fixed + n_variable;
995
996 /* similar sum to determine how much local memory is required */
997 n_alloc = n_fixed + n_variable;
998
999 /* For the moment, grow enough for the fixed-size part */
1000 rc = ndr_outer_grow(outer_ref, n_pdu_total);
1001 if (!rc)
1002 return (rc); /* error already set */
1003
1004 switch (nds->m_op) {
1005 case NDR_M_OP_MARSHALL:
1006 /*
1007 * We don't know the size yet. We have to wait for
1008 * it. Proceed with the fixed-size part, and await
1009 * the call to ndr_size_is().
1010 */
1011 size_is = 0;
1012 rc = ndr_outer_poke_sizing(outer_ref, 0, &size_is);
1013 if (!rc)
1014 return (0); /* error already set */
1015
1016 valp = outer_ref->datum;
1017 if (!valp) {
1018 NDR_SET_ERROR(outer_ref, NDR_ERR_OUTER_PARAMS_BAD);
1019 return (0);
1020 }
1021 if (outer_ref->backptr)
1022 assert(valp == *outer_ref->backptr);
1023 break;
1024
1025 case NDR_M_OP_UNMARSHALL:
1026 /*
1027 * We know the size of the variable part because
1028 * of the CONFORMANT header. We will verify
1029 * the header against the [size_is(X)] advice
1030 * later when ndr_size_is() is called.
1031 */
1032 rc = ndr_outer_peek_sizing(outer_ref, 0, &size_is);
1033 if (!rc)
1034 return (0); /* error already set */
1035
1036 /* recalculate metrics */
1037 n_variable = size_is * ti->pdu_size_variable_part;
1038 n_pdu_total = n_hdr + n_fixed + n_variable;
1039 n_alloc = n_fixed + n_variable;
1040
1041 rc = ndr_outer_grow(outer_ref, n_pdu_total);
1042 if (!rc)
1043 return (rc); /* error already set */
1044
1045 outer_ref->size_is = size_is; /* verified later */
1046
1047 valp = NDS_MALLOC(nds, n_alloc, outer_ref);
1048 if (!valp) {
1049 NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED);
1050 return (0);
1051 }
1052 if (outer_ref->backptr)
1053 *outer_ref->backptr = valp;
1054 outer_ref->datum = valp;
1055 break;
1056
1057 default:
1058 NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1059 return (0);
1060 }
1061
1062 outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total;
1063 outer_ref->type_flags = NDR_F_SIZE_IS; /* indicate pending */
1064 outer_ref->inner_flags = NDR_F_NONE; /* indicate pending */
1065
1066 bzero(&myref, sizeof (myref));
1067 myref.stream = nds;
1068 myref.enclosing = outer_ref;
1069 myref.ti = outer_ref->ti;
1070 myref.datum = outer_ref->datum;
1071 myref.name = "CONFORMANT-CONSTRUCT";
1072 myref.outer_flags = NDR_F_NONE;
1073 myref.inner_flags = NDR_F_NONE;
1074 myref.size_is = outer_ref->size_is;
1075
1076 myref.pdu_offset = outer_ref->pdu_offset + 4;
1077
1078 rc = ndr_inner(&myref);
1079 if (!rc)
1080 return (rc); /* error already set */
1081
1082 nds->pdu_scan_offset = outer_ref->pdu_end_offset;
1083
1084 if (outer_ref->inner_flags != NDR_F_SIZE_IS) {
1085 NDR_SET_ERROR(&myref, NDR_ERR_SIZE_IS_MISMATCH_AFTER);
1086 return (0);
1087 }
1088
1089 return (1);
1090 }
1091
1092 int
ndr_size_is(ndr_ref_t * ref)1093 ndr_size_is(ndr_ref_t *ref)
1094 {
1095 ndr_stream_t *nds = ref->stream;
1096 ndr_ref_t *outer_ref = nds->outer_current;
1097 ndr_typeinfo_t *ti = outer_ref->ti;
1098 unsigned long size_is;
1099 int rc;
1100 unsigned n_hdr;
1101 unsigned n_fixed;
1102 unsigned n_variable;
1103 unsigned n_pdu_total;
1104
1105 assert(ref->inner_flags & NDR_F_SIZE_IS);
1106 size_is = ref->size_is;
1107
1108 if (outer_ref->type_flags != NDR_F_SIZE_IS) {
1109 NDR_SET_ERROR(ref, NDR_ERR_SIZE_IS_UNEXPECTED);
1110 return (0);
1111 }
1112
1113 if (outer_ref->inner_flags & NDR_F_SIZE_IS) {
1114 NDR_SET_ERROR(ref, NDR_ERR_SIZE_IS_DUPLICATED);
1115 return (0);
1116 }
1117
1118 /* repeat metrics, see ndr_conformant_construct() above */
1119 n_hdr = 4;
1120 n_fixed = ti->pdu_size_fixed_part;
1121 n_variable = size_is * ti->pdu_size_variable_part;
1122 n_pdu_total = n_hdr + n_fixed + n_variable;
1123
1124 rc = ndr_outer_grow(outer_ref, n_pdu_total);
1125 if (!rc)
1126 return (rc); /* error already set */
1127
1128 switch (nds->m_op) {
1129 case NDR_M_OP_MARSHALL:
1130 /*
1131 * We have to set the sizing header and extend
1132 * the size of the PDU (already done).
1133 */
1134 rc = ndr_outer_poke_sizing(outer_ref, 0, &size_is);
1135 if (!rc)
1136 return (0); /* error already set */
1137 break;
1138
1139 case NDR_M_OP_UNMARSHALL:
1140 /*
1141 * Allocation done during ndr_conformant_construct().
1142 * All we are doing here is verifying that the
1143 * intended size (ref->size_is) matches the sizing header.
1144 */
1145 if (size_is != outer_ref->size_is) {
1146 NDR_SET_ERROR(ref, NDR_ERR_SIZE_IS_MISMATCH_PDU);
1147 return (0);
1148 }
1149 break;
1150
1151 default:
1152 NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1153 return (0);
1154 }
1155
1156 outer_ref->inner_flags |= NDR_F_SIZE_IS;
1157 outer_ref->size_is = ref->size_is;
1158 return (1);
1159 }
1160
1161 int
ndr_outer_string(ndr_ref_t * outer_ref)1162 ndr_outer_string(ndr_ref_t *outer_ref)
1163 {
1164 ndr_stream_t *nds = outer_ref->stream;
1165 ndr_typeinfo_t *ti = outer_ref->ti;
1166 ndr_ref_t myref;
1167 char *valp = NULL;
1168 unsigned is_varlen = ti->pdu_size_variable_part;
1169 int is_union = NDR_IS_UNION(ti);
1170 int is_string = NDR_IS_STRING(ti);
1171 int rc;
1172 unsigned n_zeroes;
1173 unsigned ix;
1174 unsigned long size_is;
1175 unsigned long first_is;
1176 unsigned long length_is;
1177 unsigned n_hdr;
1178 unsigned n_fixed;
1179 unsigned n_variable;
1180 unsigned n_alloc;
1181 unsigned n_pdu_total;
1182 int params;
1183
1184 params = outer_ref->outer_flags & NDR_F_PARAMS_MASK;
1185
1186 assert(is_varlen && is_string && !is_union);
1187 assert(params == NDR_F_NONE);
1188
1189 /* string header for this: size_is first_is length_is */
1190 n_hdr = 12;
1191
1192 /* fixed part -- exactly none of these */
1193 n_fixed = 0;
1194
1195 if (!ndr_outer_grow(outer_ref, n_hdr))
1196 return (0); /* error already set */
1197
1198 switch (nds->m_op) {
1199 case NDR_M_OP_MARSHALL:
1200 valp = outer_ref->datum;
1201 if (!valp) {
1202 NDR_SET_ERROR(outer_ref, NDR_ERR_OUTER_PARAMS_BAD);
1203 return (0);
1204 }
1205
1206 if (outer_ref->backptr)
1207 assert(valp == *outer_ref->backptr);
1208
1209 if (ti == &ndt_s_wchar) {
1210 /*
1211 * size_is is the number of characters in the
1212 * (multibyte) string, including the null.
1213 */
1214 size_is = smb_wcequiv_strlen(valp) /
1215 sizeof (smb_wchar_t);
1216
1217 if (!(nds->flags & NDS_F_NONULL))
1218 ++size_is;
1219
1220 if (size_is > NDR_STRING_MAX) {
1221 NDR_SET_ERROR(outer_ref, NDR_ERR_STRLEN);
1222 return (0);
1223 }
1224 } else {
1225 valp = outer_ref->datum;
1226 n_zeroes = 0;
1227 for (ix = 0; ix < NDR_STRING_MAX; ix++) {
1228 if (valp[ix] == 0) {
1229 n_zeroes++;
1230 if (n_zeroes >= is_varlen &&
1231 ix % is_varlen == 0) {
1232 break;
1233 }
1234 } else {
1235 n_zeroes = 0;
1236 }
1237 }
1238 if (ix >= NDR_STRING_MAX) {
1239 NDR_SET_ERROR(outer_ref, NDR_ERR_STRLEN);
1240 return (0);
1241 }
1242 size_is = ix+1;
1243 }
1244
1245 first_is = 0;
1246
1247 if (nds->flags & NDS_F_NOTERM)
1248 length_is = size_is - 1;
1249 else
1250 length_is = size_is;
1251
1252 if (!ndr_outer_poke_sizing(outer_ref, 0, &size_is) ||
1253 !ndr_outer_poke_sizing(outer_ref, 4, &first_is) ||
1254 !ndr_outer_poke_sizing(outer_ref, 8, &length_is))
1255 return (0); /* error already set */
1256 break;
1257
1258 case NDR_M_OP_UNMARSHALL:
1259 if (!ndr_outer_peek_sizing(outer_ref, 0, &size_is) ||
1260 !ndr_outer_peek_sizing(outer_ref, 4, &first_is) ||
1261 !ndr_outer_peek_sizing(outer_ref, 8, &length_is))
1262 return (0); /* error already set */
1263
1264 /*
1265 * In addition to the first_is check, we used to check that
1266 * size_is or size_is-1 was equal to length_is but Windows95
1267 * doesn't conform to this "rule" (see variable part below).
1268 * The srvmgr tool for Windows95 sent the following values
1269 * for a path string:
1270 *
1271 * size_is = 261 (0x105)
1272 * first_is = 0
1273 * length_is = 53 (0x35)
1274 *
1275 * The length_is was correct (for the given path) but the
1276 * size_is was the maximum path length rather than being
1277 * related to length_is.
1278 */
1279 if (first_is != 0) {
1280 NDR_SET_ERROR(outer_ref, NDR_ERR_STRING_SIZING);
1281 return (0);
1282 }
1283
1284 if (ti == &ndt_s_wchar) {
1285 /*
1286 * Decoding Unicode to UTF-8; we need to allow
1287 * for the maximum possible char size. It would
1288 * be nice to use mbequiv_strlen but the string
1289 * may not be null terminated.
1290 */
1291 n_alloc = (size_is + 1) * MTS_MB_CHAR_MAX;
1292 } else {
1293 n_alloc = (size_is + 1) * is_varlen;
1294 }
1295
1296 valp = NDS_MALLOC(nds, n_alloc, outer_ref);
1297 if (!valp) {
1298 NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED);
1299 return (0);
1300 }
1301
1302 bzero(valp, (size_is+1) * is_varlen);
1303
1304 if (outer_ref->backptr)
1305 *outer_ref->backptr = valp;
1306 outer_ref->datum = valp;
1307 break;
1308
1309 default:
1310 NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1311 return (0);
1312 }
1313
1314 /*
1315 * Variable part - exactly length_is of these.
1316 *
1317 * Usually, length_is is same as size_is and includes nul.
1318 * Some protocols use length_is = size_is-1, and length_is does
1319 * not include the nul (which is more consistent with DCE spec).
1320 * If the length_is is 0, there is no data following the
1321 * sizing header, regardless of size_is.
1322 */
1323 n_variable = length_is * is_varlen;
1324
1325 /* sum them up to determine the PDU space required */
1326 n_pdu_total = n_hdr + n_fixed + n_variable;
1327
1328 /* similar sum to determine how much local memory is required */
1329 n_alloc = n_fixed + n_variable;
1330
1331 rc = ndr_outer_grow(outer_ref, n_pdu_total);
1332 if (!rc)
1333 return (rc); /* error already set */
1334
1335 if (length_is > 0) {
1336 bzero(&myref, sizeof (myref));
1337 myref.stream = nds;
1338 myref.enclosing = outer_ref;
1339 myref.ti = outer_ref->ti;
1340 myref.datum = outer_ref->datum;
1341 myref.name = "OUTER-STRING";
1342 myref.outer_flags = NDR_F_IS_STRING;
1343 myref.inner_flags = NDR_F_NONE;
1344
1345 /*
1346 * Set up size_is and strlen_is for ndr_s_wchar.
1347 */
1348 myref.size_is = size_is;
1349 myref.strlen_is = length_is;
1350 }
1351
1352 myref.pdu_offset = outer_ref->pdu_offset + 12;
1353
1354 /*
1355 * Don't try to decode empty strings.
1356 */
1357 if ((size_is == 0) && (first_is == 0) && (length_is == 0)) {
1358 nds->pdu_scan_offset = outer_ref->pdu_end_offset;
1359 return (1);
1360 }
1361
1362 if ((size_is != 0) && (length_is != 0)) {
1363 rc = ndr_inner(&myref);
1364 if (!rc)
1365 return (rc); /* error already set */
1366 }
1367
1368 nds->pdu_scan_offset = outer_ref->pdu_end_offset;
1369 return (1);
1370 }
1371
1372 int
ndr_outer_peek_sizing(ndr_ref_t * outer_ref,unsigned offset,unsigned long * sizing_p)1373 ndr_outer_peek_sizing(ndr_ref_t *outer_ref, unsigned offset,
1374 unsigned long *sizing_p)
1375 {
1376 ndr_stream_t *nds = outer_ref->stream;
1377 unsigned long pdu_offset;
1378 int rc;
1379
1380 pdu_offset = outer_ref->pdu_offset + offset;
1381
1382 if (pdu_offset < nds->outer_current->pdu_offset ||
1383 pdu_offset > nds->outer_current->pdu_end_offset ||
1384 pdu_offset+4 > nds->outer_current->pdu_end_offset) {
1385 NDR_SET_ERROR(outer_ref, NDR_ERR_BOUNDS_CHECK);
1386 return (0);
1387 }
1388
1389 switch (nds->m_op) {
1390 case NDR_M_OP_MARSHALL:
1391 NDR_SET_ERROR(outer_ref, NDR_ERR_UNIMPLEMENTED);
1392 return (0);
1393
1394 case NDR_M_OP_UNMARSHALL:
1395 rc = NDS_GET_PDU(nds, pdu_offset, 4, (char *)sizing_p,
1396 nds->swap, outer_ref);
1397 break;
1398
1399 default:
1400 NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1401 return (0);
1402 }
1403
1404 return (rc);
1405 }
1406
1407 int
ndr_outer_poke_sizing(ndr_ref_t * outer_ref,unsigned offset,unsigned long * sizing_p)1408 ndr_outer_poke_sizing(ndr_ref_t *outer_ref, unsigned offset,
1409 unsigned long *sizing_p)
1410 {
1411 ndr_stream_t *nds = outer_ref->stream;
1412 unsigned long pdu_offset;
1413 int rc;
1414
1415 pdu_offset = outer_ref->pdu_offset + offset;
1416
1417 if (pdu_offset < nds->outer_current->pdu_offset ||
1418 pdu_offset > nds->outer_current->pdu_end_offset ||
1419 pdu_offset+4 > nds->outer_current->pdu_end_offset) {
1420 NDR_SET_ERROR(outer_ref, NDR_ERR_BOUNDS_CHECK);
1421 return (0);
1422 }
1423
1424 switch (nds->m_op) {
1425 case NDR_M_OP_MARSHALL:
1426 rc = NDS_PUT_PDU(nds, pdu_offset, 4, (char *)sizing_p,
1427 nds->swap, outer_ref);
1428 break;
1429
1430 case NDR_M_OP_UNMARSHALL:
1431 NDR_SET_ERROR(outer_ref, NDR_ERR_UNIMPLEMENTED);
1432 return (0);
1433
1434 default:
1435 NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1436 return (0);
1437 }
1438
1439 return (rc);
1440 }
1441
1442 /*
1443 * All OUTER constructs begin on a mod4 (dword) boundary - except
1444 * for the ones that don't: some MSRPC calls appear to use word or
1445 * packed alignment. Strings appear to be dword aligned.
1446 */
1447 int
ndr_outer_align(ndr_ref_t * outer_ref)1448 ndr_outer_align(ndr_ref_t *outer_ref)
1449 {
1450 ndr_stream_t *nds = outer_ref->stream;
1451 int rc;
1452 unsigned n_pad;
1453 unsigned align;
1454
1455 if (outer_ref->packed_alignment && outer_ref->ti != &ndt_s_wchar) {
1456 align = outer_ref->ti->alignment;
1457 n_pad = ((align + 1) - nds->pdu_scan_offset) & align;
1458 } else {
1459 n_pad = NDR_ALIGN4(nds->pdu_scan_offset);
1460 }
1461
1462 if (n_pad == 0)
1463 return (1); /* already aligned, often the case */
1464
1465 if (!ndr_outer_grow(outer_ref, n_pad))
1466 return (0); /* error already set */
1467
1468 switch (nds->m_op) {
1469 case NDR_M_OP_MARSHALL:
1470 rc = NDS_PAD_PDU(nds, nds->pdu_scan_offset, n_pad, outer_ref);
1471 if (!rc) {
1472 NDR_SET_ERROR(outer_ref, NDR_ERR_PAD_FAILED);
1473 return (0);
1474 }
1475 break;
1476
1477 case NDR_M_OP_UNMARSHALL:
1478 break;
1479
1480 default:
1481 NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1482 return (0);
1483 }
1484
1485 nds->pdu_scan_offset += n_pad;
1486 return (1);
1487 }
1488
1489 int
ndr_outer_grow(ndr_ref_t * outer_ref,unsigned n_total)1490 ndr_outer_grow(ndr_ref_t *outer_ref, unsigned n_total)
1491 {
1492 ndr_stream_t *nds = outer_ref->stream;
1493 unsigned long pdu_want_size;
1494 int rc, is_ok = 0;
1495
1496 pdu_want_size = nds->pdu_scan_offset + n_total;
1497
1498 if (pdu_want_size <= nds->pdu_max_size) {
1499 is_ok = 1;
1500 }
1501
1502 switch (nds->m_op) {
1503 case NDR_M_OP_MARSHALL:
1504 if (is_ok)
1505 break;
1506 rc = NDS_GROW_PDU(nds, pdu_want_size, outer_ref);
1507 if (!rc) {
1508 NDR_SET_ERROR(outer_ref, NDR_ERR_GROW_FAILED);
1509 return (0);
1510 }
1511 break;
1512
1513 case NDR_M_OP_UNMARSHALL:
1514 if (is_ok)
1515 break;
1516 NDR_SET_ERROR(outer_ref, NDR_ERR_UNDERFLOW);
1517 return (0);
1518
1519 default:
1520 NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID);
1521 return (0);
1522 }
1523
1524 if (nds->pdu_size < pdu_want_size)
1525 nds->pdu_size = pdu_want_size;
1526
1527 outer_ref->pdu_end_offset = pdu_want_size;
1528 return (1);
1529 }
1530
1531 /*
1532 * INNER ELEMENTS
1533 *
1534 * The local datum (arg_ref->datum) already exists, there is no need to
1535 * malloc() it. The datum should point at a member of a structure.
1536 *
1537 * For the most part, ndr_inner() and its helpers are just a sanity
1538 * check. The underlying ti->ndr_func() could be called immediately
1539 * for non-pointer elements. For the sake of robustness, we detect
1540 * run-time errors here. Most of the situations this protects against
1541 * have already been checked by the IDL compiler. This is also a
1542 * common point for processing of all data, and so is a convenient
1543 * place to work from for debugging.
1544 */
1545 int
ndr_inner(ndr_ref_t * arg_ref)1546 ndr_inner(ndr_ref_t *arg_ref)
1547 {
1548 ndr_typeinfo_t *ti = arg_ref->ti;
1549 int is_varlen = ti->pdu_size_variable_part;
1550 int is_union = NDR_IS_UNION(ti);
1551 int error = NDR_ERR_INNER_PARAMS_BAD;
1552 int params;
1553
1554 params = arg_ref->inner_flags & NDR_F_PARAMS_MASK;
1555
1556 switch (params) {
1557 case NDR_F_NONE:
1558 if (is_union) {
1559 error = NDR_ERR_SWITCH_VALUE_MISSING;
1560 break;
1561 }
1562 return (*ti->ndr_func)(arg_ref);
1563
1564 case NDR_F_SIZE_IS:
1565 case NDR_F_DIMENSION_IS:
1566 case NDR_F_IS_POINTER+NDR_F_SIZE_IS: /* pointer to something */
1567 case NDR_F_IS_REFERENCE+NDR_F_SIZE_IS: /* pointer to something */
1568 if (is_varlen) {
1569 error = NDR_ERR_ARRAY_VARLEN_ILLEGAL;
1570 break;
1571 }
1572 if (is_union) {
1573 error = NDR_ERR_ARRAY_UNION_ILLEGAL;
1574 break;
1575 }
1576 if (params & NDR_F_IS_POINTER)
1577 return (ndr_inner_pointer(arg_ref));
1578 else if (params & NDR_F_IS_REFERENCE)
1579 return (ndr_inner_reference(arg_ref));
1580 else
1581 return (ndr_inner_array(arg_ref));
1582
1583 case NDR_F_IS_POINTER: /* type is pointer to one something */
1584 if (is_union) {
1585 error = NDR_ERR_ARRAY_UNION_ILLEGAL;
1586 break;
1587 }
1588 return (ndr_inner_pointer(arg_ref));
1589
1590 case NDR_F_IS_REFERENCE: /* type is pointer to one something */
1591 if (is_union) {
1592 error = NDR_ERR_ARRAY_UNION_ILLEGAL;
1593 break;
1594 }
1595 return (ndr_inner_reference(arg_ref));
1596
1597 case NDR_F_SWITCH_IS:
1598 if (!is_union) {
1599 error = NDR_ERR_SWITCH_VALUE_ILLEGAL;
1600 break;
1601 }
1602 return (*ti->ndr_func)(arg_ref);
1603
1604 default:
1605 error = NDR_ERR_INNER_PARAMS_BAD;
1606 break;
1607 }
1608
1609 /*
1610 * If we get here, something is wrong. Most likely,
1611 * the params flags do not match
1612 */
1613 NDR_SET_ERROR(arg_ref, error);
1614 return (0);
1615 }
1616
1617 int
ndr_inner_pointer(ndr_ref_t * arg_ref)1618 ndr_inner_pointer(ndr_ref_t *arg_ref)
1619 {
1620 ndr_stream_t *nds = arg_ref->stream;
1621 /*LINTED E_BAD_PTR_CAST_ALIGN*/
1622 char **valpp = (char **)arg_ref->datum;
1623 ndr_ref_t *outer_ref;
1624
1625 if (!ndr__ulong(arg_ref))
1626 return (0); /* error */
1627 if (!*valpp)
1628 return (1); /* NULL pointer */
1629
1630 outer_ref = ndr_enter_outer_queue(arg_ref);
1631 if (!outer_ref)
1632 return (0); /* error already set */
1633
1634 /*
1635 * Move advice in inner_flags to outer_flags.
1636 * Retain pointer flag for conformant arrays.
1637 */
1638 outer_ref->outer_flags = arg_ref->inner_flags & NDR_F_PARAMS_MASK;
1639 if ((outer_ref->outer_flags & NDR_F_SIZE_IS) == 0)
1640 outer_ref->outer_flags &= ~NDR_F_IS_POINTER;
1641 #ifdef NDR_INNER_PTR_NOT_YET
1642 outer_ref->outer_flags |= NDR_F_BACKPTR;
1643 if (outer_ref->outer_flags & NDR_F_SIZE_IS) {
1644 outer_ref->outer_flags |= NDR_F_ARRAY+NDR_F_CONFORMANT;
1645 }
1646 #endif /* NDR_INNER_PTR_NOT_YET */
1647
1648 outer_ref->backptr = valpp;
1649
1650 switch (nds->m_op) {
1651 case NDR_M_OP_MARSHALL:
1652 outer_ref->datum = *valpp;
1653 break;
1654
1655 case NDR_M_OP_UNMARSHALL:
1656 /*
1657 * This is probably wrong if the application allocated
1658 * memory in advance. Indicate no value for now.
1659 * ONC RPC handles this case.
1660 */
1661 *valpp = 0;
1662 outer_ref->datum = 0;
1663 break;
1664 }
1665
1666 return (1); /* pointer dereference scheduled */
1667 }
1668
1669 int
ndr_inner_reference(ndr_ref_t * arg_ref)1670 ndr_inner_reference(ndr_ref_t *arg_ref)
1671 {
1672 ndr_stream_t *nds = arg_ref->stream;
1673 /*LINTED E_BAD_PTR_CAST_ALIGN*/
1674 char **valpp = (char **)arg_ref->datum;
1675 ndr_ref_t *outer_ref;
1676
1677 outer_ref = ndr_enter_outer_queue(arg_ref);
1678 if (!outer_ref)
1679 return (0); /* error already set */
1680
1681 /*
1682 * Move advice in inner_flags to outer_flags.
1683 * Retain reference flag for conformant arrays.
1684 */
1685 outer_ref->outer_flags = arg_ref->inner_flags & NDR_F_PARAMS_MASK;
1686 if ((outer_ref->outer_flags & NDR_F_SIZE_IS) == 0)
1687 outer_ref->outer_flags &= ~NDR_F_IS_REFERENCE;
1688 #ifdef NDR_INNER_REF_NOT_YET
1689 outer_ref->outer_flags |= NDR_F_BACKPTR;
1690 if (outer_ref->outer_flags & NDR_F_SIZE_IS) {
1691 outer_ref->outer_flags |= NDR_F_ARRAY+NDR_F_CONFORMANT;
1692 }
1693 #endif /* NDR_INNER_REF_NOT_YET */
1694
1695 outer_ref->backptr = valpp;
1696
1697 switch (nds->m_op) {
1698 case NDR_M_OP_MARSHALL:
1699 outer_ref->datum = *valpp;
1700 break;
1701
1702 case NDR_M_OP_UNMARSHALL:
1703 /*
1704 * This is probably wrong if the application allocated
1705 * memory in advance. Indicate no value for now.
1706 * ONC RPC handles this case.
1707 */
1708 *valpp = 0;
1709 outer_ref->datum = 0;
1710 break;
1711 }
1712
1713 return (1); /* pointer dereference scheduled */
1714 }
1715
1716 int
ndr_inner_array(ndr_ref_t * encl_ref)1717 ndr_inner_array(ndr_ref_t *encl_ref)
1718 {
1719 ndr_typeinfo_t *ti = encl_ref->ti;
1720 ndr_ref_t myref;
1721 unsigned long pdu_offset = encl_ref->pdu_offset;
1722 unsigned long n_elem;
1723 unsigned long i;
1724 char name[30];
1725
1726 if (encl_ref->inner_flags & NDR_F_SIZE_IS) {
1727 /* now is the time to check/set size */
1728 if (!ndr_size_is(encl_ref))
1729 return (0); /* error already set */
1730 n_elem = encl_ref->size_is;
1731 } else {
1732 assert(encl_ref->inner_flags & NDR_F_DIMENSION_IS);
1733 n_elem = encl_ref->dimension_is;
1734 }
1735
1736 bzero(&myref, sizeof (myref));
1737 myref.enclosing = encl_ref;
1738 myref.stream = encl_ref->stream;
1739 myref.packed_alignment = 0;
1740 myref.ti = ti;
1741 myref.inner_flags = NDR_F_NONE;
1742
1743 for (i = 0; i < n_elem; i++) {
1744 (void) sprintf(name, "[%lu]", i);
1745 myref.name = name;
1746 myref.pdu_offset = pdu_offset + i * ti->pdu_size_fixed_part;
1747 myref.datum = encl_ref->datum + i * ti->c_size_fixed_part;
1748
1749 if (!ndr_inner(&myref))
1750 return (0);
1751 }
1752
1753 return (1);
1754 }
1755
1756
1757 /*
1758 * BASIC TYPES
1759 */
1760 #define MAKE_BASIC_TYPE_BASE(TYPE, SIZE) \
1761 extern int ndr_##TYPE(struct ndr_reference *encl_ref); \
1762 ndr_typeinfo_t ndt_##TYPE = { \
1763 1, /* NDR version */ \
1764 (SIZE)-1, /* alignment */ \
1765 NDR_F_NONE, /* flags */ \
1766 ndr_##TYPE, /* ndr_func */ \
1767 SIZE, /* pdu_size_fixed_part */ \
1768 0, /* pdu_size_variable_part */ \
1769 SIZE, /* c_size_fixed_part */ \
1770 0, /* c_size_variable_part */ \
1771 }; \
1772 int ndr_##TYPE(struct ndr_reference *ref) { \
1773 return (ndr_basic_integer(ref, SIZE)); \
1774 }
1775
1776 #define MAKE_BASIC_TYPE_STRING(TYPE, SIZE) \
1777 extern int ndr_s##TYPE(struct ndr_reference *encl_ref); \
1778 ndr_typeinfo_t ndt_s##TYPE = { \
1779 1, /* NDR version */ \
1780 (SIZE)-1, /* alignment */ \
1781 NDR_F_STRING, /* flags */ \
1782 ndr_s##TYPE, /* ndr_func */ \
1783 0, /* pdu_size_fixed_part */ \
1784 SIZE, /* pdu_size_variable_part */ \
1785 0, /* c_size_fixed_part */ \
1786 SIZE, /* c_size_variable_part */ \
1787 }; \
1788 int ndr_s##TYPE(struct ndr_reference *ref) { \
1789 return (ndr_string_basic_integer(ref, &ndt_##TYPE)); \
1790 }
1791
1792 #define MAKE_BASIC_TYPE(TYPE, SIZE) \
1793 MAKE_BASIC_TYPE_BASE(TYPE, SIZE) \
1794 MAKE_BASIC_TYPE_STRING(TYPE, SIZE)
1795
1796 int ndr_basic_integer(ndr_ref_t *, unsigned);
1797 int ndr_string_basic_integer(ndr_ref_t *, ndr_typeinfo_t *);
1798
1799
1800 MAKE_BASIC_TYPE(_char, 1)
1801 MAKE_BASIC_TYPE(_uchar, 1)
1802 MAKE_BASIC_TYPE(_short, 2)
1803 MAKE_BASIC_TYPE(_ushort, 2)
1804 MAKE_BASIC_TYPE(_long, 4)
1805 MAKE_BASIC_TYPE(_ulong, 4)
1806
1807 MAKE_BASIC_TYPE_BASE(_wchar, 2)
1808
1809 int
ndr_basic_integer(ndr_ref_t * ref,unsigned size)1810 ndr_basic_integer(ndr_ref_t *ref, unsigned size)
1811 {
1812 ndr_stream_t *nds = ref->stream;
1813 char *valp = (char *)ref->datum;
1814 int rc;
1815
1816 switch (nds->m_op) {
1817 case NDR_M_OP_MARSHALL:
1818 rc = NDS_PUT_PDU(nds, ref->pdu_offset, size,
1819 valp, nds->swap, ref);
1820 break;
1821
1822 case NDR_M_OP_UNMARSHALL:
1823 rc = NDS_GET_PDU(nds, ref->pdu_offset, size,
1824 valp, nds->swap, ref);
1825 break;
1826
1827 default:
1828 NDR_SET_ERROR(ref, NDR_ERR_M_OP_INVALID);
1829 return (0);
1830 }
1831
1832 return (rc);
1833 }
1834
1835 int
ndr_string_basic_integer(ndr_ref_t * encl_ref,ndr_typeinfo_t * type_under)1836 ndr_string_basic_integer(ndr_ref_t *encl_ref, ndr_typeinfo_t *type_under)
1837 {
1838 unsigned long pdu_offset = encl_ref->pdu_offset;
1839 unsigned size = type_under->pdu_size_fixed_part;
1840 char *valp;
1841 ndr_ref_t myref;
1842 unsigned long i;
1843 long sense = 0;
1844 char name[30];
1845
1846 assert(size != 0);
1847
1848 bzero(&myref, sizeof (myref));
1849 myref.enclosing = encl_ref;
1850 myref.stream = encl_ref->stream;
1851 myref.packed_alignment = 0;
1852 myref.ti = type_under;
1853 myref.inner_flags = NDR_F_NONE;
1854 myref.name = name;
1855
1856 for (i = 0; i < NDR_STRING_MAX; i++) {
1857 (void) sprintf(name, "[%lu]", i);
1858 myref.pdu_offset = pdu_offset + i * size;
1859 valp = encl_ref->datum + i * size;
1860 myref.datum = valp;
1861
1862 if (!ndr_inner(&myref))
1863 return (0);
1864
1865 switch (size) {
1866 case 1: sense = *valp; break;
1867 /*LINTED E_BAD_PTR_CAST_ALIGN*/
1868 case 2: sense = *(short *)valp; break;
1869 /*LINTED E_BAD_PTR_CAST_ALIGN*/
1870 case 4: sense = *(long *)valp; break;
1871 }
1872
1873 if (!sense)
1874 break;
1875 }
1876
1877 return (1);
1878 }
1879
1880
1881 extern int ndr_s_wchar(ndr_ref_t *encl_ref);
1882 ndr_typeinfo_t ndt_s_wchar = {
1883 1, /* NDR version */
1884 2-1, /* alignment */
1885 NDR_F_STRING, /* flags */
1886 ndr_s_wchar, /* ndr_func */
1887 0, /* pdu_size_fixed_part */
1888 2, /* pdu_size_variable_part */
1889 0, /* c_size_fixed_part */
1890 1, /* c_size_variable_part */
1891 };
1892
1893
1894 /*
1895 * Hand coded wchar function because all strings are transported
1896 * as wide characters. During NDR_M_OP_MARSHALL, we convert from
1897 * multi-byte to wide characters. During NDR_M_OP_UNMARSHALL, we
1898 * convert from wide characters to multi-byte.
1899 *
1900 * It appeared that NT would sometimes leave a spurious character
1901 * in the data stream before the null wide_char, which would get
1902 * included in the string decode because we processed until the
1903 * null character. It now looks like NT does not always terminate
1904 * RPC Unicode strings and the terminating null is a side effect
1905 * of field alignment. So now we rely on the strlen_is (set up in
1906 * ndr_outer_string) of the enclosing reference. This may or may
1907 * not include the null but it doesn't matter, the algorithm will
1908 * get it right.
1909 */
1910 int
ndr_s_wchar(ndr_ref_t * encl_ref)1911 ndr_s_wchar(ndr_ref_t *encl_ref)
1912 {
1913 ndr_stream_t *nds = encl_ref->stream;
1914 unsigned short wide_char;
1915 char *valp;
1916 ndr_ref_t myref;
1917 unsigned long i;
1918 char name[30];
1919 int count;
1920 int char_count = 0;
1921
1922 if (nds->m_op == NDR_M_OP_UNMARSHALL) {
1923 /*
1924 * To avoid problems with zero length strings
1925 * we can just null terminate here and be done.
1926 */
1927 if (encl_ref->strlen_is == 0) {
1928 encl_ref->datum[0] = '\0';
1929 return (1);
1930 }
1931 }
1932
1933 bzero(&myref, sizeof (myref));
1934 myref.enclosing = encl_ref;
1935 myref.stream = encl_ref->stream;
1936 myref.packed_alignment = 0;
1937 myref.ti = &ndt__wchar;
1938 myref.inner_flags = NDR_F_NONE;
1939 myref.datum = (char *)&wide_char;
1940 myref.name = name;
1941 myref.pdu_offset = encl_ref->pdu_offset;
1942
1943 valp = encl_ref->datum;
1944 count = 0;
1945
1946 for (i = 0; i < NDR_STRING_MAX; i++) {
1947 (void) sprintf(name, "[%lu]", i);
1948
1949 if (nds->m_op == NDR_M_OP_MARSHALL) {
1950 count = smb_mbtowc((smb_wchar_t *)&wide_char, valp,
1951 MTS_MB_CHAR_MAX);
1952 if (count < 0) {
1953 return (0);
1954 } else if (count == 0) {
1955 if (encl_ref->strlen_is != encl_ref->size_is)
1956 break;
1957
1958 /*
1959 * If the input char is 0, mbtowc
1960 * returns 0 without setting wide_char.
1961 * Set wide_char to 0 and a count of 1.
1962 */
1963 wide_char = *valp;
1964 count = 1;
1965 }
1966 }
1967
1968 if (!ndr_inner(&myref))
1969 return (0);
1970
1971 if (nds->m_op == NDR_M_OP_UNMARSHALL) {
1972 count = smb_wctomb(valp, wide_char);
1973
1974 if ((++char_count) == encl_ref->strlen_is) {
1975 valp += count;
1976 *valp = '\0';
1977 break;
1978 }
1979 }
1980
1981 if (!wide_char)
1982 break;
1983
1984 myref.pdu_offset += sizeof (wide_char);
1985 valp += count;
1986 }
1987
1988 return (1);
1989 }
1990
1991 /*
1992 * Converts a multibyte character string to a little-endian, wide-char
1993 * string. No more than nwchars wide characters are stored.
1994 * A terminating null wide character is appended if there is room.
1995 *
1996 * Returns the number of wide characters converted, not counting
1997 * any terminating null wide character. Returns -1 if an invalid
1998 * multibyte character is encountered.
1999 */
2000 size_t
ndr_mbstowcs(ndr_stream_t * nds,smb_wchar_t * wcs,const char * mbs,size_t nwchars)2001 ndr_mbstowcs(ndr_stream_t *nds, smb_wchar_t *wcs, const char *mbs,
2002 size_t nwchars)
2003 {
2004 smb_wchar_t *start = wcs;
2005 int nbytes;
2006
2007 while (nwchars--) {
2008 nbytes = ndr_mbtowc(nds, wcs, mbs, MTS_MB_CHAR_MAX);
2009 if (nbytes < 0) {
2010 *wcs = 0;
2011 return ((size_t)-1);
2012 }
2013
2014 if (*mbs == 0)
2015 break;
2016
2017 ++wcs;
2018 mbs += nbytes;
2019 }
2020
2021 return (wcs - start);
2022 }
2023
2024 /*
2025 * Converts a multibyte character to a little-endian, wide-char, which
2026 * is stored in wcharp. Up to nbytes bytes are examined.
2027 *
2028 * If mbchar is valid, returns the number of bytes processed in mbchar.
2029 * If mbchar is invalid, returns -1. See also smb_mbtowc().
2030 */
2031 /*ARGSUSED*/
2032 int
ndr_mbtowc(ndr_stream_t * nds,smb_wchar_t * wcharp,const char * mbchar,size_t nbytes)2033 ndr_mbtowc(ndr_stream_t *nds, smb_wchar_t *wcharp, const char *mbchar,
2034 size_t nbytes)
2035 {
2036 int rc;
2037
2038 if ((rc = smb_mbtowc(wcharp, mbchar, nbytes)) < 0)
2039 return (rc);
2040
2041 #ifdef _BIG_ENDIAN
2042 if (nds == NULL || NDR_MODE_MATCH(nds, NDR_MODE_RETURN_SEND))
2043 *wcharp = BSWAP_16(*wcharp);
2044 #endif
2045
2046 return (rc);
2047 }
2048