diff options
Diffstat (limited to 'usr/src/lib/libmlrpc')
-rw-r--r-- | usr/src/lib/libmlrpc/Makefile.com | 3 | ||||
-rw-r--r-- | usr/src/lib/libmlrpc/common/libmlrpc.h | 83 | ||||
-rw-r--r-- | usr/src/lib/libmlrpc/common/mapfile-vers | 3 | ||||
-rw-r--r-- | usr/src/lib/libmlrpc/common/mlrpc_clh.c | 23 | ||||
-rw-r--r-- | usr/src/lib/libmlrpc/common/ndr.h | 57 | ||||
-rw-r--r-- | usr/src/lib/libmlrpc/common/ndr_auth.c | 171 | ||||
-rw-r--r-- | usr/src/lib/libmlrpc/common/ndr_client.c | 158 | ||||
-rw-r--r-- | usr/src/lib/libmlrpc/common/ndr_marshal.c | 314 | ||||
-rw-r--r-- | usr/src/lib/libmlrpc/common/ndr_ops.c | 8 | ||||
-rw-r--r-- | usr/src/lib/libmlrpc/common/ndr_process.c | 71 | ||||
-rw-r--r-- | usr/src/lib/libmlrpc/common/ndrtypes.ndl | 4 | ||||
-rw-r--r-- | usr/src/lib/libmlrpc/common/rpcpdu.ndl | 41 |
12 files changed, 867 insertions, 69 deletions
diff --git a/usr/src/lib/libmlrpc/Makefile.com b/usr/src/lib/libmlrpc/Makefile.com index 700a8c8968..8e289594cc 100644 --- a/usr/src/lib/libmlrpc/Makefile.com +++ b/usr/src/lib/libmlrpc/Makefile.com @@ -23,7 +23,7 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# Copyright 2013 Nexenta Systems, Inc. All rights reserved. +# Copyright 2020 Tintri by DDN, Inc. All rights reserved. # LIBRARY = libmlrpc.a @@ -31,6 +31,7 @@ VERS = .2 OBJS_COMMON = \ mlrpc_clh.o \ + ndr_auth.o \ ndr_client.o \ ndr_heap.o \ ndr_marshal.o \ diff --git a/usr/src/lib/libmlrpc/common/libmlrpc.h b/usr/src/lib/libmlrpc/common/libmlrpc.h index d020532cbe..6b195e4c26 100644 --- a/usr/src/lib/libmlrpc/common/libmlrpc.h +++ b/usr/src/lib/libmlrpc/common/libmlrpc.h @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ #ifndef _LIBMLRPC_H @@ -117,6 +117,7 @@ extern "C" { /* 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) @@ -154,6 +155,10 @@ extern "C" { #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 @@ -179,6 +184,32 @@ extern "C" { #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; @@ -224,12 +255,12 @@ typedef struct ndr_service { * conn->binding_pool, N_BINDING_POOL); */ typedef struct ndr_binding { - struct ndr_binding *next; + 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; + void *instance_specific; } ndr_binding_t; #define NDR_BIND_SIDE_CLIENT 1 @@ -422,14 +453,39 @@ typedef struct ndr_xa { 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); +} 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. */ @@ -459,6 +515,8 @@ typedef struct ndr_client { uint32_t next_call_id; unsigned next_p_cont_id; + + ndr_auth_ctx_t auth_ctx; } ndr_client_t; typedef struct ndr_handle { @@ -507,6 +565,19 @@ 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 *); @@ -542,7 +613,10 @@ ssize_t ndr_uiomove(caddr_t, size_t, enum uio_rw, struct uio *); * 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 + * 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 */ @@ -550,6 +624,7 @@ typedef struct mlrpc_handle { } 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 *); diff --git a/usr/src/lib/libmlrpc/common/mapfile-vers b/usr/src/lib/libmlrpc/common/mapfile-vers index 241d8309d0..21fee6d1ca 100644 --- a/usr/src/lib/libmlrpc/common/mapfile-vers +++ b/usr/src/lib/libmlrpc/common/mapfile-vers @@ -20,7 +20,7 @@ # # # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. -# Copyright 2013 Nexenta Systems, Inc. All rights reserved. +# Copyright 2020 Tintri by DDN, Inc. All rights reserved. # # @@ -45,6 +45,7 @@ SYMBOL_VERSION SUNWprivate { mlrpc_clh_bind; mlrpc_clh_create; mlrpc_clh_free; + mlrpc_clh_set_auth; mlrpc_clh_unbind; # Allow debug/test programs to provide these. diff --git a/usr/src/lib/libmlrpc/common/mlrpc_clh.c b/usr/src/lib/libmlrpc/common/mlrpc_clh.c index 72c051d675..2810ab4544 100644 --- a/usr/src/lib/libmlrpc/common/mlrpc_clh.c +++ b/usr/src/lib/libmlrpc/common/mlrpc_clh.c @@ -23,7 +23,7 @@ * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ /* @@ -71,9 +71,8 @@ mlrpc_clh_create(mlrpc_handle_t *handle, void *ctx) /* * Allocate... */ - if ((clnt = malloc(sizeof (*clnt))) == NULL) + if ((clnt = calloc(1, sizeof (*clnt))) == NULL) return (ENOMEM); - bzero(clnt, sizeof (*clnt)); clnt->xa_fd = -1; @@ -108,6 +107,24 @@ nomem: 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 diff --git a/usr/src/lib/libmlrpc/common/ndr.h b/usr/src/lib/libmlrpc/common/ndr.h index f6af9a22cf..0ed7010f18 100644 --- a/usr/src/lib/libmlrpc/common/ndr.h +++ b/usr/src/lib/libmlrpc/common/ndr.h @@ -22,7 +22,7 @@ * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ #ifndef _SMBSRV_NDR_H @@ -145,6 +145,7 @@ extern "C" { #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; @@ -230,11 +231,43 @@ typedef struct ndr_stream_ops { #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; @@ -325,6 +358,22 @@ typedef struct ndr_stream { #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); \ @@ -374,16 +423,16 @@ typedef struct ndr_stream { return (0); \ } -#define NDR_TOPMOST_MEMBER(TYPE, MEMBER) \ +#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_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_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) \ diff --git a/usr/src/lib/libmlrpc/common/ndr_auth.c b/usr/src/lib/libmlrpc/common/ndr_auth.c new file mode 100644 index 0000000000..6976f2b908 --- /dev/null +++ b/usr/src/lib/libmlrpc/common/ndr_auth.c @@ -0,0 +1,171 @@ +/* + * 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. + */ + +#include <libmlrpc.h> +#include <sys/sysmacros.h> +#include <strings.h> + +/* + * 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 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) + 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) + return (NDR_DRC_FAULT_SEC_LEVEL_UNIMPLEMENTED); + + if ((rc = ndr_add_auth_token(ctx, mxa)) != 0) + return (rc); + + 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) + return (NDR_DRC_FAULT_SEC_AUTH_LEVEL_INVALID); + + return (ctx->auth_ops.nao_verify(ctx->auth_ctx, mxa, + ctx->auth_verify_resp)); +} diff --git a/usr/src/lib/libmlrpc/common/ndr_client.c b/usr/src/lib/libmlrpc/common/ndr_client.c index 56cc1847ef..7dbbfed83b 100644 --- a/usr/src/lib/libmlrpc/common/ndr_client.c +++ b/usr/src/lib/libmlrpc/common/ndr_client.c @@ -22,7 +22,7 @@ * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ #include <sys/errno.h> @@ -45,6 +45,7 @@ ndr_clnt_bind(ndr_client_t *clnt, ndr_service_t *msvc, 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; @@ -59,8 +60,8 @@ ndr_clnt_bind(ndr_client_t *clnt, ndr_service_t *msvc, ndr_clnt_init_hdr(clnt, &mxa); bhdr = &mxa.send_hdr.bind_hdr; - bhdr->common_hdr.ptype = NDR_PTYPE_BIND; - bhdr->common_hdr.frag_length = sizeof (*bhdr); + 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; @@ -89,6 +90,30 @@ ndr_clnt_bind(ndr_client_t *clnt, ndr_service_t *msvc, 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; @@ -102,21 +127,36 @@ ndr_clnt_bind(ndr_client_t *clnt, ndr_service_t *msvc, if (NDR_DRC_IS_FAULT(rc)) goto fault_exit; - /* done with buffers */ - (*clnt->xa_destruct)(clnt, &mxa); + 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) - return (NDR_DRC_FAULT_RECEIVED_MALFORMED); + if (mxa.ptype != NDR_PTYPE_BIND_ACK) { + rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; + goto fault_exit; + } - if (bahdr->p_result_list.n_results != 1) - return (NDR_DRC_FAULT_RECEIVED_MALFORMED); + 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) - return (NDR_DRC_FAULT_RECEIVED_MALFORMED); + 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; @@ -139,7 +179,7 @@ ndr_clnt_call(ndr_binding_t *mbind, int opnum, void *params) ndr_xa_t mxa; ndr_request_hdr_t *reqhdr; ndr_common_header_t *rsphdr; - unsigned long recv_pdu_scan_offset; + unsigned long recv_pdu_scan_offset, recv_pdu_size; int rc; bzero(&mxa, sizeof (mxa)); @@ -160,20 +200,36 @@ ndr_clnt_call(ndr_binding_t *mbind, int opnum, void *params) /* 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; - mxa.send_nds.pdu_scan_offset = 0; + /* + * 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 and calculate the alloc_hint. + * frag_length. + * Also reset pdu_scan_offset to header. */ mxa.send_hdr.common_hdr.frag_length = mxa.send_nds.pdu_size; - reqhdr->alloc_hint = mxa.send_nds.pdu_size - - sizeof (ndr_request_hdr_t); + mxa.send_nds.pdu_scan_offset = 0; rc = ndr_encode_pdu_hdr(&mxa); if (NDR_DRC_IS_FAULT(rc)) @@ -192,26 +248,44 @@ ndr_clnt_call(ndr_binding_t *mbind, int opnum, void *params) 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 scan offset while getting + * 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); - recv_pdu_scan_offset = mxa.recv_nds.pdu_scan_offset; - mxa.recv_nds.pdu_scan_offset = rsphdr->frag_length; - mxa.recv_nds.pdu_size = rsphdr->frag_length; + /* + * 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; - if (ndr_clnt_get_frags(clnt, &mxa) < 0) { - rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; + 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; } @@ -225,6 +299,15 @@ ndr_clnt_call(ndr_binding_t *mbind, int opnum, void *params) 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); } @@ -270,23 +353,44 @@ ndr_clnt_get_frags(ndr_client_t *clnt, ndr_xa_t *mxa) 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 (-1); + 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 (-1); + 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 += frag_size - NDR_RSP_HDR_SIZE; + nds->pdu_scan_offset += + nds->pdu_body_size - mxa->recv_auth.auth_pad_len; } while (!last_frag); return (0); @@ -307,7 +411,7 @@ ndr_clnt_get_frag(ndr_client_t *clnt, ndr_xa_t *mxa, ndr_common_header_t *hdr) available = nds->pdu_size - nds->pdu_scan_offset; while (available < NDR_RSP_HDR_SIZE) { - if ((nbytes += (*clnt->xa_read)(clnt, mxa)) <= 0) + if ((nbytes = (*clnt->xa_read)(clnt, mxa)) <= 0) return (-1); available += nbytes; } diff --git a/usr/src/lib/libmlrpc/common/ndr_marshal.c b/usr/src/lib/libmlrpc/common/ndr_marshal.c index 4d34030d2a..33345aa3dd 100644 --- a/usr/src/lib/libmlrpc/common/ndr_marshal.c +++ b/usr/src/lib/libmlrpc/common/ndr_marshal.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ #include <assert.h> @@ -38,18 +38,40 @@ static const int ndr_native_byte_order = NDR_REPLAB_INTG_LITTLE_ENDIAN; 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_encode_decode_common(ndr_stream_t *nds, unsigned opnum, - ndr_typeinfo_t *ti, void *datum) +ndr_convert_nds_error(ndr_stream_t *nds) { int rc; - /* - * Perform the (un)marshalling - */ - if (ndo_operation(nds, ti, opnum, datum)) - return (NDR_DRC_OK); - switch (nds->error) { case NDR_ERR_MALLOC_FAILED: rc = NDR_DRC_FAULT_OUT_OF_MEMORY; @@ -78,6 +100,31 @@ ndr_encode_decode_common(ndr_stream_t *nds, unsigned opnum, 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) { @@ -115,7 +162,7 @@ ndr_buf_fini(ndr_buf_t *nbuf) * * pac_info_t info; * - * if ((nbuf = ndr_buf_init(&TYPEINFO(ndr_pac)) != NULL) { + * if ((nbuf = ndr_buf_init(&TYPEINFO(ndr_pac)) != NULL) { * rc = ndr_decode_buf(nbuf, opnum, data, datalen, &info); * ... * ndr_buf_fini(nbuf); @@ -145,6 +192,7 @@ ndr_buf_decode(ndr_buf_t *nbuf, unsigned hdr_type, unsigned opnum, return (rc); bcopy(data, nbuf->nb_nds.pdu_base_addr, datalen); + nbuf->nb_nds.pdu_size = datalen; switch (hdr_type) { case NDR_PTYPE_COMMON: @@ -252,7 +300,9 @@ 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); @@ -264,6 +314,16 @@ ndr_decode_pdu_hdr(ndr_xa_t *mxa) 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); } @@ -274,6 +334,7 @@ ndr_decode_hdr_common(ndr_stream_t *nds, ndr_common_header_t *hdr) 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); @@ -281,8 +342,8 @@ ndr_decode_hdr_common(ndr_stream_t *nds, ndr_common_header_t *hdr) /* * All PDU headers are at least this big */ - rc = NDS_GROW_PDU(nds, sizeof (ndr_common_header_t), 0); - if (!rc) + saved_offset = nds->pdu_scan_offset; + if ((nds->pdu_size - saved_offset) < sizeof (ndr_common_header_t)) return (NDR_DRC_FAULT_RPCHDR_RECEIVED_RUNT); /* @@ -315,6 +376,8 @@ ndr_decode_hdr_common(ndr_stream_t *nds, ndr_common_header_t *hdr) 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)); } @@ -329,8 +392,7 @@ ndr_decode_pac_hdr(ndr_stream_t *nds, ndr_pac_hdr_t *hdr) /* * All PDU headers are at least this big */ - rc = NDS_GROW_PDU(nds, sizeof (ndr_pac_hdr_t), 0); - if (!rc) + if ((nds->pdu_size - nds->pdu_scan_offset) < sizeof (ndr_pac_hdr_t)) return (NDR_DRC_FAULT_RPCHDR_RECEIVED_RUNT); /* @@ -388,6 +450,13 @@ ndr_decode_frag_hdr(ndr_stream_t *nds, ndr_common_header_t *hdr) 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); } /* @@ -418,7 +487,10 @@ ndr_remove_frag_hdr(ndr_stream_t *nds) data = hdr + NDR_RSP_HDR_SIZE; nbytes = nds->pdu_size - nds->pdu_scan_offset - NDR_RSP_HDR_SIZE; - bcopy(data, hdr, nbytes); + /* + * 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; } @@ -442,9 +514,24 @@ ndr_show_hdr(ndr_common_header_t *hdr) fragtype = "intermediate"; ndo_printf(NULL, NULL, - "ndr hdr: %d.%d ptype=%d, %s frag (flags=0x%08x) len=%d", + "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); + 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: <null>"); + 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 @@ -646,3 +733,196 @@ ndr_alter_context_rsp_hdr_size(void) 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); +} diff --git a/usr/src/lib/libmlrpc/common/ndr_ops.c b/usr/src/lib/libmlrpc/common/ndr_ops.c index 4ca1cb6295..7716ddd7b0 100644 --- a/usr/src/lib/libmlrpc/common/ndr_ops.c +++ b/usr/src/lib/libmlrpc/common/ndr_ops.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ /* @@ -186,9 +186,11 @@ nds_show_state(ndr_stream_t *nds) return; } - ndo_printf(NULL, NULL, "nds: base=0x%x, size=%d, max=%d, scan=%d", + ndo_printf(NULL, NULL, "nds: base=0x%x, size=%d, max=%d, scan=%d, " + "hdr_size=%d, body_size=%d, body_offset=%d", nds->pdu_base_offset, nds->pdu_size, nds->pdu_max_size, - nds->pdu_scan_offset); + nds->pdu_scan_offset, nds->pdu_hdr_size, nds->pdu_body_size, + nds->pdu_body_offset); } /* diff --git a/usr/src/lib/libmlrpc/common/ndr_process.c b/usr/src/lib/libmlrpc/common/ndr_process.c index 4b2ea938e8..c39380f367 100644 --- a/usr/src/lib/libmlrpc/common/ndr_process.c +++ b/usr/src/lib/libmlrpc/common/ndr_process.c @@ -23,7 +23,7 @@ * Use is subject to license terms. * * Copyright 2012 Milan Jurik. All rights reserved. - * Copyright 2019 Nexenta by DDN, Inc. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ /* @@ -1474,7 +1474,7 @@ ndr_outer_align(ndr_ref_t *outer_ref) n_pad = NDR_ALIGN4(nds->pdu_scan_offset); } - if (n_pad == 0) + 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)) @@ -1544,6 +1544,57 @@ ndr_outer_grow(ndr_ref_t *outer_ref, unsigned n_total) } /* + * 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 @@ -1568,6 +1619,22 @@ ndr_inner(ndr_ref_t *arg_ref) 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) { diff --git a/usr/src/lib/libmlrpc/common/ndrtypes.ndl b/usr/src/lib/libmlrpc/common/ndrtypes.ndl index b7d5fdb716..a5f0e3fd61 100644 --- a/usr/src/lib/libmlrpc/common/ndrtypes.ndl +++ b/usr/src/lib/libmlrpc/common/ndrtypes.ndl @@ -22,7 +22,7 @@ * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ #ifndef _NDRTYPES_NDL_ @@ -42,6 +42,7 @@ #define IN [in] #define OUT [out] #define INOUT [in out] +#define FAKE [fake] #define STRING [string] #define SIZE_IS(X) [size_is(X)] @@ -85,6 +86,7 @@ #define IN #define OUT #define INOUT +#define FAKE #define STRING #define SIZE_IS(X) diff --git a/usr/src/lib/libmlrpc/common/rpcpdu.ndl b/usr/src/lib/libmlrpc/common/rpcpdu.ndl index f1ea15cf00..fae76738da 100644 --- a/usr/src/lib/libmlrpc/common/rpcpdu.ndl +++ b/usr/src/lib/libmlrpc/common/rpcpdu.ndl @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ #ifndef _RPCPDU_NDL_ @@ -132,7 +133,7 @@ typedef struct ndr_representation_label ndr_replab_t; * 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. + * 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 */ @@ -202,7 +203,7 @@ EXTERNTYPEINFO(ndr_common_header) * 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 */ @@ -232,7 +233,7 @@ EXTERNTYPEINFO(ndr_serialtype1_priv_hdr) * 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 */ @@ -362,7 +363,35 @@ EXTERNTYPEINFO(ndr_port_any) #define NDR_USER_DATA_NOT_READABLE 6 #define NDR_NO_PSAP_AVAILABLE 7 #define NDR_AUTH_TYPE_NOT_RECOGNIZED 8 -#define NDR_INAVLID_CHECKSUM 9 +#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) @@ -376,7 +405,7 @@ struct ndr_alter_context_hdr { 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: */ @@ -406,7 +435,7 @@ struct ndr_alter_context_rsp_hdr { 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 */ |