summaryrefslogtreecommitdiff
path: root/usr/src/lib/libmlrpc
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libmlrpc')
-rw-r--r--usr/src/lib/libmlrpc/Makefile.com3
-rw-r--r--usr/src/lib/libmlrpc/common/libmlrpc.h83
-rw-r--r--usr/src/lib/libmlrpc/common/mapfile-vers3
-rw-r--r--usr/src/lib/libmlrpc/common/mlrpc_clh.c23
-rw-r--r--usr/src/lib/libmlrpc/common/ndr.h57
-rw-r--r--usr/src/lib/libmlrpc/common/ndr_auth.c171
-rw-r--r--usr/src/lib/libmlrpc/common/ndr_client.c158
-rw-r--r--usr/src/lib/libmlrpc/common/ndr_marshal.c314
-rw-r--r--usr/src/lib/libmlrpc/common/ndr_ops.c8
-rw-r--r--usr/src/lib/libmlrpc/common/ndr_process.c71
-rw-r--r--usr/src/lib/libmlrpc/common/ndrtypes.ndl4
-rw-r--r--usr/src/lib/libmlrpc/common/rpcpdu.ndl41
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 */