/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2012 Milan Jurik. All rights reserved. */ /* * Network Data Representation (NDR) is a compatible subset of the DCE RPC * and MSRPC NDR. NDR is used to move parameters consisting of * complicated trees of data constructs between an RPC client and server. */ #include #include #include #include #include #include #include #include #define NDR_STRING_MAX 4096 #define NDR_IS_UNION(T) \ (((T)->type_flags & NDR_F_TYPEOP_MASK) == NDR_F_UNION) #define NDR_IS_STRING(T) \ (((T)->type_flags & NDR_F_TYPEOP_MASK) == NDR_F_STRING) extern ndr_typeinfo_t ndt_s_wchar; /* * The following synopsis describes the terms TOP-MOST, OUTER and INNER. * * Each parameter (call arguments and return values) is a TOP-MOST item. * A TOP-MOST item consists of one or more OUTER items. An OUTER item * consists of one or more INNER items. There are important differences * between each kind, which, primarily, have to do with the allocation * of memory to contain data structures and the order of processing. * * This is most easily demonstrated with a short example. * Consider these structures: * * struct top_param { * long level; * struct list * head; * long count; * }; * * struct list { * struct list * next; * char * str; // a string * }; * * Now, consider an instance tree like this: * * +---------+ +-------+ +-------+ * |top_param| +--->|list #1| +--->|list #2| * +---------+ | +-------+ | +-------+ * | level | | | next ----+ | next --->(NULL) * | head ----+ | str -->"foo" | str -->"bar" * | count | | flag | | flag | * +---------+ +-------+ +-------+ * * The DCE(MS)/RPC Stub Data encoding for the tree is the following. * The vertical bars (|) indicate OUTER construct boundaries. * * +-----+----------------------+----------------------+-----+-----+-----+ * |level|#1.next #1.str #1.flag|#2.next #2.str #2.flag|"bar"|"foo"|count| * +-----+----------------------+----------------------+-----+-----+-----+ * level |<----------------------- head -------------------------->|count * TOP TOP TOP * * Here's what to notice: * * - The members of the TOP-MOST construct are scattered through the Stub * Data in the order they occur. This example shows a TOP-MOST construct * consisting of atomic types (pointers and integers). A construct * (struct) within the TOP-MOST construct would be contiguous and not * scattered. * * - The members of OUTER constructs are contiguous, which allows for * non-copied relocated (fixed-up) data structures at the packet's * destination. We don't do fix-ups here. The pointers within the * OUTER constructs are processed depth-first in the order that they * occur. If they were processed breadth first, the sequence would * be #1,"foo",#2,"bar". This is tricky because OUTER constructs may * be variable length, and pointers are often encountered before the * size(s) is known. * * - The INNER constructs are simply the members of an OUTER construct. * * For comparison, consider how ONC RPC would handle the same tree of * data. ONC requires very little buffering, while DCE requires enough * buffer space for the entire message. ONC does atom-by-atom depth-first * (de)serialization and copy, while DCE allows for constructs to be * "fixed-up" (relocated) in place at the destination. The packet data * for the same tree processed by ONC RPC would look like this: * * +---------------------------------------------------------------------+ * |level #1.next #2.next #2.str "bar" #2.flag #1.str "foo" #1.flag count| * +---------------------------------------------------------------------+ * TOP #1 #2 #2 bar #2 #1 foo #1 TOP * * More details about each TOP-MOST, OUTER, and INNER constructs appear * throughout this source file near where such constructs are processed. * * NDR_REFERENCE * * The primary object for NDR is the ndr_ref_t. * * An ndr reference indicates the local datum (i.e. native "C" data * format), and the element within the Stub Data (contained within the * RPC PDU (protocol data unit). An ndr reference also indicates, * largely as a debugging aid, something about the type of the * element/datum, and the enclosing construct for the element. The * ndr reference's are typically allocated on the stack as locals, * and the chain of ndr-reference.enclosing references is in reverse * order of the call graph. * * The ndr-reference.datum is a pointer to the local memory that * contains/receives the value. The ndr-reference.pdu_offset indicates * where in the Stub Data the value is to be stored/retrieved. * * The ndr-reference also contains various parameters to the NDR * process, such as ndr-reference.size_is, which indicates the size * of variable length data, or ndr-reference.switch_is, which * indicates the arm of a union to use. * * QUEUE OF OUTER REFERENCES * * Some OUTER constructs are variable size. Sometimes (often) we don't * know the size of the OUTER construct until after pointers have been * encountered. Hence, we can not begin processing the referent of the * pointer until after the referring OUTER construct is completely * processed, i.e. we don't know where to find/put the referent in the * Stub Data until we know the size of all its predecessors. * * This is managed using the queue of OUTER references. The queue is * anchored in ndr_stream.outer_queue_head. At any time, * ndr_stream.outer_queue_tailp indicates where to put the * ndr-reference for the next encountered pointer. * * Refer to the example above as we illustrate the queue here. In these * illustrations, the queue entries are not the data structures themselves. * Rather, they are ndr-reference entries which **refer** to the data * structures in both the PDU and local memory. * * During some point in the processing, the queue looks like this: * * outer_current -------v * outer_queue_head --> list#1 --0 * outer_queue_tailp ---------& * * When the pointer #1.next is encountered, and entry is added to the * queue, * * outer_current -------v * outer_queue_head --> list#1 --> list#2 --0 * outer_queue_tailp --------------------& * * and the members of #1 continue to be processed, which encounters * #1.str: * * outer_current -------v * outer_queue_head --> list#1 --> list#2 --> "foo" --0 * outer_queue_tailp ------------------------------& * * Upon the completion of list#1, the processing continues by moving to * ndr_stream.outer_current->next, and the tail is set to this outer member: * * outer_current ------------------v * outer_queue_head --> list#1 --> list#2 --> "foo" --0 * outer_queue_tailp --------------------& * * Space for list#2 is allocated, either in the Stub Data or of local * memory. When #2.next is encountered, it is found to be the null * pointer and no reference is added to the queue. When #2.str is * encountered, it is found to be valid, and a reference is added: * * outer_current ------------------v * outer_queue_head --> list#1 --> list#2 --> "bar" --> "foo" --0 * outer_queue_tailp ------------------------------& * * Processing continues in a similar fashion with the string "bar", * which is variable-length. At this point, memory for "bar" may be * malloc()ed during NDR_M_OP_UNMARSHALL: * * outer_current -----------------------------v * outer_queue_head --> list#1 --> list#2 --> "bar" --> "foo" --0 * outer_queue_tailp ------------------------------& * * And finishes on string "foo". Notice that because "bar" is a * variable length string, and we don't know the PDU offset for "foo" * until we reach this point. * * When the queue is drained (current->next==0), processing continues * with the next TOP-MOST member. * * The queue of OUTER constructs manages the variable-length semantics * of OUTER constructs and satisfies the depth-first requirement. * We allow the queue to linger until the entire TOP-MOST structure is * processed as an aid to debugging. */ static ndr_ref_t *ndr_enter_outer_queue(ndr_ref_t *); extern int ndr__ulong(ndr_ref_t *); /* * TOP-MOST ELEMENTS * * This is fundamentally the first OUTER construct of the parameter, * possibly followed by more OUTER constructs due to pointers. The * datum (local memory) for TOP-MOST constructs (structs) is allocated * by the caller of NDR. * * After the element is transferred, the outer_queue is drained. * * All we have to do is add an entry to the outer_queue for this * top-most member, and commence the outer_queue processing. */ int ndo_process(ndr_stream_t *nds, ndr_typeinfo_t *ti, char *datum) { ndr_ref_t myref; bzero(&myref, sizeof (myref)); myref.stream = nds; myref.datum = datum; myref.name = "PROCESS"; myref.ti = ti; return (ndr_topmost(&myref)); } int ndo_operation(ndr_stream_t *nds, ndr_typeinfo_t *ti, int opnum, char *datum) { ndr_ref_t myref; bzero(&myref, sizeof (myref)); myref.stream = nds; myref.datum = datum; myref.name = "OPERATION"; myref.ti = ti; myref.inner_flags = NDR_F_SWITCH_IS; myref.switch_is = opnum; if (ti->type_flags != NDR_F_INTERFACE) { NDR_SET_ERROR(&myref, NDR_ERR_NOT_AN_INTERFACE); return (0); } return ((*ti->ndr_func)(&myref)); } int ndr_params(ndr_ref_t *params_ref) { ndr_typeinfo_t *ti = params_ref->ti; if (ti->type_flags == NDR_F_OPERATION) return (*ti->ndr_func) (params_ref); else return (ndr_topmost(params_ref)); } int ndr_topmost(ndr_ref_t *top_ref) { ndr_stream_t *nds; ndr_typeinfo_t *ti; ndr_ref_t *outer_ref = 0; int is_varlen; int is_string; int error; int rc; unsigned n_fixed; int params; assert(top_ref); assert(top_ref->stream); assert(top_ref->ti); nds = top_ref->stream; ti = top_ref->ti; is_varlen = ti->pdu_size_variable_part; is_string = NDR_IS_STRING(ti); assert(nds->outer_queue_tailp && !*nds->outer_queue_tailp); assert(!nds->outer_current); params = top_ref->inner_flags & NDR_F_PARAMS_MASK; switch (params) { case NDR_F_NONE: case NDR_F_SWITCH_IS: if (is_string || is_varlen) { error = NDR_ERR_TOPMOST_VARLEN_ILLEGAL; NDR_SET_ERROR(outer_ref, error); return (0); } n_fixed = ti->pdu_size_fixed_part; break; case NDR_F_SIZE_IS: error = NDR_ERR_TOPMOST_VARLEN_ILLEGAL; NDR_SET_ERROR(outer_ref, error); return (0); case NDR_F_DIMENSION_IS: if (is_varlen) { error = NDR_ERR_ARRAY_VARLEN_ILLEGAL; NDR_SET_ERROR(outer_ref, error); return (0); } n_fixed = ti->pdu_size_fixed_part * top_ref->dimension_is; break; case NDR_F_IS_POINTER: case NDR_F_IS_POINTER+NDR_F_SIZE_IS: n_fixed = 4; break; case NDR_F_IS_REFERENCE: case NDR_F_IS_REFERENCE+NDR_F_SIZE_IS: n_fixed = 0; break; default: error = NDR_ERR_OUTER_PARAMS_BAD; NDR_SET_ERROR(outer_ref, error); return (0); } outer_ref = ndr_enter_outer_queue(top_ref); if (!outer_ref) return (0); /* error already set */ /* * Hand-craft the first OUTER construct and directly call * ndr_inner(). Then, run the outer_queue. We do this * because ndr_outer() wants to malloc() memory for * the construct, and we already have the memory. */ /* move the flags, etc, around again, undoes enter_outer_queue() */ outer_ref->inner_flags = top_ref->inner_flags; outer_ref->outer_flags = 0; outer_ref->datum = top_ref->datum; /* All outer constructs start on a mod4 (longword) boundary */ if (!ndr_outer_align(outer_ref)) return (0); /* error already set */ /* Regardless of what it is, this is where it starts */ outer_ref->pdu_offset = nds->pdu_scan_offset; rc = ndr_outer_grow(outer_ref, n_fixed); if (!rc) return (0); /* error already set */ outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_fixed; /* set-up outer_current, as though run_outer_queue() was doing it */ nds->outer_current = outer_ref; nds->outer_queue_tailp = &nds->outer_current->next; nds->pdu_scan_offset = outer_ref->pdu_end_offset; /* do the topmost member */ rc = ndr_inner(outer_ref); if (!rc) return (0); /* error already set */ nds->pdu_scan_offset = outer_ref->pdu_end_offset; /* advance, as though run_outer_queue() was doing it */ nds->outer_current = nds->outer_current->next; return (ndr_run_outer_queue(nds)); } static ndr_ref_t * ndr_enter_outer_queue(ndr_ref_t *arg_ref) { ndr_stream_t *nds = arg_ref->stream; ndr_ref_t *outer_ref; /*LINTED E_BAD_PTR_CAST_ALIGN*/ outer_ref = (ndr_ref_t *)NDS_MALLOC(nds, sizeof (*outer_ref), arg_ref); if (!outer_ref) { NDR_SET_ERROR(arg_ref, NDR_ERR_MALLOC_FAILED); return (0); } *outer_ref = *arg_ref; /* move advice in inner_flags to outer_flags */ outer_ref->outer_flags = arg_ref->inner_flags & NDR_F_PARAMS_MASK; outer_ref->inner_flags = 0; outer_ref->enclosing = nds->outer_current; outer_ref->backptr = 0; outer_ref->datum = 0; assert(nds->outer_queue_tailp); outer_ref->next = *nds->outer_queue_tailp; *nds->outer_queue_tailp = outer_ref; nds->outer_queue_tailp = &outer_ref->next; return (outer_ref); } int ndr_run_outer_queue(ndr_stream_t *nds) { while (nds->outer_current) { nds->outer_queue_tailp = &nds->outer_current->next; if (!ndr_outer(nds->outer_current)) return (0); nds->outer_current = nds->outer_current->next; } return (1); } /* * OUTER CONSTRUCTS * * OUTER constructs are where the real work is, which stems from the * variable-length potential. * * DCE(MS)/RPC VARIABLE LENGTH -- CONFORMANT, VARYING, VARYING/CONFORMANT * * DCE(MS)/RPC provides for three forms of variable length: CONFORMANT, * VARYING, and VARYING/CONFORMANT. * * What makes this so tough is that the variable-length array may be well * encapsulated within the outer construct. Further, because DCE(MS)/RPC * tries to keep the constructs contiguous in the data stream, the sizing * information precedes the entire OUTER construct. The sizing information * must be used at the appropriate time, which can be after many, many, * many fixed-length elements. During IDL type analysis, we know in * advance constructs that encapsulate variable-length constructs. So, * we know when we have a sizing header and when we don't. The actual * semantics of the header are largely deferred. * * Currently, VARYING constructs are not implemented but they are described * here in case they have to be implemented in the future. Similarly, * DCE(MS)/RPC provides for multi-dimensional arrays, which are currently * not implemented. Only one-dimensional, variable-length arrays are * supported. * * CONFORMANT CONSTRUCTS -- VARIABLE LENGTH ARRAYS START THE SHOW * * All variable-length values are arrays. These arrays may be embedded * well within another construct. However, a variable-length construct * may ONLY appear as the last member of an enclosing construct. Example: * * struct credentials { * ulong uid, gid; * ulong n_gids; * [size_is(n_gids)] * ulong gids[*]; // variable-length. * }; * * CONFORMANT constructs have a dynamic size in local memory and in the * PDU. The CONFORMANT quality is indicated by the [size_is()] advice. * CONFORMANT constructs have the following header: * * struct conformant_header { * ulong size_is; * }; * * (Multi-dimensional CONFORMANT arrays have a similar header for each * dimension - not implemented). * * Example CONFORMANT construct: * * struct user { * char * name; * struct credentials cred; // see above * }; * * Consider the data tree: * * +--------+ * | user | * +--------+ * | name ----> "fred" (the string is a different OUTER) * | uid | * | gid | * | n_gids | for example, 3 * | gids[0]| * | gids[1]| * | gids[2]| * +--------+ * * The OUTER construct in the Stub Data would be: * * +---+---------+---------------------------------------------+ * |pad|size_is=3 name uid gid n_gids=3 gids[0] gids[1] gids[2]| * +---+---------+---------------------------------------------+ * szing hdr|user |<-------------- user.cred ------------>| * |<--- fixed-size ---->|<----- conformant ---->| * * The ndr_typeinfo for struct user will have: * pdu_fixed_size_part = 16 four long words (name uid gid n_gids) * pdu_variable_size_part = 4 per element, sizeof gids[0] * * VARYING CONSTRUCTS -- NOT IMPLEMENTED * * VARYING constructs have the following header: * * struct varying_header { * ulong first_is; * ulong length_is; * }; * * This indicates which interval of an array is significant. * Non-intersecting elements of the array are undefined and usually * zero-filled. The first_is parameter for C arrays is always 0 for * the first element. * * N.B. Constructs may contain one CONFORMANT element, which is always * last, but may contain many VARYING elements, which can be anywhere. * * VARYING CONFORMANT constructs have the sizing headers arranged like * this: * * struct conformant_header all_conformant[N_CONFORMANT_DIM]; * struct varying_header all_varying[N_VARYING_ELEMS_AND_DIMS]; * * The sizing header is immediately followed by the values for the * construct. Again, we don't support more than one dimension and * we don't support VARYING constructs at this time. * * A good example of a VARYING/CONFORMANT data structure is the UNIX * directory entry: * * struct dirent { * ushort reclen; * ushort namlen; * ulong inum; * [size_is(reclen-8) length_is(namlen+1)] // -(2+2+4), +1 for NUL * uchar name[*]; * }; * * * STRINGS ARE A SPECIAL CASE * * Strings are handled specially. MS/RPC uses VARYING/CONFORMANT structures * for strings. This is a simple one-dimensional variable-length array, * typically with its last element all zeroes. We handle strings with the * header: * * struct string_header { * ulong size_is; * ulong first_is; // always 0 * ulong length_is; // always same as size_is * }; * * If general support for VARYING and VARYING/CONFORMANT mechanisms is * implemented, we probably won't need the strings special case. */ int ndr_outer(ndr_ref_t *outer_ref) { ndr_stream_t *nds = outer_ref->stream; ndr_typeinfo_t *ti = outer_ref->ti; int is_varlen = ti->pdu_size_variable_part; int is_union = NDR_IS_UNION(ti); int is_string = NDR_IS_STRING(ti); int error = NDR_ERR_OUTER_PARAMS_BAD; int params; params = outer_ref->outer_flags & NDR_F_PARAMS_MASK; NDR_TATTLE(outer_ref, "--OUTER--"); /* All outer constructs start on a mod4 (longword) boundary */ if (!ndr_outer_align(outer_ref)) return (0); /* error already set */ /* Regardless of what it is, this is where it starts */ outer_ref->pdu_offset = nds->pdu_scan_offset; if (is_union) { error = NDR_ERR_OUTER_UNION_ILLEGAL; NDR_SET_ERROR(outer_ref, error); return (0); } switch (params) { case NDR_F_NONE: if (is_string) return (ndr_outer_string(outer_ref)); if (is_varlen) return (ndr_outer_conformant_construct(outer_ref)); return (ndr_outer_fixed(outer_ref)); case NDR_F_SIZE_IS: case NDR_F_DIMENSION_IS: case NDR_F_IS_POINTER+NDR_F_SIZE_IS: case NDR_F_IS_REFERENCE+NDR_F_SIZE_IS: if (is_varlen) { error = NDR_ERR_ARRAY_VARLEN_ILLEGAL; break; } if (params & NDR_F_SIZE_IS) return (ndr_outer_conformant_array(outer_ref)); else return (ndr_outer_fixed_array(outer_ref)); default: error = NDR_ERR_OUTER_PARAMS_BAD; break; } /* * If we get here, something is wrong. Most likely, * the params flags do not match. */ NDR_SET_ERROR(outer_ref, error); return (0); } int ndr_outer_fixed(ndr_ref_t *outer_ref) { ndr_stream_t *nds = outer_ref->stream; ndr_typeinfo_t *ti = outer_ref->ti; ndr_ref_t myref; char *valp = NULL; int is_varlen = ti->pdu_size_variable_part; int is_union = NDR_IS_UNION(ti); int is_string = NDR_IS_STRING(ti); int rc; unsigned n_hdr; unsigned n_fixed; unsigned n_variable; unsigned n_alloc; unsigned n_pdu_total; int params; params = outer_ref->outer_flags & NDR_F_PARAMS_MASK; assert(!is_varlen && !is_string && !is_union); assert(params == NDR_F_NONE); /* no header for this */ n_hdr = 0; /* fixed part -- exactly one of these */ n_fixed = ti->pdu_size_fixed_part; assert(n_fixed > 0); /* variable part -- exactly none of these */ n_variable = 0; /* sum them up to determine the PDU space required */ n_pdu_total = n_hdr + n_fixed + n_variable; /* similar sum to determine how much local memory is required */ n_alloc = n_fixed + n_variable; rc = ndr_outer_grow(outer_ref, n_pdu_total); if (!rc) return (rc); /* error already set */ switch (nds->m_op) { case NDR_M_OP_MARSHALL: valp = outer_ref->datum; if (!valp) { NDR_SET_ERROR(outer_ref, NDR_ERR_OUTER_PARAMS_BAD); return (0); } if (outer_ref->backptr) assert(valp == *outer_ref->backptr); break; case NDR_M_OP_UNMARSHALL: valp = NDS_MALLOC(nds, n_alloc, outer_ref); if (!valp) { NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED); return (0); } if (outer_ref->backptr) *outer_ref->backptr = valp; outer_ref->datum = valp; break; default: NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID); return (0); } bzero(&myref, sizeof (myref)); myref.stream = nds; myref.enclosing = outer_ref; myref.ti = outer_ref->ti; myref.datum = outer_ref->datum; myref.name = "FIXED-VALUE"; myref.outer_flags = NDR_F_NONE; myref.inner_flags = NDR_F_NONE; myref.pdu_offset = outer_ref->pdu_offset; outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total; rc = ndr_inner(&myref); if (!rc) return (rc); /* error already set */ nds->pdu_scan_offset = outer_ref->pdu_end_offset; return (1); } int ndr_outer_fixed_array(ndr_ref_t *outer_ref) { ndr_stream_t *nds = outer_ref->stream; ndr_typeinfo_t *ti = outer_ref->ti; ndr_ref_t myref; char *valp = NULL; int is_varlen = ti->pdu_size_variable_part; int is_union = NDR_IS_UNION(ti); int is_string = NDR_IS_STRING(ti); int rc; unsigned n_hdr; unsigned n_fixed; unsigned n_variable; unsigned n_alloc; unsigned n_pdu_total; int params; params = outer_ref->outer_flags & NDR_F_PARAMS_MASK; assert(!is_varlen && !is_string && !is_union); assert(params == NDR_F_DIMENSION_IS); /* no header for this */ n_hdr = 0; /* fixed part -- exactly dimension_is of these */ n_fixed = ti->pdu_size_fixed_part * outer_ref->dimension_is; assert(n_fixed > 0); /* variable part -- exactly none of these */ n_variable = 0; /* sum them up to determine the PDU space required */ n_pdu_total = n_hdr + n_fixed + n_variable; /* similar sum to determine how much local memory is required */ n_alloc = n_fixed + n_variable; rc = ndr_outer_grow(outer_ref, n_pdu_total); if (!rc) return (rc); /* error already set */ switch (nds->m_op) { case NDR_M_OP_MARSHALL: valp = outer_ref->datum; if (!valp) { NDR_SET_ERROR(outer_ref, NDR_ERR_OUTER_PARAMS_BAD); return (0); } if (outer_ref->backptr) assert(valp == *outer_ref->backptr); break; case NDR_M_OP_UNMARSHALL: valp = NDS_MALLOC(nds, n_alloc, outer_ref); if (!valp) { NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED); return (0); } if (outer_ref->backptr) *outer_ref->backptr = valp; outer_ref->datum = valp; break; default: NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID); return (0); } bzero(&myref, sizeof (myref)); myref.stream = nds; myref.enclosing = outer_ref; myref.ti = outer_ref->ti; myref.datum = outer_ref->datum; myref.name = "FIXED-ARRAY"; myref.outer_flags = NDR_F_NONE; myref.inner_flags = NDR_F_DIMENSION_IS; myref.dimension_is = outer_ref->dimension_is; myref.pdu_offset = outer_ref->pdu_offset; outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total; rc = ndr_inner(&myref); if (!rc) return (rc); /* error already set */ nds->pdu_scan_offset = outer_ref->pdu_end_offset; return (1); } int ndr_outer_conformant_array(ndr_ref_t *outer_ref) { ndr_stream_t *nds = outer_ref->stream; ndr_typeinfo_t *ti = outer_ref->ti; ndr_ref_t myref; char *valp = NULL; int is_varlen = ti->pdu_size_variable_part; int is_union = NDR_IS_UNION(ti); int is_string = NDR_IS_STRING(ti); unsigned long size_is; int rc; unsigned n_hdr; unsigned n_fixed; unsigned n_variable; unsigned n_alloc; unsigned n_pdu_total; unsigned n_ptr_offset; int params; params = outer_ref->outer_flags & NDR_F_PARAMS_MASK; assert(!is_varlen && !is_string && !is_union); assert(params & NDR_F_SIZE_IS); /* conformant header for this */ n_hdr = 4; /* fixed part -- exactly none of these */ n_fixed = 0; /* variable part -- exactly size_of of these */ /* notice that it is the **fixed** size of the ti */ n_variable = ti->pdu_size_fixed_part * outer_ref->size_is; /* sum them up to determine the PDU space required */ n_pdu_total = n_hdr + n_fixed + n_variable; /* similar sum to determine how much local memory is required */ n_alloc = n_fixed + n_variable; rc = ndr_outer_grow(outer_ref, n_pdu_total); if (!rc) return (rc); /* error already set */ switch (nds->m_op) { case NDR_M_OP_MARSHALL: size_is = outer_ref->size_is; rc = ndr_outer_poke_sizing(outer_ref, 0, &size_is); if (!rc) return (0); /* error already set */ valp = outer_ref->datum; if (!valp) { NDR_SET_ERROR(outer_ref, NDR_ERR_OUTER_PARAMS_BAD); return (0); } if (outer_ref->backptr) assert(valp == *outer_ref->backptr); n_ptr_offset = 4; break; case NDR_M_OP_UNMARSHALL: if (params & NDR_F_IS_REFERENCE) { size_is = outer_ref->size_is; n_ptr_offset = 0; } else { /* NDR_F_IS_POINTER */ rc = ndr_outer_peek_sizing(outer_ref, 0, &size_is); if (!rc) return (0); /* error already set */ if (size_is != outer_ref->size_is) { NDR_SET_ERROR(outer_ref, NDR_ERR_SIZE_IS_MISMATCH_PDU); return (0); } n_ptr_offset = 4; } if (size_is > 0) { valp = NDS_MALLOC(nds, n_alloc, outer_ref); if (!valp) { NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED); return (0); } } if (outer_ref->backptr) *outer_ref->backptr = valp; outer_ref->datum = valp; break; default: NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID); return (0); } outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total; outer_ref->type_flags = NDR_F_NONE; outer_ref->inner_flags = NDR_F_NONE; if (size_is > 0) { bzero(&myref, sizeof (myref)); myref.stream = nds; myref.enclosing = outer_ref; myref.ti = outer_ref->ti; myref.datum = outer_ref->datum; myref.name = "CONFORMANT-ARRAY"; myref.outer_flags = NDR_F_NONE; myref.inner_flags = NDR_F_SIZE_IS; myref.size_is = outer_ref->size_is; myref.inner_flags = NDR_F_DIMENSION_IS; /* convenient */ myref.dimension_is = outer_ref->size_is; /* convenient */ myref.pdu_offset = outer_ref->pdu_offset + n_ptr_offset; rc = ndr_inner(&myref); if (!rc) return (rc); /* error already set */ } nds->pdu_scan_offset = outer_ref->pdu_end_offset; return (1); } int ndr_outer_conformant_construct(ndr_ref_t *outer_ref) { ndr_stream_t *nds = outer_ref->stream; ndr_typeinfo_t *ti = outer_ref->ti; ndr_ref_t myref; char *valp = NULL; int is_varlen = ti->pdu_size_variable_part; int is_union = NDR_IS_UNION(ti); int is_string = NDR_IS_STRING(ti); unsigned long size_is; int rc; unsigned n_hdr; unsigned n_fixed; unsigned n_variable; unsigned n_alloc; unsigned n_pdu_total; int params; params = outer_ref->outer_flags & NDR_F_PARAMS_MASK; assert(is_varlen && !is_string && !is_union); assert(params == NDR_F_NONE); /* conformant header for this */ n_hdr = 4; /* fixed part -- exactly one of these */ n_fixed = ti->pdu_size_fixed_part; /* variable part -- exactly size_of of these */ n_variable = 0; /* 0 for the moment */ /* sum them up to determine the PDU space required */ n_pdu_total = n_hdr + n_fixed + n_variable; /* similar sum to determine how much local memory is required */ n_alloc = n_fixed + n_variable; /* For the moment, grow enough for the fixed-size part */ rc = ndr_outer_grow(outer_ref, n_pdu_total); if (!rc) return (rc); /* error already set */ switch (nds->m_op) { case NDR_M_OP_MARSHALL: /* * We don't know the size yet. We have to wait for * it. Proceed with the fixed-size part, and await * the call to ndr_size_is(). */ size_is = 0; rc = ndr_outer_poke_sizing(outer_ref, 0, &size_is); if (!rc) return (0); /* error already set */ valp = outer_ref->datum; if (!valp) { NDR_SET_ERROR(outer_ref, NDR_ERR_OUTER_PARAMS_BAD); return (0); } if (outer_ref->backptr) assert(valp == *outer_ref->backptr); break; case NDR_M_OP_UNMARSHALL: /* * We know the size of the variable part because * of the CONFORMANT header. We will verify * the header against the [size_is(X)] advice * later when ndr_size_is() is called. */ rc = ndr_outer_peek_sizing(outer_ref, 0, &size_is); if (!rc) return (0); /* error already set */ /* recalculate metrics */ n_variable = size_is * ti->pdu_size_variable_part; n_pdu_total = n_hdr + n_fixed + n_variable; n_alloc = n_fixed + n_variable; rc = ndr_outer_grow(outer_ref, n_pdu_total); if (!rc) return (rc); /* error already set */ outer_ref->size_is = size_is; /* verified later */ valp = NDS_MALLOC(nds, n_alloc, outer_ref); if (!valp) { NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED); return (0); } if (outer_ref->backptr) *outer_ref->backptr = valp; outer_ref->datum = valp; break; default: NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID); return (0); } outer_ref->pdu_end_offset = outer_ref->pdu_offset + n_pdu_total; outer_ref->type_flags = NDR_F_SIZE_IS; /* indicate pending */ outer_ref->inner_flags = NDR_F_NONE; /* indicate pending */ bzero(&myref, sizeof (myref)); myref.stream = nds; myref.enclosing = outer_ref; myref.ti = outer_ref->ti; myref.datum = outer_ref->datum; myref.name = "CONFORMANT-CONSTRUCT"; myref.outer_flags = NDR_F_NONE; myref.inner_flags = NDR_F_NONE; myref.size_is = outer_ref->size_is; myref.pdu_offset = outer_ref->pdu_offset + 4; rc = ndr_inner(&myref); if (!rc) return (rc); /* error already set */ nds->pdu_scan_offset = outer_ref->pdu_end_offset; if (outer_ref->inner_flags != NDR_F_SIZE_IS) { NDR_SET_ERROR(&myref, NDR_ERR_SIZE_IS_MISMATCH_AFTER); return (0); } return (1); } int ndr_size_is(ndr_ref_t *ref) { ndr_stream_t *nds = ref->stream; ndr_ref_t *outer_ref = nds->outer_current; ndr_typeinfo_t *ti = outer_ref->ti; unsigned long size_is; int rc; unsigned n_hdr; unsigned n_fixed; unsigned n_variable; unsigned n_pdu_total; assert(ref->inner_flags & NDR_F_SIZE_IS); size_is = ref->size_is; if (outer_ref->type_flags != NDR_F_SIZE_IS) { NDR_SET_ERROR(ref, NDR_ERR_SIZE_IS_UNEXPECTED); return (0); } if (outer_ref->inner_flags & NDR_F_SIZE_IS) { NDR_SET_ERROR(ref, NDR_ERR_SIZE_IS_DUPLICATED); return (0); } /* repeat metrics, see ndr_conformant_construct() above */ n_hdr = 4; n_fixed = ti->pdu_size_fixed_part; n_variable = size_is * ti->pdu_size_variable_part; n_pdu_total = n_hdr + n_fixed + n_variable; rc = ndr_outer_grow(outer_ref, n_pdu_total); if (!rc) return (rc); /* error already set */ switch (nds->m_op) { case NDR_M_OP_MARSHALL: /* * We have to set the sizing header and extend * the size of the PDU (already done). */ rc = ndr_outer_poke_sizing(outer_ref, 0, &size_is); if (!rc) return (0); /* error already set */ break; case NDR_M_OP_UNMARSHALL: /* * Allocation done during ndr_conformant_construct(). * All we are doing here is verifying that the * intended size (ref->size_is) matches the sizing header. */ if (size_is != outer_ref->size_is) { NDR_SET_ERROR(ref, NDR_ERR_SIZE_IS_MISMATCH_PDU); return (0); } break; default: NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID); return (0); } outer_ref->inner_flags |= NDR_F_SIZE_IS; outer_ref->size_is = ref->size_is; return (1); } int ndr_outer_string(ndr_ref_t *outer_ref) { ndr_stream_t *nds = outer_ref->stream; ndr_typeinfo_t *ti = outer_ref->ti; ndr_ref_t myref; char *valp = NULL; unsigned is_varlen = ti->pdu_size_variable_part; int is_union = NDR_IS_UNION(ti); int is_string = NDR_IS_STRING(ti); int rc; unsigned n_zeroes; unsigned ix; unsigned long size_is; unsigned long first_is; unsigned long length_is; unsigned n_hdr; unsigned n_fixed; unsigned n_variable; unsigned n_alloc; unsigned n_pdu_total; int params; params = outer_ref->outer_flags & NDR_F_PARAMS_MASK; assert(is_varlen && is_string && !is_union); assert(params == NDR_F_NONE); /* string header for this: size_is first_is length_is */ n_hdr = 12; /* fixed part -- exactly none of these */ n_fixed = 0; if (!ndr_outer_grow(outer_ref, n_hdr)) return (0); /* error already set */ switch (nds->m_op) { case NDR_M_OP_MARSHALL: valp = outer_ref->datum; if (!valp) { NDR_SET_ERROR(outer_ref, NDR_ERR_OUTER_PARAMS_BAD); return (0); } if (outer_ref->backptr) assert(valp == *outer_ref->backptr); if (ti == &ndt_s_wchar) { /* * size_is is the number of characters in the * (multibyte) string, including the null. */ size_is = smb_wcequiv_strlen(valp) / sizeof (smb_wchar_t); if (!(nds->flags & NDS_F_NONULL)) ++size_is; if (size_is > NDR_STRING_MAX) { NDR_SET_ERROR(outer_ref, NDR_ERR_STRLEN); return (0); } } else { valp = outer_ref->datum; n_zeroes = 0; for (ix = 0; ix < NDR_STRING_MAX; ix++) { if (valp[ix] == 0) { n_zeroes++; if (n_zeroes >= is_varlen && ix % is_varlen == 0) { break; } } else { n_zeroes = 0; } } if (ix >= NDR_STRING_MAX) { NDR_SET_ERROR(outer_ref, NDR_ERR_STRLEN); return (0); } size_is = ix+1; } first_is = 0; if (nds->flags & NDS_F_NOTERM) length_is = size_is - 1; else length_is = size_is; if (!ndr_outer_poke_sizing(outer_ref, 0, &size_is) || !ndr_outer_poke_sizing(outer_ref, 4, &first_is) || !ndr_outer_poke_sizing(outer_ref, 8, &length_is)) return (0); /* error already set */ break; case NDR_M_OP_UNMARSHALL: if (!ndr_outer_peek_sizing(outer_ref, 0, &size_is) || !ndr_outer_peek_sizing(outer_ref, 4, &first_is) || !ndr_outer_peek_sizing(outer_ref, 8, &length_is)) return (0); /* error already set */ /* * In addition to the first_is check, we used to check that * size_is or size_is-1 was equal to length_is but Windows95 * doesn't conform to this "rule" (see variable part below). * The srvmgr tool for Windows95 sent the following values * for a path string: * * size_is = 261 (0x105) * first_is = 0 * length_is = 53 (0x35) * * The length_is was correct (for the given path) but the * size_is was the maximum path length rather than being * related to length_is. */ if (first_is != 0) { NDR_SET_ERROR(outer_ref, NDR_ERR_STRING_SIZING); return (0); } if (ti == &ndt_s_wchar) { /* * Decoding Unicode to UTF-8; we need to allow * for the maximum possible char size. It would * be nice to use mbequiv_strlen but the string * may not be null terminated. */ n_alloc = (size_is + 1) * MTS_MB_CHAR_MAX; } else { n_alloc = (size_is + 1) * is_varlen; } valp = NDS_MALLOC(nds, n_alloc, outer_ref); if (!valp) { NDR_SET_ERROR(outer_ref, NDR_ERR_MALLOC_FAILED); return (0); } bzero(valp, (size_is+1) * is_varlen); if (outer_ref->backptr) *outer_ref->backptr = valp; outer_ref->datum = valp; break; default: NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID); return (0); } /* * Variable part - exactly length_is of these. * * Usually, length_is is same as size_is and includes nul. * Some protocols use length_is = size_is-1, and length_is does * not include the nul (which is more consistent with DCE spec). * If the length_is is 0, there is no data following the * sizing header, regardless of size_is. */ n_variable = length_is * is_varlen; /* sum them up to determine the PDU space required */ n_pdu_total = n_hdr + n_fixed + n_variable; /* similar sum to determine how much local memory is required */ n_alloc = n_fixed + n_variable; rc = ndr_outer_grow(outer_ref, n_pdu_total); if (!rc) return (rc); /* error already set */ if (length_is > 0) { bzero(&myref, sizeof (myref)); myref.stream = nds; myref.enclosing = outer_ref; myref.ti = outer_ref->ti; myref.datum = outer_ref->datum; myref.name = "OUTER-STRING"; myref.outer_flags = NDR_F_IS_STRING; myref.inner_flags = NDR_F_NONE; /* * Set up size_is and strlen_is for ndr_s_wchar. */ myref.size_is = size_is; myref.strlen_is = length_is; } myref.pdu_offset = outer_ref->pdu_offset + 12; /* * Don't try to decode empty strings. */ if ((size_is == 0) && (first_is == 0) && (length_is == 0)) { nds->pdu_scan_offset = outer_ref->pdu_end_offset; return (1); } if ((size_is != 0) && (length_is != 0)) { rc = ndr_inner(&myref); if (!rc) return (rc); /* error already set */ } nds->pdu_scan_offset = outer_ref->pdu_end_offset; return (1); } int ndr_outer_peek_sizing(ndr_ref_t *outer_ref, unsigned offset, unsigned long *sizing_p) { ndr_stream_t *nds = outer_ref->stream; unsigned long pdu_offset; int rc; pdu_offset = outer_ref->pdu_offset + offset; if (pdu_offset < nds->outer_current->pdu_offset || pdu_offset > nds->outer_current->pdu_end_offset || pdu_offset+4 > nds->outer_current->pdu_end_offset) { NDR_SET_ERROR(outer_ref, NDR_ERR_BOUNDS_CHECK); return (0); } switch (nds->m_op) { case NDR_M_OP_MARSHALL: NDR_SET_ERROR(outer_ref, NDR_ERR_UNIMPLEMENTED); return (0); case NDR_M_OP_UNMARSHALL: rc = NDS_GET_PDU(nds, pdu_offset, 4, (char *)sizing_p, nds->swap, outer_ref); break; default: NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID); return (0); } return (rc); } int ndr_outer_poke_sizing(ndr_ref_t *outer_ref, unsigned offset, unsigned long *sizing_p) { ndr_stream_t *nds = outer_ref->stream; unsigned long pdu_offset; int rc; pdu_offset = outer_ref->pdu_offset + offset; if (pdu_offset < nds->outer_current->pdu_offset || pdu_offset > nds->outer_current->pdu_end_offset || pdu_offset+4 > nds->outer_current->pdu_end_offset) { NDR_SET_ERROR(outer_ref, NDR_ERR_BOUNDS_CHECK); return (0); } switch (nds->m_op) { case NDR_M_OP_MARSHALL: rc = NDS_PUT_PDU(nds, pdu_offset, 4, (char *)sizing_p, nds->swap, outer_ref); break; case NDR_M_OP_UNMARSHALL: NDR_SET_ERROR(outer_ref, NDR_ERR_UNIMPLEMENTED); return (0); default: NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID); return (0); } return (rc); } /* * All OUTER constructs begin on a mod4 (dword) boundary - except * for the ones that don't: some MSRPC calls appear to use word or * packed alignment. Strings appear to be dword aligned. */ int ndr_outer_align(ndr_ref_t *outer_ref) { ndr_stream_t *nds = outer_ref->stream; int rc; unsigned n_pad; unsigned align; if (outer_ref->packed_alignment && outer_ref->ti != &ndt_s_wchar) { align = outer_ref->ti->alignment; n_pad = ((align + 1) - nds->pdu_scan_offset) & align; } else { n_pad = NDR_ALIGN4(nds->pdu_scan_offset); } if (n_pad == 0) return (1); /* already aligned, often the case */ if (!ndr_outer_grow(outer_ref, n_pad)) return (0); /* error already set */ switch (nds->m_op) { case NDR_M_OP_MARSHALL: rc = NDS_PAD_PDU(nds, nds->pdu_scan_offset, n_pad, outer_ref); if (!rc) { NDR_SET_ERROR(outer_ref, NDR_ERR_PAD_FAILED); return (0); } break; case NDR_M_OP_UNMARSHALL: break; default: NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID); return (0); } nds->pdu_scan_offset += n_pad; return (1); } int ndr_outer_grow(ndr_ref_t *outer_ref, unsigned n_total) { ndr_stream_t *nds = outer_ref->stream; unsigned long pdu_want_size; int rc, is_ok = 0; pdu_want_size = nds->pdu_scan_offset + n_total; if (pdu_want_size <= nds->pdu_max_size) { is_ok = 1; } switch (nds->m_op) { case NDR_M_OP_MARSHALL: if (is_ok) break; rc = NDS_GROW_PDU(nds, pdu_want_size, outer_ref); if (!rc) { NDR_SET_ERROR(outer_ref, NDR_ERR_GROW_FAILED); return (0); } break; case NDR_M_OP_UNMARSHALL: if (is_ok) break; NDR_SET_ERROR(outer_ref, NDR_ERR_UNDERFLOW); return (0); default: NDR_SET_ERROR(outer_ref, NDR_ERR_M_OP_INVALID); return (0); } if (nds->pdu_size < pdu_want_size) nds->pdu_size = pdu_want_size; outer_ref->pdu_end_offset = pdu_want_size; return (1); } /* * INNER ELEMENTS * * The local datum (arg_ref->datum) already exists, there is no need to * malloc() it. The datum should point at a member of a structure. * * For the most part, ndr_inner() and its helpers are just a sanity * check. The underlying ti->ndr_func() could be called immediately * for non-pointer elements. For the sake of robustness, we detect * run-time errors here. Most of the situations this protects against * have already been checked by the IDL compiler. This is also a * common point for processing of all data, and so is a convenient * place to work from for debugging. */ int ndr_inner(ndr_ref_t *arg_ref) { ndr_typeinfo_t *ti = arg_ref->ti; int is_varlen = ti->pdu_size_variable_part; int is_union = NDR_IS_UNION(ti); int error = NDR_ERR_INNER_PARAMS_BAD; int params; params = arg_ref->inner_flags & NDR_F_PARAMS_MASK; switch (params) { case NDR_F_NONE: if (is_union) { error = NDR_ERR_SWITCH_VALUE_MISSING; break; } return (*ti->ndr_func)(arg_ref); case NDR_F_SIZE_IS: case NDR_F_DIMENSION_IS: case NDR_F_IS_POINTER+NDR_F_SIZE_IS: /* pointer to something */ case NDR_F_IS_REFERENCE+NDR_F_SIZE_IS: /* pointer to something */ if (is_varlen) { error = NDR_ERR_ARRAY_VARLEN_ILLEGAL; break; } if (is_union) { error = NDR_ERR_ARRAY_UNION_ILLEGAL; break; } if (params & NDR_F_IS_POINTER) return (ndr_inner_pointer(arg_ref)); else if (params & NDR_F_IS_REFERENCE) return (ndr_inner_reference(arg_ref)); else return (ndr_inner_array(arg_ref)); case NDR_F_IS_POINTER: /* type is pointer to one something */ if (is_union) { error = NDR_ERR_ARRAY_UNION_ILLEGAL; break; } return (ndr_inner_pointer(arg_ref)); case NDR_F_IS_REFERENCE: /* type is pointer to one something */ if (is_union) { error = NDR_ERR_ARRAY_UNION_ILLEGAL; break; } return (ndr_inner_reference(arg_ref)); case NDR_F_SWITCH_IS: if (!is_union) { error = NDR_ERR_SWITCH_VALUE_ILLEGAL; break; } return (*ti->ndr_func)(arg_ref); default: error = NDR_ERR_INNER_PARAMS_BAD; break; } /* * If we get here, something is wrong. Most likely, * the params flags do not match */ NDR_SET_ERROR(arg_ref, error); return (0); } int ndr_inner_pointer(ndr_ref_t *arg_ref) { ndr_stream_t *nds = arg_ref->stream; /*LINTED E_BAD_PTR_CAST_ALIGN*/ char **valpp = (char **)arg_ref->datum; ndr_ref_t *outer_ref; if (!ndr__ulong(arg_ref)) return (0); /* error */ if (!*valpp) return (1); /* NULL pointer */ outer_ref = ndr_enter_outer_queue(arg_ref); if (!outer_ref) return (0); /* error already set */ /* * Move advice in inner_flags to outer_flags. * Retain pointer flag for conformant arrays. */ outer_ref->outer_flags = arg_ref->inner_flags & NDR_F_PARAMS_MASK; if ((outer_ref->outer_flags & NDR_F_SIZE_IS) == 0) outer_ref->outer_flags &= ~NDR_F_IS_POINTER; #ifdef NDR_INNER_PTR_NOT_YET outer_ref->outer_flags |= NDR_F_BACKPTR; if (outer_ref->outer_flags & NDR_F_SIZE_IS) { outer_ref->outer_flags |= NDR_F_ARRAY+NDR_F_CONFORMANT; } #endif /* NDR_INNER_PTR_NOT_YET */ outer_ref->backptr = valpp; switch (nds->m_op) { case NDR_M_OP_MARSHALL: outer_ref->datum = *valpp; break; case NDR_M_OP_UNMARSHALL: /* * This is probably wrong if the application allocated * memory in advance. Indicate no value for now. * ONC RPC handles this case. */ *valpp = 0; outer_ref->datum = 0; break; } return (1); /* pointer dereference scheduled */ } int ndr_inner_reference(ndr_ref_t *arg_ref) { ndr_stream_t *nds = arg_ref->stream; /*LINTED E_BAD_PTR_CAST_ALIGN*/ char **valpp = (char **)arg_ref->datum; ndr_ref_t *outer_ref; outer_ref = ndr_enter_outer_queue(arg_ref); if (!outer_ref) return (0); /* error already set */ /* * Move advice in inner_flags to outer_flags. * Retain reference flag for conformant arrays. */ outer_ref->outer_flags = arg_ref->inner_flags & NDR_F_PARAMS_MASK; if ((outer_ref->outer_flags & NDR_F_SIZE_IS) == 0) outer_ref->outer_flags &= ~NDR_F_IS_REFERENCE; #ifdef NDR_INNER_REF_NOT_YET outer_ref->outer_flags |= NDR_F_BACKPTR; if (outer_ref->outer_flags & NDR_F_SIZE_IS) { outer_ref->outer_flags |= NDR_F_ARRAY+NDR_F_CONFORMANT; } #endif /* NDR_INNER_REF_NOT_YET */ outer_ref->backptr = valpp; switch (nds->m_op) { case NDR_M_OP_MARSHALL: outer_ref->datum = *valpp; break; case NDR_M_OP_UNMARSHALL: /* * This is probably wrong if the application allocated * memory in advance. Indicate no value for now. * ONC RPC handles this case. */ *valpp = 0; outer_ref->datum = 0; break; } return (1); /* pointer dereference scheduled */ } int ndr_inner_array(ndr_ref_t *encl_ref) { ndr_typeinfo_t *ti = encl_ref->ti; ndr_ref_t myref; unsigned long pdu_offset = encl_ref->pdu_offset; unsigned long n_elem; unsigned long i; char name[30]; if (encl_ref->inner_flags & NDR_F_SIZE_IS) { /* now is the time to check/set size */ if (!ndr_size_is(encl_ref)) return (0); /* error already set */ n_elem = encl_ref->size_is; } else { assert(encl_ref->inner_flags & NDR_F_DIMENSION_IS); n_elem = encl_ref->dimension_is; } bzero(&myref, sizeof (myref)); myref.enclosing = encl_ref; myref.stream = encl_ref->stream; myref.packed_alignment = 0; myref.ti = ti; myref.inner_flags = NDR_F_NONE; for (i = 0; i < n_elem; i++) { (void) sprintf(name, "[%lu]", i); myref.name = name; myref.pdu_offset = pdu_offset + i * ti->pdu_size_fixed_part; myref.datum = encl_ref->datum + i * ti->c_size_fixed_part; if (!ndr_inner(&myref)) return (0); } return (1); } /* * BASIC TYPES */ #define MAKE_BASIC_TYPE_BASE(TYPE, SIZE) \ extern int ndr_##TYPE(struct ndr_reference *encl_ref); \ ndr_typeinfo_t ndt_##TYPE = { \ 1, /* NDR version */ \ (SIZE)-1, /* alignment */ \ NDR_F_NONE, /* flags */ \ ndr_##TYPE, /* ndr_func */ \ SIZE, /* pdu_size_fixed_part */ \ 0, /* pdu_size_variable_part */ \ SIZE, /* c_size_fixed_part */ \ 0, /* c_size_variable_part */ \ }; \ int ndr_##TYPE(struct ndr_reference *ref) { \ return (ndr_basic_integer(ref, SIZE)); \ } #define MAKE_BASIC_TYPE_STRING(TYPE, SIZE) \ extern int ndr_s##TYPE(struct ndr_reference *encl_ref); \ ndr_typeinfo_t ndt_s##TYPE = { \ 1, /* NDR version */ \ (SIZE)-1, /* alignment */ \ NDR_F_STRING, /* flags */ \ ndr_s##TYPE, /* ndr_func */ \ 0, /* pdu_size_fixed_part */ \ SIZE, /* pdu_size_variable_part */ \ 0, /* c_size_fixed_part */ \ SIZE, /* c_size_variable_part */ \ }; \ int ndr_s##TYPE(struct ndr_reference *ref) { \ return (ndr_string_basic_integer(ref, &ndt_##TYPE)); \ } #define MAKE_BASIC_TYPE(TYPE, SIZE) \ MAKE_BASIC_TYPE_BASE(TYPE, SIZE) \ MAKE_BASIC_TYPE_STRING(TYPE, SIZE) int ndr_basic_integer(ndr_ref_t *, unsigned); int ndr_string_basic_integer(ndr_ref_t *, ndr_typeinfo_t *); MAKE_BASIC_TYPE(_char, 1) MAKE_BASIC_TYPE(_uchar, 1) MAKE_BASIC_TYPE(_short, 2) MAKE_BASIC_TYPE(_ushort, 2) MAKE_BASIC_TYPE(_long, 4) MAKE_BASIC_TYPE(_ulong, 4) MAKE_BASIC_TYPE_BASE(_wchar, 2) int ndr_basic_integer(ndr_ref_t *ref, unsigned size) { ndr_stream_t *nds = ref->stream; char *valp = (char *)ref->datum; int rc; switch (nds->m_op) { case NDR_M_OP_MARSHALL: rc = NDS_PUT_PDU(nds, ref->pdu_offset, size, valp, nds->swap, ref); break; case NDR_M_OP_UNMARSHALL: rc = NDS_GET_PDU(nds, ref->pdu_offset, size, valp, nds->swap, ref); break; default: NDR_SET_ERROR(ref, NDR_ERR_M_OP_INVALID); return (0); } return (rc); } int ndr_string_basic_integer(ndr_ref_t *encl_ref, ndr_typeinfo_t *type_under) { unsigned long pdu_offset = encl_ref->pdu_offset; unsigned size = type_under->pdu_size_fixed_part; char *valp; ndr_ref_t myref; unsigned long i; long sense = 0; char name[30]; assert(size != 0); bzero(&myref, sizeof (myref)); myref.enclosing = encl_ref; myref.stream = encl_ref->stream; myref.packed_alignment = 0; myref.ti = type_under; myref.inner_flags = NDR_F_NONE; myref.name = name; for (i = 0; i < NDR_STRING_MAX; i++) { (void) sprintf(name, "[%lu]", i); myref.pdu_offset = pdu_offset + i * size; valp = encl_ref->datum + i * size; myref.datum = valp; if (!ndr_inner(&myref)) return (0); switch (size) { case 1: sense = *valp; break; /*LINTED E_BAD_PTR_CAST_ALIGN*/ case 2: sense = *(short *)valp; break; /*LINTED E_BAD_PTR_CAST_ALIGN*/ case 4: sense = *(long *)valp; break; } if (!sense) break; } return (1); } extern int ndr_s_wchar(ndr_ref_t *encl_ref); ndr_typeinfo_t ndt_s_wchar = { 1, /* NDR version */ 2-1, /* alignment */ NDR_F_STRING, /* flags */ ndr_s_wchar, /* ndr_func */ 0, /* pdu_size_fixed_part */ 2, /* pdu_size_variable_part */ 0, /* c_size_fixed_part */ 1, /* c_size_variable_part */ }; /* * Hand coded wchar function because all strings are transported * as wide characters. During NDR_M_OP_MARSHALL, we convert from * multi-byte to wide characters. During NDR_M_OP_UNMARSHALL, we * convert from wide characters to multi-byte. * * It appeared that NT would sometimes leave a spurious character * in the data stream before the null wide_char, which would get * included in the string decode because we processed until the * null character. It now looks like NT does not always terminate * RPC Unicode strings and the terminating null is a side effect * of field alignment. So now we rely on the strlen_is (set up in * ndr_outer_string) of the enclosing reference. This may or may * not include the null but it doesn't matter, the algorithm will * get it right. */ int ndr_s_wchar(ndr_ref_t *encl_ref) { ndr_stream_t *nds = encl_ref->stream; unsigned short wide_char; char *valp; ndr_ref_t myref; unsigned long i; char name[30]; int count; int char_count = 0; if (nds->m_op == NDR_M_OP_UNMARSHALL) { /* * To avoid problems with zero length strings * we can just null terminate here and be done. */ if (encl_ref->strlen_is == 0) { encl_ref->datum[0] = '\0'; return (1); } } bzero(&myref, sizeof (myref)); myref.enclosing = encl_ref; myref.stream = encl_ref->stream; myref.packed_alignment = 0; myref.ti = &ndt__wchar; myref.inner_flags = NDR_F_NONE; myref.datum = (char *)&wide_char; myref.name = name; myref.pdu_offset = encl_ref->pdu_offset; valp = encl_ref->datum; count = 0; for (i = 0; i < NDR_STRING_MAX; i++) { (void) sprintf(name, "[%lu]", i); if (nds->m_op == NDR_M_OP_MARSHALL) { count = smb_mbtowc((smb_wchar_t *)&wide_char, valp, MTS_MB_CHAR_MAX); if (count < 0) { return (0); } else if (count == 0) { if (encl_ref->strlen_is != encl_ref->size_is) break; /* * If the input char is 0, mbtowc * returns 0 without setting wide_char. * Set wide_char to 0 and a count of 1. */ wide_char = *valp; count = 1; } } if (!ndr_inner(&myref)) return (0); if (nds->m_op == NDR_M_OP_UNMARSHALL) { count = smb_wctomb(valp, wide_char); if ((++char_count) == encl_ref->strlen_is) { valp += count; *valp = '\0'; break; } } if (!wide_char) break; myref.pdu_offset += sizeof (wide_char); valp += count; } return (1); } /* * Converts a multibyte character string to a little-endian, wide-char * string. No more than nwchars wide characters are stored. * A terminating null wide character is appended if there is room. * * Returns the number of wide characters converted, not counting * any terminating null wide character. Returns -1 if an invalid * multibyte character is encountered. */ size_t ndr_mbstowcs(ndr_stream_t *nds, smb_wchar_t *wcs, const char *mbs, size_t nwchars) { smb_wchar_t *start = wcs; int nbytes; while (nwchars--) { nbytes = ndr_mbtowc(nds, wcs, mbs, MTS_MB_CHAR_MAX); if (nbytes < 0) { *wcs = 0; return ((size_t)-1); } if (*mbs == 0) break; ++wcs; mbs += nbytes; } return (wcs - start); } /* * Converts a multibyte character to a little-endian, wide-char, which * is stored in wcharp. Up to nbytes bytes are examined. * * If mbchar is valid, returns the number of bytes processed in mbchar. * If mbchar is invalid, returns -1. See also smb_mbtowc(). */ /*ARGSUSED*/ int ndr_mbtowc(ndr_stream_t *nds, smb_wchar_t *wcharp, const char *mbchar, size_t nbytes) { int rc; if ((rc = smb_mbtowc(wcharp, mbchar, nbytes)) < 0) return (rc); #ifdef _BIG_ENDIAN if (nds == NULL || NDR_MODE_MATCH(nds, NDR_MODE_RETURN_SEND)) *wcharp = BSWAP_16(*wcharp); #endif return (rc); }