/* * 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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2020 Tintri by DDN, Inc. All rights reserved. * Copyright 2023 RackTop Systems, Inc. */ #ifndef _LIBMLRPC_H #define _LIBMLRPC_H #include #include #include #include #ifdef __cplusplus extern "C" { #endif /* * An MSRPC compatible implementation of OSF DCE RPC. DCE RPC is derived * from the Apollo Network Computing Architecture (NCA) RPC implementation. * * CAE Specification (1997) * DCE 1.1: Remote Procedure Call * Document Number: C706 * The Open Group * ogspecs@opengroup.org * * This implementation is based on the DCE Remote Procedure Call spec with * enhancements to support Unicode strings. The diagram below shows the * DCE RPC layers compared against ONC SUN RPC. * * NDR RPC Layers Sun RPC Layers Remark * +---------------+ +---------------+ +---------------+ * +---------------+ +---------------+ * | Application | | Application | The application * +---------------+ +---------------+ * | Hand coded | | RPCGEN gen'd | Where the real * | client/server | | client/server | work happens * | srvsvc.ndl | | *_svc.c *_clnt| * | srvsvc.c | | | * +---------------+ +---------------+ * | RPC Library | | RPC Library | Calls/Return * | ndr_*.c | | | Binding/PMAP * +---------------+ +---------------+ * | RPC Protocol | | RPC Protocol | Headers, Auth, * | rpcpdu.ndl | | | * +---------------+ +---------------+ * | IDL gen'd | | RPCGEN gen'd | Aggregate * | NDR stubs | | XDR stubs | Composition * | *__ndr.c | | *_xdr.c | * +---------------+ +---------------+ * | NDR Represen | | XDR Represen | Byte order, padding * +---------------+ +---------------+ * | Packet Heaps | | Network Conn | DCERPC does not talk * | ndo_*.c | | clnt_{tcp,udp}| directly to network. * +---------------+ +---------------+ * * There are two major differences between the DCE RPC and ONC RPC: * * 1. NDR RPC only generates or processes packets from buffers. Other * layers must take care of packet transmission and reception. * The packet heaps are managed through a simple interface provided * by the Network Data Representation (NDR) module called ndr_stream_t. * ndo_*.c modules implement the different flavors (operations) of * packet heaps. * * ONC RPC communicates directly with the network. You have to do * something special for the RPC packet to be placed in a buffer * rather than sent to the wire. * * 2. NDR RPC uses application provided heaps to support operations. * A heap is a single, monolithic chunk of memory that NDR RPC manages * as it allocates. When the operation and its result are done, the * heap is disposed of as a single item. The transaction, which * is the anchor of most operations, contains the necessary book- * keeping for the heap. * * ONC RPC uses malloc() liberally throughout its run-time system. * To free results, ONC RPC supports an XDR_FREE operation that * traverses data structures freeing memory as it goes, whether * it was malloc'd or not. */ /* * Dispatch Return Code (DRC) * * 0x8000 15:01 Set to indicate a fault, clear indicates status * 0x7F00 08:07 Status/Fault specific * 0x00FF 00:08 PTYPE_... of PDU, 0xFF for header */ #define NDR_DRC_OK 0x0000 #define NDR_DRC_MASK_FAULT 0x8000 #define NDR_DRC_MASK_SPECIFIER 0xFF00 #define NDR_DRC_MASK_PTYPE 0x00FF /* Fake PTYPE DRC discriminators */ #define NDR_DRC_PTYPE_RPCHDR(DRC) ((DRC) | 0x00FF) #define NDR_DRC_PTYPE_API(DRC) ((DRC) | 0x00AA) #define NDR_DRC_PTYPE_SEC(DRC) ((DRC) | 0x00CC) /* DRC Recognizers */ #define NDR_DRC_IS_OK(DRC) (((DRC) & NDR_DRC_MASK_SPECIFIER) == 0) #define NDR_DRC_IS_FAULT(DRC) (((DRC) & NDR_DRC_MASK_FAULT) != 0) /* * (Un)Marshalling category specifiers */ #define NDR_DRC_FAULT_MODE_MISMATCH 0x8100 #define NDR_DRC_RECEIVED 0x0200 #define NDR_DRC_FAULT_RECEIVED_RUNT 0x8300 #define NDR_DRC_FAULT_RECEIVED_MALFORMED 0x8400 #define NDR_DRC_DECODED 0x0500 #define NDR_DRC_FAULT_DECODE_FAILED 0x8600 #define NDR_DRC_ENCODED 0x0700 #define NDR_DRC_FAULT_ENCODE_FAILED 0x8800 #define NDR_DRC_FAULT_ENCODE_TOO_BIG 0x8900 #define NDR_DRC_SENT 0x0A00 #define NDR_DRC_FAULT_SEND_FAILED 0x8B00 /* * Resource category specifier */ #define NDR_DRC_FAULT_RESOURCE_1 0x9100 #define NDR_DRC_FAULT_RESOURCE_2 0x9200 /* * Parameters. Usually #define'd with useful alias */ #define NDR_DRC_FAULT_PARAM_0_INVALID 0xC000 #define NDR_DRC_FAULT_PARAM_0_UNIMPLEMENTED 0xD000 #define NDR_DRC_FAULT_PARAM_1_INVALID 0xC100 #define NDR_DRC_FAULT_PARAM_1_UNIMPLEMENTED 0xD100 #define NDR_DRC_FAULT_PARAM_2_INVALID 0xC200 #define NDR_DRC_FAULT_PARAM_2_UNIMPLEMENTED 0xD200 #define NDR_DRC_FAULT_PARAM_3_INVALID 0xC300 #define NDR_DRC_FAULT_PARAM_3_UNIMPLEMENTED 0xD300 #define NDR_DRC_FAULT_PARAM_4_INVALID 0xC400 #define NDR_DRC_FAULT_PARAM_4_UNIMPLEMENTED 0xD400 #define NDR_DRC_FAULT_PARAM_5_INVALID 0xC500 #define NDR_DRC_FAULT_PARAM_5_UNIMPLEMENTED 0xD500 #define NDR_DRC_FAULT_OUT_OF_MEMORY 0xF000 /* RPCHDR */ #define NDR_DRC_FAULT_RPCHDR_MODE_MISMATCH 0x81FF #define NDR_DRC_FAULT_RPCHDR_RECEIVED_RUNT 0x83FF #define NDR_DRC_FAULT_RPCHDR_DECODE_FAILED 0x86FF #define NDR_DRC_FAULT_RPCHDR_PTYPE_INVALID 0xC0FF /* PARAM_0_INVALID */ #define NDR_DRC_FAULT_RPCHDR_PTYPE_UNIMPLEMENTED 0xD0FF /* PARAM_0_UNIMP */ /* Request */ #define NDR_DRC_FAULT_REQUEST_PCONT_INVALID 0xC000 /* PARAM_0_INVALID */ #define NDR_DRC_FAULT_REQUEST_OPNUM_INVALID 0xC100 /* PARAM_1_INVALID */ /* Bind */ #define NDR_DRC_BINDING_MADE 0x000B /* OK */ #define NDR_DRC_FAULT_BIND_PCONT_BUSY 0xC00B /* PARAM_0_INVALID */ #define NDR_DRC_FAULT_BIND_UNKNOWN_SERVICE 0xC10B /* PARAM_1_INVALID */ #define NDR_DRC_FAULT_BIND_NO_SLOTS 0x910B /* RESOURCE_1 */ /* API */ #define NDR_DRC_FAULT_API_SERVICE_INVALID 0xC0AA /* PARAM_0_INVALID */ #define NDR_DRC_FAULT_API_BIND_NO_SLOTS 0x91AA /* RESOURCE_1 */ #define NDR_DRC_FAULT_API_OPNUM_INVALID 0xC1AA /* PARAM_1_INVALID */ /* Secure RPC and SSPs */ #define NDR_DRC_FAULT_SEC_TYPE_UNIMPLEMENTED \ NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_PARAM_0_UNIMPLEMENTED) #define NDR_DRC_FAULT_SEC_LEVEL_UNIMPLEMENTED \ NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_PARAM_1_UNIMPLEMENTED) #define NDR_DRC_FAULT_SEC_SSP_FAILED \ NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_RESOURCE_1) #define NDR_DRC_FAULT_SEC_ENCODE_TOO_BIG \ NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_ENCODE_TOO_BIG) #define NDR_DRC_FAULT_SEC_AUTH_LENGTH_INVALID \ NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_PARAM_2_INVALID) #define NDR_DRC_FAULT_SEC_AUTH_TYPE_INVALID \ NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_PARAM_0_INVALID) #define NDR_DRC_FAULT_SEC_AUTH_LEVEL_INVALID \ NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_PARAM_1_INVALID) #define NDR_DRC_FAULT_SEC_OUT_OF_MEMORY \ NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_OUT_OF_MEMORY) #define NDR_DRC_FAULT_SEC_ENCODE_FAILED \ NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_ENCODE_FAILED) #define NDR_DRC_FAULT_SEC_META_INVALID \ NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_PARAM_3_INVALID) #define NDR_DRC_FAULT_SEC_SEQNUM_INVALID \ NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_PARAM_4_INVALID) #define NDR_DRC_FAULT_SEC_SIG_INVALID \ NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_PARAM_5_INVALID) struct ndr_xa; struct ndr_client; typedef struct ndr_stub_table { int (*func)(void *, struct ndr_xa *); unsigned short opnum; } ndr_stub_table_t; typedef struct ndr_service { char *name; char *desc; char *endpoint; char *sec_addr_port; char *abstract_syntax_uuid; int abstract_syntax_version; char *transfer_syntax_uuid; int transfer_syntax_version; unsigned bind_instance_size; int (*bind_req)(); int (*unbind_and_close)(); int (*call_stub)(struct ndr_xa *); ndr_typeinfo_t *interface_ti; ndr_stub_table_t *stub_table; } ndr_service_t; /* * The list of bindings is anchored at a connection. Nothing in the * RPC mechanism allocates them. Binding elements which have service==0 * indicate free elements. When a connection is instantiated, at least * one free binding entry should also be established. Something like * this should suffice for most (all) situations: * * struct connection { * .... * ndr_binding_t *binding_list_head; * ndr_binding_t binding_pool[N_BINDING_POOL]; * .... * }; * * init_connection(struct connection *conn) { * .... * ndr_svc_binding_pool_init(&conn->binding_list_head, * conn->binding_pool, N_BINDING_POOL); */ typedef struct ndr_binding { struct ndr_binding *next; ndr_p_context_id_t p_cont_id; unsigned char which_side; struct ndr_client *clnt; ndr_service_t *service; void *instance_specific; } ndr_binding_t; #define NDR_BIND_SIDE_CLIENT 1 #define NDR_BIND_SIDE_SERVER 2 #define NDR_BINDING_TO_SPECIFIC(BINDING, TYPE) \ ((TYPE *) (BINDING)->instance_specific) /* * The binding list space must be provided by the application library * for use by the underlying RPC library. We need at least two binding * slots per connection. */ #define NDR_N_BINDING_POOL 2 typedef struct ndr_pipe { void *np_listener; const char *np_endpoint; struct smb_netuserinfo *np_user; int (*np_send)(struct ndr_pipe *, void *, size_t); int (*np_recv)(struct ndr_pipe *, void *, size_t); int np_fid; uint16_t np_max_xmit_frag; uint16_t np_max_recv_frag; ndr_binding_t *np_binding; ndr_binding_t np_binding_pool[NDR_N_BINDING_POOL]; } ndr_pipe_t; /* * Number of bytes required to align SIZE on the next dword/4-byte * boundary. */ #define NDR_ALIGN4(SIZE) ((4 - (SIZE)) & 3); /* * DCE RPC strings (CAE section 14.3.4) are represented as varying or varying * and conformant one-dimensional arrays. Characters can be single-byte * or multi-byte as long as all characters conform to a fixed element size, * i.e. UCS-2 is okay but UTF-8 is not a valid DCE RPC string format. The * string is terminated by a null character of the appropriate element size. * * MSRPC strings should always be varying/conformant and not null terminated. * This format uses the size_is, first_is and length_is attributes (CAE * section 4.2.18). * * typedef struct string { * DWORD size_is; * DWORD first_is; * DWORD length_is; * wchar_t string[ANY_SIZE_ARRAY]; * } string_t; * * The size_is attribute is used to specify the number of data elements in * each dimension of an array. * * The first_is attribute is used to define the lower bound for significant * elements in each dimension of an array. For strings this is always 0. * * The length_is attribute is used to define the number of significant * elements in each dimension of an array. For strings this is typically * the same as size_is. Although it might be (size_is - 1) if the string * is null terminated. * * 4 bytes 4 bytes 4 bytes 2bytes 2bytes 2bytes 2bytes * +---------+---------+---------+------+------+------+------+ * |size_is |first_is |length_is| char | char | char | char | * +---------+---------+---------+------+------+------+------+ * * Unfortunately, not all MSRPC Unicode strings are null terminated, which * means that the recipient has to manually null-terminate the string after * it has been unmarshalled. There may be a wide-char pad following a * string, and it may sometimes contains zero, but it's not guaranteed. * * To deal with this, MSRPC sometimes uses an additional wrapper with two * more fields, as shown below. * length: the array length in bytes excluding terminating null bytes * maxlen: the array length in bytes including null terminator bytes * LPTSTR: converted to a string_t by NDR * * typedef struct ms_string { * WORD length; * WORD maxlen; * LPTSTR str; * } ms_string_t; */ typedef struct ndr_mstring { uint16_t length; uint16_t allosize; LPTSTR str; } ndr_mstring_t; /* * A number of heap areas are used during marshalling and unmarshalling. * Under some circumstances these areas can be discarded by the library * code, i.e. on the server side before returning to the client and on * completion of a client side bind. In the case of a client side RPC * call, these areas must be preserved after an RPC returns to give the * caller time to take a copy of the data. In this case the client must * call ndr_clnt_free_heap to free the memory. * * The heap management data definition looks a bit like this: * * heap -> +---------------+ +------------+ * | iovec[0].base | --> | data block | * | iovec[0].len | +------------+ * +---------------+ * :: * :: * iov -> +---------------+ +------------+ * | iovec[n].base | --> | data block | * | iovec[n].len | +------------+ * +---------------+ ^ ^ * | | * next ----------------------+ | * top -----------------------------------+ * */ /* * Setting MAXIOV to 384 will use ((8 * 384) + 16) = 3088 bytes * of the first heap block. */ #define NDR_HEAP_MAXIOV 384 #define NDR_HEAP_BLKSZ 8192 typedef struct ndr_heap { struct iovec iovec[NDR_HEAP_MAXIOV]; struct iovec *iov; int iovcnt; char *top; char *next; } ndr_heap_t; /* * Alternate varying/conformant string definition * - for non-null-terminated strings. */ typedef struct ndr_vcs { /* * size_is (actually a copy of length_is) will * be inserted here by the marshalling library. */ uint32_t vc_first_is; uint32_t vc_length_is; uint16_t buffer[ANY_SIZE_ARRAY]; } ndr_vcs_t; typedef struct ndr_vcstr { uint16_t wclen; uint16_t wcsize; ndr_vcs_t *vcs; } ndr_vcstr_t; typedef struct ndr_vcb { /* * size_is (actually a copy of length_is) will * be inserted here by the marshalling library. */ uint32_t vc_first_is; uint32_t vc_length_is; uint8_t buffer[ANY_SIZE_ARRAY]; } ndr_vcb_t; typedef struct ndr_vcbuf { uint16_t len; uint16_t size; ndr_vcb_t *vcb; } ndr_vcbuf_t; ndr_heap_t *ndr_heap_create(void); void ndr_heap_destroy(ndr_heap_t *); void *ndr_heap_dupmem(ndr_heap_t *, const void *, size_t); void *ndr_heap_malloc(ndr_heap_t *, unsigned); void *ndr_heap_strdup(ndr_heap_t *, const char *); int ndr_heap_mstring(ndr_heap_t *, const char *, ndr_mstring_t *); void ndr_heap_mkvcs(ndr_heap_t *, char *, ndr_vcstr_t *); void ndr_heap_mkvcb(ndr_heap_t *, uint8_t *, uint32_t, ndr_vcbuf_t *); int ndr_heap_used(ndr_heap_t *); int ndr_heap_avail(ndr_heap_t *); #define NDR_MALLOC(XA, SZ) ndr_heap_malloc((XA)->heap, SZ) #define NDR_NEW(XA, T) ndr_heap_malloc((XA)->heap, sizeof (T)) #define NDR_NEWN(XA, T, N) ndr_heap_malloc((XA)->heap, sizeof (T)*(N)) #define NDR_STRDUP(XA, S) ndr_heap_strdup((XA)->heap, (S)) #define NDR_MSTRING(XA, S, OUT) ndr_heap_mstring((XA)->heap, (S), (OUT)) #define NDR_SIDDUP(XA, S) ndr_heap_dupmem((XA)->heap, (S), smb_sid_len(S)) typedef struct ndr_xa { unsigned short ptype; /* high bits special */ unsigned short opnum; ndr_stream_t recv_nds; ndr_hdr_t recv_hdr; ndr_sec_t recv_auth; ndr_stream_t send_nds; ndr_hdr_t send_hdr; ndr_sec_t send_auth; ndr_binding_t *binding; /* what we're using */ ndr_binding_t *binding_list; /* from connection */ ndr_heap_t *heap; ndr_pipe_t *pipe; } ndr_xa_t; typedef struct ndr_auth_ops { int (*nao_init)(void *, ndr_xa_t *); int (*nao_recv)(void *, ndr_xa_t *); int (*nao_sign)(void *, ndr_xa_t *); int (*nao_verify)(void *, ndr_xa_t *, boolean_t); int (*nao_encrypt)(void *, ndr_xa_t *); int (*nao_decrypt)(void *, ndr_xa_t *, boolean_t); } ndr_auth_ops_t; /* * A client provides this structure during bind to indicate * that the RPC runtime should use "Secure RPC" (RPC-level auth). * * Currently, only NETLOGON uses this, and only NETLOGON-based * Integrity protection is supported. */ typedef struct ndr_auth_ctx { ndr_auth_ops_t auth_ops; void *auth_ctx; /* SSP-specific context */ uint32_t auth_context_id; uint8_t auth_type; uint8_t auth_level; boolean_t auth_verify_resp; } ndr_auth_ctx_t; /* * 20-byte opaque id used by various RPC services. */ CONTEXT_HANDLE(ndr_hdid) ndr_hdid_t; typedef struct ndr_client { /* transport stuff (xa_* members) */ int (*xa_init)(struct ndr_client *, ndr_xa_t *); int (*xa_exchange)(struct ndr_client *, ndr_xa_t *); int (*xa_read)(struct ndr_client *, ndr_xa_t *); void (*xa_preserve)(struct ndr_client *, ndr_xa_t *); void (*xa_destruct)(struct ndr_client *, ndr_xa_t *); void (*xa_release)(struct ndr_client *); void *xa_private; int xa_fd; ndr_hdid_t *handle; ndr_binding_t *binding; ndr_binding_t *binding_list; ndr_binding_t binding_pool[NDR_N_BINDING_POOL]; boolean_t nonull; boolean_t heap_preserved; ndr_heap_t *heap; ndr_stream_t *recv_nds; ndr_stream_t *send_nds; uint32_t next_call_id; unsigned next_p_cont_id; ndr_auth_ctx_t auth_ctx; } ndr_client_t; typedef struct ndr_handle { ndr_hdid_t nh_id; struct ndr_handle *nh_next; ndr_pipe_t *nh_pipe; const ndr_service_t *nh_svc; ndr_client_t *nh_clnt; void *nh_data; void (*nh_data_free)(void *); } ndr_handle_t; #define NDR_PDU_SIZE_HINT_DEFAULT (16*1024) #define NDR_BUF_MAGIC 0x4E425546 /* NBUF */ typedef struct ndr_buf { uint32_t nb_magic; ndr_stream_t nb_nds; ndr_heap_t *nb_heap; ndr_typeinfo_t *nb_ti; } ndr_buf_t; /* ndr_ops.c */ int nds_initialize(ndr_stream_t *, unsigned, int, ndr_heap_t *); void nds_destruct(ndr_stream_t *); void nds_show_state(ndr_stream_t *); /* ndr_client.c */ int ndr_clnt_bind(ndr_client_t *, ndr_service_t *, ndr_binding_t **); int ndr_clnt_call(ndr_binding_t *, int, void *); void ndr_clnt_free_heap(ndr_client_t *); /* ndr_marshal.c */ ndr_buf_t *ndr_buf_init(ndr_typeinfo_t *); void ndr_buf_fini(ndr_buf_t *); int ndr_buf_decode(ndr_buf_t *, unsigned, unsigned, const char *data, size_t, void *); int ndr_decode_call(ndr_xa_t *, void *); int ndr_encode_return(ndr_xa_t *, void *); int ndr_encode_call(ndr_xa_t *, void *); int ndr_decode_return(ndr_xa_t *, void *); int ndr_decode_pdu_hdr(ndr_xa_t *); int ndr_encode_pdu_hdr(ndr_xa_t *); void ndr_decode_frag_hdr(ndr_stream_t *, ndr_common_header_t *); void ndr_remove_frag_hdr(ndr_stream_t *); void ndr_show_hdr(ndr_common_header_t *); unsigned ndr_bind_ack_hdr_size(ndr_xa_t *); unsigned ndr_alter_context_rsp_hdr_size(void); int ndr_decode_pdu_auth(ndr_xa_t *); int ndr_encode_pdu_auth(ndr_xa_t *); void ndr_show_auth(ndr_sec_t *); /* * MS-RPCE "Secure RPC" (RPC-level auth). * These call the functions in ndr_auth_ops_t, which should be * GSSAPI (or equivalent) calls. */ int ndr_add_sec_context(ndr_auth_ctx_t *, ndr_xa_t *); int ndr_recv_sec_context(ndr_auth_ctx_t *, ndr_xa_t *); int ndr_add_auth(ndr_auth_ctx_t *, ndr_xa_t *); int ndr_check_auth(ndr_auth_ctx_t *, ndr_xa_t *); /* ndr_server.c */ void ndr_pipe_worker(ndr_pipe_t *); int ndr_generic_call_stub(ndr_xa_t *); /* ndr_svc.c */ ndr_stub_table_t *ndr_svc_find_stub(ndr_service_t *, int); ndr_service_t *ndr_svc_lookup_name(const char *); ndr_service_t *ndr_svc_lookup_uuid(ndr_uuid_t *, int, ndr_uuid_t *, int); int ndr_svc_register(ndr_service_t *); void ndr_svc_unregister(ndr_service_t *); void ndr_svc_binding_pool_init(ndr_binding_t **, ndr_binding_t pool[], int); ndr_binding_t *ndr_svc_find_binding(ndr_xa_t *, ndr_p_context_id_t); ndr_binding_t *ndr_svc_new_binding(ndr_xa_t *); int ndr_uuid_parse(char *, ndr_uuid_t *); void ndr_uuid_unparse(ndr_uuid_t *, char *); ndr_hdid_t *ndr_hdalloc(const ndr_xa_t *, const void *); void ndr_hdfree(const ndr_xa_t *, const ndr_hdid_t *); ndr_handle_t *ndr_hdlookup(const ndr_xa_t *, const ndr_hdid_t *); void ndr_hdclose(ndr_pipe_t *); ssize_t ndr_uiomove(caddr_t, size_t, enum uio_rw, struct uio *); /* * An ndr_client_t is created while binding a client connection to hold * the context for calls made using that connection. * * Handles are RPC call specific and we use an inheritance mechanism to * ensure that each handle has a pointer to the client_t. When the top * level (bind) handle is released, we close the connection. * * There are some places in libmlsvc where the code assumes that the * handle member is first in this struct. Careful! * * Note that this entire structure is bzero()'d once the ndr_client_t * has been created. */ typedef struct mlrpc_handle { ndr_hdid_t handle; /* keep first */ ndr_client_t *clnt; } mlrpc_handle_t; int mlrpc_clh_create(mlrpc_handle_t *, void *); uint32_t mlrpc_clh_set_auth(mlrpc_handle_t *, ndr_auth_ctx_t *); uint32_t mlrpc_clh_bind(mlrpc_handle_t *, ndr_service_t *); void mlrpc_clh_unbind(mlrpc_handle_t *); void *mlrpc_clh_free(mlrpc_handle_t *); int ndr_rpc_call(mlrpc_handle_t *, int, void *); int ndr_rpc_get_ssnkey(mlrpc_handle_t *, unsigned char *, size_t); void *ndr_rpc_malloc(mlrpc_handle_t *, size_t); ndr_heap_t *ndr_rpc_get_heap(mlrpc_handle_t *); void ndr_rpc_release(mlrpc_handle_t *); void ndr_rpc_set_nonull(mlrpc_handle_t *); boolean_t ndr_is_null_handle(mlrpc_handle_t *); boolean_t ndr_is_bind_handle(mlrpc_handle_t *); void ndr_inherit_handle(mlrpc_handle_t *, mlrpc_handle_t *); #ifdef __cplusplus } #endif #endif /* _LIBMLRPC_H */ # # 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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2020 Tintri by DDN, Inc. All rights reserved. # # # # MAPFILE HEADER START # # WARNING: STOP NOW. DO NOT MODIFY THIS FILE. # Object versioning must comply with the rules detailed in # # usr/src/lib/README.mapfiles # # You should not be making modifications here until you've read the most current # copy of that file. If you need help, contact a gatekeeper for guidance. # # MAPFILE HEADER END # $mapfile_version 2 SYMBOL_VERSION SUNWprivate { global: mlrpc_clh_bind; mlrpc_clh_create; mlrpc_clh_free; mlrpc_clh_set_auth; mlrpc_clh_unbind; # Allow debug/test programs to provide these. ndo_printf { FLAGS = NODIRECT; }; ndo_trace { FLAGS = NODIRECT; }; ndr_buf_decode; ndr_buf_fini; ndr_buf_init; ndr_clnt_bind; ndr_clnt_call; ndr_clnt_free_heap; ndr_generic_call_stub; ndr_heap_avail; ndr_heap_create; ndr_heap_destroy; ndr_heap_dupmem; ndr_heap_malloc; ndr_heap_mkvcb; ndr_heap_mkvcs; ndr_heap_mstring; ndr_heap_strdup; ndr_heap_used; ndr_hdalloc; ndr_hdclose; ndr_hdfree; ndr_hdlookup; ndr_inherit_handle; ndr_inner; ndr_is_bind_handle; ndr_is_null_handle; ndr_mbstowcs; ndr_params; ndr_pipe_worker; ndr_rpc_call; ndr_rpc_get_heap; ndr_rpc_get_ssnkey; ndr_rpc_malloc; ndr_rpc_release; ndr_rpc_set_nonull; ndr_svc_binding_pool_init; ndr_svc_lookup_name; ndr_svc_register; ndr_topmost; ndr_uuid_parse; ndr_uuid_unparse; nds_destruct; nds_initialize; $if _ELF32 ndt__char { ASSERT = { TYPE = OBJECT; SIZE = 16; }; }; ndt_s_char { ASSERT = { TYPE = OBJECT; SIZE = 16; }; }; ndt__uchar { ASSERT = { TYPE = OBJECT; SIZE = 16; }; }; ndt_s_uchar { ASSERT = { TYPE = OBJECT; SIZE = 16; }; }; ndt__wchar { ASSERT = { TYPE = OBJECT; SIZE = 16; }; }; ndt_s_wchar { ASSERT = { TYPE = OBJECT; SIZE = 16; }; }; ndt__short { ASSERT = { TYPE = OBJECT; SIZE = 16; }; }; ndt_s_short { ASSERT = { TYPE = OBJECT; SIZE = 16; }; }; ndt__ushort { ASSERT = { TYPE = OBJECT; SIZE = 16; }; }; ndt_s_ushort { ASSERT = { TYPE = OBJECT; SIZE = 16; }; }; ndt__long { ASSERT = { TYPE = OBJECT; SIZE = 16; }; }; ndt_s_long { ASSERT = { TYPE = OBJECT; SIZE = 16; }; }; ndt__ulong { ASSERT = { TYPE = OBJECT; SIZE = 16; }; }; ndt_s_ulong { ASSERT = { TYPE = OBJECT; SIZE = 16; }; }; $elif _ELF64 ndt__char { ASSERT = { TYPE = OBJECT; SIZE = 24; }; }; ndt_s_char { ASSERT = { TYPE = OBJECT; SIZE = 24; }; }; ndt__uchar { ASSERT = { TYPE = OBJECT; SIZE = 24; }; }; ndt_s_uchar { ASSERT = { TYPE = OBJECT; SIZE = 24; }; }; ndt__wchar { ASSERT = { TYPE = OBJECT; SIZE = 24; }; }; ndt_s_wchar { ASSERT = { TYPE = OBJECT; SIZE = 24; }; }; ndt__short { ASSERT = { TYPE = OBJECT; SIZE = 24; }; }; ndt_s_short { ASSERT = { TYPE = OBJECT; SIZE = 24; }; }; ndt__ushort { ASSERT = { TYPE = OBJECT; SIZE = 24; }; }; ndt_s_ushort { ASSERT = { TYPE = OBJECT; SIZE = 24; }; }; ndt__long { ASSERT = { TYPE = OBJECT; SIZE = 24; }; }; ndt_s_long { ASSERT = { TYPE = OBJECT; SIZE = 24; }; }; ndt__ulong { ASSERT = { TYPE = OBJECT; SIZE = 24; }; }; ndt_s_ulong { ASSERT = { TYPE = OBJECT; SIZE = 24; }; }; $else $error unknown ELFCLASS $endif local: *; }; /* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ /* * ML-RPC Client handle interface and support functions. */ #include #include #include #include #include #include #include #include #include #include static int ndr_xa_init(ndr_client_t *, ndr_xa_t *); static int ndr_xa_exchange(ndr_client_t *, ndr_xa_t *); static int ndr_xa_read(ndr_client_t *, ndr_xa_t *); static void ndr_xa_preserve(ndr_client_t *, ndr_xa_t *); static void ndr_xa_destruct(ndr_client_t *, ndr_xa_t *); static void ndr_xa_release(ndr_client_t *); /* See notes in mlrpc_clh_bind */ int rpc_pipe_open_retries = 10; /* * Create an RPC client binding handle using the given smb_ctx. * That context must already have a session and tree connected. * * Returns zero or an errno value. */ int mlrpc_clh_create(mlrpc_handle_t *handle, void *ctx) { ndr_client_t *clnt = NULL; if (ctx == NULL) return (EINVAL); /* * Allocate... */ if ((clnt = calloc(1, sizeof (*clnt))) == NULL) return (ENOMEM); clnt->xa_fd = -1; /* * Setup the transport functions. * Always a named pipe (for now). */ clnt->xa_private = ctx; clnt->xa_init = ndr_xa_init; clnt->xa_exchange = ndr_xa_exchange; clnt->xa_read = ndr_xa_read; clnt->xa_preserve = ndr_xa_preserve; clnt->xa_destruct = ndr_xa_destruct; clnt->xa_release = ndr_xa_release; /* See _is_bind_handle */ clnt->handle = &handle->handle; ndr_svc_binding_pool_init(&clnt->binding_list, clnt->binding_pool, NDR_N_BINDING_POOL); if ((clnt->heap = ndr_heap_create()) == NULL) goto nomem; /* success! */ bzero(handle, sizeof (*handle)); handle->clnt = clnt; return (0); nomem: free(clnt); return (ENOMEM); } /* * Set up this handle to perform RPC-level authentication. */ uint32_t mlrpc_clh_set_auth(mlrpc_handle_t *handle, ndr_auth_ctx_t *auth_ctx) { ndr_client_t *clnt = NULL; if ((clnt = handle->clnt) == NULL) return (NT_STATUS_INTERNAL_ERROR); if (auth_ctx != NULL) { /* struct copy */ clnt->auth_ctx = *auth_ctx; } return (NT_STATUS_SUCCESS); } /* * This call must be made to initialize an RPC client structure and bind * to the remote service before any RPCs can be exchanged with that service. * * The mlrpc_handle_t is a wrapper that is used to associate an RPC handle * with the client context for an instance of the interface. The handle * is zeroed to ensure that it doesn't look like a valid handle - * handle content is provided by the remove service. * * The client points to this top-level handle so that we know when to * unbind and teardown the connection. As each handle is initialized it * will inherit a reference to the client context. * * * Similar to MSRPC RpcBindingBind() * * Returns 0 or an NT_STATUS: (failed in...) * * RPC_NT_SERVER_TOO_BUSY (open pipe) * RPC_NT_SERVER_UNAVAILABLE (open pipe) * NT_STATUS_ACCESS_DENIED (open pipe) * NT_STATUS_INVALID_PARAMETER (rpc bind) * NT_STATUS_INTERNAL_ERROR (bad args etc) * NT_STATUS_NO_MEMORY */ uint32_t mlrpc_clh_bind(mlrpc_handle_t *handle, ndr_service_t *svc) { ndr_client_t *clnt = NULL; struct smb_ctx *ctx = NULL; uint32_t status = 0; int fd = -1; int rc, retries; if ((clnt = handle->clnt) == NULL) return (NT_STATUS_INTERNAL_ERROR); if ((ctx = clnt->xa_private) == NULL) return (NT_STATUS_INTERNAL_ERROR); if (clnt->xa_fd != -1) return (NT_STATUS_INTERNAL_ERROR); /* * Open the named pipe. * * Sometimes a DC may return NT_STATUS_PIPE_NOT_AVAILABLE for * the first few seconds during service auto-start. The client * translates that to EBUSY, so when we see that, wait a bit * and retry the open for up to rpc_pipe_open_retries. If we * fail even after retries, return RPC_NT_SERVER_TOO_BUSY, * which is how callers of this layer expect that reported. * We try up to 10 times, with a 0.5 sec. wait after each * BUSY failure, giving a total wait here of 5 sec. */ retries = rpc_pipe_open_retries; retry_open: fd = smb_fh_open(ctx, svc->endpoint, O_RDWR); if (fd < 0) { rc = errno; switch (rc) { case EBUSY: if (--retries > 0) { (void) poll(NULL, 0, 500); goto retry_open; } status = RPC_NT_SERVER_TOO_BUSY; break; case EACCES: status = NT_STATUS_ACCESS_DENIED; break; default: status = RPC_NT_SERVER_UNAVAILABLE; break; } return (status); } clnt->xa_fd = fd; /* Paranoia, in case of re-bind. */ bzero(&handle->handle, sizeof (ndr_hdid_t)); /* * Do the OtW RPC bind. */ rc = ndr_clnt_bind(clnt, svc, &clnt->binding); switch (rc) { case NDR_DRC_FAULT_OUT_OF_MEMORY: status = NT_STATUS_NO_MEMORY; break; case NDR_DRC_FAULT_API_SERVICE_INVALID: /* svc->..._uuid parse errors */ status = NT_STATUS_INTERNAL_ERROR; break; default: if (NDR_DRC_IS_FAULT(rc)) { status = RPC_NT_PROTOCOL_ERROR; break; } /* FALLTHROUGH */ case NDR_DRC_OK: status = NT_STATUS_SUCCESS; } if (status != 0) { if (fd != -1) (void) smb_fh_close(fd); clnt->xa_fd = -1; } return (status); } /* * Unbind and close the pipe to an RPC service. * * Similar to MSRPC RpcBindingUnbind() * This should be called after a dropped connection. */ void mlrpc_clh_unbind(mlrpc_handle_t *handle) { ndr_client_t *clnt = handle->clnt; if (clnt->xa_fd != -1) { (void) smb_fh_close(clnt->xa_fd); clnt->xa_fd = -1; } } /* * If the heap has been preserved we need to go through an xa release. * The heap is preserved during an RPC call because that's where data * returned from the server is stored. * * Otherwise we destroy the heap directly. * * Returns the xa_private pointer (if non-NULL) to inform the caller * that it can now be destroyed. */ void * mlrpc_clh_free(mlrpc_handle_t *handle) { ndr_client_t *clnt = handle->clnt; void *private; if (clnt == NULL) return (NULL); /* * Should never get an unbind on inherited handles. * Callers of ndr_inherit_handle() check handles * with ndr_is_bind_handle() before calling this. * * Maybe make this function more tolerant? */ assert(handle->clnt->handle == &handle->handle); mlrpc_clh_unbind(handle); if (clnt->heap_preserved) ndr_clnt_free_heap(clnt); /* xa_release */ else ndr_heap_destroy(clnt->heap); /* * Note: Caller will free the smb_ctx stored in * clnt->xa_private (or possibly reuse it). */ private = clnt->xa_private; free(clnt); bzero(handle, sizeof (*handle)); return (private); } /* * Call the RPC function identified by opnum. The remote service is * identified by the handle, which should have been initialized by * ndr_rpc_bind. * * If the RPC call is successful (returns 0), the caller must call * ndr_rpc_release to release the heap. Otherwise, we release the * heap here. */ int ndr_rpc_call(mlrpc_handle_t *handle, int opnum, void *params) { ndr_client_t *clnt = handle->clnt; int rc; if (ndr_rpc_get_heap(handle) == NULL) return (-1); rc = ndr_clnt_call(clnt->binding, opnum, params); /* * Always clear the nonull flag to ensure * it is not applied to subsequent calls. */ clnt->nonull = B_FALSE; if (NDR_DRC_IS_FAULT(rc)) { ndr_rpc_release(handle); return (-1); } return (0); } /* * Outgoing strings should not be null terminated. */ void ndr_rpc_set_nonull(mlrpc_handle_t *handle) { handle->clnt->nonull = B_TRUE; } /* * Get the session key from a bound RPC client handle. * * The key returned is the 16-byte "user session key" * established by the underlying authentication protocol * (either Kerberos or NTLM). This key is needed for * SAM RPC calls such as SamrSetInformationUser, etc. * See [MS-SAMR] sections: 2.2.3.3, 2.2.7.21, 2.2.7.25. * * Returns zero (success) or an errno. */ int ndr_rpc_get_ssnkey(mlrpc_handle_t *handle, uchar_t *key, size_t len) { ndr_client_t *clnt = handle->clnt; if (clnt == NULL || clnt->xa_fd == -1) return (EINVAL); return (smb_fh_getssnkey(clnt->xa_fd, key, len)); } void * ndr_rpc_malloc(mlrpc_handle_t *handle, size_t size) { ndr_heap_t *heap; if ((heap = ndr_rpc_get_heap(handle)) == NULL) return (NULL); return (ndr_heap_malloc(heap, size)); } ndr_heap_t * ndr_rpc_get_heap(mlrpc_handle_t *handle) { ndr_client_t *clnt = handle->clnt; if (clnt->heap == NULL) clnt->heap = ndr_heap_create(); return (clnt->heap); } /* * Must be called by RPC clients to free the heap after a successful RPC * call, i.e. ndr_rpc_call returned 0. The caller should take a copy * of any data returned by the RPC prior to calling this function because * returned data is in the heap. */ void ndr_rpc_release(mlrpc_handle_t *handle) { ndr_client_t *clnt = handle->clnt; if (clnt->heap_preserved) ndr_clnt_free_heap(clnt); else ndr_heap_destroy(clnt->heap); clnt->heap = NULL; } /* * Returns true if the handle is null. * Otherwise returns false. */ boolean_t ndr_is_null_handle(mlrpc_handle_t *handle) { static const ndr_hdid_t hdid0 = {0}; if (handle == NULL || handle->clnt == NULL) return (B_TRUE); if (!memcmp(&handle->handle, &hdid0, sizeof (hdid0))) return (B_TRUE); return (B_FALSE); } /* * Returns true if the handle is the top level bind handle. * Otherwise returns false. */ boolean_t ndr_is_bind_handle(mlrpc_handle_t *handle) { return (handle->clnt->handle == &handle->handle); } /* * Pass the client reference from parent to child. */ void ndr_inherit_handle(mlrpc_handle_t *child, mlrpc_handle_t *parent) { child->clnt = parent->clnt; } /* * ndr_rpc_status remains in libmlsvc mlsvc_client.c */ /* * The following functions provide the client callback interface. * If the caller hasn't provided a heap, create one here. */ static int ndr_xa_init(ndr_client_t *clnt, ndr_xa_t *mxa) { ndr_stream_t *recv_nds = &mxa->recv_nds; ndr_stream_t *send_nds = &mxa->send_nds; ndr_heap_t *heap = clnt->heap; int rc; if (heap == NULL) { if ((heap = ndr_heap_create()) == NULL) return (-1); clnt->heap = heap; } mxa->heap = heap; rc = nds_initialize(send_nds, 0, NDR_MODE_CALL_SEND, heap); if (rc == 0) rc = nds_initialize(recv_nds, NDR_PDU_SIZE_HINT_DEFAULT, NDR_MODE_RETURN_RECV, heap); if (rc != 0) { nds_destruct(&mxa->recv_nds); nds_destruct(&mxa->send_nds); ndr_heap_destroy(mxa->heap); mxa->heap = NULL; clnt->heap = NULL; return (-1); } if (clnt->nonull) NDS_SETF(send_nds, NDS_F_NONULL); return (0); } /* * This is the entry pointy for an RPC client call exchange with * a server, which will result in an smbrdr SmbTransact request. * * SmbTransact should return the number of bytes received, which * we record as the PDU size, or a negative error code. */ static int ndr_xa_exchange(ndr_client_t *clnt, ndr_xa_t *mxa) { ndr_stream_t *recv_nds = &mxa->recv_nds; ndr_stream_t *send_nds = &mxa->send_nds; int err, more, nbytes; nbytes = recv_nds->pdu_max_size; err = smb_fh_xactnp(clnt->xa_fd, send_nds->pdu_size, (char *)send_nds->pdu_base_offset, &nbytes, (char *)recv_nds->pdu_base_offset, &more); if (err) { recv_nds->pdu_size = 0; return (-1); } recv_nds->pdu_size = nbytes; return (0); } /* * This entry point will be invoked if the xa-exchange response contained * only the first fragment of a multi-fragment response. The RPC client * code will then make repeated xa-read requests to obtain the remaining * fragments, which will result in smbrdr SmbReadX requests. * * SmbReadX should return the number of bytes received, in which case we * expand the PDU size to include the received data, or a negative error * code. */ static int ndr_xa_read(ndr_client_t *clnt, ndr_xa_t *mxa) { ndr_stream_t *nds = &mxa->recv_nds; int len; int nbytes; if ((len = (nds->pdu_max_size - nds->pdu_size)) < 0) return (-1); nbytes = smb_fh_read(clnt->xa_fd, 0, len, (char *)nds->pdu_base_offset + nds->pdu_size); if (nbytes < 0) return (-1); nds->pdu_size += nbytes; if (nds->pdu_size > nds->pdu_max_size) { nds->pdu_size = nds->pdu_max_size; return (-1); } return (nbytes); } /* * Preserve the heap so that the client application has access to data * returned from the server after an RPC call. */ static void ndr_xa_preserve(ndr_client_t *clnt, ndr_xa_t *mxa) { assert(clnt->heap == mxa->heap); clnt->heap_preserved = B_TRUE; mxa->heap = NULL; } /* * Dispose of the transaction streams. If the heap has not been * preserved, we can destroy it here. */ static void ndr_xa_destruct(ndr_client_t *clnt, ndr_xa_t *mxa) { nds_destruct(&mxa->recv_nds); nds_destruct(&mxa->send_nds); if (!clnt->heap_preserved) { ndr_heap_destroy(mxa->heap); mxa->heap = NULL; clnt->heap = NULL; } } /* * Dispose of a preserved heap. */ static void ndr_xa_release(ndr_client_t *clnt) { if (clnt->heap_preserved) { ndr_heap_destroy(clnt->heap); clnt->heap = NULL; clnt->heap_preserved = B_FALSE; } } /* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ #ifndef _SMBSRV_NDR_H #define _SMBSRV_NDR_H /* * Network Data Representation (NDR) is a compatible subset of DCE RPC * and MSRPC NDR. NDR is used to move parameters consisting of * complicated trees of data constructs between an RPC client and server. * * CAE Specification (1997) * DCE 1.1: Remote Procedure Call * Document Number: C706 * The Open Group * ogspecs@opengroup.org */ #include #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif /* * Normal sequence: * - Application calls client-side stub w/ TOP-MOST arg structure * - client stub performs NDR_M_OP_MARSHALL+NDR_DIR_IN * - PDU conveyed (request, aka call, aka query) * - server stub performs NDR_M_OP_UNMARSHALL+NDR_DIR_IN * - server function called w/ TOP-MOST arg structure * - server function returns w/ TOP-MOST arg structure modified * - server stub performs NDR_M_OP_MARSHALL+NDR_DIR_OUT * - PDU conveyed (reply, aka result, aka response) * - client stub performs NDR_M_OP_UNMARSHALL+NDR_DIR_OUT * - return to Application w/ TOP-MOST arg structure modified * * An interface is a sequence of top-most constructs. Each top-most * construct corresponds to one parameter, either argument or return * value. * * A top-most construct is a sequence of outer constructs. The first * outer construct is the referent of the argument, and the subsequent * outer constructs are descendents referenced by pointers from prior * constructs. * * An outer construct is a sequence of variable-sized info, fixed-sized * data, and variable-sized data. */ /* * Terminology * * The ALL UPPER CASE terms recur in the DCE/RPC documentation. * The mixed-case names have been introduced as a reading aid. * * Size The size of an array in elements. Think of this * as the amount to malloc(). * * Length The number of elements of an array which are significant * Think of this as the amount to bcopy(). * * Known Size/length is known at build time. * * Determined Size/length is determined at run time. * * FIXED The Size and Length are Known. * Think of this as a string constant or a DOS 8.3 file name. * char array[] = "A Constant Size/Length"; * * CONFORMANT The Size is Determined. Length is the same as Size. * Think of this as strdup(). * char *array = strdup("Something"); * * VARYING The Size is Known. The Length is determined. * Think of this as a strcpy() of a variable length string * into a fixed length buffer: * char array[100]; * strcpy(array, "very short string"); * * VARYING/CONFORMANT * The Size is Determined. The Length is separately Determined. * Think of this like: * char *array = malloc(size); * strcpy(array, "short string"); * * STRING Strings can be CONFORMANT, VARYING, or CONFORMANT/VARYING. * A string is fundamentally an array with the last * significant element some sort of NULL. */ #define NDR_F_NONE 0x0000 /* no flags */ #define NDR_F_PARAMS_MASK 0x00FF #define NDR_F_SIZE_IS 0x0001 /* [size_is(X)] required/given */ #define NDR_F_LENGTH_IS 0x0002 /* not implemented */ #define NDR_F_SWITCH_IS 0x0004 /* [switch_is(X)] req./given */ #define NDR_F_IS_STRING 0x0008 /* [string] req./given */ #define NDR_F_IS_POINTER 0x0010 /* TYPE * ... req./given */ #define NDR_F_IS_REFERENCE 0x0020 /* TYPE & ... req./given */ #define NDR_F_DIMENSION_IS 0x0040 /* TYPE [N] req./given */ #define NDR_F_WHENCE_MASK 0x00F0 #define NDR_F_BACKPTR 0x0010 /* ref cause by pointer */ #define NDR_F_OUTER 0x0020 /* ref caused by outer */ #define NDR_F_TOPMOST 0x0040 /* ref caused by topmost */ #define NDR_F_TYPEOP_MASK 0x0F00 #define NDR_F_ARRAY 0x0100 /* type is array of somethings */ #define NDR_F_POINTER 0x0200 /* type is pointer to something(s) */ #define NDR_F_STRING 0x0300 /* type is string of somethings */ #define NDR_F_UNION 0x0400 /* type is a union */ #define NDR_F_STRUCT 0x0500 /* type is a structure */ #define NDR_F_OPERATION 0x0600 /* type is a structure, special */ #define NDR_F_INTERFACE 0x0700 /* type is a union, special */ #define NDR_F_CONFORMANT 0x1000 /* struct conforming (var-size tail) */ #define NDR_F_VARYING 0x2000 /* not implemented */ #define NDR_F_FAKE 0x4000 /* not a real struct */ struct ndr_heap; struct ndr_stream; struct ndr_reference; typedef uint16_t ndr_wchar_t; typedef struct ndr_typeinfo { unsigned char version; /* sanity check */ unsigned char alignment; /* mask */ unsigned short type_flags; /* NDR_F_... */ int (*ndr_func)(struct ndr_reference *); unsigned short pdu_size_fixed_part; unsigned short pdu_size_variable_part; unsigned short c_size_fixed_part; unsigned short c_size_variable_part; } ndr_typeinfo_t; typedef struct ndr_reference { struct ndr_reference *next; /* queue list (outer only) */ struct ndr_reference *enclosing; /* e.g. struct for this memb */ struct ndr_stream *stream; /* root of NDR */ ndr_typeinfo_t *ti; /* type of data referenced */ char *name; /* name of this member */ unsigned long pdu_offset; /* referent in stub data */ char *datum; /* referent in local memory */ char **backptr; /* referer to set */ unsigned short outer_flags; /* XXX_is() from top level */ unsigned short inner_flags; /* XXX_is() in encapsulated */ unsigned short type_flags; /* "requires" */ unsigned short packed_alignment; unsigned long size_is; /* conforming constructs */ unsigned long strlen_is; /* strings */ unsigned long switch_is; /* union arg selector */ unsigned long dimension_is; /* fixed-len array size */ unsigned long pdu_end_offset; /* offset for limit of PDU */ } ndr_ref_t; /* * For all operations, the ndr_stream, which is the root of NDR processing, * is the primary object. When available, the appropriate ndr_ref_t * is passed, NULL otherwise. Functions that return 'int' should return * TRUE (!0) or FALSE (0). When functions return FALSE, including * ndo_malloc() returning NULL, they should set the stream->error to an * appropriate indicator of what went wrong. * * Functions ndo_get_pdu(), ndo_put_pdu(), and ndo_pad_pdu() must * never grow the PDU data. A request for out-of-bounds data is an error. * The swap_bytes flag is 1 if NDR knows that the byte-order in the PDU * is different from the local system. ndo_pad_pdu() advised that the * affected bytes should be zero filled. */ typedef struct ndr_stream_ops { char *(*ndo_malloc)(struct ndr_stream *, unsigned, ndr_ref_t *); int (*ndo_free)(struct ndr_stream *, char *, ndr_ref_t *); int (*ndo_grow_pdu)(struct ndr_stream *, unsigned long, ndr_ref_t *); int (*ndo_pad_pdu)(struct ndr_stream *, unsigned long, unsigned long, ndr_ref_t *); int (*ndo_get_pdu)(struct ndr_stream *, unsigned long, unsigned long, char *, int, ndr_ref_t *); int (*ndo_put_pdu)(struct ndr_stream *, unsigned long, unsigned long, char *, int, ndr_ref_t *); void (*ndo_tattle)(struct ndr_stream *, char *, ndr_ref_t *); void (*ndo_tattle_error)(struct ndr_stream *, ndr_ref_t *); int (*ndo_reset)(struct ndr_stream *); void (*ndo_destruct)(struct ndr_stream *); } ndr_stream_ops_t; #define NDS_MALLOC(NDS, LEN, REF) \ (*(NDS)->ndo->ndo_malloc)(NDS, LEN, REF) #define NDS_GROW_PDU(NDS, WANT_END_OFF, REF) \ (*(NDS)->ndo->ndo_grow_pdu)(NDS, WANT_END_OFF, REF) #define NDS_PAD_PDU(NDS, PDU_OFFSET, N_BYTES, REF) \ (*(NDS)->ndo->ndo_pad_pdu)(NDS, PDU_OFFSET, N_BYTES, REF) #define NDS_GET_PDU(NDS, PDU_OFFSET, N_BYTES, BUF, SWAP, REF) \ (*(NDS)->ndo->ndo_get_pdu)(NDS, PDU_OFFSET, N_BYTES, BUF, SWAP, REF) #define NDS_PUT_PDU(NDS, PDU_OFFSET, N_BYTES, BUF, SWAP, REF) \ (*(NDS)->ndo->ndo_put_pdu)(NDS, PDU_OFFSET, N_BYTES, BUF, SWAP, REF) #define NDS_TATTLE(NDS, WHAT, REF) \ (*(NDS)->ndo->ndo_tattle)(NDS, WHAT, REF) #define NDS_TATTLE_ERROR(NDS, WHAT, REF) \ (*(NDS)->ndo->ndo_tattle_error)(NDS, REF) #define NDS_RESET(NDS) (*(NDS)->ndo->ndo_reset)(NDS) #define NDS_DESTRUCT(NDS) (*(NDS)->ndo->ndo_destruct)(NDS) /* * The ndr_stream_t tracks the state for a particular RPC call or response. * A single call/response may be transmitted as multiple fragments, where * each fragment has its own header and sec_trailer. For the layout of * a fragment, see the comment at the top of ndr_marshal.c. * * pdu_base_addr is the buffer in which marshalled/received data is stored. * * pdu_base_offset is pdu_base_addr with a different type. * * pdu_max_size is the size of the buffer in pdu_base_addr. * * pdu_size represents the total amount of data stored in pdu_base_addr. * It is typically less than pdu_max_size, and may contain more than one * fragment (when reconstructing fragmented calls/responses, for example). * * pdu_body_offset points to the offset from pdu_base_addr of the PDU body * of the currently encoded/decoded fragment. * * pdu_body_size is the size of the PDU body in the currently encoded/decoded * fragment. * For what constitutes the PDU Body, see ndr_marshal.c. * * pdu_hdr_size is the size of the header for the currently encoded/decoded * fragment. * * pdu_scan_offset points to the next valid byte in pdu_base_addr * (the next free space for encode, and the next unread byte for decode). */ typedef struct ndr_stream { unsigned long pdu_size; unsigned long pdu_max_size; unsigned long pdu_base_offset; unsigned long pdu_scan_offset; unsigned long pdu_body_offset; unsigned long pdu_body_size; unsigned long pdu_hdr_size; unsigned char *pdu_base_addr; ndr_stream_ops_t *ndo; unsigned char m_op; unsigned char dir; unsigned char swap; /* native/net endian swap */ unsigned char flags; short error; short error_ref; ndr_ref_t *outer_queue_head; ndr_ref_t **outer_queue_tailp; ndr_ref_t *outer_current; struct ndr_heap *heap; } ndr_stream_t; #define NDR_M_OP_NONE 0x00 #define NDR_M_OP_MARSHALL 0x01 /* data moving from datum to PDU */ #define NDR_M_OP_UNMARSHALL 0x02 /* data moving from PDU to datum */ #define NDR_DIR_NONE 0x00 #define NDR_DIR_IN 0x10 /* data moving from caller to callee */ #define NDR_DIR_OUT 0x20 /* data moving from callee to caller */ #define NDR_MODE_CALL_SEND (NDR_M_OP_MARSHALL + NDR_DIR_IN) #define NDR_MODE_CALL_RECV (NDR_M_OP_UNMARSHALL + NDR_DIR_IN) #define NDR_MODE_RETURN_SEND (NDR_M_OP_MARSHALL + NDR_DIR_OUT) #define NDR_MODE_RETURN_RECV (NDR_M_OP_UNMARSHALL + NDR_DIR_OUT) #define NDR_MODE_BUF_ENCODE NDR_MODE_CALL_SEND #define NDR_MODE_BUF_DECODE NDR_MODE_RETURN_RECV #define NDR_MODE_TO_M_OP(MODE) ((MODE) & 0x0F) #define NDR_MODE_TO_DIR(MODE) ((MODE) & 0xF0) #define NDR_M_OP_AND_DIR_TO_MODE(M_OP, DIR) ((M_OP)|(DIR)) #define NDR_MODE_MATCH(NDS, MODE) \ (NDR_M_OP_AND_DIR_TO_MODE((NDS)->m_op, (NDS)->dir) == (MODE)) #define NDR_IS_FIRST_FRAG(F) ((F) & NDR_PFC_FIRST_FRAG) #define NDR_IS_LAST_FRAG(F) ((F) & NDR_PFC_LAST_FRAG) #define NDR_IS_SINGLE_FRAG(F) \ (NDR_IS_FIRST_FRAG((F)) && NDR_IS_LAST_FRAG((F))) #define NDS_F_NONE 0x00 #define NDS_F_NOTERM 0x01 /* strings are not null terminated */ #define NDS_F_NONULL 0x02 /* strings: no null on size_is */ #define NDS_SETF(S, F) ((S)->flags |= (F)) #define NDS_CLEARF(S, F) ((S)->flags &= ~(F)) #define NDR_ERR_MALLOC_FAILED -1 #define NDR_ERR_M_OP_INVALID -2 #define NDR_ERR_UNDERFLOW -3 #define NDR_ERR_GROW_FAILED -4 /* overflow */ #define NDR_ERR_PAD_FAILED -5 /* couldn't possibly happen */ #define NDR_ERR_OUTER_HEADER_BAD -6 #define NDR_ERR_SWITCH_VALUE_ILLEGAL -7 #define NDR_ERR_SWITCH_VALUE_INVALID -8 #define NDR_ERR_SWITCH_VALUE_MISSING -9 #define NDR_ERR_SIZE_IS_MISMATCH_PDU -10 #define NDR_ERR_SIZE_IS_MISMATCH_AFTER -11 #define NDR_ERR_SIZE_IS_UNEXPECTED -12 #define NDR_ERR_SIZE_IS_DUPLICATED -13 #define NDR_ERR_OUTER_PARAMS_MISMATCH -14 #define NDR_ERR_ARRAY_VARLEN_ILLEGAL -15 #define NDR_ERR_ARRAY_UNION_ILLEGAL -16 #define NDR_ERR_OUTER_PARAMS_BAD -17 #define NDR_ERR_OUTER_UNION_ILLEGAL -18 #define NDR_ERR_TOPMOST_UNION_ILLEGAL -19 #define NDR_ERR_TOPMOST_VARLEN_ILLEGAL -20 #define NDR_ERR_INNER_PARAMS_BAD -21 #define NDR_ERR_UNIMPLEMENTED -22 #define NDR_ERR_NOT_AN_INTERFACE -23 #define NDR_ERR_STRLEN -24 #define NDR_ERR_STRING_SIZING -25 #define NDR_ERR_BOUNDS_CHECK -26 #define NDR_SET_ERROR(REF, ERROR) \ ((REF)->stream->error = (ERROR), \ (REF)->stream->error_ref = __LINE__, \ NDS_TATTLE_ERROR((REF)->stream, 0, REF)) #define NDR_TATTLE(REF, WHAT) \ (*(REF)->stream->ndo->ndo_tattle)((REF)->stream, WHAT, REF) #define MEMBER_STR(MEMBER) #MEMBER #define NDR_DIR_IS_IN (encl_ref->stream->dir == NDR_DIR_IN) #define NDR_DIR_IS_OUT (encl_ref->stream->dir == NDR_DIR_OUT) #define NDR_MEMBER_PTR_WITH_ARG(TYPE, MEMBER, OFFSET, \ ARGFLAGS, ARGMEM, ARGVAL) { \ myref.pdu_offset = encl_ref->pdu_offset + (OFFSET); \ myref.name = MEMBER_STR(MEMBER); \ myref.datum = (char *)val->MEMBER; \ myref.inner_flags = ARGFLAGS; \ myref.ti = &ndt_##TYPE; \ myref.ARGMEM = ARGVAL; \ if (!ndr_inner(&myref)) \ return (0); \ } #define NDR_MEMBER_PTR_WITH_DIMENSION(TYPE, MEMBER, OFFSET, SIZE_IS) \ NDR_MEMBER_PTR_WITH_ARG(TYPE, MEMBER, OFFSET, \ NDR_F_DIMENSION_IS, dimension_is, SIZE_IS) #define NDR_MEMBER_WITH_ARG(TYPE, MEMBER, OFFSET, \ ARGFLAGS, ARGMEM, ARGVAL) { \ myref.pdu_offset = encl_ref->pdu_offset + (OFFSET); \ myref.name = MEMBER_STR(MEMBER); \ myref.datum = (char *)&val->MEMBER; \ myref.inner_flags = ARGFLAGS; \ myref.ti = &ndt_##TYPE; \ myref.ARGMEM = ARGVAL; \ if (!ndr_inner(&myref)) \ return (0); \ } #define NDR_MEMBER(TYPE, MEMBER, OFFSET) \ NDR_MEMBER_WITH_ARG(TYPE, MEMBER, OFFSET, \ NDR_F_NONE, size_is, 0) #define NDR_MEMBER_ARR_WITH_SIZE_IS(TYPE, MEMBER, OFFSET, SIZE_IS) \ NDR_MEMBER_WITH_ARG(TYPE, MEMBER, OFFSET, \ NDR_F_SIZE_IS, size_is, SIZE_IS) #define NDR_MEMBER_ARR_WITH_DIMENSION(TYPE, MEMBER, OFFSET, SIZE_IS) \ NDR_MEMBER_WITH_ARG(TYPE, MEMBER, OFFSET, \ NDR_F_DIMENSION_IS, dimension_is, SIZE_IS) #define NDR_MEMBER_PTR_WITH_SIZE_IS(TYPE, MEMBER, OFFSET, SIZE_IS) \ NDR_MEMBER_WITH_ARG(TYPE, MEMBER, OFFSET, \ NDR_F_SIZE_IS+NDR_F_IS_POINTER, size_is, SIZE_IS) #define NDR_MEMBER_PTR(TYPE, MEMBER, OFFSET) \ NDR_MEMBER_WITH_ARG(TYPE, MEMBER, OFFSET, \ NDR_F_IS_POINTER, size_is, 0) #define NDR_MEMBER_WITH_SWITCH_IS(TYPE, MEMBER, OFFSET, SWITCH_IS) \ NDR_MEMBER_WITH_ARG(TYPE, MEMBER, OFFSET, \ NDR_F_SWITCH_IS, switch_is, SWITCH_IS) #define NDR_TOPMOST_MEMBER_WITH_ARG(TYPE, MEMBER, \ ARGFLAGS, ARGMEM, ARGVAL) { \ myref.pdu_offset = -1; \ myref.name = MEMBER_STR(MEMBER); \ myref.datum = (char *)&val->MEMBER; \ myref.inner_flags = ARGFLAGS; \ myref.ti = &ndt_##TYPE; \ myref.ARGMEM = ARGVAL; \ if (!ndr_topmost(&myref)) \ return (0); \ } #define NDR_TOPMOST_MEMBER(TYPE, MEMBER) \ NDR_TOPMOST_MEMBER_WITH_ARG(TYPE, MEMBER, \ NDR_F_NONE, size_is, 0) #define NDR_TOPMOST_MEMBER_ARR_WITH_SIZE_IS(TYPE, MEMBER, SIZE_IS) \ NDR_TOPMOST_MEMBER_WITH_ARG(TYPE, MEMBER, \ NDR_F_SIZE_IS, size_is, SIZE_IS) #define NDR_TOPMOST_MEMBER_ARR_WITH_DIMENSION(TYPE, MEMBER, SIZE_IS) \ NDR_TOPMOST_MEMBER_WITH_ARG(TYPE, MEMBER, \ NDR_F_DIMENSION_IS, dimension_is, SIZE_IS) #define NDR_TOPMOST_MEMBER_PTR_WITH_SIZE_IS(TYPE, MEMBER, SIZE_IS) \ NDR_TOPMOST_MEMBER_WITH_ARG(TYPE, MEMBER, \ NDR_F_SIZE_IS+NDR_F_IS_POINTER, size_is, SIZE_IS) #define NDR_TOPMOST_MEMBER_PTR(TYPE, MEMBER) \ NDR_TOPMOST_MEMBER_WITH_ARG(TYPE, MEMBER, \ NDR_F_IS_POINTER, size_is, 0) #define NDR_TOPMOST_MEMBER_REF(TYPE, MEMBER) \ NDR_TOPMOST_MEMBER_WITH_ARG(TYPE, MEMBER, \ NDR_F_IS_REFERENCE, size_is, 0) #define NDR_TOPMOST_MEMBER_REF_WITH_SIZE_IS(TYPE, MEMBER, SIZE_IS) \ NDR_TOPMOST_MEMBER_WITH_ARG(TYPE, MEMBER, \ NDR_F_SIZE_IS+NDR_F_IS_REFERENCE, size_is, SIZE_IS) #define NDR_TOPMOST_MEMBER_WITH_SWITCH_IS(TYPE, MEMBER, SWITCH_IS) \ NDR_TOPMOST_MEMBER_WITH_ARG(TYPE, MEMBER, \ NDR_F_SWITCH_IS, switch_is, SWITCH_IS) /* this is assuming offset+0 */ #define NDR_PARAMS_MEMBER_WITH_ARG(TYPE, MEMBER, ARGFLAGS, \ ARGMEM, ARGVAL) { \ myref.pdu_offset = encl_ref->pdu_offset; \ myref.name = MEMBER_STR(MEMBER); \ myref.datum = (char *)&val->MEMBER; \ myref.inner_flags = ARGFLAGS; \ myref.ti = &ndt_##TYPE; \ myref.ARGMEM = ARGVAL; \ if (!ndr_params(&myref)) \ return (0); \ } #define NDR_PARAMS_MEMBER(TYPE, MEMBER) \ NDR_PARAMS_MEMBER_WITH_ARG(TYPE, MEMBER, \ NDR_F_NONE, size_is, 0) #define NDR_STRING_DIM 1 #define NDR_ANYSIZE_DIM 1 int ndo_process(struct ndr_stream *, ndr_typeinfo_t *, char *); int ndo_operation(struct ndr_stream *, ndr_typeinfo_t *, int opnum, char *); void ndo_printf(struct ndr_stream *, ndr_ref_t *, const char *, ...); void ndo_trace(const char *); void ndo_fmt(struct ndr_stream *, ndr_ref_t *, char *); int ndr_params(ndr_ref_t *); int ndr_topmost(ndr_ref_t *); int ndr_run_outer_queue(struct ndr_stream *); int ndr_outer(ndr_ref_t *); int ndr_outer_fixed(ndr_ref_t *); int ndr_outer_fixed_array(ndr_ref_t *); int ndr_outer_conformant_array(ndr_ref_t *); int ndr_outer_conformant_construct(ndr_ref_t *); int ndr_size_is(ndr_ref_t *); int ndr_outer_string(ndr_ref_t *); int ndr_outer_peek_sizing(ndr_ref_t *, unsigned, unsigned long *); int ndr_outer_poke_sizing(ndr_ref_t *, unsigned, unsigned long *); int ndr_outer_align(ndr_ref_t *); int ndr_outer_grow(ndr_ref_t *, unsigned); int ndr_inner(ndr_ref_t *); int ndr_inner_pointer(ndr_ref_t *); int ndr_inner_reference(ndr_ref_t *); int ndr_inner_array(ndr_ref_t *); size_t ndr_mbstowcs(struct ndr_stream *, ndr_wchar_t *, const char *, size_t); void nds_bswap(void *src, void *dst, size_t len); #ifdef __cplusplus } #endif #endif /* _SMBSRV_NDR_H */ /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2020 Tintri by DDN, Inc. All Rights Reserved. * Copyright 2023 RackTop Systems, Inc. */ #include #include #include /* * Initializes the sec_trailer (ndr_sec_t). * The actual token is allocated and set later (in the SSP). */ int ndr_add_auth_token(ndr_auth_ctx_t *ctx, ndr_xa_t *mxa) { ndr_stream_t *nds = &mxa->send_nds; ndr_sec_t *secp = &mxa->send_auth; secp->auth_type = ctx->auth_type; secp->auth_level = ctx->auth_level; secp->auth_rsvd = 0; /* * [MS-RPCE] 2.2.2.12 "Authentication Tokens" * auth_pad_len aligns the packet to 16 bytes. */ secp->auth_pad_len = P2ROUNDUP(nds->pdu_scan_offset, 16) - nds->pdu_scan_offset; if (NDS_PAD_PDU(nds, nds->pdu_scan_offset, secp->auth_pad_len, NULL) == 0) return (NDR_DRC_FAULT_SEC_ENCODE_TOO_BIG); /* PAD_PDU doesn't adjust scan_offset */ nds->pdu_scan_offset += secp->auth_pad_len; nds->pdu_body_size = nds->pdu_scan_offset - nds->pdu_body_offset; secp->auth_context_id = ctx->auth_context_id; return (NDR_DRC_OK); } /* * Does gss_init_sec_context (or equivalent) and creates * the sec_trailer and the auth token. * * Used during binds (and alter context). * * Currently, only NETLOGON auth with Integrity/Privacy protection * is implemented. */ int ndr_add_sec_context(ndr_auth_ctx_t *ctx, ndr_xa_t *mxa) { int rc; if (ctx->auth_level == NDR_C_AUTHN_NONE || ctx->auth_type == NDR_C_AUTHN_NONE) return (NDR_DRC_OK); if (ctx->auth_type != NDR_C_AUTHN_GSS_NETLOGON) return (NDR_DRC_FAULT_SEC_TYPE_UNIMPLEMENTED); if (ctx->auth_level != NDR_C_AUTHN_LEVEL_PKT_INTEGRITY && ctx->auth_level != NDR_C_AUTHN_LEVEL_PKT_PRIVACY) return (NDR_DRC_FAULT_SEC_LEVEL_UNIMPLEMENTED); if ((rc = ndr_add_auth_token(ctx, mxa)) != 0) return (rc); return (ctx->auth_ops.nao_init(ctx->auth_ctx, mxa)); } /* * Does response-side gss_init_sec_context (or equivalent) and validates * the sec_trailer and the auth token. * * Used during bind (and alter context) ACKs. */ int ndr_recv_sec_context(ndr_auth_ctx_t *ctx, ndr_xa_t *mxa) { ndr_sec_t *bind_secp = &mxa->send_auth; ndr_sec_t *ack_secp = &mxa->recv_auth; if (ctx->auth_level == NDR_C_AUTHN_NONE || ctx->auth_type == NDR_C_AUTHN_NONE) { if (mxa->recv_hdr.common_hdr.auth_length != 0) return (NDR_DRC_FAULT_SEC_AUTH_LENGTH_INVALID); return (NDR_DRC_OK); } else if (mxa->recv_hdr.common_hdr.auth_length == 0) { return (NDR_DRC_FAULT_SEC_AUTH_LENGTH_INVALID); } if (bind_secp->auth_type != ack_secp->auth_type) return (NDR_DRC_FAULT_SEC_AUTH_TYPE_INVALID); if (bind_secp->auth_level != ack_secp->auth_level) return (NDR_DRC_FAULT_SEC_AUTH_LEVEL_INVALID); return (ctx->auth_ops.nao_recv(ctx->auth_ctx, mxa)); } /* * Does gss_MICEx (or equivalent) and creates * the sec_trailer and the auth token. * * Used upon sending a request (client)/response (server) packet. */ int ndr_add_auth(ndr_auth_ctx_t *ctx, ndr_xa_t *mxa) { int rc; if (ctx->auth_level == NDR_C_AUTHN_NONE || ctx->auth_type == NDR_C_AUTHN_NONE) return (NDR_DRC_OK); if (ctx->auth_type != NDR_C_AUTHN_GSS_NETLOGON) return (NDR_DRC_FAULT_SEC_TYPE_UNIMPLEMENTED); if (ctx->auth_level != NDR_C_AUTHN_LEVEL_PKT_INTEGRITY && ctx->auth_level != NDR_C_AUTHN_LEVEL_PKT_PRIVACY) return (NDR_DRC_FAULT_SEC_LEVEL_UNIMPLEMENTED); if ((rc = ndr_add_auth_token(ctx, mxa)) != 0) return (rc); if (ctx->auth_level == NDR_C_AUTHN_LEVEL_PKT_PRIVACY) return (ctx->auth_ops.nao_encrypt(ctx->auth_ctx, mxa)); return (ctx->auth_ops.nao_sign(ctx->auth_ctx, mxa)); } /* * Does gss_VerifyMICEx (or equivalent) and validates * the sec_trailer and the auth token. * * Used upon receiving a request (server)/response (client) packet. * * If auth_verify_resp is B_FALSE, this doesn't verify responses (but * the SSP may still have side-effects). */ int ndr_check_auth(ndr_auth_ctx_t *ctx, ndr_xa_t *mxa) { ndr_sec_t *secp = &mxa->recv_auth; if (ctx->auth_level == NDR_C_AUTHN_NONE || ctx->auth_type == NDR_C_AUTHN_NONE) { if (mxa->recv_hdr.common_hdr.auth_length != 0) return (NDR_DRC_FAULT_SEC_AUTH_LENGTH_INVALID); return (NDR_DRC_OK); } else if (mxa->recv_hdr.common_hdr.auth_length == 0) { return (NDR_DRC_FAULT_SEC_AUTH_LENGTH_INVALID); } if (ctx->auth_type != secp->auth_type || ctx->auth_type != NDR_C_AUTHN_GSS_NETLOGON) return (NDR_DRC_FAULT_SEC_AUTH_TYPE_INVALID); if (ctx->auth_level != secp->auth_level || (ctx->auth_level != NDR_C_AUTHN_LEVEL_PKT_INTEGRITY && ctx->auth_level != NDR_C_AUTHN_LEVEL_PKT_PRIVACY)) return (NDR_DRC_FAULT_SEC_AUTH_LEVEL_INVALID); if (ctx->auth_level == NDR_C_AUTHN_LEVEL_PKT_PRIVACY) return (ctx->auth_ops.nao_decrypt(ctx->auth_ctx, mxa, ctx->auth_verify_resp)); return (ctx->auth_ops.nao_verify(ctx->auth_ctx, mxa, ctx->auth_verify_resp)); } /* * 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 2020 Tintri by DDN, Inc. All rights reserved. */ #include #include #include #include #define NDR_DEFAULT_FRAGSZ 8192 #define NDR_MULTI_FRAGSZ (60 * 1024) static void ndr_clnt_init_hdr(ndr_client_t *, ndr_xa_t *); static int ndr_clnt_get_frags(ndr_client_t *, ndr_xa_t *); static int ndr_clnt_get_frag(ndr_client_t *, ndr_xa_t *, ndr_common_header_t *); int ndr_clnt_bind(ndr_client_t *clnt, ndr_service_t *msvc, ndr_binding_t **ret_binding_p) { ndr_binding_t *mbind; ndr_xa_t mxa; ndr_bind_hdr_t *bhdr; ndr_common_header_t *hdr; ndr_p_cont_elem_t *pce; ndr_bind_ack_hdr_t *bahdr; ndr_p_result_t *pre; int rc; bzero(&mxa, sizeof (mxa)); mxa.binding_list = clnt->binding_list; if ((mbind = ndr_svc_new_binding(&mxa)) == NULL) return (NDR_DRC_FAULT_API_BIND_NO_SLOTS); ndr_clnt_init_hdr(clnt, &mxa); bhdr = &mxa.send_hdr.bind_hdr; hdr = &mxa.send_hdr.bind_hdr.common_hdr; hdr->ptype = NDR_PTYPE_BIND; bhdr->max_xmit_frag = NDR_DEFAULT_FRAGSZ; bhdr->max_recv_frag = NDR_DEFAULT_FRAGSZ; bhdr->assoc_group_id = 0; bhdr->p_context_elem.n_context_elem = 1; /* Assign presentation context id */ pce = &bhdr->p_context_elem.p_cont_elem[0]; pce->p_cont_id = clnt->next_p_cont_id++; pce->n_transfer_syn = 1; /* Set up UUIDs and versions from the service */ pce->abstract_syntax.if_version = msvc->abstract_syntax_version; rc = ndr_uuid_parse(msvc->abstract_syntax_uuid, &pce->abstract_syntax.if_uuid); if (rc != 0) return (NDR_DRC_FAULT_API_SERVICE_INVALID); pce->transfer_syntaxes[0].if_version = msvc->transfer_syntax_version; rc = ndr_uuid_parse(msvc->transfer_syntax_uuid, &pce->transfer_syntaxes[0].if_uuid); if (rc != 0) return (NDR_DRC_FAULT_API_SERVICE_INVALID); /* Format and exchange the PDU */ if ((*clnt->xa_init)(clnt, &mxa) < 0) return (NDR_DRC_FAULT_OUT_OF_MEMORY); /* Reserve room for hdr */ mxa.send_nds.pdu_scan_offset = sizeof (*bhdr); /* GSS_Init_sec_context */ rc = ndr_add_sec_context(&clnt->auth_ctx, &mxa); if (NDR_DRC_IS_FAULT(rc)) goto fault_exit; rc = ndr_encode_pdu_auth(&mxa); if (NDR_DRC_IS_FAULT(rc)) goto fault_exit; /* * If we have auth data, then pdu_size has been initialized. * Otherwise, it hasn't. */ if (hdr->auth_length == 0) hdr->frag_length = sizeof (*bhdr); else hdr->frag_length = mxa.send_nds.pdu_size; /* Reset scan_offset to header */ mxa.send_nds.pdu_scan_offset = 0; rc = ndr_encode_pdu_hdr(&mxa); if (NDR_DRC_IS_FAULT(rc)) goto fault_exit; if ((*clnt->xa_exchange)(clnt, &mxa) < 0) { rc = NDR_DRC_FAULT_SEND_FAILED; goto fault_exit; } rc = ndr_decode_pdu_hdr(&mxa); if (NDR_DRC_IS_FAULT(rc)) goto fault_exit; rc = ndr_decode_pdu_auth(&mxa); if (NDR_DRC_IS_FAULT(rc)) goto fault_exit; bahdr = &mxa.recv_hdr.bind_ack_hdr; if (mxa.ptype != NDR_PTYPE_BIND_ACK) { rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; goto fault_exit; } if (bahdr->p_result_list.n_results != 1) { rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; goto fault_exit; } pre = &bahdr->p_result_list.p_results[0]; if (pre->result != NDR_PCDR_ACCEPTANCE) { rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; goto fault_exit; } /* GSS_init_sec_context 2 */ rc = ndr_recv_sec_context(&clnt->auth_ctx, &mxa); if (NDR_DRC_IS_FAULT(rc)) goto fault_exit; /* done with buffers */ (*clnt->xa_destruct)(clnt, &mxa); mbind->p_cont_id = pce->p_cont_id; mbind->which_side = NDR_BIND_SIDE_CLIENT; mbind->clnt = clnt; mbind->service = msvc; mbind->instance_specific = 0; *ret_binding_p = mbind; return (NDR_DRC_OK); fault_exit: (*clnt->xa_destruct)(clnt, &mxa); return (rc); } int ndr_clnt_call(ndr_binding_t *mbind, int opnum, void *params) { ndr_client_t *clnt = mbind->clnt; ndr_xa_t mxa; ndr_request_hdr_t *reqhdr; ndr_common_header_t *rsphdr; unsigned long recv_pdu_scan_offset, recv_pdu_size; int rc; bzero(&mxa, sizeof (mxa)); mxa.ptype = NDR_PTYPE_REQUEST; mxa.opnum = opnum; mxa.binding = mbind; ndr_clnt_init_hdr(clnt, &mxa); reqhdr = &mxa.send_hdr.request_hdr; reqhdr->common_hdr.ptype = NDR_PTYPE_REQUEST; reqhdr->p_cont_id = mbind->p_cont_id; reqhdr->opnum = opnum; rc = (*clnt->xa_init)(clnt, &mxa); if (NDR_DRC_IS_FAULT(rc)) return (rc); /* Reserve room for hdr */ mxa.send_nds.pdu_scan_offset = sizeof (*reqhdr); /* pdu_scan_offset now points to start of stub */ mxa.send_nds.pdu_body_offset = mxa.send_nds.pdu_scan_offset; rc = ndr_encode_call(&mxa, params); if (!NDR_DRC_IS_OK(rc)) goto fault_exit; /* * With the Stub data encoded, calculate the alloc_hint * before we add padding or auth data. */ reqhdr->alloc_hint = mxa.send_nds.pdu_size - sizeof (ndr_request_hdr_t); /* GSS_WrapEx/VerifyMICEx */ rc = ndr_add_auth(&clnt->auth_ctx, &mxa); if (NDR_DRC_IS_FAULT(rc)) goto fault_exit; rc = ndr_encode_pdu_auth(&mxa); if (NDR_DRC_IS_FAULT(rc)) goto fault_exit; /* * Now we have the PDU size, we need to set up the * frag_length. * Also reset pdu_scan_offset to header. */ mxa.send_hdr.common_hdr.frag_length = mxa.send_nds.pdu_size; mxa.send_nds.pdu_scan_offset = 0; rc = ndr_encode_pdu_hdr(&mxa); if (NDR_DRC_IS_FAULT(rc)) goto fault_exit; rc = (*clnt->xa_exchange)(clnt, &mxa); if (NDR_DRC_IS_FAULT(rc)) goto fault_exit; rc = ndr_decode_pdu_hdr(&mxa); if (NDR_DRC_IS_FAULT(rc)) goto fault_exit; if (mxa.ptype != NDR_PTYPE_RESPONSE) { rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; goto fault_exit; } rc = ndr_decode_pdu_auth(&mxa); if (NDR_DRC_IS_FAULT(rc)) goto fault_exit; rc = ndr_check_auth(&clnt->auth_ctx, &mxa); if (NDR_DRC_IS_FAULT(rc)) goto fault_exit; rsphdr = &mxa.recv_hdr.common_hdr; if (!NDR_IS_LAST_FRAG(rsphdr->pfc_flags)) { /* * This is a multi-fragment response. * Preserve the current body offset while getting * fragments so that we can continue afterward * as if we had received the entire response as * a single PDU. * * GROW_PDU trashes pdu_size; reset it afterwards. */ recv_pdu_size = mxa.recv_nds.pdu_size; (void) NDS_GROW_PDU(&mxa.recv_nds, NDR_MULTI_FRAGSZ, NULL); /* * pdu_scan_offset needs to be the first byte after the first * fragment in pdu_base_addr (minus the sec_trailer). * * pdu_size needs to be all of the (usable) data we've * received thus far. */ recv_pdu_scan_offset = mxa.recv_nds.pdu_body_offset; mxa.recv_nds.pdu_scan_offset = mxa.recv_nds.pdu_body_offset + mxa.recv_nds.pdu_body_size - mxa.recv_auth.auth_pad_len; mxa.recv_nds.pdu_size = recv_pdu_size; rc = ndr_clnt_get_frags(clnt, &mxa); if (NDR_DRC_IS_FAULT(rc)) goto fault_exit; mxa.recv_nds.pdu_scan_offset = recv_pdu_scan_offset; } rc = ndr_decode_return(&mxa, params); if (NDR_DRC_IS_FAULT(rc)) goto fault_exit; (*clnt->xa_preserve)(clnt, &mxa); (*clnt->xa_destruct)(clnt, &mxa); return (NDR_DRC_OK); fault_exit: ndr_show_hdr(&mxa.send_hdr.common_hdr); nds_show_state(&mxa.send_nds); if (mxa.send_hdr.common_hdr.auth_length != 0) ndr_show_auth(&mxa.send_auth); ndr_show_hdr(&mxa.recv_hdr.common_hdr); nds_show_state(&mxa.recv_nds); if (mxa.recv_hdr.common_hdr.auth_length != 0) ndr_show_auth(&mxa.recv_auth); (*clnt->xa_destruct)(clnt, &mxa); return (rc); } void ndr_clnt_free_heap(ndr_client_t *clnt) { (*clnt->xa_release)(clnt); } static void ndr_clnt_init_hdr(ndr_client_t *clnt, ndr_xa_t *mxa) { ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr; hdr->rpc_vers = 5; hdr->rpc_vers_minor = 0; hdr->pfc_flags = NDR_PFC_FIRST_FRAG + NDR_PFC_LAST_FRAG; hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII; #ifndef _BIG_ENDIAN hdr->packed_drep.intg_char_rep |= NDR_REPLAB_INTG_LITTLE_ENDIAN; #endif /* hdr->frag_length */ hdr->auth_length = 0; hdr->call_id = clnt->next_call_id++; } /* * ndr_clnt_get_frags * * A DCE RPC message that is larger than a single fragment is transmitted * as a series of fragments: 5280 bytes for Windows NT and 4280 bytes for * both Windows 2000 and 2003. * * Collect RPC fragments and append them to the receive stream buffer. * Each received fragment has a header, which we need to remove as we * build the full RPC PDU. The scan offset is used to track frag headers. */ static int ndr_clnt_get_frags(ndr_client_t *clnt, ndr_xa_t *mxa) { ndr_stream_t *nds = &mxa->recv_nds; ndr_common_header_t hdr; int frag_size; int last_frag; int rc; do { if (ndr_clnt_get_frag(clnt, mxa, &hdr) < 0) { nds_show_state(nds); return (NDR_DRC_FAULT_RECEIVED_RUNT); } last_frag = NDR_IS_LAST_FRAG(hdr.pfc_flags); frag_size = hdr.frag_length; /* * ndr_clnt_get_frag() doesn't change pdu_scan_offset. */ if (frag_size > (nds->pdu_size - nds->pdu_scan_offset)) { nds_show_state(nds); return (NDR_DRC_FAULT_RECEIVED_MALFORMED); } if (hdr.auth_length != 0 && hdr.auth_length > (hdr.frag_length - nds->pdu_hdr_size - SEC_TRAILER_SIZE)) return (NDR_DRC_FAULT_RECEIVED_MALFORMED); rc = ndr_decode_pdu_auth(mxa); if (NDR_DRC_IS_FAULT(rc)) return (rc); rc = ndr_check_auth(&clnt->auth_ctx, mxa); if (NDR_DRC_IS_FAULT(rc)) return (rc); /* * Headers, Auth Padding, and auth data shouldn't be kept * from fragments. */ ndr_remove_frag_hdr(nds); nds->pdu_scan_offset += nds->pdu_body_size - mxa->recv_auth.auth_pad_len; } while (!last_frag); return (0); } /* * Read the next RPC fragment. The xa_read() calls correspond to SmbReadX * requests. Note that there is no correspondence between SmbReadX buffering * and DCE RPC fragment alignment. */ static int ndr_clnt_get_frag(ndr_client_t *clnt, ndr_xa_t *mxa, ndr_common_header_t *hdr) { ndr_stream_t *nds = &mxa->recv_nds; unsigned long available; int nbytes = 0; available = nds->pdu_size - nds->pdu_scan_offset; while (available < NDR_RSP_HDR_SIZE) { if ((nbytes = (*clnt->xa_read)(clnt, mxa)) <= 0) return (-1); available += nbytes; } ndr_decode_frag_hdr(nds, hdr); ndr_show_hdr(hdr); while (available < hdr->frag_length) { if ((nbytes = (*clnt->xa_read)(clnt, mxa)) <= 0) return (-1); available += nbytes; } return (nbytes); } /* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ /* * NDR heap management. The heap is used for temporary storage by * both the client and server side library routines. In order to * support the different requirements of the various RPCs, the heap * can grow dynamically if required. We start with a single block * and perform sub-allocations from it. If an RPC requires more space * we will continue to add it a block at a time. This means that we * don't hog lots of memory on every call to support the few times * that we actually need a lot heap space. * * Note that there is no individual free function. Once space has been * allocated, it remains allocated until the heap is destroyed. This * shouldn't be an issue because the heap is being filled with data to * be marshalled or unmarshalled and we need it all to be there until * the point that the entire heap is no longer required. */ #include #include #include #include #include #include #include /* * Allocate a heap structure and the first heap block. For many RPC * operations this will be the only time we need to malloc memory * in this instance of the heap. The only point of note here is that * we put the heap management data in the first block to avoid a * second malloc. Make sure that sizeof(ndr_heap_t) is smaller * than NDR_HEAP_BLKSZ. * * Note that the heap management data is at the start of the first block. * * Returns a pointer to the newly created heap, which is used like an * opaque handle with the rest of the heap management interface.. */ ndr_heap_t * ndr_heap_create(void) { ndr_heap_t *heap; char *base; size_t allocsize = sizeof (ndr_heap_t) + NDR_HEAP_BLKSZ; if ((heap = malloc(allocsize)) == NULL) return (NULL); base = (char *)heap; bzero(heap, sizeof (ndr_heap_t)); heap->iovcnt = NDR_HEAP_MAXIOV; heap->iov = heap->iovec; heap->iov->iov_base = base; heap->iov->iov_len = sizeof (ndr_heap_t); heap->top = base + allocsize; heap->next = base + sizeof (ndr_heap_t); return (heap); } /* * Deallocate all of the memory associated with a heap. This is the * only way to deallocate heap memory, it isn't possible to free the * space obtained by individual malloc calls. * * Note that the first block contains the heap management data, which * is deleted last. */ void ndr_heap_destroy(ndr_heap_t *heap) { int i; char *p; if (heap) { for (i = 1; i < NDR_HEAP_MAXIOV; ++i) { if ((p = heap->iovec[i].iov_base) != NULL) free(p); } free(heap); } } /* * Allocate space in the specified heap. All requests are padded, if * required, to ensure dword alignment. If the current iov will be * exceeded, we allocate a new block and setup the next iov. Otherwise * all we have to do is move the next pointer and update the current * iov length. * * On success, a pointer to the allocated (dword aligned) area is * returned. Otherwise a null pointer is returned. */ void * ndr_heap_malloc(ndr_heap_t *heap, unsigned size) { char *p; int incr_size; size += NDR_ALIGN4(size); if (heap == NULL || size == 0) return (NULL); p = heap->next; if (p + size > heap->top) { if ((heap->iovcnt == 0) || ((--heap->iovcnt) == 0)) return (NULL); incr_size = (size < NDR_HEAP_BLKSZ) ? NDR_HEAP_BLKSZ : size; if ((p = (char *)malloc(incr_size)) == NULL) return (NULL); ++heap->iov; heap->iov->iov_base = p; heap->iov->iov_len = 0; heap->top = p + incr_size; } heap->next = p + size; heap->iov->iov_len += size; return ((void *)p); } /* * Convenience function to copy some memory into the heap. */ void * ndr_heap_dupmem(ndr_heap_t *heap, const void *mem, size_t len) { void *p; if (mem == NULL) return (NULL); if ((p = ndr_heap_malloc(heap, len)) != NULL) (void) memcpy(p, mem, len); return (p); } /* * Convenience function to do heap strdup. */ void * ndr_heap_strdup(ndr_heap_t *heap, const char *s) { int len; void *p; if (s == NULL) return (NULL); /* * We don't need to clutter the heap with empty strings. */ if ((len = strlen(s)) == 0) return (""); p = ndr_heap_dupmem(heap, s, len+1); return (p); } /* * Make an ndr_mstring_t from a regular string. */ int ndr_heap_mstring(ndr_heap_t *heap, const char *s, ndr_mstring_t *out) { size_t slen; if (s == NULL || out == NULL) return (-1); /* * Determine the WC strlen of s * Was ndr__wcequiv_strlen(s) */ slen = ndr__mbstowcs(NULL, s, NDR_STRING_MAX); if (slen == (size_t)-1) return (-1); out->length = slen * sizeof (ndr_wchar_t); out->allosize = out->length + sizeof (ndr_wchar_t); if ((out->str = ndr_heap_strdup(heap, s)) == NULL) return (-1); return (0); } /* * Our regular string marshalling always creates null terminated strings * but some Windows clients and servers are pedantic about the string * formats they will accept and require non-null terminated strings. * This function can be used to build a wide-char, non-null terminated * string in the heap as a varying/conformant array. We need to do the * wide-char conversion here because the marshalling code won't be * aware that this is really a string. */ void ndr_heap_mkvcs(ndr_heap_t *heap, char *s, ndr_vcstr_t *vc) { size_t slen; int mlen; /* * Determine the WC strlen of s * Was ndr__wcequiv_strlen(s) */ slen = ndr__mbstowcs(NULL, s, NDR_STRING_MAX); if (slen == (size_t)-1) slen = 0; vc->wclen = slen * sizeof (ndr_wchar_t); vc->wcsize = vc->wclen; /* * alloc one extra wchar for a null * See slen + 1 arg for mbstowcs */ mlen = sizeof (ndr_vcs_t) + vc->wcsize + sizeof (ndr_wchar_t); vc->vcs = ndr_heap_malloc(heap, mlen); if (vc->vcs) { vc->vcs->vc_first_is = 0; vc->vcs->vc_length_is = slen; (void) ndr__mbstowcs(vc->vcs->buffer, s, slen + 1); } } void ndr_heap_mkvcb(ndr_heap_t *heap, uint8_t *data, uint32_t datalen, ndr_vcbuf_t *vcbuf) { int mlen; if (data == NULL || datalen == 0) { bzero(vcbuf, sizeof (ndr_vcbuf_t)); return; } vcbuf->len = datalen; vcbuf->size = datalen; mlen = sizeof (ndr_vcbuf_t) + datalen; vcbuf->vcb = ndr_heap_malloc(heap, mlen); if (vcbuf->vcb) { vcbuf->vcb->vc_first_is = 0; vcbuf->vcb->vc_length_is = datalen; bcopy(data, vcbuf->vcb->buffer, datalen); } } /* * Removed ndr_heap_siddup(), now using ndr_heap_dupmem(). */ int ndr_heap_used(ndr_heap_t *heap) { int used = 0; int i; for (i = 0; i < NDR_HEAP_MAXIOV; ++i) used += heap->iovec[i].iov_len; return (used); } int ndr_heap_avail(ndr_heap_t *heap) { int avail; int count; count = (heap->iovcnt == 0) ? 0 : (heap->iovcnt - 1); avail = count * NDR_HEAP_BLKSZ; avail += (heap->top - heap->next); return (avail); } /* * 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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2021 Tintri by DDN, Inc. All rights reserved. */ #include #include #include #include #include #ifdef _BIG_ENDIAN static const int ndr_native_byte_order = NDR_REPLAB_INTG_BIG_ENDIAN; #else static const int ndr_native_byte_order = NDR_REPLAB_INTG_LITTLE_ENDIAN; #endif static int ndr_decode_hdr_common(ndr_stream_t *, ndr_common_header_t *); static int ndr_decode_pac_hdr(ndr_stream_t *, ndr_pac_hdr_t *); /* * This is the layout of an RPC PDU, as shown in * [MS-RPCE] 2.2.2.13 "Verification Trailer". * * +-------------------------------+ * | PDU Header | * +-------------------------------+ ==== * | Stub Data | * +-------------------------------+ PDU * | Stub Padding Octets | * +-------------------------------+ Body * | Verification Trailer | * +-------------------------------+ Here * | Authentication Padding | * +-------------------------------+ ==== * | sec_trailer | * +-------------------------------+ * | Authentication Token | * +-------------------------------+ * * We don't use the "Verification Trailer" for anything yet. * sec_trailer and Authentication Token are for Secure RPC, * and are collectively the 'auth_verifier_co' in DCERPC. * * Each fragment of a multi-fragment response has a unique * header and, if authentication was requested, a unique * sec_trailer. */ static int ndr_convert_nds_error(ndr_stream_t *nds) { int rc; switch (nds->error) { case NDR_ERR_MALLOC_FAILED: rc = NDR_DRC_FAULT_OUT_OF_MEMORY; break; case NDR_ERR_SWITCH_VALUE_INVALID: rc = NDR_DRC_FAULT_PARAM_0_INVALID; break; case NDR_ERR_UNDERFLOW: rc = NDR_DRC_FAULT_RECEIVED_RUNT; break; case NDR_ERR_GROW_FAILED: rc = NDR_DRC_FAULT_ENCODE_TOO_BIG; break; default: if (nds->m_op == NDR_M_OP_MARSHALL) rc = NDR_DRC_FAULT_ENCODE_FAILED; else rc = NDR_DRC_FAULT_DECODE_FAILED; break; } return (rc); } static int ndr_encode_decode_common(ndr_stream_t *nds, unsigned opnum, ndr_typeinfo_t *ti, void *datum) { /* * Perform the (un)marshalling */ if (ndo_operation(nds, ti, opnum, datum)) return (NDR_DRC_OK); return (ndr_convert_nds_error(nds)); } static int ndr_encode_decode_type(ndr_stream_t *nds, ndr_typeinfo_t *ti, void *datum) { /* * Perform the (un)marshalling */ if (ndo_process(nds, ti, datum)) return (NDR_DRC_OK); return (ndr_convert_nds_error(nds)); } ndr_buf_t * ndr_buf_init(ndr_typeinfo_t *ti) { ndr_buf_t *nbuf; if ((nbuf = calloc(1, sizeof (ndr_buf_t))) == NULL) return (NULL); if ((nbuf->nb_heap = ndr_heap_create()) == NULL) { free(nbuf); return (NULL); } nbuf->nb_ti = ti; nbuf->nb_magic = NDR_BUF_MAGIC; return (nbuf); } void ndr_buf_fini(ndr_buf_t *nbuf) { assert(nbuf->nb_magic == NDR_BUF_MAGIC); nds_destruct(&nbuf->nb_nds); ndr_heap_destroy(nbuf->nb_heap); nbuf->nb_magic = 0; free(nbuf); } /* * Decode an NDR encoded buffer. The buffer is expected to contain * a single fragment packet with a valid PDU header followed by NDR * encoded data. The structure to which result points should be * of the appropriate type to hold the decoded output. For example: * * pac_info_t info; * * if ((nbuf = ndr_buf_init(&TYPEINFO(ndr_pac)) != NULL) { * rc = ndr_decode_buf(nbuf, opnum, data, datalen, &info); * ... * ndr_buf_fini(nbuf); * } */ int ndr_buf_decode(ndr_buf_t *nbuf, unsigned hdr_type, unsigned opnum, const char *data, size_t datalen, void *result) { ndr_common_header_t hdr; ndr_pac_hdr_t pac_hdr; unsigned pdu_size_hint; int rc; assert(nbuf->nb_magic == NDR_BUF_MAGIC); assert(nbuf->nb_heap != NULL); assert(nbuf->nb_ti != NULL); if (datalen < NDR_PDU_SIZE_HINT_DEFAULT) pdu_size_hint = NDR_PDU_SIZE_HINT_DEFAULT; else pdu_size_hint = datalen; rc = nds_initialize(&nbuf->nb_nds, pdu_size_hint, NDR_MODE_BUF_DECODE, nbuf->nb_heap); if (NDR_DRC_IS_FAULT(rc)) return (rc); bcopy(data, nbuf->nb_nds.pdu_base_addr, datalen); nbuf->nb_nds.pdu_size = datalen; switch (hdr_type) { case NDR_PTYPE_COMMON: rc = ndr_decode_hdr_common(&nbuf->nb_nds, &hdr); if (NDR_DRC_IS_FAULT(rc)) return (rc); if (!NDR_IS_SINGLE_FRAG(hdr.pfc_flags)) return (NDR_DRC_FAULT_DECODE_FAILED); break; case NDR_PTYPE_PAC: rc = ndr_decode_pac_hdr(&nbuf->nb_nds, &pac_hdr); if (NDR_DRC_IS_FAULT(rc)) return (rc); if (pac_hdr.common_hdr.hdrlen != sizeof (ndr_serialtype1_hdr_t)) return (NDR_DRC_FAULT_DECODE_FAILED); break; default: return (NDR_ERR_UNIMPLEMENTED); } rc = ndr_encode_decode_common(&nbuf->nb_nds, opnum, nbuf->nb_ti, result); return (rc); } /* * Use the receive stream to unmarshall data (NDR_MODE_CALL_RECV). */ int ndr_decode_call(ndr_xa_t *mxa, void *params) { ndr_stream_t *nds = &mxa->recv_nds; int rc; if (!NDR_MODE_MATCH(nds, NDR_MODE_CALL_RECV)) return (NDR_DRC_FAULT_MODE_MISMATCH); rc = ndr_encode_decode_common(nds, mxa->opnum, mxa->binding->service->interface_ti, params); return (rc + NDR_PTYPE_REQUEST); } /* * Use the send stream to marshall data (NDR_MODE_RETURN_SEND). */ int ndr_encode_return(ndr_xa_t *mxa, void *params) { ndr_stream_t *nds = &mxa->send_nds; int rc; if (!NDR_MODE_MATCH(nds, NDR_MODE_RETURN_SEND)) return (NDR_DRC_FAULT_MODE_MISMATCH); rc = ndr_encode_decode_common(nds, mxa->opnum, mxa->binding->service->interface_ti, params); return (rc + NDR_PTYPE_RESPONSE); } /* * Use the send stream to marshall data (NDR_MODE_CALL_SEND). */ int ndr_encode_call(ndr_xa_t *mxa, void *params) { ndr_stream_t *nds = &mxa->send_nds; int rc; if (!NDR_MODE_MATCH(nds, NDR_MODE_CALL_SEND)) return (NDR_DRC_FAULT_MODE_MISMATCH); rc = ndr_encode_decode_common(nds, mxa->opnum, mxa->binding->service->interface_ti, params); return (rc + NDR_PTYPE_REQUEST); } /* * Use the receive stream to unmarshall data (NDR_MODE_RETURN_RECV). */ int ndr_decode_return(ndr_xa_t *mxa, void *params) { ndr_stream_t *nds = &mxa->recv_nds; int rc; if (!NDR_MODE_MATCH(nds, NDR_MODE_RETURN_RECV)) return (NDR_DRC_FAULT_MODE_MISMATCH); rc = ndr_encode_decode_common(nds, mxa->opnum, mxa->binding->service->interface_ti, params); return (rc + NDR_PTYPE_RESPONSE); } int ndr_decode_pdu_hdr(ndr_xa_t *mxa) { ndr_common_header_t *hdr = &mxa->recv_hdr.common_hdr; ndr_stream_t *nds = &mxa->recv_nds; int rc; ulong_t saved_offset; saved_offset = nds->pdu_scan_offset; rc = ndr_decode_hdr_common(nds, hdr); if (NDR_DRC_IS_FAULT(rc)) return (rc); /* * Verify the protocol version. */ if ((hdr->rpc_vers != 5) || (hdr->rpc_vers_minor != 0)) return (NDR_DRC_FAULT_RPCHDR_DECODE_FAILED); mxa->ptype = hdr->ptype; /* pdu_scan_offset now points to (this fragment's) stub data */ nds->pdu_body_offset = nds->pdu_scan_offset; nds->pdu_hdr_size = nds->pdu_scan_offset - saved_offset; nds->pdu_body_size = hdr->frag_length - hdr->auth_length - nds->pdu_hdr_size - ((hdr->auth_length != 0) ? SEC_TRAILER_SIZE : 0); if (hdr->auth_length != 0 && hdr->auth_length > (hdr->frag_length - nds->pdu_hdr_size - SEC_TRAILER_SIZE)) return (NDR_DRC_FAULT_RECEIVED_MALFORMED); return (NDR_DRC_OK); } static int ndr_decode_hdr_common(ndr_stream_t *nds, ndr_common_header_t *hdr) { int ptype; int rc; int charset; int byte_order; ulong_t saved_offset; if (nds->m_op != NDR_M_OP_UNMARSHALL) return (NDR_DRC_FAULT_RPCHDR_MODE_MISMATCH); /* * All PDU headers are at least this big */ saved_offset = nds->pdu_scan_offset; if ((nds->pdu_size - saved_offset) < sizeof (ndr_common_header_t)) return (NDR_DRC_FAULT_RPCHDR_RECEIVED_RUNT); /* * Peek at the first eight bytes to figure out what we're doing. */ rc = NDS_GET_PDU(nds, 0, 8, (char *)hdr, 0, 0); if (!rc) return (NDR_DRC_FAULT_RPCHDR_DECODE_FAILED); /* * Check for ASCII as the character set. This is an ASCII * versus EBCDIC option and has nothing to do with Unicode. */ charset = hdr->packed_drep.intg_char_rep & NDR_REPLAB_CHAR_MASK; if (charset != NDR_REPLAB_CHAR_ASCII) return (NDR_DRC_FAULT_RPCHDR_DECODE_FAILED); /* * Set the byte swap flag if the PDU byte-order * is different from the local byte-order. */ byte_order = hdr->packed_drep.intg_char_rep & NDR_REPLAB_INTG_MASK; nds->swap = (byte_order != ndr_native_byte_order) ? 1 : 0; ptype = hdr->ptype; if (ptype == NDR_PTYPE_REQUEST && (hdr->pfc_flags & NDR_PFC_OBJECT_UUID) != 0) { ptype = NDR_PTYPE_REQUEST_WITH; /* fake for sizing */ } rc = ndr_encode_decode_common(nds, ptype, &TYPEINFO(ndr_hdr), hdr); if (hdr->frag_length > (nds->pdu_size - saved_offset)) rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; return (NDR_DRC_PTYPE_RPCHDR(rc)); } static int ndr_decode_pac_hdr(ndr_stream_t *nds, ndr_pac_hdr_t *hdr) { int rc; if (nds->m_op != NDR_M_OP_UNMARSHALL) return (NDR_DRC_FAULT_RPCHDR_MODE_MISMATCH); /* * All PDU headers are at least this big */ if ((nds->pdu_size - nds->pdu_scan_offset) < sizeof (ndr_pac_hdr_t)) return (NDR_DRC_FAULT_RPCHDR_RECEIVED_RUNT); /* * Peek at the first eight bytes to figure out what we're doing. */ rc = NDS_GET_PDU(nds, 0, 8, (char *)hdr, 0, 0); if (!rc) return (NDR_DRC_FAULT_RPCHDR_DECODE_FAILED); /* Must be set to 1 to indicate type serialization version 1. */ if (hdr->common_hdr.version != 1) return (NDR_DRC_FAULT_RPCHDR_DECODE_FAILED); /* * Set the byte swap flag if the PDU byte-order * is different from the local byte-order. */ nds->swap = (hdr->common_hdr.endianness != ndr_native_byte_order) ? 1 : 0; rc = ndr_encode_decode_common(nds, NDR_PTYPE_PAC, &TYPEINFO(ndr_hdr), hdr); return (NDR_DRC_PTYPE_RPCHDR(rc)); } CTASSERT(sizeof (ndr_common_header_t) >= NDR_CMN_HDR_SIZE); /* * Decode an RPC fragment header. Use ndr_decode_pdu_hdr() to process * the first fragment header then this function to process additional * fragment headers. */ void ndr_decode_frag_hdr(ndr_stream_t *nds, ndr_common_header_t *hdr) { ndr_common_header_t *tmp; uint8_t *pdu; int byte_order; pdu = (uint8_t *)nds->pdu_base_offset + nds->pdu_scan_offset; bcopy(pdu, hdr, NDR_CMN_HDR_SIZE); /* * Swap non-byte fields if the PDU byte-order * is different from the local byte-order. */ byte_order = hdr->packed_drep.intg_char_rep & NDR_REPLAB_INTG_MASK; if (byte_order != ndr_native_byte_order) { /*LINTED E_BAD_PTR_CAST_ALIGN*/ tmp = (ndr_common_header_t *)pdu; nds_bswap(&tmp->frag_length, &hdr->frag_length, sizeof (WORD)); nds_bswap(&tmp->auth_length, &hdr->auth_length, sizeof (WORD)); nds_bswap(&tmp->call_id, &hdr->call_id, sizeof (DWORD)); } /* pdu_scan_offset points to byte 0 of this fragment */ nds->pdu_hdr_size = NDR_RSP_HDR_SIZE; nds->pdu_body_offset = nds->pdu_scan_offset + nds->pdu_hdr_size; nds->pdu_body_size = hdr->frag_length - hdr->auth_length - nds->pdu_hdr_size - ((hdr->auth_length != 0) ? SEC_TRAILER_SIZE : 0); } /* * Remove an RPC fragment header from the received data stream. * * NDR stream on entry: * * |<--- frag --->| * +-----+--------+-----+--------+-----+---------+-----+ * | hdr | data | hdr | data | hdr | data | ... | * +-----+--------+-----+--------+-----+---------+-----+ * <---- * * NDR stream on return: * * +-----+----------------+-----+---------+-----+ * | hdr | data | hdr | data | ... | * +-----+----------------+-----+---------+-----+ */ void ndr_remove_frag_hdr(ndr_stream_t *nds) { char *hdr; char *data; int nbytes; hdr = (char *)nds->pdu_base_offset + nds->pdu_scan_offset; data = hdr + NDR_RSP_HDR_SIZE; nbytes = nds->pdu_size - nds->pdu_scan_offset - NDR_RSP_HDR_SIZE; /* * Move all of the data after the header back to where the header began. */ memmove(hdr, data, nbytes); nds->pdu_size -= NDR_RSP_HDR_SIZE; } void ndr_show_hdr(ndr_common_header_t *hdr) { char *fragtype; if (hdr == NULL) { ndo_printf(NULL, NULL, "ndr hdr: "); return; } if (NDR_IS_SINGLE_FRAG(hdr->pfc_flags)) fragtype = "single"; else if (NDR_IS_FIRST_FRAG(hdr->pfc_flags)) fragtype = "first"; else if (NDR_IS_LAST_FRAG(hdr->pfc_flags)) fragtype = "last"; else fragtype = "intermediate"; ndo_printf(NULL, NULL, "ndr hdr: %d.%d ptype=%d, %s frag (flags=0x%08x) len=%d " "auth_len=%d", hdr->rpc_vers, hdr->rpc_vers_minor, hdr->ptype, fragtype, hdr->pfc_flags, hdr->frag_length, hdr->auth_length); } void ndr_show_auth(ndr_sec_t *auth) { if (auth == NULL) { ndo_printf(NULL, NULL, "ndr auth: "); return; } ndo_printf(NULL, NULL, "ndr auth: type=0x%x, level=0x%x, pad_len=%d, ctx_id=%d", auth->auth_type, auth->auth_level, auth->auth_pad_len, auth->auth_context_id); } int ndr_encode_pdu_hdr(ndr_xa_t *mxa) { ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr; ndr_stream_t *nds = &mxa->send_nds; int ptype; int rc; if (nds->m_op != NDR_M_OP_MARSHALL) return (NDR_DRC_FAULT_RPCHDR_MODE_MISMATCH); ptype = hdr->ptype; if (ptype == NDR_PTYPE_REQUEST && (hdr->pfc_flags & NDR_PFC_OBJECT_UUID) != 0) { ptype = NDR_PTYPE_REQUEST_WITH; /* fake for sizing */ } rc = ndr_encode_decode_common(nds, ptype, &TYPEINFO(ndr_hdr), hdr); return (NDR_DRC_PTYPE_RPCHDR(rc)); } /* * This is a hand-coded derivative of the automatically generated * (un)marshalling routine for bind_ack headers. bind_ack headers * have an interior conformant array, which is inconsistent with * IDL/NDR rules. */ extern struct ndr_typeinfo ndt__uchar; extern struct ndr_typeinfo ndt__ushort; extern struct ndr_typeinfo ndt__ulong; int ndr__ndr_bind_ack_hdr(ndr_ref_t *encl_ref); ndr_typeinfo_t ndt__ndr_bind_ack_hdr = { 1, /* NDR version */ 3, /* alignment */ NDR_F_STRUCT, /* flags */ ndr__ndr_bind_ack_hdr, /* ndr_func */ 68, /* pdu_size_fixed_part */ 0, /* pdu_size_variable_part */ 68, /* c_size_fixed_part */ 0, /* c_size_variable_part */ }; /* * [_no_reorder] */ int ndr__ndr_bind_ack_hdr(ndr_ref_t *encl_ref) { ndr_stream_t *nds = encl_ref->stream; struct ndr_bind_ack_hdr *val = /*LINTED E_BAD_PTR_CAST_ALIGN*/ (struct ndr_bind_ack_hdr *)encl_ref->datum; ndr_ref_t myref; unsigned long offset; bzero(&myref, sizeof (myref)); myref.enclosing = encl_ref; myref.stream = encl_ref->stream; myref.packed_alignment = 0; /* do all members in order */ NDR_MEMBER(_ndr_common_header, common_hdr, 0UL); NDR_MEMBER(_ushort, max_xmit_frag, 16UL); NDR_MEMBER(_ushort, max_recv_frag, 18UL); NDR_MEMBER(_ulong, assoc_group_id, 20UL); /* port any is the conformant culprit */ offset = 24UL; switch (nds->m_op) { case NDR_M_OP_MARSHALL: val->sec_addr.length = strlen((char *)val->sec_addr.port_spec) + 1; break; case NDR_M_OP_UNMARSHALL: break; default: NDR_SET_ERROR(encl_ref, NDR_ERR_M_OP_INVALID); return (0); } NDR_MEMBER(_ushort, sec_addr.length, offset); NDR_MEMBER_ARR_WITH_DIMENSION(_uchar, sec_addr.port_spec, offset+2UL, val->sec_addr.length); offset += 2; offset += val->sec_addr.length; offset += NDR_ALIGN4(offset); NDR_MEMBER(_ndr_p_result_list, p_result_list, offset); return (1); } /* * Assume a single presentation context element in the result list. */ unsigned ndr_bind_ack_hdr_size(ndr_xa_t *mxa) { ndr_bind_ack_hdr_t *bahdr = &mxa->send_hdr.bind_ack_hdr; unsigned offset; unsigned length; /* port any is the conformant culprit */ offset = 24UL; length = strlen((char *)bahdr->sec_addr.port_spec) + 1; offset += 2; offset += length; offset += NDR_ALIGN4(offset); offset += sizeof (ndr_p_result_list_t); return (offset); } /* * This is a hand-coded derivative of the automatically generated * (un)marshalling routine for alter_context_rsp headers. * Alter context response headers have an interior conformant array, * which is inconsistent with IDL/NDR rules. */ int ndr__ndr_alter_context_rsp_hdr(ndr_ref_t *encl_ref); ndr_typeinfo_t ndt__ndr_alter_context_rsp_hdr = { 1, /* NDR version */ 3, /* alignment */ NDR_F_STRUCT, /* flags */ ndr__ndr_alter_context_rsp_hdr, /* ndr_func */ 56, /* pdu_size_fixed_part */ 0, /* pdu_size_variable_part */ 56, /* c_size_fixed_part */ 0, /* c_size_variable_part */ }; /* * [_no_reorder] */ int ndr__ndr_alter_context_rsp_hdr(ndr_ref_t *encl_ref) { ndr_stream_t *nds = encl_ref->stream; ndr_alter_context_rsp_hdr_t *val = /*LINTED E_BAD_PTR_CAST_ALIGN*/ (ndr_alter_context_rsp_hdr_t *)encl_ref->datum; ndr_ref_t myref; unsigned long offset; bzero(&myref, sizeof (myref)); myref.enclosing = encl_ref; myref.stream = encl_ref->stream; myref.packed_alignment = 0; /* do all members in order */ NDR_MEMBER(_ndr_common_header, common_hdr, 0UL); NDR_MEMBER(_ushort, max_xmit_frag, 16UL); NDR_MEMBER(_ushort, max_recv_frag, 18UL); NDR_MEMBER(_ulong, assoc_group_id, 20UL); offset = 24UL; /* offset of sec_addr */ switch (nds->m_op) { case NDR_M_OP_MARSHALL: val->sec_addr.length = 0; break; case NDR_M_OP_UNMARSHALL: break; default: NDR_SET_ERROR(encl_ref, NDR_ERR_M_OP_INVALID); return (0); } NDR_MEMBER(_ushort, sec_addr.length, offset); NDR_MEMBER_ARR_WITH_DIMENSION(_uchar, sec_addr.port_spec, offset+2UL, val->sec_addr.length); offset += 2; /* sizeof (sec_addr.length) */ offset += NDR_ALIGN4(offset); NDR_MEMBER(_ndr_p_result_list, p_result_list, offset); return (1); } /* * Assume a single presentation context element in the result list. */ unsigned ndr_alter_context_rsp_hdr_size(void) { unsigned offset; offset = 24UL; /* offset of sec_addr */ offset += 2; /* sizeof (sec_addr.length) */ offset += NDR_ALIGN4(offset); offset += sizeof (ndr_p_result_list_t); return (offset); } /* * This is a hand-coded (un)marshalling routine for auth_verifier_co * (aka ndr_sec_t). * * We need to pretend this structure isn't variably sized, until ndrgen * has been modified to support variable-sized arrays. * Here, we only account for the fixed-size members (8 bytes), plus * a pointer for the C structure. * * We then convert between a pointer to the auth token (auth_value, * allocated here during unmarshall) and a flat, 'fixed'-sized array. */ int ndr__auth_verifier_co(ndr_ref_t *encl_ref); ndr_typeinfo_t ndt__auth_verifier_co = { 1, /* NDR version */ 3, /* alignment */ NDR_F_STRUCT, /* flags */ ndr__auth_verifier_co, /* ndr_func */ 8, /* pdu_size_fixed_part */ 0, /* pdu_size_variable_part */ 8 + sizeof (void *), /* c_size_fixed_part */ 0, /* c_size_variable_part */ }; /* * [_no_reorder] */ int ndr__auth_verifier_co(ndr_ref_t *encl_ref) { ndr_stream_t *nds = encl_ref->stream; ndr_xa_t *mxa = /*LINTED E_BAD_PTR_CAST_ALIGN*/ (ndr_xa_t *)encl_ref->datum; ndr_common_header_t *hdr; ndr_ref_t myref; ndr_sec_t *val; /* * Assumes scan_offset points to the end of PDU body. * (That's base + frag_len - auth_len - SEC_TRAILER_SIZE) * * At some point, NDRGEN could use struct initializers instead of * bzero() + initialization. */ bzero(&myref, sizeof (myref)); myref.enclosing = encl_ref; myref.stream = encl_ref->stream; switch (nds->m_op) { case NDR_M_OP_MARSHALL: val = &mxa->send_auth; hdr = &mxa->send_hdr.common_hdr; break; case NDR_M_OP_UNMARSHALL: val = &mxa->recv_auth; hdr = &mxa->recv_hdr.common_hdr; val->auth_value = (uchar_t *)NDS_MALLOC(nds, hdr->auth_length, encl_ref); break; default: NDR_SET_ERROR(encl_ref, NDR_ERR_M_OP_INVALID); return (0); } /* * ndr_topmost() can't account for auth_length (pdu_scan/end_offset). * This would only matter if any of this struct's members * are treated as 'outer' constructs, but they aren't. */ encl_ref->pdu_end_offset += hdr->auth_length; nds->pdu_scan_offset += hdr->auth_length; NDR_MEMBER(_uchar, auth_type, 0UL); NDR_MEMBER(_uchar, auth_level, 1UL); NDR_MEMBER(_uchar, auth_pad_len, 2UL); NDR_MEMBER(_uchar, auth_rsvd, 3UL); NDR_MEMBER(_ulong, auth_context_id, 4UL); NDR_MEMBER_PTR_WITH_DIMENSION(_uchar, auth_value, 8UL, hdr->auth_length); return (1); } int ndr_encode_pdu_auth(ndr_xa_t *mxa) { ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr; ndr_stream_t *nds = &mxa->send_nds; int rc; ulong_t want_size; if (nds->m_op != NDR_M_OP_MARSHALL) return (NDR_DRC_FAULT_MODE_MISMATCH); if (hdr->auth_length == 0) return (NDR_DRC_OK); want_size = nds->pdu_scan_offset + hdr->auth_length + SEC_TRAILER_SIZE; /* * Make sure we have space for the sec trailer - the marshaller * doesn't know how large the auth token is. * Note: ndr_add_auth_token() has already added padding. * * NDS_GROW_PDU will adjust pdu_size for us. */ if (nds->pdu_max_size < want_size) { if (NDS_GROW_PDU(nds, want_size, NULL) == 0) return (NDR_DRC_FAULT_ENCODE_TOO_BIG); } else { nds->pdu_size = want_size; } rc = ndr_encode_decode_type(nds, &TYPEINFO(auth_verifier_co), mxa); return (rc); } int ndr_decode_pdu_auth(ndr_xa_t *mxa) { ndr_common_header_t *hdr = &mxa->recv_hdr.common_hdr; ndr_stream_t *nds = &mxa->recv_nds; ndr_sec_t *auth = &mxa->recv_auth; int rc; ulong_t saved_offset; size_t auth_size; if (nds->m_op != NDR_M_OP_UNMARSHALL) return (NDR_DRC_FAULT_MODE_MISMATCH); mxa->recv_auth.auth_pad_len = 0; if (hdr->auth_length == 0) return (NDR_DRC_OK); /* * Save the current offset, and skip to the sec_trailer. * That's located after the (fragment of) stub data and the auth * pad bytes (collectively the 'PDU Body'). */ saved_offset = nds->pdu_scan_offset; nds->pdu_scan_offset = nds->pdu_body_offset + nds->pdu_body_size; /* auth_length is all of the data after the sec_trailer */ if (hdr->auth_length > (nds->pdu_size - nds->pdu_scan_offset - SEC_TRAILER_SIZE)) { nds->pdu_scan_offset = saved_offset; return (NDR_DRC_FAULT_RECEIVED_MALFORMED); } rc = ndr_encode_decode_type(nds, &TYPEINFO(auth_verifier_co), mxa); /* * Reset the scan_offset for call decode processing. * If we were successful, remove the sec trailer and padding * from size accounting. */ if (auth->auth_pad_len > nds->pdu_body_size) rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; else if (rc == NDR_DRC_OK) { auth_size = hdr->auth_length + SEC_TRAILER_SIZE + auth->auth_pad_len; /* * After the authenticator has been decoded, * pdu_scan_offset points to just after the auth token, * which is the end of the fragment. * * If there's no data after the authenticator, then we * just remove the authenticator from size accounting. * Otherwise, need to memmove() all of that data back to after * the stub data. The data we move starts at the beginning of * the next fragment. */ if (nds->pdu_size > nds->pdu_scan_offset) { uchar_t *next_frag_ptr = nds->pdu_base_addr + nds->pdu_scan_offset; memmove(next_frag_ptr - auth_size, next_frag_ptr, nds->pdu_size - nds->pdu_scan_offset); } nds->pdu_size -= auth_size; } nds->pdu_scan_offset = saved_offset; return (rc); } /* * 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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ /* * Server-side NDR stream (PDU) operations. Stream operations should * return TRUE (non-zero) on success or FALSE (zero or a null pointer) * on failure. When an operation returns FALSE, including ndo_malloc() * returning NULL, it should set the nds->error to indicate what went * wrong. * * When available, the relevant ndr reference is passed to the * operation but keep in mind that it may be a null pointer. * * Functions ndo_get_pdu(), ndo_put_pdu(), and ndo_pad_pdu() * must never grow the PDU data. A request for out-of-bounds data is * an error. The swap_bytes flag is 1 if NDR knows that the byte- * order in the PDU is different from the local system. */ #include #include #include #include #include #include #include #include #include #define NDOBUFSZ 128 #define NDR_PDU_BLOCK_SIZE (4*1024) #define NDR_PDU_BLOCK_MASK (NDR_PDU_BLOCK_SIZE - 1) #define NDR_PDU_ALIGN(N) \ (((N) + NDR_PDU_BLOCK_SIZE) & ~NDR_PDU_BLOCK_MASK) #define NDR_PDU_MAX_SIZE (64*1024*1024) static char *ndo_malloc(ndr_stream_t *, unsigned, ndr_ref_t *); static int ndo_free(ndr_stream_t *, char *, ndr_ref_t *); static int ndo_grow_pdu(ndr_stream_t *, unsigned long, ndr_ref_t *); static int ndo_pad_pdu(ndr_stream_t *, unsigned long, unsigned long, ndr_ref_t *); static int ndo_get_pdu(ndr_stream_t *, unsigned long, unsigned long, char *, int, ndr_ref_t *); static int ndo_put_pdu(ndr_stream_t *, unsigned long, unsigned long, char *, int, ndr_ref_t *); static void ndo_tattle(ndr_stream_t *, char *, ndr_ref_t *); static void ndo_tattle_error(ndr_stream_t *, ndr_ref_t *); static int ndo_reset(ndr_stream_t *); static void ndo_destruct(ndr_stream_t *); static void ndo_hexfmt(uint8_t *, int, int, char *, int); /* * The ndr stream operations table. */ static ndr_stream_ops_t nds_ops = { ndo_malloc, ndo_free, ndo_grow_pdu, ndo_pad_pdu, ndo_get_pdu, ndo_put_pdu, ndo_tattle, ndo_tattle_error, ndo_reset, ndo_destruct }; /* * nds_bswap * * Copies len bytes from src to dst such that dst contains the bytes * from src in reverse order. * * We expect to be dealing with bytes, words, dwords etc. So the * length must be non-zero and a power of 2. */ void nds_bswap(void *srcbuf, void *dstbuf, size_t len) { uint8_t *src = (uint8_t *)srcbuf; uint8_t *dst = (uint8_t *)dstbuf; if ((len != 0) && ((len & (len - 1)) == 0)) { src += len; while (len--) *dst++ = *(--src); } } /* * nds_initialize * * Initialize a stream. Sets up the PDU parameters and assigns the stream * operations and the reference to the heap. An external heap is provided * to the stream, rather than each stream creating its own heap. */ int nds_initialize(ndr_stream_t *nds, unsigned pdu_size_hint, int composite_op, ndr_heap_t *heap) { unsigned size; assert(nds); assert(heap); bzero(nds, sizeof (*nds)); nds->ndo = &nds_ops; nds->heap = (struct ndr_heap *)heap; if (pdu_size_hint > NDR_PDU_MAX_SIZE) { nds->error = NDR_ERR_BOUNDS_CHECK; nds->error_ref = __LINE__; NDS_TATTLE_ERROR(nds, NULL, NULL); return (NDR_DRC_FAULT_RESOURCE_1); } size = (pdu_size_hint == 0) ? NDR_PDU_BLOCK_SIZE : pdu_size_hint; if ((nds->pdu_base_addr = malloc(size)) == NULL) { nds->error = NDR_ERR_MALLOC_FAILED; nds->error_ref = __LINE__; NDS_TATTLE_ERROR(nds, NULL, NULL); return (NDR_DRC_FAULT_OUT_OF_MEMORY); } nds->pdu_max_size = size; nds->pdu_size = 0; nds->pdu_base_offset = (unsigned long)nds->pdu_base_addr; nds->m_op = NDR_MODE_TO_M_OP(composite_op); nds->dir = NDR_MODE_TO_DIR(composite_op); nds->outer_queue_tailp = &nds->outer_queue_head; return (0); } /* * nds_destruct * * Destroy a stream. This is an external interface to provide access to * the stream's destruct operation. */ void nds_destruct(ndr_stream_t *nds) { if ((nds == NULL) || (nds->ndo == NULL)) return; NDS_DESTRUCT(nds); } /* * Print NDR stream state. */ void nds_show_state(ndr_stream_t *nds) { if (nds == NULL) { ndo_printf(NULL, NULL, "nds: pdu_base_offset, nds->pdu_size, nds->pdu_max_size, nds->pdu_scan_offset, nds->pdu_hdr_size, nds->pdu_body_size, nds->pdu_body_offset); } /* * ndo_malloc * * Allocate memory from the stream heap. */ /*ARGSUSED*/ static char * ndo_malloc(ndr_stream_t *nds, unsigned len, ndr_ref_t *ref) { return (ndr_heap_malloc((ndr_heap_t *)nds->heap, len)); } /* * ndo_free * * Always succeeds: cannot free individual stream allocations. */ /*ARGSUSED*/ static int ndo_free(ndr_stream_t *nds, char *p, ndr_ref_t *ref) { return (1); } /* * ndo_grow_pdu * * This is the only place that should change the size of the PDU. If the * desired offset is beyond the current PDU size, we realloc the PDU * buffer to accommodate the request. For efficiency, the PDU is always * extended to a NDR_PDU_BLOCK_SIZE boundary. Requests to grow the PDU * beyond NDR_PDU_MAX_SIZE are rejected. * * Returns 1 to indicate success. Otherwise 0 to indicate failure. */ static int ndo_grow_pdu(ndr_stream_t *nds, unsigned long want_end_offset, ndr_ref_t *ref) { unsigned char *pdu_addr; unsigned pdu_max_size; ndo_printf(nds, ref, "grow %d", want_end_offset); pdu_max_size = nds->pdu_max_size; if (want_end_offset > pdu_max_size) { pdu_max_size = NDR_PDU_ALIGN(want_end_offset); if (pdu_max_size >= NDR_PDU_MAX_SIZE) return (0); pdu_addr = realloc(nds->pdu_base_addr, pdu_max_size); if (pdu_addr == 0) return (0); nds->pdu_max_size = pdu_max_size; nds->pdu_base_addr = pdu_addr; nds->pdu_base_offset = (unsigned long)pdu_addr; } nds->pdu_size = want_end_offset; return (1); } static int ndo_pad_pdu(ndr_stream_t *nds, unsigned long pdu_offset, unsigned long n_bytes, ndr_ref_t *ref) { unsigned char *data; data = (unsigned char *)nds->pdu_base_offset; data += pdu_offset; ndo_printf(nds, ref, "pad %d@%-3d", n_bytes, pdu_offset); bzero(data, n_bytes); return (1); } /* * ndo_get_pdu * * The swap flag is 1 if NDR knows that the byte-order in the PDU * is different from the local system. * * Returns 1 on success or 0 to indicate failure. */ static int ndo_get_pdu(ndr_stream_t *nds, unsigned long pdu_offset, unsigned long n_bytes, char *buf, int swap_bytes, ndr_ref_t *ref) { unsigned char *data; char hexbuf[NDOBUFSZ]; data = (unsigned char *)nds->pdu_base_offset; data += pdu_offset; ndo_hexfmt(data, n_bytes, swap_bytes, hexbuf, NDOBUFSZ); ndo_printf(nds, ref, "get %d@%-3d = %s", n_bytes, pdu_offset, hexbuf); if (!swap_bytes) bcopy(data, buf, n_bytes); else nds_bswap(data, (unsigned char *)buf, n_bytes); return (1); } /* * ndo_put_pdu * * This is a receiver makes right protocol. So we do not need * to be concerned about the byte-order of an outgoing PDU. */ /*ARGSUSED*/ static int ndo_put_pdu(ndr_stream_t *nds, unsigned long pdu_offset, unsigned long n_bytes, char *buf, int swap_bytes, ndr_ref_t *ref) { unsigned char *data; char hexbuf[NDOBUFSZ]; data = (unsigned char *)nds->pdu_base_offset; data += pdu_offset; ndo_hexfmt((uint8_t *)buf, n_bytes, 0, hexbuf, NDOBUFSZ); ndo_printf(nds, ref, "put %d@%-3d = %s", n_bytes, pdu_offset, hexbuf); bcopy(buf, data, n_bytes); return (1); } static void ndo_tattle(ndr_stream_t *nds, char *what, ndr_ref_t *ref) { ndo_printf(nds, ref, what); } static void ndo_tattle_error(ndr_stream_t *nds, ndr_ref_t *ref) { unsigned char *data; char hexbuf[NDOBUFSZ]; if (nds->pdu_base_addr != NULL) { data = (unsigned char *)nds->pdu_base_offset; if (ref) data += ref->pdu_offset; else data += nds->pdu_scan_offset; ndo_hexfmt(data, 16, 0, hexbuf, NDOBUFSZ); } else { bzero(hexbuf, NDOBUFSZ); } ndo_printf(nds, ref, "ERROR=%d REF=%d OFFSET=%d SIZE=%d/%d", nds->error, nds->error_ref, nds->pdu_scan_offset, nds->pdu_size, nds->pdu_max_size); ndo_printf(nds, ref, " %s", hexbuf); } /* * ndo_reset * * Reset a stream: zap the outer_queue. We don't need to tamper * with the stream heap: it's handled externally to the stream. */ static int ndo_reset(ndr_stream_t *nds) { ndo_printf(nds, 0, "reset"); nds->pdu_size = 0; nds->pdu_scan_offset = 0; nds->outer_queue_head = 0; nds->outer_current = 0; nds->outer_queue_tailp = &nds->outer_queue_head; return (1); } /* * ndo_destruct * * Destruct a stream: zap the outer_queue. * Note: heap management (creation/destruction) is external to the stream. */ static void ndo_destruct(ndr_stream_t *nds) { ndo_printf(nds, 0, "destruct"); if (nds == NULL) return; if (nds->pdu_base_addr != NULL) { free(nds->pdu_base_addr); nds->pdu_base_addr = NULL; nds->pdu_base_offset = 0; } nds->outer_queue_head = 0; nds->outer_current = 0; nds->outer_queue_tailp = &nds->outer_queue_head; } /* * Printf style formatting for NDR operations. */ void ndo_printf(ndr_stream_t *nds, ndr_ref_t *ref, const char *fmt, ...) { va_list ap; char buf[NDOBUFSZ]; va_start(ap, fmt); (void) vsnprintf(buf, NDOBUFSZ, fmt, ap); va_end(ap); if (nds) ndo_fmt(nds, ref, buf); else ndo_trace(buf); } /* * Main output formatter for NDR operations. * * UI 03 ... rpc_vers get 1@0 = 5 {05} * UI 03 ... rpc_vers_minor get 1@1 = 0 {00} * * U Marshalling flag (M=marshal, U=unmarshal) * I Direction flag (I=in, O=out) * ... Field name * get PDU operation (get or put) * 1@0 Bytes @ offset (i.e. 1 byte at offset 0) * {05} Value */ void ndo_fmt(ndr_stream_t *nds, ndr_ref_t *ref, char *note) { ndr_ref_t *p; int indent; char ref_name[NDOBUFSZ]; char buf[NDOBUFSZ]; int m_op_c = '?', dir_c = '?'; switch (nds->m_op) { case 0: m_op_c = '-'; break; case NDR_M_OP_MARSHALL: m_op_c = 'M'; break; case NDR_M_OP_UNMARSHALL: m_op_c = 'U'; break; default: m_op_c = '?'; break; } switch (nds->dir) { case 0: dir_c = '-'; break; case NDR_DIR_IN: dir_c = 'I'; break; case NDR_DIR_OUT: dir_c = 'O'; break; default: dir_c = '?'; break; } for (indent = 0, p = ref; p; p = p->enclosing) indent++; if (ref && ref->name) { if (*ref->name == '[' && ref->enclosing) { indent--; (void) snprintf(ref_name, NDOBUFSZ, "%s%s", ref->enclosing->name, ref->name); } else { (void) strlcpy(ref_name, ref->name, NDOBUFSZ); } } else { (void) strlcpy(ref_name, "----", NDOBUFSZ); } (void) snprintf(buf, NDOBUFSZ, "%c%c %-.*s %-*s %s", m_op_c, dir_c, indent, "....+....+....+....+....+....", 20 - indent, ref_name, note); ndo_trace(buf); } /*ARGSUSED*/ void ndo_trace(const char *s) { /* * Temporary fbt for dtrace until user space sdt enabled. */ } /* * Format data as hex bytes (limit is 10 bytes): * * 1188689424 {10 f6 d9 46} * * If the input data is greater than 10 bytes, an ellipsis will * be inserted before the closing brace. */ static void ndo_hexfmt(uint8_t *data, int size, int swap_bytes, char *buf, int len) { char *p = buf; int interp = 1; uint32_t c; int n; int i; n = (size > 10) ? 10 : size; if (n > len-1) n = len-1; switch (size) { case 1: c = *(uint8_t *)data; break; case 2: if (swap_bytes == 0) /*LINTED E_BAD_PTR_CAST_ALIGN*/ c = *(uint16_t *)data; else c = (data[0] << 8) | data[1]; break; case 4: if (swap_bytes == 0) { /*LINTED E_BAD_PTR_CAST_ALIGN*/ c = *(uint32_t *)data; } else { c = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; } break; default: c = 0; interp = 0; break; } if (interp) p += sprintf(p, "%4u {", c); else p += sprintf(p, " {"); p += sprintf(p, "%02x", data[0]); for (i = 1; i < n; i++) p += sprintf(p, " %02x", data[i]); if (size > 10) p += sprintf(p, " ...}"); else p += sprintf(p, "}"); /* * Show c if it's a printable character or wide-char. */ if (size < 4 && isprint((uint8_t)c)) (void) sprintf(p, " %c", (uint8_t)c); } /* * 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. * Copyright 2020 Tintri by DDN, Inc. 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_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. * In other words, symbols, not bytes. */ size_t wlen; wlen = ndr__mbstowcs(NULL, valp, NDR_STRING_MAX); if (wlen == (size_t)-1) { /* illegal sequence error? */ NDR_SET_ERROR(outer_ref, NDR_ERR_STRLEN); return (0); } if ((nds->flags & NDS_F_NONULL) == 0) wlen++; if (wlen > NDR_STRING_MAX) { NDR_SET_ERROR(outer_ref, NDR_ERR_STRLEN); return (0); } size_is = wlen; } 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 */ /* * Enforce bounds on: size_is, first_is, length_is * * 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 (size_is > NDR_STRING_MAX) { NDR_SET_ERROR(outer_ref, NDR_ERR_STRING_SIZING); return (0); } if (first_is != 0) { NDR_SET_ERROR(outer_ref, NDR_ERR_STRING_SIZING); return (0); } if (length_is > size_is) { NDR_SET_ERROR(outer_ref, NDR_ERR_STRLEN); 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) * NDR_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 ((outer_ref->ti->type_flags & NDR_F_FAKE) != 0 || 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); } /* * Some 'outer' constructs incorrectly align the entire construct * on a 4-byte boundary, when each of its members needs to be * aligned separately. This function handles aligning 'inner' * members on their natural alignment boundary. * * NOTE: This assumes it is not being used for headers. * Headers present some unique concerns that this may not * adequately address (e.g. reserved space, pdu_body_offset). */ int ndr_inner_align(ndr_ref_t *arg_ref) { ndr_stream_t *nds = arg_ref->stream; int rc; unsigned n_pad; n_pad = ((arg_ref->ti->alignment + 1) - arg_ref->pdu_offset) & arg_ref->ti->alignment; if (n_pad == 0) return (1); /* already aligned, often the case */ if (!ndr_outer_grow(arg_ref->enclosing, n_pad)) return (0); /* error already set */ switch (nds->m_op) { case NDR_M_OP_MARSHALL: rc = NDS_PAD_PDU(nds, arg_ref->pdu_offset, n_pad, arg_ref); if (!rc) { NDR_SET_ERROR(arg_ref, NDR_ERR_PAD_FAILED); return (0); } break; case NDR_M_OP_UNMARSHALL: break; default: NDR_SET_ERROR(arg_ref, NDR_ERR_M_OP_INVALID); return (0); } /* All current and future offsets need to advance */ arg_ref->enclosing->pdu_offset += n_pad; arg_ref->pdu_offset += n_pad; /* ndr_outer_grow changed pdu_end_offset */ nds->pdu_scan_offset += n_pad; 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; /* * Switched unions are meant to be converted to and from encapsulated * structures on the wire. However, NDRGEN doesn't implement this. * As a result, our interface definitions use 'fake' structures * to represent switched unions. * This causes the structure to be aligned on a struct (4 byte) * boundary, with none of its members having separate alignment - * but that's not correct. Each of its members has its own, * natural alignment, and we need to honor that. * That happens here. */ if (arg_ref->enclosing != NULL && (arg_ref->enclosing->ti->type_flags & NDR_F_FAKE) != 0 && !ndr_inner_align(arg_ref)) return (0); /* error already set */ 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) snprintf(name, sizeof (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 *); /* BEGIN CSTYLED */ /* Comments to be nice to those searching for these types. */ MAKE_BASIC_TYPE(_char, 1) /* ndt__char, ndt_s_char */ MAKE_BASIC_TYPE(_uchar, 1) /* ndt__uchar, ndt_s_uchar */ MAKE_BASIC_TYPE(_short, 2) /* ndt__short, ndt_s_short */ MAKE_BASIC_TYPE(_ushort, 2) /* ndt__ushort, ndt_s_ushort */ MAKE_BASIC_TYPE(_long, 4) /* ndt__long, ndt_s_long */ MAKE_BASIC_TYPE(_ulong, 4) /* ndt__ulong, ndt_s_ulong */ MAKE_BASIC_TYPE_BASE(_wchar, 2) /* ndt__wchar, ndt_s_wchar */ /* END CSTYLED */ 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) snprintf(name, sizeof (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. * * The most critical thing to get right in this function is to * marshall or unmarshall _exactly_ the number of elements the * OtW length specifies, as saved by the caller in: strlen_is. * Doing otherwise would leave us positioned at the wrong place * in the data stream for whatever follows this. Note that the * string data covered by strlen_is may or may not include any * null termination, but the converted string provided by the * caller or returned always has a null terminator. */ int ndr_s_wchar(ndr_ref_t *encl_ref) { ndr_stream_t *nds = encl_ref->stream; char *valp = encl_ref->datum; ndr_ref_t myref; char name[30]; ndr_wchar_t wcs[NDR_STRING_MAX+1]; size_t i, slen, wlen; /* This is enforced in ndr_outer_string() */ assert(encl_ref->strlen_is <= NDR_STRING_MAX); 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); } } /* * If we're marshalling, convert the given string * from UTF-8 into a local UCS-2 string. */ if (nds->m_op == NDR_M_OP_MARSHALL) { wlen = ndr__mbstowcs(wcs, valp, NDR_STRING_MAX); if (wlen == (size_t)-1) return (0); /* * Add a nulls to make strlen_is. * (always zero or one of them) * Then null terminate at wlen, * just for debug convenience. */ while (wlen < encl_ref->strlen_is) wcs[wlen++] = 0; wcs[wlen] = 0; } /* * Copy wire data to or from the local wc string. * Always exactly strlen_is elements. */ 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.name = name; myref.pdu_offset = encl_ref->pdu_offset; myref.datum = (char *)wcs; wlen = encl_ref->strlen_is; for (i = 0; i < wlen; i++) { (void) snprintf(name, sizeof (name), "[%lu]", i); if (!ndr_inner(&myref)) return (0); myref.pdu_offset += sizeof (ndr_wchar_t); myref.datum += sizeof (ndr_wchar_t); } /* * If this is unmarshall, convert the local UCS-2 string * into a UTF-8 string in the caller's buffer. The caller * previously determined the space required and provides a * buffer of sufficient size. */ if (nds->m_op == NDR_M_OP_UNMARSHALL) { wcs[wlen] = 0; slen = encl_ref->size_is * NDR_MB_CHAR_MAX; slen = ndr__wcstombs(valp, wcs, slen); if (slen == (size_t)-1) return (0); valp[slen] = '\0'; } 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. */ /* ARGSUSED */ size_t ndr_mbstowcs(ndr_stream_t *nds, ndr_wchar_t *wcs, const char *mbs, size_t nwchars) { size_t len; #ifdef _BIG_ENDIAN if (nds == NULL || NDR_MODE_MATCH(nds, NDR_MODE_RETURN_SEND)) { /* Make WC string in LE order. */ len = ndr__mbstowcs_le(wcs, mbs, nwchars); } else #endif len = ndr__mbstowcs(wcs, mbs, nwchars); return (len); } /* * 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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2015 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2018, Joyent, Inc. */ /* * Server side RPC handler. */ #include #include #include #include #include #include #include #include #include #define NDR_PIPE_SEND(np, buf, len) \ ((np)->np_send)((np), (buf), (len)) #define NDR_PIPE_RECV(np, buf, len) \ ((np)->np_recv)((np), (buf), (len)) static int ndr_svc_process(ndr_xa_t *); static int ndr_svc_bind(ndr_xa_t *); static int ndr_svc_request(ndr_xa_t *); static void ndr_reply_prepare_hdr(ndr_xa_t *); static int ndr_svc_alter_context(ndr_xa_t *); static void ndr_reply_fault(ndr_xa_t *, unsigned long); static int ndr_recv_request(ndr_xa_t *mxa); static int ndr_recv_frag(ndr_xa_t *mxa); static int ndr_send_reply(ndr_xa_t *); static int ndr_pipe_process(ndr_pipe_t *, ndr_xa_t *); /* * External entry point called by smbd. */ void ndr_pipe_worker(ndr_pipe_t *np) { ndr_xa_t *mxa; int rc; ndr_svc_binding_pool_init(&np->np_binding, np->np_binding_pool, NDR_N_BINDING_POOL); if ((mxa = malloc(sizeof (*mxa))) == NULL) return; do { bzero(mxa, sizeof (*mxa)); rc = ndr_pipe_process(np, mxa); } while (rc == 0); free(mxa); /* * Ensure that there are no RPC service policy handles * (associated with this fid) left around. */ ndr_hdclose(np); } /* * Process one server-side RPC request. */ static int ndr_pipe_process(ndr_pipe_t *np, ndr_xa_t *mxa) { ndr_stream_t *recv_nds; ndr_stream_t *send_nds; int rc = ENOMEM; mxa->pipe = np; mxa->binding_list = np->np_binding; if ((mxa->heap = ndr_heap_create()) == NULL) goto out1; recv_nds = &mxa->recv_nds; rc = nds_initialize(recv_nds, 0, NDR_MODE_CALL_RECV, mxa->heap); if (rc != 0) goto out2; send_nds = &mxa->send_nds; rc = nds_initialize(send_nds, 0, NDR_MODE_RETURN_SEND, mxa->heap); if (rc != 0) goto out3; rc = ndr_recv_request(mxa); if (rc != 0) goto out4; (void) ndr_svc_process(mxa); (void) ndr_send_reply(mxa); rc = 0; out4: nds_destruct(&mxa->send_nds); out3: nds_destruct(&mxa->recv_nds); out2: ndr_heap_destroy(mxa->heap); out1: return (rc); } /* * Receive an entire RPC request (all fragments) * Returns zero or an NDR fault code. */ static int ndr_recv_request(ndr_xa_t *mxa) { ndr_common_header_t *hdr = &mxa->recv_hdr.common_hdr; ndr_stream_t *nds = &mxa->recv_nds; unsigned long saved_size; int rc; rc = ndr_recv_frag(mxa); if (rc != 0) return (rc); if (!NDR_IS_FIRST_FRAG(hdr->pfc_flags)) return (NDR_DRC_FAULT_DECODE_FAILED); while (!NDR_IS_LAST_FRAG(hdr->pfc_flags)) { rc = ndr_recv_frag(mxa); if (rc != 0) return (rc); } nds->pdu_scan_offset = 0; /* * This whacks nds->pdu_size, so save/restore. * It leaves scan_offset after the header. */ saved_size = nds->pdu_size; rc = ndr_decode_pdu_hdr(mxa); nds->pdu_size = saved_size; return (rc); } /* * Read one fragment, leaving the decoded frag header in * recv_hdr.common_hdr, and the data in the recv_nds. * * Returns zero or an NDR fault code. * * If a first frag, the header is included in the data * placed in recv_nds (because it's not fully decoded * until later - we only decode the common part here). * Additional frags are placed in the recv_nds without * the header, so that after the first frag header, * the remaining data will be contiguous. We do this * by simply not advancing the offset in recv_nds after * reading and decoding these additional fragments, so * the payload of such frags will overwrite what was * (temporarily) the frag header. */ static int ndr_recv_frag(ndr_xa_t *mxa) { ndr_common_header_t *hdr = &mxa->recv_hdr.common_hdr; ndr_stream_t *nds = &mxa->recv_nds; unsigned char *data; unsigned long next_offset; unsigned long pay_size; int rc; /* Make room for the frag header. */ next_offset = nds->pdu_scan_offset + NDR_RSP_HDR_SIZE; if (!NDS_GROW_PDU(nds, next_offset, 0)) return (NDR_DRC_FAULT_OUT_OF_MEMORY); /* Read the frag header. */ data = nds->pdu_base_addr + nds->pdu_scan_offset; rc = NDR_PIPE_RECV(mxa->pipe, data, NDR_RSP_HDR_SIZE); if (rc != 0) return (NDR_DRC_FAULT_RPCHDR_RECEIVED_RUNT); /* * Decode the frag header, get the length. * NB: It uses nds->pdu_scan_offset */ ndr_decode_frag_hdr(nds, hdr); ndr_show_hdr(hdr); if (hdr->frag_length < NDR_RSP_HDR_SIZE || hdr->frag_length > mxa->pipe->np_max_xmit_frag) return (NDR_DRC_FAULT_DECODE_FAILED); if (nds->pdu_scan_offset == 0) { /* First frag: header stays in the data. */ nds->pdu_scan_offset = next_offset; } /* else overwrite with the payload */ /* Make room for the payload. */ pay_size = hdr->frag_length - NDR_RSP_HDR_SIZE; next_offset = nds->pdu_scan_offset + pay_size; if (!NDS_GROW_PDU(nds, next_offset, 0)) return (NDR_DRC_FAULT_OUT_OF_MEMORY); /* Read the payload. */ data = nds->pdu_base_addr + nds->pdu_scan_offset; rc = NDR_PIPE_RECV(mxa->pipe, data, pay_size); if (rc != 0) return (NDR_DRC_FAULT_RPCHDR_RECEIVED_RUNT); nds->pdu_scan_offset = next_offset; return (NDR_DRC_OK); } /* * This is the entry point for all server-side RPC processing. * It is assumed that the PDU has already been received. */ static int ndr_svc_process(ndr_xa_t *mxa) { int rc; (void) ndr_reply_prepare_hdr(mxa); switch (mxa->ptype) { case NDR_PTYPE_BIND: rc = ndr_svc_bind(mxa); break; case NDR_PTYPE_REQUEST: rc = ndr_svc_request(mxa); break; case NDR_PTYPE_ALTER_CONTEXT: rc = ndr_svc_alter_context(mxa); break; default: rc = NDR_DRC_FAULT_RPCHDR_PTYPE_INVALID; break; } if (NDR_DRC_IS_FAULT(rc)) ndr_reply_fault(mxa, rc); return (rc); } /* * Multiple p_cont_elem[]s, multiple transfer_syntaxes[] and multiple * p_results[] not supported. */ static int ndr_svc_bind(ndr_xa_t *mxa) { ndr_p_cont_list_t *cont_list; ndr_p_result_list_t *result_list; ndr_p_result_t *result; unsigned p_cont_id; ndr_binding_t *mbind; ndr_uuid_t *as_uuid; ndr_uuid_t *ts_uuid; int as_vers; int ts_vers; ndr_service_t *msvc; int rc; ndr_port_any_t *sec_addr; /* acquire targets */ cont_list = &mxa->recv_hdr.bind_hdr.p_context_elem; result_list = &mxa->send_hdr.bind_ack_hdr.p_result_list; result = &result_list->p_results[0]; /* * Set up temporary secondary address port. * We will correct this later (below). */ sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr; sec_addr->length = 13; (void) strcpy((char *)sec_addr->port_spec, "\\PIPE\\ntsvcs"); result_list->n_results = 1; result_list->reserved = 0; result_list->reserved2 = 0; result->result = NDR_PCDR_ACCEPTANCE; result->reason = 0; bzero(&result->transfer_syntax, sizeof (result->transfer_syntax)); /* sanity check */ if (cont_list->n_context_elem != 1 || cont_list->p_cont_elem[0].n_transfer_syn != 1) { ndo_trace("ndr_svc_bind: warning: multiple p_cont_elem"); } p_cont_id = cont_list->p_cont_elem[0].p_cont_id; if ((mbind = ndr_svc_find_binding(mxa, p_cont_id)) != NULL) { /* * Duplicate presentation context id. */ ndo_trace("ndr_svc_bind: duplicate binding"); return (NDR_DRC_FAULT_BIND_PCONT_BUSY); } if ((mbind = ndr_svc_new_binding(mxa)) == NULL) { /* * No free binding slot */ result->result = NDR_PCDR_PROVIDER_REJECTION; result->reason = NDR_PPR_LOCAL_LIMIT_EXCEEDED; ndo_trace("ndr_svc_bind: no resources"); return (NDR_DRC_OK); } as_uuid = &cont_list->p_cont_elem[0].abstract_syntax.if_uuid; as_vers = cont_list->p_cont_elem[0].abstract_syntax.if_version; ts_uuid = &cont_list->p_cont_elem[0].transfer_syntaxes[0].if_uuid; ts_vers = cont_list->p_cont_elem[0].transfer_syntaxes[0].if_version; msvc = ndr_svc_lookup_uuid(as_uuid, as_vers, ts_uuid, ts_vers); if (msvc == NULL) { result->result = NDR_PCDR_PROVIDER_REJECTION; result->reason = NDR_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED; return (NDR_DRC_OK); } /* * We can now use the correct secondary address port. */ sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr; sec_addr->length = strlen(msvc->sec_addr_port) + 1; (void) strlcpy((char *)sec_addr->port_spec, msvc->sec_addr_port, NDR_PORT_ANY_MAX_PORT_SPEC); mbind->p_cont_id = p_cont_id; mbind->which_side = NDR_BIND_SIDE_SERVER; /* mbind->context set by app */ mbind->service = msvc; mbind->instance_specific = 0; mxa->binding = mbind; if (msvc->bind_req) { /* * Call the service-specific bind() handler. If * this fails, we shouild send a specific error * on the bind ack. */ rc = (msvc->bind_req)(mxa); if (NDR_DRC_IS_FAULT(rc)) { mbind->service = 0; /* free binding slot */ mbind->which_side = 0; mbind->p_cont_id = 0; mbind->instance_specific = 0; return (rc); } } result->transfer_syntax = cont_list->p_cont_elem[0].transfer_syntaxes[0]; return (NDR_DRC_BINDING_MADE); } /* * ndr_svc_alter_context * * The alter context request is used to request additional presentation * context for another interface and/or version. It is very similar to * a bind request. */ static int ndr_svc_alter_context(ndr_xa_t *mxa) { ndr_p_result_list_t *result_list; ndr_p_result_t *result; ndr_p_cont_list_t *cont_list; ndr_binding_t *mbind; ndr_service_t *msvc; unsigned p_cont_id; ndr_uuid_t *as_uuid; ndr_uuid_t *ts_uuid; int as_vers; int ts_vers; ndr_port_any_t *sec_addr; result_list = &mxa->send_hdr.alter_context_rsp_hdr.p_result_list; result_list->n_results = 1; result_list->reserved = 0; result_list->reserved2 = 0; result = &result_list->p_results[0]; result->result = NDR_PCDR_ACCEPTANCE; result->reason = 0; bzero(&result->transfer_syntax, sizeof (result->transfer_syntax)); cont_list = &mxa->recv_hdr.alter_context_hdr.p_context_elem; p_cont_id = cont_list->p_cont_elem[0].p_cont_id; if (ndr_svc_find_binding(mxa, p_cont_id) != NULL) return (NDR_DRC_FAULT_BIND_PCONT_BUSY); if ((mbind = ndr_svc_new_binding(mxa)) == NULL) { result->result = NDR_PCDR_PROVIDER_REJECTION; result->reason = NDR_PPR_LOCAL_LIMIT_EXCEEDED; return (NDR_DRC_OK); } as_uuid = &cont_list->p_cont_elem[0].abstract_syntax.if_uuid; as_vers = cont_list->p_cont_elem[0].abstract_syntax.if_version; ts_uuid = &cont_list->p_cont_elem[0].transfer_syntaxes[0].if_uuid; ts_vers = cont_list->p_cont_elem[0].transfer_syntaxes[0].if_version; msvc = ndr_svc_lookup_uuid(as_uuid, as_vers, ts_uuid, ts_vers); if (msvc == NULL) { result->result = NDR_PCDR_PROVIDER_REJECTION; result->reason = NDR_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED; return (NDR_DRC_OK); } mbind->p_cont_id = p_cont_id; mbind->which_side = NDR_BIND_SIDE_SERVER; /* mbind->context set by app */ mbind->service = msvc; mbind->instance_specific = 0; mxa->binding = mbind; sec_addr = &mxa->send_hdr.alter_context_rsp_hdr.sec_addr; sec_addr->length = 0; bzero(sec_addr->port_spec, NDR_PORT_ANY_MAX_PORT_SPEC); result->transfer_syntax = cont_list->p_cont_elem[0].transfer_syntaxes[0]; return (NDR_DRC_BINDING_MADE); } static int ndr_svc_request(ndr_xa_t *mxa) { ndr_binding_t *mbind; ndr_service_t *msvc; unsigned p_cont_id; int rc; mxa->opnum = mxa->recv_hdr.request_hdr.opnum; p_cont_id = mxa->recv_hdr.request_hdr.p_cont_id; if ((mbind = ndr_svc_find_binding(mxa, p_cont_id)) == NULL) return (NDR_DRC_FAULT_REQUEST_PCONT_INVALID); mxa->binding = mbind; msvc = mbind->service; /* * Make room for the response hdr. */ mxa->send_nds.pdu_scan_offset = NDR_RSP_HDR_SIZE; if (msvc->call_stub) rc = (*msvc->call_stub)(mxa); else rc = ndr_generic_call_stub(mxa); if (NDR_DRC_IS_FAULT(rc)) { ndo_printf(0, 0, "%s[0x%02x]: 0x%04x", msvc->name, mxa->opnum, rc); } return (rc); } /* * The transaction and the two nds streams use the same heap, which * should already exist at this point. The heap will also be available * to the stub. */ int ndr_generic_call_stub(ndr_xa_t *mxa) { ndr_binding_t *mbind = mxa->binding; ndr_service_t *msvc = mbind->service; ndr_typeinfo_t *intf_ti = msvc->interface_ti; ndr_stub_table_t *ste; int opnum = mxa->opnum; unsigned p_len = intf_ti->c_size_fixed_part; char *param; int rc; if (mxa->heap == NULL) { ndo_printf(0, 0, "%s[0x%02x]: no heap", msvc->name, opnum); return (NDR_DRC_FAULT_OUT_OF_MEMORY); } if ((ste = ndr_svc_find_stub(msvc, opnum)) == NULL) { ndo_printf(0, 0, "%s[0x%02x]: invalid opnum", msvc->name, opnum); return (NDR_DRC_FAULT_REQUEST_OPNUM_INVALID); } if ((param = ndr_heap_malloc(mxa->heap, p_len)) == NULL) return (NDR_DRC_FAULT_OUT_OF_MEMORY); bzero(param, p_len); rc = ndr_decode_call(mxa, param); if (!NDR_DRC_IS_OK(rc)) return (rc); rc = (*ste->func)(param, mxa); if (rc == NDR_DRC_OK) rc = ndr_encode_return(mxa, param); return (rc); } /* * We can perform some initial setup of the response header here. * We also need to cache some of the information from the bind * negotiation for use during subsequent RPC calls. */ static void ndr_reply_prepare_hdr(ndr_xa_t *mxa) { ndr_common_header_t *rhdr = &mxa->recv_hdr.common_hdr; ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr; hdr->rpc_vers = 5; hdr->rpc_vers_minor = 0; hdr->pfc_flags = NDR_PFC_FIRST_FRAG + NDR_PFC_LAST_FRAG; hdr->packed_drep = rhdr->packed_drep; hdr->frag_length = 0; hdr->auth_length = 0; hdr->call_id = rhdr->call_id; #ifdef _BIG_ENDIAN hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII | NDR_REPLAB_INTG_BIG_ENDIAN; #else hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII | NDR_REPLAB_INTG_LITTLE_ENDIAN; #endif switch (mxa->ptype) { case NDR_PTYPE_BIND: /* * Compute the maximum fragment sizes for xmit/recv * and store in the pipe endpoint. Note "xmit" is * client-to-server; "recv" is server-to-client. */ if (mxa->pipe->np_max_xmit_frag > mxa->recv_hdr.bind_hdr.max_xmit_frag) mxa->pipe->np_max_xmit_frag = mxa->recv_hdr.bind_hdr.max_xmit_frag; if (mxa->pipe->np_max_recv_frag > mxa->recv_hdr.bind_hdr.max_recv_frag) mxa->pipe->np_max_recv_frag = mxa->recv_hdr.bind_hdr.max_recv_frag; hdr->ptype = NDR_PTYPE_BIND_ACK; mxa->send_hdr.bind_ack_hdr.max_xmit_frag = mxa->pipe->np_max_xmit_frag; mxa->send_hdr.bind_ack_hdr.max_recv_frag = mxa->pipe->np_max_recv_frag; /* * We're supposed to assign a unique "assoc group" * (identifies this connection for the client). * Using the pipe address is adequate. */ mxa->send_hdr.bind_ack_hdr.assoc_group_id = mxa->recv_hdr.bind_hdr.assoc_group_id; if (mxa->send_hdr.bind_ack_hdr.assoc_group_id == 0) mxa->send_hdr.bind_ack_hdr.assoc_group_id = (DWORD)(uintptr_t)mxa->pipe; break; case NDR_PTYPE_REQUEST: hdr->ptype = NDR_PTYPE_RESPONSE; /* mxa->send_hdr.response_hdr.alloc_hint */ mxa->send_hdr.response_hdr.p_cont_id = mxa->recv_hdr.request_hdr.p_cont_id; mxa->send_hdr.response_hdr.cancel_count = 0; mxa->send_hdr.response_hdr.reserved = 0; break; case NDR_PTYPE_ALTER_CONTEXT: hdr->ptype = NDR_PTYPE_ALTER_CONTEXT_RESP; /* * The max_xmit_frag, max_recv_frag and assoc_group_id are * ignored by the client but it's useful to fill them in. */ mxa->send_hdr.alter_context_rsp_hdr.max_xmit_frag = mxa->recv_hdr.alter_context_hdr.max_xmit_frag; mxa->send_hdr.alter_context_rsp_hdr.max_recv_frag = mxa->recv_hdr.alter_context_hdr.max_recv_frag; mxa->send_hdr.alter_context_rsp_hdr.assoc_group_id = mxa->recv_hdr.alter_context_hdr.assoc_group_id; break; default: hdr->ptype = 0xFF; } } /* * Signal an RPC fault. The stream is reset and we overwrite whatever * was in the response header with the fault information. */ static void ndr_reply_fault(ndr_xa_t *mxa, unsigned long drc) { ndr_common_header_t *rhdr = &mxa->recv_hdr.common_hdr; ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr; ndr_stream_t *nds = &mxa->send_nds; unsigned long fault_status; (void) NDS_RESET(nds); hdr->rpc_vers = 5; hdr->rpc_vers_minor = 0; hdr->pfc_flags = NDR_PFC_FIRST_FRAG + NDR_PFC_LAST_FRAG; hdr->packed_drep = rhdr->packed_drep; hdr->frag_length = sizeof (mxa->send_hdr.fault_hdr); hdr->auth_length = 0; hdr->call_id = rhdr->call_id; #ifdef _BIG_ENDIAN hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII | NDR_REPLAB_INTG_BIG_ENDIAN; #else hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII | NDR_REPLAB_INTG_LITTLE_ENDIAN; #endif switch (drc & NDR_DRC_MASK_SPECIFIER) { case NDR_DRC_FAULT_OUT_OF_MEMORY: case NDR_DRC_FAULT_ENCODE_TOO_BIG: fault_status = NDR_FAULT_NCA_OUT_ARGS_TOO_BIG; break; case NDR_DRC_FAULT_REQUEST_PCONT_INVALID: fault_status = NDR_FAULT_NCA_INVALID_PRES_CONTEXT_ID; break; case NDR_DRC_FAULT_REQUEST_OPNUM_INVALID: fault_status = NDR_FAULT_NCA_OP_RNG_ERROR; break; case NDR_DRC_FAULT_DECODE_FAILED: case NDR_DRC_FAULT_ENCODE_FAILED: fault_status = NDR_FAULT_NCA_PROTO_ERROR; break; default: fault_status = NDR_FAULT_NCA_UNSPEC_REJECT; break; } mxa->send_hdr.fault_hdr.common_hdr.ptype = NDR_PTYPE_FAULT; mxa->send_hdr.fault_hdr.status = fault_status; mxa->send_hdr.response_hdr.alloc_hint = hdr->frag_length; } /* * Note that the frag_length for bind ack and alter context is * non-standard. */ static int ndr_send_reply(ndr_xa_t *mxa) { ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr; ndr_stream_t *nds = &mxa->send_nds; uint8_t *pdu_buf; unsigned long pdu_size; unsigned long frag_size; unsigned long pdu_data_size; unsigned long frag_data_size; frag_size = mxa->pipe->np_max_recv_frag; pdu_size = nds->pdu_size; pdu_buf = nds->pdu_base_addr; if (pdu_size <= frag_size) { /* * Single fragment response. The PDU size may be zero * here (i.e. bind or fault response). So don't make * any assumptions about it until after the header is * encoded. */ switch (hdr->ptype) { case NDR_PTYPE_BIND_ACK: hdr->frag_length = ndr_bind_ack_hdr_size(mxa); break; case NDR_PTYPE_FAULT: /* already setup */ break; case NDR_PTYPE_RESPONSE: hdr->frag_length = pdu_size; mxa->send_hdr.response_hdr.alloc_hint = hdr->frag_length; break; case NDR_PTYPE_ALTER_CONTEXT_RESP: hdr->frag_length = ndr_alter_context_rsp_hdr_size(); break; default: hdr->frag_length = pdu_size; break; } nds->pdu_scan_offset = 0; (void) ndr_encode_pdu_hdr(mxa); pdu_size = nds->pdu_size; (void) NDR_PIPE_SEND(mxa->pipe, pdu_buf, pdu_size); return (0); } /* * Multiple fragment response. * * We need to update the RPC header for every fragment. * * pdu_data_size: total data remaining to be handled * frag_size: total fragment size including header * frag_data_size: data in fragment * (i.e. frag_size - NDR_RSP_HDR_SIZE) */ pdu_data_size = pdu_size - NDR_RSP_HDR_SIZE; frag_data_size = frag_size - NDR_RSP_HDR_SIZE; /* * Send the first frag. */ hdr->pfc_flags = NDR_PFC_FIRST_FRAG; hdr->frag_length = frag_size; mxa->send_hdr.response_hdr.alloc_hint = pdu_data_size; nds->pdu_scan_offset = 0; (void) ndr_encode_pdu_hdr(mxa); (void) NDR_PIPE_SEND(mxa->pipe, pdu_buf, frag_size); pdu_data_size -= frag_data_size; pdu_buf += frag_data_size; /* * Send "middle" (full-sized) fragments... */ hdr->pfc_flags = 0; while (pdu_data_size > frag_data_size) { hdr->frag_length = frag_size; mxa->send_hdr.response_hdr.alloc_hint = pdu_data_size; nds->pdu_scan_offset = 0; (void) ndr_encode_pdu_hdr(mxa); bcopy(nds->pdu_base_addr, pdu_buf, NDR_RSP_HDR_SIZE); (void) NDR_PIPE_SEND(mxa->pipe, pdu_buf, frag_size); pdu_data_size -= frag_data_size; pdu_buf += frag_data_size; } /* * Last frag (pdu_data_size <= frag_data_size) */ hdr->pfc_flags = NDR_PFC_LAST_FRAG; frag_size = pdu_data_size + NDR_RSP_HDR_SIZE; hdr->frag_length = frag_size; mxa->send_hdr.response_hdr.alloc_hint = pdu_data_size; nds->pdu_scan_offset = 0; (void) ndr_encode_pdu_hdr(mxa); bcopy(nds->pdu_base_addr, pdu_buf, NDR_RSP_HDR_SIZE); (void) NDR_PIPE_SEND(mxa->pipe, pdu_buf, frag_size); return (0); } /* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include /* * Global list of allocated handles. Handles are used in various * server-side RPC functions: typically, issued when a service is * opened and obsoleted when it is closed. Clients should treat * handles as opaque data. */ static ndr_handle_t *ndr_handle_list; static mutex_t ndr_handle_lock; /* * Table of registered services. */ #define NDR_MAX_SERVICES 32 static ndr_service_t *ndr_services[NDR_MAX_SERVICES]; /* * Register a service. * * Returns: * 0 Success * -1 Duplicate service * -2 Duplicate name * -3 Table overflow */ int ndr_svc_register(ndr_service_t *svc) { ndr_service_t *p; int free_slot = -1; int i; for (i = 0; i < NDR_MAX_SERVICES; i++) { if ((p = ndr_services[i]) == NULL) { if (free_slot < 0) free_slot = i; continue; } if (p == svc) return (-1); if (strcasecmp(p->name, svc->name) == 0) return (-2); } if (free_slot < 0) return (-3); ndr_services[free_slot] = svc; return (0); } void ndr_svc_unregister(ndr_service_t *svc) { int i; for (i = 0; i < NDR_MAX_SERVICES; i++) { if (ndr_services[i] == svc) ndr_services[i] = NULL; } } ndr_stub_table_t * ndr_svc_find_stub(ndr_service_t *svc, int opnum) { ndr_stub_table_t *ste; for (ste = svc->stub_table; ste->func; ste++) { if (ste->opnum == opnum) return (ste); } return (NULL); } ndr_service_t * ndr_svc_lookup_name(const char *name) { ndr_service_t *svc; int i; for (i = 0; i < NDR_MAX_SERVICES; i++) { if ((svc = ndr_services[i]) == NULL) continue; if (strcasecmp(name, svc->name) != 0) continue; ndo_printf(0, 0, "%s %s", svc->name, svc->desc); return (svc); } return (NULL); } ndr_service_t * ndr_svc_lookup_uuid(ndr_uuid_t *as_uuid, int as_vers, ndr_uuid_t *ts_uuid, int ts_vers) { ndr_service_t *svc; char abstract_syntax[UUID_PRINTABLE_STRING_LENGTH]; char transfer_syntax[UUID_PRINTABLE_STRING_LENGTH]; int i; if (as_uuid) ndr_uuid_unparse(as_uuid, abstract_syntax); if (ts_uuid) ndr_uuid_unparse(ts_uuid, transfer_syntax); for (i = 0; i < NDR_MAX_SERVICES; i++) { if ((svc = ndr_services[i]) == NULL) continue; if (as_uuid) { if (svc->abstract_syntax_uuid == 0) continue; if (svc->abstract_syntax_version != as_vers) continue; if (strcasecmp(abstract_syntax, svc->abstract_syntax_uuid)) continue; } if (ts_uuid) { if (svc->transfer_syntax_uuid == 0) continue; if (svc->transfer_syntax_version != ts_vers) continue; if (strcasecmp(transfer_syntax, svc->transfer_syntax_uuid)) continue; } ndo_printf(0, 0, "%s %s", svc->name, svc->desc); return (svc); } ndo_printf(0, 0, "ndr_svc_lookup_uuid: unknown service"); ndo_printf(0, 0, "abstract=%s v%d, transfer=%s v%d", abstract_syntax, as_vers, transfer_syntax, ts_vers); return (NULL); } /* * Allocate a handle for use with the server-side RPC functions. * * An arbitrary caller context can be associated with the handle * via data; it will not be dereferenced by the handle API. */ ndr_hdid_t * ndr_hdalloc(const ndr_xa_t *xa, const void *data) { static ndr_hdid_t id; ndr_handle_t *hd; uuid_t uu; if ((hd = malloc(sizeof (ndr_handle_t))) == NULL) return (NULL); if (id.data2 == 0) { uuid_generate_random(uu); bcopy(uu, &id.data2, sizeof (uuid_t)); id.data1 = 0; id.data2 = 0; } ++id.data2; bcopy(&id, &hd->nh_id, sizeof (ndr_hdid_t)); hd->nh_pipe = xa->pipe; hd->nh_svc = xa->binding->service; hd->nh_data = (void *)data; hd->nh_data_free = NULL; (void) mutex_lock(&ndr_handle_lock); hd->nh_next = ndr_handle_list; ndr_handle_list = hd; (void) mutex_unlock(&ndr_handle_lock); return (&hd->nh_id); } /* * Remove a handle from the global list and free it. */ void ndr_hdfree(const ndr_xa_t *xa, const ndr_hdid_t *id) { ndr_service_t *svc = xa->binding->service; ndr_handle_t *hd; ndr_handle_t **pphd; assert(id); (void) mutex_lock(&ndr_handle_lock); pphd = &ndr_handle_list; while (*pphd) { hd = *pphd; if (bcmp(&hd->nh_id, id, sizeof (ndr_hdid_t)) == 0) { if (hd->nh_svc == svc) { *pphd = hd->nh_next; free(hd); } break; } pphd = &(*pphd)->nh_next; } (void) mutex_unlock(&ndr_handle_lock); } /* * Lookup a handle by id. If the handle is in the list and it matches * the specified service, a pointer to it is returned. Otherwise a null * pointer is returned. */ ndr_handle_t * ndr_hdlookup(const ndr_xa_t *xa, const ndr_hdid_t *id) { ndr_service_t *svc = xa->binding->service; ndr_handle_t *hd; assert(id); (void) mutex_lock(&ndr_handle_lock); hd = ndr_handle_list; while (hd) { if (bcmp(&hd->nh_id, id, sizeof (ndr_hdid_t)) == 0) { if (hd->nh_svc != svc) break; (void) mutex_unlock(&ndr_handle_lock); return (hd); } hd = hd->nh_next; } (void) mutex_unlock(&ndr_handle_lock); return (NULL); } /* * Called when a pipe is closed to release any associated handles. */ void ndr_hdclose(ndr_pipe_t *pipe) { ndr_handle_t *hd; ndr_handle_t **pphd; (void) mutex_lock(&ndr_handle_lock); pphd = &ndr_handle_list; while (*pphd) { hd = *pphd; if (hd->nh_pipe == pipe) { *pphd = hd->nh_next; if (hd->nh_data_free) (*hd->nh_data_free)(hd->nh_data); free(hd); continue; } pphd = &(*pphd)->nh_next; } (void) mutex_unlock(&ndr_handle_lock); } /* * Convert a UUID to a string. */ void ndr_uuid_unparse(ndr_uuid_t *uuid, char *out) { (void) sprintf(out, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", uuid->data1, uuid->data2, uuid->data3, uuid->data4[0], uuid->data4[1], uuid->data4[2], uuid->data4[3], uuid->data4[4], uuid->data4[5], uuid->data4[6], uuid->data4[7]); } /* * Convert a string to a UUID. */ int ndr_uuid_parse(char *in, ndr_uuid_t *uuid) { char *p = in; char *q; char buf[4]; int i; if (strlen(in) != UUID_PRINTABLE_STRING_LENGTH - 1) return (-1); uuid->data1 = strtoul(p, &p, 16); if (*p != '-') return (-1); p++; uuid->data2 = strtol(p, &p, 16); if (*p != '-') return (-1); p++; uuid->data3 = strtol(p, &p, 16); if (*p != '-') return (-1); p++; for (i = 0; i < 8; i++) { if (*p == '-') p++; if (p[0] == 0 || p[1] == 0) return (-1); buf[0] = *p++; buf[1] = *p++; buf[2] = 0; uuid->data4[i] = strtol(buf, &q, 16); if (*q != 0) return (-1); } if (*p != 0) return (-1); return (0); } void ndr_svc_binding_pool_init(ndr_binding_t **headpp, ndr_binding_t pool[], int n_pool) { ndr_binding_t *head = NULL; int ix; for (ix = n_pool - 1; ix >= 0; ix--) { pool[ix].next = head; pool[ix].service = NULL; pool[ix].p_cont_id = 0xffff; pool[ix].instance_specific = 0; head = &pool[ix]; } *headpp = head; } ndr_binding_t * ndr_svc_find_binding(ndr_xa_t *mxa, ndr_p_context_id_t p_cont_id) { ndr_binding_t *mbind; for (mbind = mxa->binding_list; mbind; mbind = mbind->next) { if (mbind->service != NULL && mbind->which_side == NDR_BIND_SIDE_SERVER && mbind->p_cont_id == p_cont_id) break; } return (mbind); } ndr_binding_t * ndr_svc_new_binding(ndr_xa_t *mxa) { ndr_binding_t *mbind; for (mbind = mxa->binding_list; mbind; mbind = mbind->next) { if (mbind->service == NULL) break; } return (mbind); } /* * Move bytes between a buffer and a uio structure. * The transfer direction is controlled by rw: * UIO_READ: transfer from buf to uio * UIO_WRITE: transfer from uio to buf * * Returns the number of bytes moved. */ ssize_t ndr_uiomove(caddr_t buf, size_t buflen, enum uio_rw rw, struct uio *uio) { struct iovec *iov; int reading = (rw == UIO_READ); size_t nbytes; size_t nxfer = 0; assert(rw == UIO_READ || rw == UIO_WRITE); while (buflen && uio->uio_resid && uio->uio_iovcnt) { iov = uio->uio_iov; if ((nbytes = iov->iov_len) == 0) { uio->uio_iov++; uio->uio_iovcnt--; continue; } if (nbytes > buflen) nbytes = buflen; if (reading) bcopy(buf, iov->iov_base, nbytes); else bcopy(iov->iov_base, buf, nbytes); iov->iov_base += nbytes; iov->iov_len -= nbytes; uio->uio_resid -= nbytes; uio->uio_offset += nbytes; buf += nbytes; buflen -= nbytes; nxfer += nbytes; } return (nxfer); } /* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ /* * Some wchar support functions used by this library. * Mostlly just wrappers that call sys/u8_textprep.h * functions: uconv_u8tou16, uconv_u16tou8. */ #include #include #include #include "ndr_wchar.h" /* * When we just want lengths, we need an output buffer to pass to the * uconv_... functions. Nothing ever reads this output, so we can * use shared space for the unwanted output. */ static uint16_t junk_wcs[NDR_STRING_MAX]; static char junk_mbs[NDR_MB_CUR_MAX * NDR_STRING_MAX]; static size_t ndr__mbstowcs_x(uint16_t *, const char *, size_t, int); /* * Like mbstowcs(3C), but with UCS-2 wchar_t */ size_t ndr__mbstowcs(uint16_t *wcs, const char *mbs, size_t nwchars) { return (ndr__mbstowcs_x(wcs, mbs, nwchars, UCONV_OUT_SYSTEM_ENDIAN)); } /* * Like above, but put UCS-2 little-endian. */ size_t ndr__mbstowcs_le(uint16_t *wcs, const char *mbs, size_t nwchars) { return (ndr__mbstowcs_x(wcs, mbs, nwchars, UCONV_OUT_LITTLE_ENDIAN)); } /* * Like mbstowcs(3C), but with UCS-2 wchar_t, and * one extra arg for the byte order flags. */ static size_t ndr__mbstowcs_x(uint16_t *wcs, const char *mbs, size_t nwchars, int flags) { size_t obytes, mbslen, wcslen; int err; /* NULL or empty input is allowed. */ if (mbs == NULL || *mbs == '\0') { if (wcs != NULL && nwchars > 0) *wcs = 0; return (0); } /* * If wcs == NULL, caller just wants the length. * Convert into some throw-away space. */ obytes = nwchars * 2; if (wcs == NULL) { if (obytes > sizeof (junk_wcs)) return ((size_t)-1); wcs = junk_wcs; } mbslen = strlen(mbs); wcslen = nwchars; err = uconv_u8tou16((const uchar_t *)mbs, &mbslen, wcs, &wcslen, flags); if (err != 0) return ((size_t)-1); if (wcslen < nwchars) wcs[wcslen] = 0; return (wcslen); } /* * Like wcstombs(3C), but with UCS-2 wchar_t. */ size_t ndr__wcstombs(char *mbs, const uint16_t *wcs, size_t nbytes) { size_t mbslen, wcslen; int err; /* NULL or empty input is allowed. */ if (wcs == NULL || *wcs == 0) { if (mbs != NULL && nbytes > 0) *mbs = '\0'; return (0); } /* * If mbs == NULL, caller just wants the length. * Convert into some throw-away space. */ if (mbs == NULL) { if (nbytes > sizeof (junk_mbs)) return ((size_t)-1); mbs = junk_mbs; } wcslen = ndr__wcslen(wcs); mbslen = nbytes; err = uconv_u16tou8(wcs, &wcslen, (uchar_t *)mbs, &mbslen, UCONV_IN_SYSTEM_ENDIAN); if (err != 0) return ((size_t)-1); if (mbslen < nbytes) mbs[mbslen] = '\0'; return (mbslen); } /* * Like wcslen(3C), but with UCS-2 wchar_t. */ size_t ndr__wcslen(const uint16_t *wc) { size_t len = 0; while (*wc++) len++; return (len); } /* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ #ifndef _NDR_WCHAR_H #define _NDR_WCHAR_H /* * Some ndr_wchar_t support stuff. */ #define NDR_MB_CUR_MAX 3 #define NDR_MB_CHAR_MAX NDR_MB_CUR_MAX #define NDR_STRING_MAX 4096 size_t ndr__mbstowcs(uint16_t *, const char *, size_t); size_t ndr__mbstowcs_le(uint16_t *, const char *, size_t); size_t ndr__wcslen(const uint16_t *); size_t ndr__wcstombs(char *, const uint16_t *, size_t); #endif /* _NDR_WCHAR_H */ /* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ #ifndef _NDRTYPES_NDL_ #define _NDRTYPES_NDL_ /* * Type definitions (and related) used in NDL files and the * NDL run-time support libraries. See also: libmlrpc.h */ #define TYPEINFO(TYPE) ndt__##TYPE #ifdef NDRGEN #define ALIGN(X) [align(X)] #define OPERATION(X) [operation(X)] #define IN [in] #define OUT [out] #define INOUT [in out] #define FAKE [fake] #define STRING [string] #define SIZE_IS(X) [size_is(X)] #define SWITCH(X) [switch_is(X)] #define CASE(X) [case(X)] #define DEFAULT [default] #define INTERFACE(X) [interface(X)] #define UUID(X) [uuid(X)] #define ARG_IS(X) [arg_is(X)] #define REFERENCE [reference] #define REF [reference] #define UNIQUE [unique] #define PTR [ptr] #define POINTER_DEFAULT(X) [pointer_default(X)] #define ANY_SIZE_ARRAY * #define IMPORT_EXTERN [extern] #define BYTE uchar #define WORD ushort #define DWORD ulong #define ntstatus_t ulong #define LPTSTR STRING wchar * #define LPBYTE uchar * #define LPWORD ushort * #define LPDWORD ulong * #define EXTERNTYPEINFO(TYPE) #else /* NDRGEN */ #define ALIGN(X) #define OPERATION(X) #define IN #define OUT #define INOUT #define FAKE #define STRING #define SIZE_IS(X) #define SWITCH(X) #define CASE(X) #define DEFAULT #define INTERFACE(X) #define UUID(X) #define ARG_IS(X) #define REFERENCE #define REF #define UNIQUE #define PTR #define POINTER_DEFAULT(X) #define IMPORT_EXTERN /* * When not using ndrgen, get BYTE, WORD, DWORD definitions from wintypes.h. */ #include #define EXTERNTYPEINFO(TYPE) extern struct ndr_typeinfo TYPEINFO(TYPE); /* *********************************************************************** * There is a bug in the way that midl and the marshalling code handles * unions so we need to fix some of the data offsets at runtime. The * following macros and the fixup function handle the correction. *********************************************************************** */ /* * DECL_FIXUP_STRUCT allows us to declare external references to data * structures generated by ndrgen in the _ndr.c file. */ #define DECL_FIXUP_STRUCT(NAME) extern struct ndr_typeinfo ndt__##NAME /* * CASE_INFO_ENT is intended to simplify the declaration of the case * statement in the fixup function. Assuming you have followed the * convention for naming the individual structures all you have to do * is add a single line to the fixup function for each new case. */ #define CASE_INFO_ENT(NAME,N) \ case N: size1 = sizeof (struct NAME##N); \ break /* * FIXUP_PDU_SIZE is used to patch the appropriate structures (identified * by DECL_FIXUP_STRUCT) at runtime. The values are based on the * switch_index. */ #define FIXUP_PDU_SIZE(NAME,SIZE) { \ ndt__##NAME.pdu_size_fixed_part = SIZE; \ ndt__##NAME.c_size_fixed_part = SIZE; \ } #endif /* NDRGEN */ /* * UNION_INFO_ENT is intended to simplify adding new entries to a union. * If the entry structures are named using the form FunctionNameX, * where X is the sitch_value, you can just add a single line. Note * that you must also update the fixup function in mlsvc_xxx.c. */ #define UNION_INFO_ENT(N,NAME) CASE(N) struct NAME##N info##N #define UNION_INFO_PTR(N,NAME) CASE(N) struct NAME##N *info##N /* * Opaque context handle. */ #ifndef CONTEXT_HANDLE #define CONTEXT_HANDLE(NAME) \ struct NAME { \ DWORD data1; \ DWORD data2; \ WORD data3[2]; \ BYTE data4[8]; \ }; \ typedef struct NAME #endif /* CONTEXT_HANDLE */ #endif /* _NDRTYPES_NDL_ */ /* * 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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2021 Tintri by DDN, Inc. All rights reserved. */ #ifndef _RPCPDU_NDL_ #define _RPCPDU_NDL_ #include "ndrtypes.ndl" /* * Normally, constructs are (un)marshalled atoms first, then * constructs, then pointers. This can be confusing sometimes * when debugging. We know that everything in here can be * safely (un)marshalled in member order, so we say so. */ #ifdef NDRGEN #define _NO_REORDER_ [_no_reorder] #else #define _NO_REORDER_ #endif #define NDR_TRANSFER_SYNTAX_UUID "8a885d04-1ceb-11c9-9fe8-08002b104860" /* * UUID (Universal Unique IDentifier) */ /* (X/Open CAE Spec Appendix A) */ struct ndr_dce_uuid { DWORD time_low; WORD time_mid; WORD time_hi_and_version; BYTE clock_seq_hi_and_reserved; BYTE clock_seq_low; BYTE node[6]; }; struct ndr_uuid { DWORD data1; WORD data2; WORD data3; BYTE data4[8]; }; typedef struct ndr_uuid ndr_uuid_t; /* * Representation label -- needed for RPC header * (X/Open CAE Spec Chapter 14.1) * * Bits Data Type Description * ---- --------- ----------- * 0-3 charset 0=ASCII * 1=EBCDIC * 4-7 byte-order 0=big-endian * 1=little-endian * 8-15 float 0=IEEE * 1=VAX * 2=Cray * 3=IBM * 16-31 reserved */ #define NDR_REPLAB_CHAR_MASK 0x0F /* low nibble of intg_char */ #define NDR_REPLAB_CHAR_ASCII 0x00 /* ASCII */ #define NDR_REPLAB_CHAR_EBCDIC 0x01 /* EBCDIC (never happen) */ #define NDR_REPLAB_INTG_MASK 0xF0 /* hi nibble of intg_char */ #define NDR_REPLAB_INTG_BIG_ENDIAN 0x00 /* big endian */ #define NDR_REPLAB_INTG_LITTLE_ENDIAN 0x10 /* little endian (x86) */ #define NDR_REPLAB_FLOAT_IEEE 0x00 #define NDR_REPLAB_FLOAT_VAX 0x01 #define NDR_REPLAB_FLOAT_CRAY 0x02 #define NDR_REPLAB_FLOAT_IBM 0x03 struct ndr_representation_label { BYTE intg_char_rep; /* integer and charset */ BYTE float_rep; BYTE _spare[2]; }; typedef struct ndr_representation_label ndr_replab_t; /* * RPC PDU (Protocol Data Unit) types **************************************************************** * (X/Open CAE Spec 12.1) */ #define NDR_PTYPE_REQUEST 0x00 /* CO/CL */ #define NDR_PTYPE_PING 0x01 /* CL */ #define NDR_PTYPE_RESPONSE 0x02 /* CO/CL */ #define NDR_PTYPE_FAULT 0x03 /* CL/CL */ #define NDR_PTYPE_WORKING 0x04 /* CL */ #define NDR_PTYPE_NOCALL 0x05 /* CL */ #define NDR_PTYPE_REJECT 0x06 /* CL */ #define NDR_PTYPE_ACK 0x07 /* CL */ #define NDR_PTYPE_CL_CANCEL 0x08 /* CL */ #define NDR_PTYPE_FACK 0x09 /* CL */ #define NDR_PTYPE_CANCEL_ACK 0x0A /* CL */ #define NDR_PTYPE_BIND 0x0B /* CO */ #define NDR_PTYPE_BIND_ACK 0x0C /* CO */ #define NDR_PTYPE_BIND_NAK 0x0D /* CO */ #define NDR_PTYPE_ALTER_CONTEXT 0x0E /* CO */ #define NDR_PTYPE_ALTER_CONTEXT_RESP 0x0F /* CO */ /* 0x10 missing from DCE/RPC */ #define NDR_PTYPE_SHUTDOWN 0x11 /* CO */ #define NDR_PTYPE_CO_CANCEL 0x12 /* CO */ #define NDR_PTYPE_ORPHANED 0x13 /* CO */ /* * Flags in the RPC header for Connection-oriented PDU data types * (X/Open CAE Spec 12.6.3.1) * * MS-RPCE 2.2.2.3 PFC_SUPPORT_HEADER_SIGN * For PDU types bind, bind_ack, alter_context and alter_context_resp, * 0x04 means PFC_SUPPORT_HEADER_SIGN. * For other PDU types 0x04 means PFC_PENDING_CANCEL. */ #define NDR_PFC_FIRST_FRAG 0x01 /* First fragment */ #define NDR_PFC_LAST_FRAG 0x02 /* Last framgent */ #define NDR_PFC_PENDING_CANCEL 0x04 /* Cancel was pending@sender*/ #define NDR_PFC_SUPPORT_HEADER_SIGN NDR_PFC_PENDING_CANCEL #define NDR_PFC_RESERVED_1 0x08 /* */ #define NDR_PFC_CONC_MPX 0x10 /* supports concurrent muxing * of single connection */ #define NDR_PFC_DID_NOT_EXECUTE 0x20 /* for PTYPE_FAULT, guarantee * call did not execute */ #define NDR_PFC_MAYBE 0x40 /* "maybe" semantics req'ed*/ #define NDR_PFC_OBJECT_UUID 0x80 /* */ /* * Security Providers * MS-RPCE 2.2.1.1.6 */ #define NDR_C_AUTHN_NONE 0x00 /* No authentication */ #define NDR_C_AUTHN_GSS_NEGOTIATE 0x09 /* SPNEGO */ #define NDR_C_AUTHN_WINNT 0x0A /* NTLM */ #define NDR_C_AUTHN_GSS_KERBEROS 0x10 /* Kerberos */ #define NDR_C_AUTHN_GSS_NETLOGON 0x44 /* Netlogon */ #define NDR_C_AUTHN_GSS_DEFAULT 0xFF /* Default is NTLM */ /* * Encoding protection levels * X/Open CAE Spec 13.1.2.1 * MS-RPCE 2.2.1.1.7 */ #define NDR_C_AUTHN_LEVEL_DEFAULT 0x00 /* Same as Connect */ #define NDR_C_AUTHN_LEVEL_NONE 0x01 #define NDR_C_AUTHN_LEVEL_CONNECT 0x02 #define NDR_C_AUTHN_LEVEL_CALL 0x03 #define NDR_C_AUTHN_LEVEL_PKT 0x04 #define NDR_C_AUTHN_LEVEL_PKT_INTEGRITY 0x05 #define NDR_C_AUTHN_LEVEL_PKT_PRIVACY 0x06 /* * Header common to all Connection-oriented RPC PDUs * (X/Open CAE Spec 12.6.3.1) */ _NO_REORDER_ struct ndr_p_syntax_id { ndr_uuid_t if_uuid; DWORD if_version; }; typedef struct ndr_p_syntax_id ndr_p_syntax_id_t; _NO_REORDER_ struct ndr_common_header { BYTE rpc_vers; /* 00:01 5 */ BYTE rpc_vers_minor; /* 01:01 0 */ BYTE ptype; /* 02:01 NDR_PTYPE_... */ BYTE pfc_flags; /* 03:01 NDR_PFC_... */ struct ndr_representation_label packed_drep; /* 04:04 NDR representation label */ WORD frag_length; /* 08:02 total length of frag */ WORD auth_length; /* 10:02 length of auth_value */ DWORD call_id; /* 12:04 call identifier */ /* 16: */ }; typedef struct ndr_common_header ndr_common_header_t; EXTERNTYPEINFO(ndr_common_header) /* * MS-RPCE 2.2.6 Type Serialization Version 1 extensions to IDL/+ pickle * One header per serialization stream: the header must be little endian. * The filler must be set to 0xcccccccc during marshaling and ignored * during unmarshaling. */ _NO_REORDER_ struct ndr_serialtype1_hdr { BYTE version; /* 00:01 1 */ BYTE endianness; /* 01:01 0=big, 1=little */ WORD hdrlen; /* 02:02 8 */ DWORD filler; /* 04:04 0xcccccccc */ /* 8: */ }; typedef struct ndr_serialtype1_hdr ndr_serialtype1_hdr_t; EXTERNTYPEINFO(ndr_serialtype1_hdr) /* * Type Serialization Version 1 Private Header. * A private header must precede a top-level NDR constructed type. */ _NO_REORDER_ struct ndr_serialtype1_priv_hdr { DWORD buflen; /* 00:04 */ DWORD filler; /* 04:04 must be zero */ /* 8: */ }; typedef struct ndr_serialtype1_priv_hdr ndr_serialtype1_priv_hdr_t; EXTERNTYPEINFO(ndr_serialtype1_priv_hdr) /* * MS-RPCE 2.2.7 Type Serialization Version 2 extensions Version 1 (2.2.6). * The header must be little endian. * The endianinfo and reserved fields must be set to 0xcccccccc during * marshaling and ignored during unmarshaling. */ _NO_REORDER_ struct ndr_serialtype2_hdr { BYTE version; /* 00:01 1 */ BYTE endianness; /* 01:01 0=big, 1=little */ WORD hdrlen; /* 02:02 8 */ DWORD endianinfo; /* 04:04 0xcccccccc */ DWORD reserved[4]; /* 08:16 0xcccccccc */ ndr_p_syntax_id_t transfer_syntax; /* 24:20 */ ndr_p_syntax_id_t interface_id; /* 44:20 */ /* 64: */ }; typedef struct ndr_serialtype2_hdr ndr_serialtype2_hdr_t; EXTERNTYPEINFO(ndr_serialtype2_hdr) /* * Type Serialization Version 2 Private Header. * A private header must precede a top-level NDR constructed type. */ _NO_REORDER_ struct ndr_serialtype2_priv_hdr { DWORD buflen; /* 00:04 */ DWORD filler[3]; /* 04:12 must be zero */ /* 16: */ }; typedef struct ndr_serialtype2_priv_hdr ndr_serialtype2_priv_hdr_t; EXTERNTYPEINFO(ndr_serialtype2_priv_hdr) /* * This header comes before the NDR-encoded KERB_VALIDATION_INFO structure, * which can be found in one of the info buffers of the PAC. */ _NO_REORDER_ struct ndr_pac_hdr { ndr_serialtype1_hdr_t common_hdr; ndr_serialtype1_priv_hdr_t priv_hdr; DWORD ref_pointer; }; typedef struct ndr_pac_hdr ndr_pac_hdr_t; EXTERNTYPEINFO(ndr_pac_hdr) /* * Supporting types (X/Open CAE Spec 12.6.3.1) */ typedef WORD ndr_p_context_id_t; _NO_REORDER_ struct ndr_p_cont_elem { ndr_p_context_id_t p_cont_id; BYTE n_transfer_syn; BYTE _reserved; ndr_p_syntax_id_t abstract_syntax; /*SIZE_IS(n_transfer_syn)*/ ndr_p_syntax_id_t transfer_syntaxes[1]; }; typedef struct ndr_p_cont_elem ndr_p_cont_elem_t; EXTERNTYPEINFO(ndr_p_cont_elem) _NO_REORDER_ struct ndr_p_cont_list { BYTE n_context_elem; BYTE _reserved; WORD _reserved2; /*SIZE_IS(n_context_elem)*/ ndr_p_cont_elem_t p_cont_elem[1]; }; typedef struct ndr_p_cont_list ndr_p_cont_list_t; EXTERNTYPEINFO(ndr_p_cont_list) typedef WORD ndr_p_cont_def_result_t; #define NDR_PCDR_ACCEPTANCE 0 #define NDR_PCDR_USER_REJECTION 1 #define NDR_PCDR_PROVIDER_REJECTION 2 /* * Reasons for provider rejection. * X/Open CAE Spec 12.6.3.1 */ typedef WORD ndr_p_provider_reason_t; #define NDR_PPR_REASON_NOT_SPECIFIED 0 #define NDR_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED 1 #define NDR_PPR_PROPOSED_TRANSFER_SYNTAXES_NOT_SUPPORTED 2 #define NDR_PPR_LOCAL_LIMIT_EXCEEDED 3 _NO_REORDER_ struct ndr_p_result { ndr_p_cont_def_result_t result; /* NDR_PCDR_... */ ndr_p_provider_reason_t reason; /* NDR_PPR_... */ ndr_p_syntax_id_t transfer_syntax; /* 0-fill if result!=ACCEPT */ }; typedef struct ndr_p_result ndr_p_result_t; EXTERNTYPEINFO(ndr_p_result) _NO_REORDER_ struct ndr_p_result_list { BYTE n_results; BYTE reserved; WORD reserved2; /*SIZE_IS(n_results)*/ ndr_p_result_t p_results[1]; }; typedef struct ndr_p_result_list ndr_p_result_list_t; EXTERNTYPEINFO(ndr_p_result_list) #define NDR_PORT_ANY_MAX_PORT_SPEC 30 _NO_REORDER_ struct ndr_port_any { WORD length; /* always 18 */ /*SIZE_IS(length)*/ BYTE port_spec[NDR_PORT_ANY_MAX_PORT_SPEC]; /* \PIPE\ntsvcs */ /* We cheat by using 18, and pad on the right with zeroes */ }; typedef struct ndr_port_any ndr_port_any_t; EXTERNTYPEINFO(ndr_port_any) /* * Reasons for rejecting an association in the bind_nak PDU. * X/Open CAE Spec 12.6.3.1 * MS-RPCE 2.2.2.5 */ #define NDR_REASON_NOT_SPECIFIED 0 #define NDR_TEMPORARY_CONGESTION 1 #define NDR_LOCAL_LIMIT_EXCEEDED 2 #define NDR_CALLED_PADDR_UNKNOWN 3 #define NDR_PROTOCOL_VERSION_NOT_SUPPORTED 4 #define NDR_DEFAULT_CONTEXT_NOT_SUPPORTED 5 #define NDR_USER_DATA_NOT_READABLE 6 #define NDR_NO_PSAP_AVAILABLE 7 #define NDR_AUTH_TYPE_NOT_RECOGNIZED 8 #define NDR_INVALID_CHECKSUM 9 /* * NDRGEN can't handle variable-length arrays, so we provide our own * marshal/unmarshal routine, so that we can convert to and from * our pointer and a fixed-size array. * * NOTE: MS-RPCE calls this a sec_trailer_t, so we use ndr_sec_t. * auth_value should also be an ANY_SIZE_ARRAY, but NDRGEN can't handle * variable-sized arrays. */ IMPORT_EXTERN _NO_REORDER_ struct auth_verifier_co { /* restore 16 byte alignment */ /* SIZE_IS(auth_pad_len) */ /* BYTE auth_pad[ANY_SIZE_ARRAY] */ /* align(16) */ BYTE auth_type; /* 00:01 */ BYTE auth_level; /* 01:01 */ BYTE auth_pad_len; /* 02:01 */ BYTE auth_rsvd; /* 03:01 */ DWORD auth_context_id; /* 04:04 */ /* SIZE_IS(hdr->auth_length) */ BYTE *auth_value; /* 08:* credentials */ }; typedef struct auth_verifier_co ndr_sec_t; EXTERNTYPEINFO(auth_verifier_co) #define SEC_TRAILER_SIZE 8 /* * Alter Context PDU (0x0E) * (X/Open CAE Spec 12.6.4.1) */ _NO_REORDER_ struct ndr_alter_context_hdr { ndr_common_header_t common_hdr; /* 00:16 (see above) */ WORD max_xmit_frag; /* 16:02 ignored */ WORD max_recv_frag; /* 18:02 ignored */ DWORD assoc_group_id; /* 20:04 ignored */ /* * Presentation context list (see bind hdr comments). */ ndr_p_cont_list_t p_context_elem; /* 24: */ /* optional authentication verifier if auth_length != 0 */ /* auth_verifier_co_t auth_verifier; */ }; typedef struct ndr_alter_context_hdr ndr_alter_context_hdr_t; /* * Alter Context Response PDU (0x0F) * (X/Open CAE Spec 12.6.4.2) * * We can't automatically generate an alter context response header because * the sec_addr is an interior conformant (variable length) array, which is * inconsistent with IDL/NDR rules. We mark this import-extern and provide * a hand-coded marshalling function. */ IMPORT_EXTERN _NO_REORDER_ struct ndr_alter_context_rsp_hdr { ndr_common_header_t common_hdr; /* 00:16 (see above) */ WORD max_xmit_frag; /* 16:02 ignored */ WORD max_recv_frag; /* 18:02 ignored */ DWORD assoc_group_id; /* 20:04 ignored */ ndr_port_any_t sec_addr; /* 24:20 ignored */ /* * Presentation context list (see bind hdr comments). */ ndr_p_result_list_t p_result_list; /* 44:nn */ /* optional authentication verifier if auth_length != 0 */ /* auth_verifier_co_t auth_verifier; */ }; typedef struct ndr_alter_context_rsp_hdr ndr_alter_context_rsp_hdr_t; /* * Bind PDU (0x0B) * (X/Open CAE Spec 12.6.4.3) */ _NO_REORDER_ struct ndr_bind_hdr { ndr_common_header_t common_hdr; /* 00:16 (see above) */ WORD max_xmit_frag; /* 16:02 max xmit frag size, bytes */ WORD max_recv_frag; /* 18:02 max recv frag size, bytes */ DWORD assoc_group_id; /* 20:04 association group */ /* 24: */ /* presentation, a variable**2 list, of presentation contexts */ ndr_p_cont_list_t p_context_elem; /* * This could be followed by more transfer_syntaxes[] for the * p_cont_elem[0], and subsequently followed by more p_cont_elem[], * each with one or more transfer_syntaxes[]. A single * p_cont_elem[] with a single transfer_syntaxes[] is so common, * though, we embed it in the bind_hdr but the bind processor must * walk through this tail if there is one. */ /* optional authentication verifier iff auth_length != 0 */ /* auth_verifier_co_t auth_verifier; */ }; typedef struct ndr_bind_hdr ndr_bind_hdr_t; /* * Bind_Ack PDU (0x0C) * (X/Open CAE Spec 12.6.4.4) * * We can't automatically generate a bind ack header because the sec_addr * is an interior conformant (variable length) array, which is inconsistent * with IDL/NDR rules. We mark this import-extern and provide a hand-coded * marshalling function. */ IMPORT_EXTERN _NO_REORDER_ struct ndr_bind_ack_hdr { ndr_common_header_t common_hdr; /* 00:16 (see above) */ WORD max_xmit_frag; /* 16:02 max xmit frag size, bytes */ WORD max_recv_frag; /* 18:02 max recv frag size, bytes */ DWORD assoc_group_id; /* 20:04 association group */ ndr_port_any_t sec_addr; /* 24:20 */ ndr_p_result_list_t p_result_list; /* 44:nn */ /* This could be followed by more. See bind_hdr above */ /* optional authentication verifier iff auth_length != 0 */ /* auth_verifier_co_t auth_verifier; */ }; typedef struct ndr_bind_ack_hdr ndr_bind_ack_hdr_t; /* * Request PDU (0x00) **************************************************************** * Two flavors, selected based on PFC_OBJECT_UUID in hdr.pfc_flags * one without the "object" (flag clear) * one with the "object" (flag set) * (X/Open CAE Spec 12.6.4.9) */ _NO_REORDER_ struct ndr_request_hdr { ndr_common_header_t common_hdr; /* 00:16 (see above) */ /* needed for request, response, or fault */ DWORD alloc_hint; /* 16:04 allocation hint */ ndr_p_context_id_t p_cont_id; /* 20:02 pres context, i.e. data rep */ WORD opnum; /* 22:02 op number w/i interface */ /* optional field if PFC_OBJECT_UUID, not present */ /* ndr_uuid_t object; */ /* stub-data, 8-octet aligned */ /* 24:nn */ /* nn = frag_len - sizeof(common_header) - auth_len */ /* optional authentication verifier iff auth_length != 0 */ /* auth_verifier_co_t auth_verifier; */ }; typedef struct ndr_request_hdr ndr_request_hdr_t; _NO_REORDER_ struct ndr_request_hdr_with_object { ndr_common_header_t common_hdr; /* 00:16 (see above) */ /* needed for request, response, or fault */ DWORD alloc_hint; /* 16:04 allocation hint */ ndr_p_context_id_t p_cont_id; /* 20:02 pres context, i.e. data rep */ WORD opnum; /* 22:02 op number w/i interface */ /* optional field if PFC_OBJECT_UUID, is present */ ndr_uuid_t object; /* 24:16 object UUID, unknown purpose*/ /* stub-data, 8-octet aligned */ /* 28:nn */ /* nn = frag_len - sizeof(common_header) - auth_len */ /* nn -= sizeof(ndr_uuid_t); */ /* optional authentication verifier iff auth_length != 0 */ /* auth_verifier_co_t auth_verifier; */ }; /* * Convenient for response header sizing and multi-fragment responses. * We know the header is going to be 24 bytes, and the 'common' part * is 16 bytes. */ #define NDR_CMN_HDR_SIZE 16 #define NDR_RSP_HDR_SIZE 24 /* * Response PDU (0x02) * (X/Open CAE Spec 12.6.4.10) */ _NO_REORDER_ struct ndr_response_hdr { ndr_common_header_t common_hdr; /* 00:16 (see above) */ /* needed for request, response, or fault */ DWORD alloc_hint; /* 16:04 allocation hint */ ndr_p_context_id_t p_cont_id; /* 20:02 pres context, i.e. data rep */ /* needed for response or fault */ BYTE cancel_count; /* 22:01 cancel count */ BYTE reserved; /* 23:01 mbz */ /* stub-data, 8-octet aligned */ /* 24:nn */ /* nn = frag_len - sizeof(common_header) - auth_len */ /* optional authentication verifier iff auth_length != 0 */ /* auth_verifier_co_t auth_verifier; */ }; typedef struct ndr_response_hdr ndr_response_hdr_t; /* * Fault PDU (0x03) * (X/Open CAE Spec 12.6.4.7) */ _NO_REORDER_ struct ndr_fault_hdr { ndr_common_header_t common_hdr; /* 00:16 (see above) */ DWORD alloc_hint; /* 16:04 allocation hint */ ndr_p_context_id_t p_cont_id; /* 20:02 pres context, i.e. data rep */ /* needed for response or fault */ BYTE cancel_count; /* 22:01 cancel count */ BYTE reserved; /* 23:01 mbz */ /* fault code */ DWORD status; /* 24:04 run-time fault code or 0 */ /* pad to 8-byte alignment */ BYTE reserved2[4]; /* 28:04 must-be-zero */ /* stub-data here if status==0. We do not use this mode. */ /* optional authentication verifier iff auth_length != 0 */ /* auth_verifier_co_t auth_verifier; */ }; typedef struct ndr_fault_hdr ndr_fault_hdr_t; /* Fault status code (X/Open CAE Spec Appendix E) */ #define NDR_FAULT_NCA_RPC_VERSION_MISMATCH 0x1c000008 /* CO/CL */ #define NDR_FAULT_NCA_UNSPEC_REJECT 0x1c000009 /* CO/CL */ #define NDR_FAULT_NCA_S_BAD_ACTID 0x1c00000A /* CL */ #define NDR_FAULT_NCA_WHO_ARE_YOU_FAILED 0x1c00000B /* CL */ #define NDR_FAULT_NCA_MANAGER_NOT_ENTERED 0x1c00000C /* CO/CL */ #define NDR_FAULT_NCA_OP_RNG_ERROR 0x1c010002 /* CO/CL */ #define NDR_FAULT_NCA_UNK_IF 0x1c010003 /* CO/CL */ #define NDR_FAULT_NCA_WRONG_BOOT_TIME 0x1c010006 /* CL */ #define NDR_FAULT_NCA_S_YOU_CRASHED 0x1c010009 /* CL */ #define NDR_FAULT_NCA_PROTO_ERROR 0x1c01000B /* CO/CL */ #define NDR_FAULT_NCA_OUT_ARGS_TOO_BIG 0x1c010013 /* CO/CL */ #define NDR_FAULT_NCA_SERVER_TOO_BUSY 0x1c010014 /* CO/CL */ #define NDR_FAULT_NCA_UNSUPPORTED_TYPE 0x1c010017 /* CO/CL */ #define NDR_FAULT_NCA_INVALID_PRES_CONTEXT_ID 0x1c00001c /* CO */ #define NDR_FAULT_NCA_UNSUPPORTED_AUTHN_LEVEL 0x1c00001d /* CO/CL */ #define NDR_FAULT_NCA_INVALID_CHECKSUM 0x1c00001f /* CO/CL */ #define NDR_FAULT_NCA_INVALID_CRC 0x1c000020 /* CO/CL */ /* * The Header Union/Switch **************************************************************** */ #define NDR_PTYPE_COMMON 999 #define NDR_PTYPE_REQUEST_WITH 998 #define NDR_PTYPE_SERIALTYPE_V1 997 #define NDR_PTYPE_SERIALTYPE_V2 996 #define NDR_PTYPE_PAC 995 INTERFACE(0) union ndr_hdr { CASE(NDR_PTYPE_COMMON) /* exceeds BYTE range, obtains common hdr */ struct ndr_common_header common_hdr; CASE(NDR_PTYPE_BIND) struct ndr_bind_hdr bind_hdr; CASE(NDR_PTYPE_BIND_ACK) struct ndr_bind_ack_hdr bind_ack_hdr; CASE(NDR_PTYPE_REQUEST) struct ndr_request_hdr request_hdr; CASE(NDR_PTYPE_REQUEST_WITH) /* exceeds BYTE range, ... */ struct ndr_request_hdr_with_object request_hdr_with; CASE(NDR_PTYPE_RESPONSE) struct ndr_response_hdr response_hdr; CASE(NDR_PTYPE_ALTER_CONTEXT) struct ndr_alter_context_hdr alter_context_hdr; CASE(NDR_PTYPE_ALTER_CONTEXT_RESP) struct ndr_alter_context_rsp_hdr alter_context_rsp_hdr; CASE(NDR_PTYPE_SERIALTYPE_V1) struct ndr_serialtype1_hdr serialtype1_hdr; CASE(NDR_PTYPE_SERIALTYPE_V2) struct ndr_serialtype2_hdr serialtype2_hdr; CASE(NDR_PTYPE_PAC) struct ndr_pac_hdr pac_hdr; CASE(NDR_PTYPE_FAULT) struct ndr_fault_hdr fault_hdr; }; typedef union ndr_hdr ndr_hdr_t; EXTERNTYPEINFO(ndr_hdr) #endif /* _RPCPDU_NDL_ */