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
 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
 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
 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
 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 *
 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
 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
 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
 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
 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
 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
 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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }