changeset 23943:4fa9ed968314 default tip

Merge pull request #919 from hadfl/smb SMB fixes
author Andy Fiddaman <andy@omnios.org>
date Sat, 16 Jan 2021 16:59:32 +0000
parents 237362548d55 (current diff) 2459678edc5e (diff)
children
files usr/src/test/libmlrpc-tests/Makefile usr/src/test/libmlrpc-tests/cfg/Makefile usr/src/test/libmlrpc-tests/cmd/Makefile usr/src/test/libmlrpc-tests/cmd/libmlrpctest.ksh usr/src/test/libmlrpc-tests/doc/Makefile usr/src/test/libmlrpc-tests/runfiles/Makefile usr/src/test/libmlrpc-tests/tests/netrlogon/Makefile
diffstat 66 files changed, 3604 insertions(+), 369 deletions(-) [+]
line wrap: on
line diff
--- a/exception_lists/copyright	Thu Jan 14 17:27:56 2021 +0100
+++ b/exception_lists/copyright	Sat Jan 16 16:59:32 2021 +0000
@@ -20,7 +20,7 @@
 #
 
 #
-# Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
+# Copyright 2020 Tintri by DDN, Inc. All rights reserved.
 # Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
 # Copyright (c) 2011 by Delphix. All rights reserved.
 # Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
@@ -391,6 +391,7 @@
 usr/src/lib/libsmbfs/smb/spnegoparse.[ch]
 usr/src/test/crypto-tests/tests/digest/data/*.rsp
 usr/src/test/crypto-tests/tests/digest/data/README
+usr/src/test/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/krb5_pac.bin
 usr/src/test/util-tests/tests/dis/*/*.out
 usr/src/test/util-tests/tests/grep_xpg4/files/gout*
 usr/src/test/util-tests/tests/grep_xpg4/files/test*
--- a/usr/src/cmd/smbsrv/smbd/server.xml	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/cmd/smbsrv/smbd/server.xml	Sat Jan 16 16:59:32 2021 +0000
@@ -22,8 +22,9 @@
 CDDL HEADER END
 
 Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
 Copyright 2016 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
-Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
+Copyright 2020 Tintri by DDN, Inc. All rights reserved.
 Copyright 2020 RackTop Systems.
 
 NOTE:  This service manifest is not editable; its contents will
@@ -240,6 +241,8 @@
 			value='20' override='true'/>
 		<propval name='maximum_credits' type='integer'
 			value='1000' override='true'/>
+		<propval name='netlogon_flags' type='integer'
+			value='0' override='true'/>
 	</property_group>
 
 	<!-- SMB service-specific shares exec configuration defaults -->
--- a/usr/src/lib/libmlrpc/Makefile.com	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/lib/libmlrpc/Makefile.com	Sat Jan 16 16:59:32 2021 +0000
@@ -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 @@
 
 OBJS_COMMON =			\
 	mlrpc_clh.o		\
+	ndr_auth.o		\
 	ndr_client.o		\
 	ndr_heap.o		\
 	ndr_marshal.o		\
--- a/usr/src/lib/libmlrpc/common/libmlrpc.h	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/lib/libmlrpc/common/libmlrpc.h	Sat Jan 16 16:59:32 2021 +0000
@@ -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 @@
 /* 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 @@
 #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 @@
 #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 @@
  *		    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 @@
 	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 @@
 
 	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_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 @@
  * 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 @@
 } 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 *);
--- a/usr/src/lib/libmlrpc/common/mapfile-vers	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/lib/libmlrpc/common/mapfile-vers	Sat Jan 16 16:59:32 2021 +0000
@@ -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 @@
 	mlrpc_clh_bind;
 	mlrpc_clh_create;
 	mlrpc_clh_free;
+	mlrpc_clh_set_auth;
 	mlrpc_clh_unbind;
 
 	# Allow debug/test programs to provide these.
--- a/usr/src/lib/libmlrpc/common/mlrpc_clh.c	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/lib/libmlrpc/common/mlrpc_clh.c	Sat Jan 16 16:59:32 2021 +0000
@@ -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 @@
 	/*
 	 * 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 @@
 	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
--- a/usr/src/lib/libmlrpc/common/ndr.h	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/lib/libmlrpc/common/ndr.h	Sat Jan 16 16:59:32 2021 +0000
@@ -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 @@
 #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 @@
 #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 @@
 #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 @@
 			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)	\
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libmlrpc/common/ndr_auth.c	Sat Jan 16 16:59:32 2021 +0000
@@ -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));
+}
--- a/usr/src/lib/libmlrpc/common/ndr_client.c	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/lib/libmlrpc/common/ndr_client.c	Sat Jan 16 16:59:32 2021 +0000
@@ -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_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_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 @@
 	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 @@
 	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_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 @@
 
 	/* 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 @@
 		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 @@
 	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_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 @@
 	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;
 	}
--- a/usr/src/lib/libmlrpc/common/ndr_marshal.c	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/lib/libmlrpc/common/ndr_marshal.c	Sat Jan 16 16:59:32 2021 +0000
@@ -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 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 @@
 	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 @@
  *
  *	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 @@
 		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_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 @@
 		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 @@
 	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 @@
 	/*
 	 * 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 @@
 
 	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 @@
 	/*
 	 * 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 @@
 		    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 @@
 	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 @@
 		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 @@
 	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);
+}
--- a/usr/src/lib/libmlrpc/common/ndr_ops.c	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/lib/libmlrpc/common/ndr_ops.c	Sat Jan 16 16:59:32 2021 +0000
@@ -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 @@
 		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);
 }
 
 /*
--- a/usr/src/lib/libmlrpc/common/ndr_process.c	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/lib/libmlrpc/common/ndr_process.c	Sat Jan 16 16:59:32 2021 +0000
@@ -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 @@
 		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 @@
 }
 
 /*
+ * 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 @@
 
 	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) {
--- a/usr/src/lib/libmlrpc/common/ndrtypes.ndl	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/lib/libmlrpc/common/ndrtypes.ndl	Sat Jan 16 16:59:32 2021 +0000
@@ -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)
--- a/usr/src/lib/libmlrpc/common/rpcpdu.ndl	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/lib/libmlrpc/common/rpcpdu.ndl	Sat Jan 16 16:59:32 2021 +0000
@@ -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 @@
  * 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 @@
  * 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 @@
  * 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 @@
 #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 @@
 	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 @@
 	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 */
--- a/usr/src/lib/smbsrv/libmlsvc/Makefile.com	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/lib/smbsrv/libmlsvc/Makefile.com	Sat Jan 16 16:59:32 2021 +0000
@@ -20,7 +20,7 @@
 #
 #
 # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
-# Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
+# Copyright 2020 Tintri by DDN, Inc. All rights reserved.
 #
 # Copyright (c) 2018, Joyent, Inc.
 
@@ -45,6 +45,7 @@
 	netdfs.o	\
 	netr_auth.o	\
 	netr_logon.o	\
+	netr_ssp.o	\
 	samlib.o	\
 	samr_clnt.o	\
 	samr_svc.o	\
@@ -85,8 +86,9 @@
 INCS += -I$(SRC)/uts/common/smbsrv/ndl
 
 LDLIBS +=	$(MACH_LDLIBS)
-LDLIBS += -lmlrpc -lsmb -lsmbns -lshare -lsmbfs -lnsl -lpkcs11 \
-	-lscf -lcmdutils -lsec -lavl -lnvpair -luutil -luuid -lgen -lzfs -lc
+LDLIBS += -lmlrpc -lsmb -lsmbns -lshare -lsmbfs -lnsl -lpkcs11 -lmd5	 \
+	-lscf -lcmdutils -lsec -lavl -lnvpair -luutil -luuid -lgen -lzfs \
+	-lresolv -lc
 
 CPPFLAGS += $(INCS) -D_REENTRANT
 CPPFLAGS += -Dsyslog=smb_syslog
--- a/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h	Sat Jan 16 16:59:32 2021 +0000
@@ -20,7 +20,7 @@
  */
 /*
  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2019 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
  */
 
 #ifndef	_LIBMLSVC_H
@@ -155,6 +155,8 @@
 void ndr_rpc_init(void);
 void ndr_rpc_fini(void);
 uint32_t ndr_rpc_bind(mlsvc_handle_t *, char *, char *, char *, const char *);
+uint32_t ndr_rpc_bind_secure(mlsvc_handle_t *, char *, char *, char *,
+    const char *, ndr_auth_ctx_t *);
 void ndr_rpc_unbind(mlsvc_handle_t *);
 void ndr_rpc_status(mlsvc_handle_t *, int, uint32_t);
 
--- a/usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers	Sat Jan 16 16:59:32 2021 +0000
@@ -20,7 +20,7 @@
 #
 #
 # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
-# Copyright 2019 Nexenta Systems, Inc.  All rights reserved.
+# Copyright 2020 Tintri by DDN, Inc. All rights reserved.
 #
 
 #
@@ -54,6 +54,9 @@
 	mlsvc_init;
 	mlsvc_join;
 	mlsvc_netlogon;
+	netlogon_init_global;
+	netlogon_logon;
+	netr_initialize;
 	smb_autohome_add;
 	smb_autohome_remove;
 	smb_ddiscover_bad_dc;
--- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h	Sat Jan 16 16:59:32 2021 +0000
@@ -20,7 +20,7 @@
  */
 /*
  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
  */
 
 #ifndef _SMBSRV_MLSVC_H
@@ -56,9 +56,12 @@
 void netdfs_finalize(void);
 
 /* netr_auth.c */
+/* No RPC-level auth */
 DWORD netr_open(char *, char *, mlsvc_handle_t *);
+/* Uses RPC-level auth if supported */
+DWORD netr_open_secure(char *, char *, mlsvc_handle_t *);
 int netr_close(mlsvc_handle_t *);
-DWORD netlogon_auth(char *, mlsvc_handle_t *, DWORD);
+DWORD netlogon_auth(char *, char *, DWORD);
 int netr_setup_authenticator(struct netr_info *, struct netr_authenticator *,
     struct netr_authenticator *);
 DWORD netr_validate_chain(struct netr_info *, struct netr_authenticator *);
--- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c	Sat Jan 16 16:59:32 2021 +0000
@@ -21,7 +21,7 @@
 
 /*
  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
  */
 
 /*
@@ -73,9 +73,9 @@
  *	NT_STATUS_INTERNAL_ERROR	(bad args etc)
  *	NT_STATUS_NO_MEMORY
  */
-DWORD
-ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain,
-    char *username, const char *service)
+static DWORD
+ndr_rpc_bind_common(mlsvc_handle_t *handle, char *server, char *domain,
+    char *username, const char *service, ndr_auth_ctx_t *auth_ctx)
 {
 	struct smb_ctx		*ctx = NULL;
 	ndr_service_t		*svc;
@@ -145,6 +145,18 @@
 	}
 
 	/*
+	 * Setup authentication, if requested.
+	 */
+	status = mlrpc_clh_set_auth(handle, auth_ctx);
+	if (status != 0) {
+		syslog(LOG_DEBUG, "ndr_rpc_bind: "
+		    "mlrpc_clh_set_auth, %s (0x%x)",
+		    xlate_nt_status(status), status);
+
+		goto errout;
+	}
+
+	/*
 	 * This does the pipe open and OtW RPC bind.
 	 * Handles pipe open retries.
 	 */
@@ -161,15 +173,36 @@
 		default:
 			break;
 		}
-		ctx = mlrpc_clh_free(handle);
-		if (ctx != NULL) {
-			smbrdr_ctx_free(ctx);
-		}
+
+		goto errout;
 	}
 
+	return (NT_STATUS_SUCCESS);
+
+errout:
+	ctx = mlrpc_clh_free(handle);
+	if (ctx != NULL) {
+		smbrdr_ctx_free(ctx);
+	}
 	return (status);
 }
 
+DWORD
+ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain,
+    char *username, const char *service)
+{
+	return (ndr_rpc_bind_common(handle, server, domain, username, service,
+	    NULL));
+}
+
+DWORD
+ndr_rpc_bind_secure(mlsvc_handle_t *handle, char *server, char *domain,
+    char *username, const char *service, ndr_auth_ctx_t *auth_ctx)
+{
+	return (ndr_rpc_bind_common(handle, server, domain, username, service,
+	    auth_ctx));
+}
+
 /*
  * Unbind and close the pipe to an RPC service
  * and cleanup the smb_ctx.
--- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_netr.c	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_netr.c	Sat Jan 16 16:59:32 2021 +0000
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
  */
 
 /*
@@ -77,7 +78,12 @@
 void
 netr_initialize(void)
 {
+	uint32_t flags;
+
 	(void) ndr_svc_register(&netr_service);
+
+	flags = smb_get_netlogon_flags();
+	netlogon_init_global(flags);
 }
 
 /*
@@ -156,18 +162,16 @@
 DECL_FIXUP_STRUCT(netr_validation_u);
 DECL_FIXUP_STRUCT(netr_validation_info);
 DECL_FIXUP_STRUCT(netr_SamLogon);
+DECL_FIXUP_STRUCT(netr_SamLogonEx);
 
 /*
- * Patch the netr_SamLogon union.
- * This function is called from mlsvc_netr_ndr.c
+ * Patch the netr_validation_info union.
  */
-void
-fixup_netr_SamLogon(struct netr_SamLogon *arg)
+static unsigned short
+fixup_netr_validation_info(WORD level)
 {
 	unsigned short size1 = 0;
 	unsigned short size2 = 0;
-	unsigned short size3 = 0;
-	WORD level = (WORD)arg->validation_level;
 
 	switch (level) {
 	case 3:
@@ -190,9 +194,43 @@
 	};
 
 	size2 = size1 + (2 * sizeof (DWORD));
-	size3 = size2 + sizeof (ndr_request_hdr_t) + sizeof (DWORD);
 
 	FIXUP_PDU_SIZE(netr_validation_u, size1);
 	FIXUP_PDU_SIZE(netr_validation_info, size2);
+
+	return (size2);
+}
+
+
+/*
+ * Patch the netr_SamLogon union.
+ * This function is called from mlsvc_netr_ndr.c
+ */
+void
+fixup_netr_SamLogon(struct netr_SamLogon *arg)
+{
+	unsigned short size2 = 0;
+	unsigned short size3 = 0;
+
+	size2 = fixup_netr_validation_info(arg->validation_level);
+	/* netr_valid ENC-UNION + hdr + ret_auth PTR + authoritative + status */
+	size3 = size2 + sizeof (ndr_request_hdr_t) + 3 * sizeof (DWORD);
 	FIXUP_PDU_SIZE(netr_SamLogon, size3);
 }
+
+/*
+ * Patch the netr_SamLogonEx union.
+ * This function is called from mlsvc_netr_ndr.c
+ */
+void
+fixup_netr_SamLogonEx(struct netr_SamLogonEx *arg)
+{
+	unsigned short size2 = 0;
+	unsigned short size3 = 0;
+
+	size2 = fixup_netr_validation_info(arg->validation_level);
+	/* netr_valid ENC-UNION + hdr + authoritative + flags + status */
+	size3 = size2 + sizeof (ndr_request_hdr_t) + 3 * sizeof (DWORD);
+
+	FIXUP_PDU_SIZE(netr_SamLogonEx, size3);
+}
--- a/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c	Sat Jan 16 16:59:32 2021 +0000
@@ -21,7 +21,7 @@
 
 /*
  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
  */
 
 /*
@@ -43,6 +43,7 @@
 #include <smbsrv/libsmb.h>
 #include <smbsrv/libsmbns.h>
 #include <smbsrv/libmlsvc.h>
+#include <mlsvc.h>
 #include <smbsrv/ndl/netlogon.ndl>
 #include <smbsrv/smbinfo.h>
 #include <smbsrv/netrauth.h>
@@ -62,7 +63,78 @@
 /*
  * Shared with netr_logon.c
  */
-netr_info_t netr_global_info;
+netr_info_t netr_global_info = {
+	.use_secure_rpc = B_TRUE,
+	.use_logon_ex = B_TRUE
+};
+extern ndr_auth_ctx_t netr_ssp_ctx;
+
+/*
+ * These flags control various parts of NetLogon RPC messages.
+ * The default is 0 - setting a bit disables some feature.
+ * They are set in smbd/netlogon_flags in svc:/network/smb/server.
+ * These are set when smbd starts. Changing them requires
+ * restarting smbd.
+ *
+ * These shouldn't be confused with either SamLogonEx's ExtraFlags,
+ * or NetrServerAuthenticate's negotiate_flags.
+ *
+ * DISABLE_SECURE_RPC causes Netlogon to use unauthenticated RPC.
+ * Note that the underlying transport is still authenticated and signed.
+ *
+ * DISABLE_RESP_VERIF instructs RPC authentication to ignore failures
+ * when verifying responses.
+ *
+ * DISABLE_SAMLOGONEX causes Netlogon to always use SamLogon, which
+ * makes use of Netlogon Authenticators.
+ */
+#define	NETR_CFG_DISABLE_SECURE_RPC	0x00000001
+#define	NETR_CFG_DISABLE_RESP_VERIF	0x00000002
+#define	NETR_CFG_DISABLE_SAMLOGONEX	0x00000004
+
+void
+netlogon_init_global(uint32_t flags)
+{
+	netr_global_info.use_secure_rpc =
+	    ((flags & NETR_CFG_DISABLE_SECURE_RPC) == 0);
+	netr_ssp_ctx.auth_verify_resp =
+	    ((flags & NETR_CFG_DISABLE_RESP_VERIF) == 0);
+	netr_global_info.use_logon_ex =
+	    ((flags & NETR_CFG_DISABLE_SAMLOGONEX) == 0);
+}
+
+/*
+ * AES-CFB8 has the odd property that 1/256 keys will encrypt
+ * a full block of 0s to all 0s. In order to mitigate this, Windows DCs
+ * now reject Challenges and Credentials where "none of the first 5 bytes
+ * are unique" (i.e. [MS-NRPC] 3.1.4.1 "Session-Key Negotiation" Step 7).
+ * This detects that condition so that we can avoid having our connection
+ * rejected unexpectedly.
+ *
+ * I've interpreted this condition as 'amongst the first 5 bytes,
+ * at least one must appear exactly once'.
+ *
+ * NOTE: Win2012r2 seems to only reject challenges whose first 5 bytes are 0.
+ */
+boolean_t
+passes_dc_mitigation(uint8_t *buf)
+{
+	int i, j;
+
+	for (i = 0; i < 5; i++) {
+		for (j = 0; j < 5; j++) {
+			if (i != j && buf[i] == buf[j])
+				break;
+		}
+
+		/* if this byte didn't match any other byte, this passes */
+		if (j == 5)
+			return (B_TRUE);
+	}
+
+	/* None of the bytes were unique - the check fails */
+	return (B_FALSE);
+}
 
 /*
  * netlogon_auth
@@ -79,32 +151,73 @@
  *
  */
 DWORD
-netlogon_auth(char *server, mlsvc_handle_t *netr_handle, DWORD flags)
+netlogon_auth(char *server, char *domain, DWORD flags)
 {
+	mlsvc_handle_t netr_handle;
 	netr_info_t *netr_info;
 	int rc;
 	DWORD leout_rc[2];
+	boolean_t retry;
+	DWORD status;
+
+	/*
+	 * [MS-NRPC] 3.1.4.1 "Session-Key Negotiation"
+	 * Negotiation happens on an 'unprotected RPC channel'
+	 * (no RPC-level auth).
+	 */
+	status = netr_open(server, domain, &netr_handle);
+
+	if (status != 0) {
+		syslog(LOG_ERR, "netlogon_auth remote open failed (%s)",
+		    xlate_nt_status(status));
+		return (status);
+	}
 
 	netr_info = &netr_global_info;
-	bzero(netr_info, sizeof (netr_info_t));
-
-	netr_info->flags |= flags;
+	bzero(&netr_info->session_key, sizeof (netr_info->session_key));
+	netr_info->flags = flags;
 
 	rc = smb_getnetbiosname(netr_info->hostname, NETBIOS_NAME_SZ);
 	if (rc != 0)
-		return (NT_STATUS_UNSUCCESSFUL);
+		goto errout;
 
 	/* server is our DC.  Note: normally an FQDN. */
 	(void) snprintf(netr_info->server, sizeof (netr_info->server),
 	    "\\\\%s", server);
 
-	LE_OUT32(&leout_rc[0], random());
-	LE_OUT32(&leout_rc[1], random());
-	(void) memcpy(&netr_info->client_challenge, leout_rc,
-	    sizeof (struct netr_credential));
+	/*
+	 * Domain (FQDN and NetBIOS) Name needed for Netlogon SSP-based
+	 * Secure RPC.
+	 */
+	rc = smb_getdomainname(netr_info->nb_domain,
+	    sizeof (netr_info->nb_domain));
+	if (rc != 0)
+		goto errout;
+
+	rc = smb_getfqdomainname(netr_info->fqdn_domain,
+	    sizeof (netr_info->fqdn_domain));
+	if (rc != 0)
+		goto errout;
 
-	if ((rc = netr_server_req_challenge(netr_handle, netr_info)) == 0) {
-		rc = netr_server_authenticate2(netr_handle, netr_info);
+	/*
+	 * [MS-NRPC] 3.1.4.1 "Session-Key Negotiation" Step 7
+	 * Windows DCs will reject negotiate attempts if none of the first
+	 * 5 bytes of the Challenge are unique.
+	 * Keep retrying until we've generated one that satisfies this.
+	 */
+	do {
+		retry = B_FALSE;
+		LE_OUT32(&leout_rc[0], arc4random());
+		LE_OUT32(&leout_rc[1], arc4random());
+		(void) memcpy(&netr_info->client_challenge, leout_rc,
+		    sizeof (struct netr_credential));
+
+		if (!passes_dc_mitigation(netr_info->client_challenge.data))
+			retry = B_TRUE;
+	} while (retry);
+
+	if ((rc = netr_server_req_challenge(&netr_handle, netr_info)) == 0) {
+		rc = netr_server_authenticate2(&netr_handle, netr_info);
 		if (rc == 0) {
 			/*
 			 * TODO: (later)  When joining a domain using a
@@ -118,6 +231,9 @@
 		}
 	}
 
+errout:
+	(void) netr_close(&netr_handle);
+
 	return ((rc) ? NT_STATUS_UNSUCCESSFUL : NT_STATUS_SUCCESS);
 }
 
@@ -145,6 +261,34 @@
 	return (status);
 }
 
+uint32_t auth_context_id = 1;
+
+DWORD
+netr_open_secure(char *server, char *domain, mlsvc_handle_t *netr_handle)
+{
+	char user[SMB_USERNAME_MAXLEN];
+	DWORD status;
+
+	smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
+
+	/*
+	 * If the server doesn't support SECURE_RPC_FLAG, or we've disabled
+	 * secure rpc (netr_global_info.use_secure_rpc), then SECURE_RPC_FLAG
+	 * won't be in the set of negotiated flags. Don't use SecureRPC if
+	 * that's the case.
+	 */
+	if ((netr_global_info.nego_flags & NETR_NEGO_SECURE_RPC_FLAG) != 0) {
+		netr_ssp_ctx.auth_context_id = auth_context_id++;
+		status = ndr_rpc_bind_secure(netr_handle, server, domain, user,
+		    "NETR", &netr_ssp_ctx);
+	} else {
+		status = ndr_rpc_bind(netr_handle, server, domain, user,
+		    "NETR");
+	}
+
+	return (status);
+}
+
 /*
  * netr_close
  *
@@ -192,8 +336,9 @@
 }
 
 uint32_t netr_server_auth2_flags =
-    NETR_NEGOTIATE_BASE_FLAGS |
-    NETR_NEGOTIATE_STRONGKEY_FLAG;
+    NETR_NEGO_BASE_FLAGS |
+    NETR_NEGO_STRONGKEY_FLAG |
+    NETR_NEGO_SECURE_RPC_FLAG;
 
 /*
  * netr_server_authenticate2
@@ -222,7 +367,15 @@
 	arg.hostname = (unsigned char *)netr_info->hostname;
 	arg.negotiate_flags = netr_server_auth2_flags;
 
-	if (arg.negotiate_flags & NETR_NEGOTIATE_STRONGKEY_FLAG) {
+	/*
+	 * If we've disabled SecureRPC, remove it from our negotiate_flags
+	 * so that the returned flags don't include it. We won't later use
+	 * SecureRPC if the returned flags don't include the flag.
+	 */
+	if (!netr_global_info.use_secure_rpc)
+		arg.negotiate_flags &= ~NETR_NEGO_SECURE_RPC_FLAG;
+
+	if (arg.negotiate_flags & NETR_NEGO_STRONGKEY_FLAG) {
 		if (netr_gen_skey128(netr_info) != SMBAUTH_SUCCESS)
 			return (-1);
 	} else {
@@ -230,15 +383,22 @@
 			return (-1);
 	}
 
+	/*
+	 * We can't 'fiddle' with anything here to prevent getting bitten by
+	 * ClientStoredCredential-based mitigations.
+	 *
+	 * If we're using SamLogonEx, we won't use authenticators unless
+	 * some other NetLogon command is implemented and used.
+	 */
 	if (netr_gen_credentials(netr_info->session_key.key,
 	    &netr_info->client_challenge, 0,
-	    &netr_info->client_credential) != SMBAUTH_SUCCESS) {
+	    &netr_info->client_credential, B_FALSE) != SMBAUTH_SUCCESS) {
 		return (-1);
 	}
 
 	if (netr_gen_credentials(netr_info->session_key.key,
 	    &netr_info->server_challenge, 0,
-	    &netr_info->server_credential) != SMBAUTH_SUCCESS) {
+	    &netr_info->server_credential, B_FALSE) != SMBAUTH_SUCCESS) {
 		return (-1);
 	}
 
@@ -254,6 +414,9 @@
 		return (-1);
 	}
 
+	/* The server returns the intersection of our flags and their flags. */
+	netr_info->nego_flags = arg.negotiate_flags;
+
 	rc = memcmp(&netr_info->server_credential, &arg.server_credential,
 	    sizeof (struct netr_credential));
 
@@ -450,7 +613,7 @@
  */
 int
 netr_gen_credentials(BYTE *session_key, netr_cred_t *challenge,
-    DWORD timestamp, netr_cred_t *out_cred)
+    DWORD timestamp, netr_cred_t *out_cred, boolean_t retry)
 {
 	unsigned char buffer[8];
 	DWORD data[2];
@@ -472,6 +635,18 @@
 	rc = smb_auth_DES(out_cred->data, 8, &session_key[NETR_DESKEY_LEN],
 	    NETR_DESKEY_LEN, buffer, 8);
 
+	/*
+	 * [MS-NRPC] 3.1.4.6 "Calling Methods Requiring Session-Key
+	 * Establishment" Step 6
+	 *
+	 * Windows DCs will reject authenticators if none of the first
+	 * 5 bytes of the ClientStoredCredential are unique.
+	 * Keep retrying until we've generated one that satisfies this,
+	 * but only if the caller can handle retries.
+	 */
+	if (retry && !passes_dc_mitigation(out_cred->data))
+		return (SMBAUTH_RETRY);
+
 	return (rc);
 }
 
--- a/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c	Sat Jan 16 16:59:32 2021 +0000
@@ -21,7 +21,7 @@
 
 /*
  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2019 Nexenta by DDN, Inc. All rights reserved.
+ * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
  */
 
 /*
@@ -46,7 +46,7 @@
 #include <smbsrv/smb_token.h>
 #include <mlsvc.h>
 
-static uint32_t netlogon_logon(smb_logon_t *, smb_token_t *, smb_domainex_t *);
+uint32_t netlogon_logon(smb_logon_t *, smb_token_t *, smb_domainex_t *);
 static uint32_t netr_server_samlogon(mlsvc_handle_t *, netr_info_t *, char *,
     smb_logon_t *, smb_token_t *);
 static void netr_invalidate_chain(void);
@@ -243,6 +243,56 @@
 	user_info->lg_status = status;
 }
 
+static uint32_t
+netr_get_handle(char *server, char *domain, mlsvc_handle_t *netr_handle)
+{
+	uint32_t status;
+	boolean_t did_renego = B_FALSE;
+
+reauth:
+	if ((netr_global_info.flags & NETR_FLG_VALID) == 0 ||
+	    !smb_match_netlogon_seqnum()) {
+		/*
+		 * This does netr_server_req_challenge() and
+		 * netr_server_authenticate2(), updating the
+		 * current netlogon sequence number.
+		 */
+		status = netlogon_auth(server, domain, NETR_FLG_NULL);
+
+		if (status != 0) {
+			syslog(LOG_ERR, "%s: auth failed (%s)",
+			    __func__, xlate_nt_status(status));
+			return (status);
+		}
+
+		netr_global_info.flags |= NETR_FLG_VALID;
+	}
+
+	/*
+	 * This netr_open_secure call does the work to connect to the DC,
+	 * get the IPC share, open the named pipe, RPC bind, etc.
+	 */
+	status = netr_open_secure(server, domain, netr_handle);
+	if (status != 0) {
+		/*
+		 * This may have failed because the DC restarted.
+		 * Re-negotiate once.
+		 */
+		if (!did_renego) {
+			did_renego = B_TRUE;
+			netr_invalidate_chain();
+			syslog(LOG_ERR, "%s: open failed (%s); "
+			    "renegotiating...",
+			    __func__, xlate_nt_status(status));
+			goto reauth;
+		}
+		syslog(LOG_ERR, "%s: open failed (%s)",
+		    __func__, xlate_nt_status(status));
+	}
+
+	return (status);
+}
+
 /*
  * Run a netr_server_samlogon call, dealing with the possible need to
  * re-establish the NetLogon credential chain.  If that fails, return
@@ -251,7 +301,7 @@
  * netr_server_samlogon() call including the many possibilities listed
  * above that function.
  */
-static uint32_t
+uint32_t
 netlogon_logon(smb_logon_t *user_info, smb_token_t *token, smb_domainex_t *di)
 {
 	char server[MAXHOSTNAMELEN];
@@ -259,18 +309,6 @@
 	uint32_t status;
 	boolean_t did_reauth = B_FALSE;
 
-	/*
-	 * This netr_open call does the work to connect to the DC,
-	 * get the IPC share, open the named pipe, RPC bind, etc.
-	 */
-	status = netr_open(di->d_dci.dc_name, di->d_primary.di_nbname,
-	    &netr_handle);
-	if (status != 0) {
-		syslog(LOG_ERR, "netlogon remote open failed (%s)",
-		    xlate_nt_status(status));
-		return (status);
-	}
-
 	if (di->d_dci.dc_name[0] != '\0' &&
 	    (*netr_global_info.server != '\0')) {
 		(void) snprintf(server, sizeof (server),
@@ -281,24 +319,13 @@
 	}
 
 reauth:
-	if ((netr_global_info.flags & NETR_FLG_VALID) == 0 ||
-	    !smb_match_netlogon_seqnum()) {
-		/*
-		 * This does netr_server_req_challenge() and
-		 * netr_server_authenticate2(), updating the
-		 * current netlogon sequence number.
-		 */
-		status = netlogon_auth(di->d_dci.dc_name, &netr_handle,
-		    NETR_FLG_NULL);
+	status = netr_get_handle(di->d_dci.dc_name,
+	    di->d_primary.di_nbname, &netr_handle);
 
-		if (status != 0) {
-			syslog(LOG_ERR, "netlogon remote auth failed (%s)",
-			    xlate_nt_status(status));
-			(void) netr_close(&netr_handle);
-			return (NT_STATUS_DOMAIN_TRUST_INCONSISTENT);
-		}
-
-		netr_global_info.flags |= NETR_FLG_VALID;
+	if (status != 0) {
+		syslog(LOG_ERR, "%s: failed to get handle (%s)",
+		    __func__, xlate_nt_status(status));
+		return (NT_STATUS_DOMAIN_TRUST_INCONSISTENT);
 	}
 
 	status = netr_server_samlogon(&netr_handle,
@@ -307,6 +334,7 @@
 	if (status == NT_STATUS_INSUFFICIENT_LOGON_INFO) {
 		if (!did_reauth) {
 			/* Call netlogon_auth() again, just once. */
+			(void) netr_close(&netr_handle);
 			did_reauth = B_TRUE;
 			goto reauth;
 		}
@@ -339,38 +367,16 @@
 	(void) mutex_unlock(&netlogon_mutex);
 
 	/*
-	 * This section like netlogon_logon(), but only does
-	 * one pass and no netr_server_samlogon call.
+	 * Like netlogon_logon(), but no netr_server_samlogon call.
+	 * We're just making sure we can connect to the NETLOGON server.
 	 */
-
-	status = netr_open(server, domain,
-	    &netr_handle);
-	if (status != 0) {
-		syslog(LOG_ERR, "netlogon remote open failed (%s)",
-		    xlate_nt_status(status));
-		goto unlock_out;
-	}
+	status = netr_get_handle(server, domain, &netr_handle);
+	if (status == 0)
+		(void) netr_close(&netr_handle);
+	else
+		syslog(LOG_ERR, "%s: failed to get handle (%s)",
+		    __func__, xlate_nt_status(status));
 
-	if ((netr_global_info.flags & NETR_FLG_VALID) == 0 ||
-	    !smb_match_netlogon_seqnum()) {
-		/*
-		 * This does netr_server_req_challenge() and
-		 * netr_server_authenticate2(), updating the
-		 * current netlogon sequence number.
-		 */
-		status = netlogon_auth(server, &netr_handle,
-		    NETR_FLG_NULL);
-		if (status != 0) {
-			syslog(LOG_ERR, "netlogon remote auth failed (%s)",
-			    xlate_nt_status(status));
-		} else {
-			netr_global_info.flags |= NETR_FLG_VALID;
-		}
-	}
-
-	(void) netr_close(&netr_handle);
-
-unlock_out:
 	(void) mutex_lock(&netlogon_mutex);
 	netlogon_busy = B_FALSE;
 	(void) cond_signal(&netlogon_cv);
@@ -477,34 +483,36 @@
 netr_server_samlogon(mlsvc_handle_t *netr_handle, netr_info_t *netr_info,
     char *server, smb_logon_t *user_info, smb_token_t *token)
 {
-	struct netr_SamLogon arg;
+	struct netr_SamLogon logon_op;
+	struct netr_SamLogonEx logon_ex_op;
 	struct netr_authenticator auth;
 	struct netr_authenticator ret_auth;
 	struct netr_logon_info1 info1;
 	struct netr_logon_info2 info2;
 	struct netr_validation_info3 *info3;
+	union netr_validation_u *valid_info;
+	union netr_logon_info_u *logon_info;
+	LPTSTR servername, hostname;
 	ndr_heap_t *heap;
 	int opnum;
 	int rc, len;
-	uint32_t status;
-
-	bzero(&arg, sizeof (struct netr_SamLogon));
-	opnum = NETR_OPNUM_SamLogon;
+	uint32_t status, *rpc_status;
+	void *rpc_arg;
 
 	/*
 	 * Should we get the server and hostname from netr_info?
 	 */
 
 	len = strlen(server) + 4;
-	arg.servername = ndr_rpc_malloc(netr_handle, len);
-	arg.hostname = ndr_rpc_malloc(netr_handle, NETBIOS_NAME_SZ);
-	if (arg.servername == NULL || arg.hostname == NULL) {
+	servername = ndr_rpc_malloc(netr_handle, len);
+	hostname = ndr_rpc_malloc(netr_handle, NETBIOS_NAME_SZ);
+	if (servername == NULL || hostname == NULL) {
 		ndr_rpc_release(netr_handle);
 		return (NT_STATUS_INTERNAL_ERROR);
 	}
 
-	(void) snprintf((char *)arg.servername, len, "\\\\%s", server);
-	if (smb_getnetbiosname((char *)arg.hostname, NETBIOS_NAME_SZ) != 0) {
+	(void) snprintf((char *)servername, len, "\\\\%s", server);
+	if (smb_getnetbiosname((char *)hostname, NETBIOS_NAME_SZ) != 0) {
 		ndr_rpc_release(netr_handle);
 		return (NT_STATUS_INTERNAL_ERROR);
 	}
@@ -515,19 +523,49 @@
 		return (NT_STATUS_INTERNAL_ERROR);
 	}
 
-	arg.auth = &auth;
-	arg.ret_auth = &ret_auth;
-	arg.validation_level = NETR_VALIDATION_LEVEL3;
-	arg.logon_info.logon_level = user_info->lg_level;
-	arg.logon_info.switch_value = user_info->lg_level;
-
+	/*
+	 * If we use Secure RPC, we can use SamLogonEx instead of SamLogon.
+	 * SamLogonEx doesn't use NetLogon authenticators, instead relying
+	 * on Secure RPC to provide security.
+	 * This allows us to avoid being bitten by mitigations in
+	 * the authenticator verification logic on DCs.
+	 */
+	if (netr_info->use_logon_ex &&
+	    (netr_info->nego_flags & NETR_NEGO_SECURE_RPC_FLAG) != 0) {
+		bzero(&logon_ex_op, sizeof (struct netr_SamLogonEx));
+		logon_ex_op.servername = servername;
+		logon_ex_op.hostname = hostname;
+		logon_ex_op.logon_info.logon_level = user_info->lg_level;
+		logon_ex_op.logon_info.switch_value = user_info->lg_level;
+		logon_ex_op.validation_level = NETR_VALIDATION_LEVEL3;
+		logon_ex_op.extra_flags = 0;
+		logon_info = &logon_ex_op.logon_info.ru;
+		valid_info = &logon_ex_op.ru;
+		rpc_status = &logon_ex_op.status;
+		rpc_arg = &logon_ex_op;
+		opnum = NETR_OPNUM_SamLogonEx;
+	} else {
+		bzero(&logon_op, sizeof (struct netr_SamLogon));
+		logon_op.servername = servername;
+		logon_op.hostname = hostname;
+		logon_op.auth = &auth;
+		logon_op.ret_auth = &ret_auth;
+		logon_op.logon_info.logon_level = user_info->lg_level;
+		logon_op.logon_info.switch_value = user_info->lg_level;
+		logon_op.validation_level = NETR_VALIDATION_LEVEL3;
+		logon_info = &logon_op.logon_info.ru;
+		valid_info = &logon_op.ru;
+		rpc_status = &logon_op.status;
+		rpc_arg = &logon_op;
+		opnum = NETR_OPNUM_SamLogon;
+	}
 	heap = ndr_rpc_get_heap(netr_handle);
 
 	switch (user_info->lg_level) {
 	case NETR_INTERACTIVE_LOGON:
 		netr_setup_identity(heap, user_info, &info1.identity);
 		netr_interactive_samlogon(netr_info, user_info, &info1);
-		arg.logon_info.ru.info1 = &info1;
+		logon_info->info1 = &info1;
 		break;
 
 	case NETR_NETWORK_LOGON:
@@ -538,7 +576,7 @@
 		}
 		netr_setup_identity(heap, user_info, &info2.identity);
 		netr_network_samlogon(heap, netr_info, user_info, &info2);
-		arg.logon_info.ru.info2 = &info2;
+		logon_info->info2 = &info2;
 		break;
 
 	default:
@@ -546,12 +584,12 @@
 		return (NT_STATUS_INVALID_PARAMETER);
 	}
 
-	rc = ndr_rpc_call(netr_handle, opnum, &arg);
+	rc = ndr_rpc_call(netr_handle, opnum, rpc_arg);
 	if (rc != 0) {
 		bzero(netr_info, sizeof (netr_info_t));
 		status = NT_STATUS_INVALID_PARAMETER;
-	} else if (arg.status != 0) {
-		status = NT_SC_VALUE(arg.status);
+	} else if (*rpc_status != 0) {
+		status = NT_SC_VALUE(*rpc_status);
 
 		/*
 		 * We need to validate the chain even though we have
@@ -559,16 +597,23 @@
 		 * this will trigger a new credential chain. However,
 		 * a valid credential is returned with some status
 		 * codes; for example, WRONG_PASSWORD.
+		 *
+		 * SamLogonEx doesn't use authenticators - nothing to validate.
 		 */
-		(void) netr_validate_chain(netr_info, arg.ret_auth);
+		if (rpc_arg == &logon_op)
+			(void) netr_validate_chain(netr_info,
+			    logon_op.ret_auth);
 	} else {
-		status = netr_validate_chain(netr_info, arg.ret_auth);
-		if (status == NT_STATUS_INSUFFICIENT_LOGON_INFO) {
-			ndr_rpc_release(netr_handle);
-			return (status);
+		if (rpc_arg == &logon_op) {
+			status = netr_validate_chain(netr_info,
+			    logon_op.ret_auth);
+			if (status == NT_STATUS_INSUFFICIENT_LOGON_INFO) {
+				ndr_rpc_release(netr_handle);
+				return (status);
+			}
 		}
 
-		info3 = arg.ru.info3;
+		info3 = valid_info->info3;
 		status = netr_setup_token(info3, user_info, netr_info, token);
 	}
 
@@ -664,16 +709,24 @@
 netr_setup_authenticator(netr_info_t *netr_info,
     struct netr_authenticator *auth, struct netr_authenticator *ret_auth)
 {
+	int rc;
 	bzero(auth, sizeof (struct netr_authenticator));
 
-	netr_info->timestamp = time(0);
-	auth->timestamp = netr_info->timestamp;
-
-	if (netr_gen_credentials(netr_info->session_key.key,
-	    &netr_info->client_credential,
-	    netr_info->timestamp,
-	    (netr_cred_t *)&auth->credential) != SMBAUTH_SUCCESS)
-		return (SMBAUTH_FAILURE);
+	/*
+	 * Windows DCs will reject Authenticators if none of the first
+	 * 5 bytes of the ClientStoredCredential are unique.
+	 * Keep retrying until we've generated one that satisfies this.
+	 */
+	netr_info->timestamp = time(0) - 1;
+	do {
+		auth->timestamp = ++netr_info->timestamp;
+		rc = netr_gen_credentials(netr_info->session_key.key,
+		    &netr_info->client_credential,
+		    netr_info->timestamp,
+		    (netr_cred_t *)&auth->credential, B_TRUE);
+		if (rc != SMBAUTH_SUCCESS && rc != SMBAUTH_RETRY)
+			return (SMBAUTH_FAILURE);
+	} while (rc == SMBAUTH_RETRY);
 
 	if (ret_auth) {
 		bzero(ret_auth, sizeof (struct netr_authenticator));
@@ -713,7 +766,7 @@
 
 	if (netr_gen_credentials(netr_info->session_key.key,
 	    &netr_info->client_credential,
-	    netr_info->timestamp, &cred) != SMBAUTH_SUCCESS)
+	    netr_info->timestamp, &cred, B_FALSE) != SMBAUTH_SUCCESS)
 		return (NT_STATUS_INTERNAL_ERROR);
 
 	if (&auth->credential == 0) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/smbsrv/libmlsvc/common/netr_ssp.c	Sat Jan 16 16:59:32 2021 +0000
@@ -0,0 +1,494 @@
+/*
+ * 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 <sys/md5.h>
+#include <strings.h>
+#include <stdio.h>
+#include <smbsrv/netrauth.h>
+#include <smbsrv/string.h>
+#include <smbsrv/libsmb.h>
+#include <libmlsvc.h>
+#include <resolv.h>
+
+/*
+ * NETLOGON SSP for "Secure RPC" works as follows:
+ * 1. The client binds to the DC without RPC-level authentication.
+ * 2. The client and server negotiate a Session Key using a client
+ * and server challenge, plus a shared secret (the machine password).
+ * This happens via NetrServerReqChallenge and NetrServerAuthenticate.
+ * The key is bound to a particular Computer/Server Name pair.
+ * 3. The client then establishes a new bind (or alters its existing one),
+ * this time requesting the NETLOGON provider for RPC-level authentication.
+ * The server uses the Computer and Domain names provided in the
+ * authentication token in the bind request in order to find
+ * the previously-negotiated Session Key (and rejects the bind if none
+ * exists).
+ * 4. The client and server then use this Session Key to provide
+ * integrity and/or confidentiality to future NETLOGON RPC messages.
+ *
+ * The functions in this file implement the NETLOGON SSP, as defined in
+ * [MS-NRPC] 3.3 "Netlogon as a Security Support Provider".
+ *
+ * Session Key negotiation is implemented in netr_auth.c.
+ * It is the same key that is used for generating NETLOGON credentials.
+ */
+
+enum nl_token_type {
+	NL_AUTH_REQUEST = 0x00000000,
+	NL_AUTH_RESPONSE = 0x00000001
+};
+
+/*
+ * DOMAIN = domain name
+ * COMPUTER = client computer name
+ * HOST = client host name
+ *
+ * NB = NetBios format
+ * DNS = FQDN
+ *
+ * OEM = OEM_STRING
+ * COMPRESSED = Compressed UTF-8 string
+ *
+ * Each of these is NULL-terminated, and delinated by such.
+ * They are always found in this order, when specified.
+ *
+ * We currently use everything but NL_HOST_DNS_COMPRESSED_FLAG.
+ */
+#define	NL_DOMAIN_NB_OEM_FLAG		0x00000001
+#define	NL_COMPUTER_NB_OEM_FLAG		0x00000002
+#define	NL_DOMAIN_DNS_COMPRESSED_FLAG	0x00000004
+#define	NL_HOST_DNS_COMPRESSED_FLAG	0x00000008
+#define	NL_COMPUTER_NB_COMPRESSED_FLAG	0x00000010
+
+#define	NL_DOMAIN_FLAGS			\
+	(NL_DOMAIN_NB_OEM_FLAG|NL_DOMAIN_DNS_COMPRESSED_FLAG)
+#define	NL_COMPUTER_FLAGS		\
+	(NL_COMPUTER_NB_OEM_FLAG|		\
+	NL_HOST_DNS_COMPRESSED_FLAG|		\
+	NL_COMPUTER_NB_COMPRESSED_FLAG)
+
+#define	MD_DIGEST_LEN 16
+
+/* These structures are OPAQUE at the RPC level - not marshalled. */
+typedef struct nl_auth_message {
+	uint32_t nam_type;
+	uint32_t nam_flags;
+	uchar_t nam_str[1];
+} nl_auth_message_t;
+
+/*
+ * The size of this structure is used for space accounting.
+ * The confounder is not present on the wire unless confidentiality
+ * has been negotiated. If we ever support confidentiality,
+ * we'll need to adjust space accounting based on whether
+ * the confounder is needed.
+ */
+typedef struct nl_auth_sig {
+	uint16_t nas_sig_alg;
+	uint16_t nas_seal_alg;
+	uint16_t nas_pad;
+	uint16_t nas_flags;
+	uchar_t nas_seqnum[8];
+	uchar_t nas_sig[8];
+	/* uchar_t nas_confounder[8]; */ /* only for encryption */
+} nl_auth_sig_t;
+
+void
+netr_show_msg(nl_auth_message_t *nam, ndr_stream_t *nds)
+{
+	ndo_printf(nds, NULL, "nl_auth_message: type=0x%x flags=0x%x");
+}
+
+void
+netr_show_sig(nl_auth_sig_t *nas, ndr_stream_t *nds)
+{
+	ndo_printf(nds, NULL, "nl_auth_sig: SignatureAlg=0x%x SealAlg=0x%x "
+	    "pad=0x%x flags=0x%x SequenceNumber=%llu Signature=0x%x",
+	    nas->nas_sig_alg, nas->nas_seal_alg, nas->nas_pad,
+	    nas->nas_flags, *(uint64_t *)nas->nas_seqnum,
+	    *(uint64_t *)nas->nas_sig);
+}
+
+/*
+ * NETLOGON SSP gss_init_sec_context equivalent
+ * [MS-RPCE] 3.3.4.1.1 "Generating an Initial NL_AUTH_MESSAGE"
+ *
+ * We need to encode at least one Computer name and at least one
+ * Domain name. The server uses this to find the Session Key
+ * negotiated earlier between this client and server.
+ *
+ * We attempt to provide NL_DOMAIN_NB_OEM_FLAG, NL_COMPUTER_NB_OEM_FLAG,
+ * NL_DOMAIN_DNS_COMPRESSED_FLAG, and NL_COMPUTER_NB_COMPRESSED_FLAG.
+ *
+ * See the above comments for how these are encoded.
+ */
+int
+netr_ssp_init(void *arg, ndr_xa_t *mxa)
+{
+	netr_info_t *auth = arg;
+	ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
+	nl_auth_message_t *nam;
+	size_t domain_len, comp_len, len;
+	int slen;
+	uchar_t *dnptrs[3], **dnlastptr;
+
+	domain_len = smb_sbequiv_strlen(auth->nb_domain);
+	comp_len = smb_sbequiv_strlen(auth->hostname);
+
+	/*
+	 * Need to allocate length for two OEM_STRINGs + NULL bytes, plus space
+	 * sufficient for two NULL-terminated compressed UTF-8 strings.
+	 * For the UTF-8 strings, use 2*len as a heuristic.
+	 */
+	len = domain_len + 1 + comp_len + 1 +
+	    strlen(auth->hostname) * 2 + strlen(auth->server) * 2;
+
+	hdr->auth_length = 0;
+
+	nam = NDR_MALLOC(mxa, len);
+	if (nam == NULL)
+		return (NDR_DRC_FAULT_SEC_OUT_OF_MEMORY);
+
+	nam->nam_type = NL_AUTH_REQUEST;
+	nam->nam_flags = 0;
+
+	if (domain_len != -1) {
+		slen = smb_mbstooem(nam->nam_str, auth->nb_domain, domain_len);
+		if (slen >= 0) {
+			hdr->auth_length += slen + 1;
+			nam->nam_str[hdr->auth_length - 1] = '\0';
+			nam->nam_flags |= NL_DOMAIN_NB_OEM_FLAG;
+		}
+	}
+
+	if (comp_len != -1) {
+		slen = smb_mbstooem(nam->nam_str + hdr->auth_length,
+		    auth->hostname, comp_len);
+		if (slen >= 0) {
+			hdr->auth_length += slen + 1;
+			nam->nam_str[hdr->auth_length - 1] = '\0';
+			nam->nam_flags |= NL_COMPUTER_NB_OEM_FLAG;
+		}
+	}
+
+	dnptrs[0] = NULL;
+	dnlastptr = &dnptrs[sizeof (dnptrs) / sizeof (dnptrs[0])];
+
+	slen = dn_comp(auth->fqdn_domain, nam->nam_str + hdr->auth_length,
+	    len - hdr->auth_length, dnptrs, dnlastptr);
+
+	if (slen >= 0) {
+		hdr->auth_length += slen;
+		nam->nam_str[hdr->auth_length] = '\0';
+		nam->nam_flags |= NL_DOMAIN_DNS_COMPRESSED_FLAG;
+	}
+
+	slen = dn_comp(auth->hostname, nam->nam_str + hdr->auth_length,
+	    len - hdr->auth_length, dnptrs, dnlastptr);
+	if (slen >= 0) {
+		hdr->auth_length += slen;
+		nam->nam_str[hdr->auth_length] = '\0';
+		nam->nam_flags |= NL_COMPUTER_NB_COMPRESSED_FLAG;
+	}
+
+	/* We must provide at least one Domain Name and Computer Name */
+	if ((nam->nam_flags & NL_DOMAIN_FLAGS) == 0 ||
+	    (nam->nam_flags & NL_COMPUTER_FLAGS) == 0)
+		return (NDR_DRC_FAULT_SEC_ENCODE_FAILED);
+
+	mxa->send_auth.auth_value = (void *)nam;
+	hdr->auth_length += sizeof (nam->nam_flags) + sizeof (nam->nam_type);
+
+	return (0);
+}
+
+/*
+ * NETLOGON SSP response-side gss_init_sec_context equivalent
+ * [MS-RPCE] 3.3.4.1.4 "Receiving a Return NL_AUTH_MESSAGE"
+ */
+int
+netr_ssp_recv(void *arg, ndr_xa_t *mxa)
+{
+	netr_info_t *auth = arg;
+	ndr_common_header_t *ahdr = &mxa->recv_hdr.common_hdr;
+	ndr_sec_t *ack_secp = &mxa->recv_auth;
+	nl_auth_message_t *nam;
+	int rc;
+
+	nam = (nl_auth_message_t *)ack_secp->auth_value;
+
+	/* We only need to verify the length ("at least 12") and the type */
+	if (ahdr->auth_length < 12) {
+		rc = NDR_DRC_FAULT_SEC_AUTH_LENGTH_INVALID;
+		goto errout;
+	}
+	if (nam->nam_type != NL_AUTH_RESPONSE) {
+		rc = NDR_DRC_FAULT_SEC_META_INVALID;
+		goto errout;
+	}
+	auth->clh_seqnum = 0;
+
+	return (NDR_DRC_OK);
+
+errout:
+	netr_show_msg(nam, &mxa->recv_nds);
+	return (rc);
+}
+
+/* returns byte N of seqnum */
+#define	CLS_BYTE(n, seqnum) ((seqnum >> (8 * (n))) & 0xff)
+
+/*
+ * NETLOGON SSP gss_MICEx equivalent
+ * [MS-RPCE] 3.3.4.2.1 "Generating a Client Netlogon Signature Token"
+ *
+ * Set up the metadata, encrypt and increment the SequenceNumber,
+ * and sign the PDU body.
+ */
+int
+netr_ssp_sign(void *arg, ndr_xa_t *mxa)
+{
+	uint32_t zeroes = 0;
+	netr_info_t *auth = arg;
+	ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
+	ndr_stream_t *nds = &mxa->send_nds;
+	nl_auth_sig_t *nas;
+	MD5_CTX md5h;
+	BYTE local_sig[MD_DIGEST_LEN];
+	BYTE enc_key[MD_DIGEST_LEN];
+
+	hdr->auth_length = sizeof (nl_auth_sig_t);
+
+	nas = NDR_MALLOC(mxa, hdr->auth_length);
+	if (nas == NULL)
+		return (NDR_DRC_FAULT_SEC_OUT_OF_MEMORY);
+
+	/*
+	 * SignatureAlgorithm is first byte 0x77, second byte 00 for HMAC-MD5
+	 * or 0x13, 0x00 for AES-HMAC-SHA256.
+	 *
+	 * SealAlgorithm is first byte 0x7A, second byte 00 for RC4
+	 * or 0x1A, 0x00 for AES-CFB8, or 0xffff for No Sealing.
+	 *
+	 * Pad is always 0xffff, and flags is always 0x0000.
+	 *
+	 * SequenceNumber is a computed, encrypted, 64-bit number.
+	 *
+	 * Each of these is always encoded in little-endian order.
+	 */
+	nas->nas_sig_alg = 0x0077;
+	nas->nas_seal_alg = 0xffff;
+	nas->nas_pad = 0xffff;
+	nas->nas_flags = 0;
+
+	/*
+	 * Calculate the SequenceNumber.
+	 * Note that byte 4 gets modified, as per the spec -
+	 * It's the only byte that is not just set to some other byte.
+	 */
+	nas->nas_seqnum[0] = CLS_BYTE(3, auth->clh_seqnum);
+	nas->nas_seqnum[1] = CLS_BYTE(2, auth->clh_seqnum);
+	nas->nas_seqnum[2] = CLS_BYTE(1, auth->clh_seqnum);
+	nas->nas_seqnum[3] = CLS_BYTE(0, auth->clh_seqnum);
+	nas->nas_seqnum[4] = CLS_BYTE(7, auth->clh_seqnum) | 0x80;
+	nas->nas_seqnum[5] = CLS_BYTE(6, auth->clh_seqnum);
+	nas->nas_seqnum[6] = CLS_BYTE(5, auth->clh_seqnum);
+	nas->nas_seqnum[7] = CLS_BYTE(4, auth->clh_seqnum);
+
+	auth->clh_seqnum++;
+
+	/*
+	 * The HMAC-MD5 signature is computed as follows:
+	 * First 8 bytes of
+	 * HMAC_MD5(
+	 *	MD5(0x00000000 | sig_alg | seal_alg | pad | flags | PDU body),
+	 *	session_key)
+	 */
+	MD5Init(&md5h);
+	MD5Update(&md5h, (uchar_t *)&zeroes, 4);
+	MD5Update(&md5h, (uchar_t *)nas, 8);
+	MD5Update(&md5h,
+	    (uchar_t *)nds->pdu_base_addr + nds->pdu_body_offset,
+	    nds->pdu_body_size);
+
+	MD5Final(local_sig, &md5h);
+	if (smb_auth_hmac_md5(local_sig, sizeof (local_sig),
+	    auth->session_key.key, auth->session_key.len,
+	    local_sig) != 0)
+		return (NDR_DRC_FAULT_SEC_SSP_FAILED);
+
+	bcopy(local_sig, nas->nas_sig, 8);
+
+	/*
+	 * Encrypt the SequenceNumber.
+	 * For RC4 Encryption, the EncryptionKey is computed as follows:
+	 * HMAC_MD5(signature, HMAC_MD5(0x00000000, session_key))
+	 */
+	if (smb_auth_hmac_md5((uchar_t *)&zeroes, 4,
+	    auth->session_key.key, auth->session_key.len,
+	    enc_key) != 0)
+		return (NDR_DRC_FAULT_SEC_SSP_FAILED);
+	if (smb_auth_hmac_md5((uchar_t *)nas->nas_sig, sizeof (nas->nas_sig),
+	    enc_key, sizeof (enc_key),
+	    enc_key) != 0)
+		return (NDR_DRC_FAULT_SEC_SSP_FAILED);
+
+	if (smb_auth_RC4(nas->nas_seqnum, sizeof (nas->nas_seqnum),
+	    enc_key, sizeof (enc_key),
+	    nas->nas_seqnum, sizeof (nas->nas_seqnum)) != 0)
+		return (NDR_DRC_FAULT_SEC_SSP_FAILED);
+
+	mxa->send_auth.auth_value = (void *)nas;
+
+	return (NDR_DRC_OK);
+}
+
+/*
+ * NETLOGON SSP gss_VerifyMICEx equivalent
+ * [MS-RPCE] 3.3.4.2.4 "Receiving a Server Netlogon Signature Token"
+ *
+ * Verify the metadata, decrypt, verify, and increment the SequenceNumber,
+ * and validate the PDU body against the provided signature.
+ */
+int
+netr_ssp_verify(void *arg, ndr_xa_t *mxa, boolean_t verify_resp)
+{
+	uint32_t zeroes = 0;
+	netr_info_t *auth = arg;
+	ndr_sec_t *secp = &mxa->recv_auth;
+	ndr_stream_t *nds = &mxa->recv_nds;
+	nl_auth_sig_t *nas;
+	MD5_CTX md5h;
+	BYTE local_sig[MD_DIGEST_LEN];
+	BYTE dec_key[MD_DIGEST_LEN];
+	BYTE local_seqnum[8];
+	int rc;
+	boolean_t seqnum_bumped = B_FALSE;
+
+	nas = (nl_auth_sig_t *)secp->auth_value;
+
+	/*
+	 * Verify SignatureAlgorithm, SealAlgorithm, and Pad are as expected.
+	 * These follow the same values as in the Client Signature.
+	 */
+	if (nas->nas_sig_alg != 0x0077 ||
+	    nas->nas_seal_alg != 0xffff ||
+	    nas->nas_pad != 0xffff) {
+		rc = NDR_DRC_FAULT_SEC_META_INVALID;
+		goto errout;
+	}
+
+	/* Decrypt the SequenceNumber. This is done the same as the Client. */
+	if (smb_auth_hmac_md5((uchar_t *)&zeroes, 4,
+	    auth->session_key.key, auth->session_key.len,
+	    dec_key) != 0) {
+		rc = NDR_DRC_FAULT_SEC_SSP_FAILED;
+		goto errout;
+	}
+	if (smb_auth_hmac_md5((uchar_t *)nas->nas_sig, sizeof (nas->nas_sig),
+	    dec_key, sizeof (dec_key),
+	    dec_key) != 0) {
+		rc = NDR_DRC_FAULT_SEC_SSP_FAILED;
+		goto errout;
+	}
+
+	if (smb_auth_RC4(nas->nas_seqnum, sizeof (nas->nas_seqnum),
+	    dec_key, sizeof (dec_key),
+	    nas->nas_seqnum, sizeof (nas->nas_seqnum)) != 0) {
+		rc = NDR_DRC_FAULT_SEC_SSP_FAILED;
+		goto errout;
+	}
+
+	/*
+	 * Calculate a local version of the SequenceNumber.
+	 * Note that byte 4 does NOT get modified, unlike the client.
+	 */
+	local_seqnum[0] = CLS_BYTE(3, auth->clh_seqnum);
+	local_seqnum[1] = CLS_BYTE(2, auth->clh_seqnum);
+	local_seqnum[2] = CLS_BYTE(1, auth->clh_seqnum);
+	local_seqnum[3] = CLS_BYTE(0, auth->clh_seqnum);
+	local_seqnum[4] = CLS_BYTE(7, auth->clh_seqnum);
+	local_seqnum[5] = CLS_BYTE(6, auth->clh_seqnum);
+	local_seqnum[6] = CLS_BYTE(5, auth->clh_seqnum);
+	local_seqnum[7] = CLS_BYTE(4, auth->clh_seqnum);
+
+	/* If the SequenceNumbers don't match, this is out of order - drop it */
+	if (bcmp(local_seqnum, nas->nas_seqnum, sizeof (local_seqnum)) != 0) {
+		ndo_printf(nds, NULL, "CalculatedSeqnum: %llu "
+		    "DecryptedSeqnum: %llu",
+		    *(uint64_t *)local_seqnum, *(uint64_t *)nas->nas_seqnum);
+		rc = NDR_DRC_FAULT_SEC_SEQNUM_INVALID;
+		goto errout;
+	}
+
+	auth->clh_seqnum++;
+	seqnum_bumped = B_TRUE;
+
+	/*
+	 * Calculate the signature.
+	 * This is done the same as the Client.
+	 */
+	MD5Init(&md5h);
+	MD5Update(&md5h, (uchar_t *)&zeroes, 4);
+	MD5Update(&md5h, (uchar_t *)nas, 8);
+	MD5Update(&md5h,
+	    (uchar_t *)nds->pdu_base_addr + nds->pdu_body_offset,
+	    nds->pdu_body_size);
+	MD5Final(local_sig, &md5h);
+	if (smb_auth_hmac_md5(local_sig, sizeof (local_sig),
+	    auth->session_key.key, auth->session_key.len,
+	    local_sig) != 0) {
+		rc = NDR_DRC_FAULT_SEC_SSP_FAILED;
+		goto errout;
+	}
+
+	/* If the first 8 bytes don't match, drop it */
+	if (bcmp(local_sig, nas->nas_sig, 8) != 0) {
+		ndo_printf(nds, NULL, "CalculatedSig: %llu "
+		    "PacketSig: %llu",
+		    *(uint64_t *)local_sig, *(uint64_t *)nas->nas_sig);
+		rc = NDR_DRC_FAULT_SEC_SIG_INVALID;
+		goto errout;
+	}
+
+	return (NDR_DRC_OK);
+
+errout:
+	netr_show_sig(nas, &mxa->recv_nds);
+
+	if (!verify_resp) {
+		if (!seqnum_bumped)
+			auth->clh_seqnum++;
+		return (NDR_DRC_OK);
+	}
+
+	return (rc);
+}
+
+extern struct netr_info netr_global_info;
+
+ndr_auth_ctx_t netr_ssp_ctx = {
+	.auth_ops = {
+		.nao_init = netr_ssp_init,
+		.nao_recv = netr_ssp_recv,
+		.nao_sign = netr_ssp_sign,
+		.nao_verify = netr_ssp_verify
+	},
+	.auth_ctx = &netr_global_info,
+	.auth_context_id = 0,
+	.auth_type = NDR_C_AUTHN_GSS_NETLOGON,
+	.auth_level = NDR_C_AUTHN_LEVEL_PKT_INTEGRITY,
+	.auth_verify_resp = B_TRUE
+};
--- a/usr/src/lib/smbsrv/libsmb/common/libsmb.h	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/lib/smbsrv/libsmb/common/libsmb.h	Sat Jan 16 16:59:32 2021 +0000
@@ -21,7 +21,7 @@
 
 /*
  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2019 Nexenta by DDN, Inc. All rights reserved.
+ * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
  * Copyright 2020 RackTop Systems, Inc.
  */
 
@@ -162,6 +162,7 @@
 	SMB_CI_MIN_PROTOCOL,
 	SMB_CI_BYPASS_TRAVERSE_CHECKING,
 	SMB_CI_ENCRYPT_CIPHER,
+	SMB_CI_NETLOGON_FLAGS,
 
 	SMB_CI_MAX
 } smb_cfg_id_t;
@@ -320,6 +321,8 @@
 
 extern int smb_getnameinfo(smb_inaddr_t *, char *, int, int);
 
+extern uint32_t smb_get_netlogon_flags(void);
+
 void smb_trace(const char *s);
 void smb_tracef(const char *fmt, ...);
 
@@ -340,6 +343,7 @@
 #define	SMBAUTH_SESSION_KEY_SZ	SMBAUTH_HASH_SZ
 #define	SMBAUTH_HEXHASH_SZ	(SMBAUTH_HASH_SZ * 2)
 
+#define	SMBAUTH_RETRY		2
 #define	SMBAUTH_FAILURE		1
 #define	SMBAUTH_SUCCESS		0
 #define	MD_DIGEST_LEN		16
--- a/usr/src/lib/smbsrv/libsmb/common/mapfile-vers	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/lib/smbsrv/libsmb/common/mapfile-vers	Sat Jan 16 16:59:32 2021 +0000
@@ -19,7 +19,7 @@
 #
 #
 # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
-# Copyright 2019 Nexenta Systems, Inc.  All rights reserved.
+# Copyright 2020 Tintri by DDN, Inc. All rights reserved.
 #
 
 #
@@ -185,6 +185,7 @@
 	smb_gen_random_passwd;
 	smb_get_dcinfo;
 	smb_get_nameservers;
+	smb_get_netlogon_flags;
 	smb_get_txid;
 	smb_getdataset;
 	smb_getdomainname;
--- a/usr/src/lib/smbsrv/libsmb/common/smb_cfg.c	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_cfg.c	Sat Jan 16 16:59:32 2021 +0000
@@ -20,7 +20,7 @@
  */
 /*
  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
  * Copyright 2020 RackTop Systems, Inc.
  */
 
@@ -152,6 +152,7 @@
 	{SMB_CI_BYPASS_TRAVERSE_CHECKING,
 	    "bypass_traverse_checking", SCF_TYPE_BOOLEAN, 0},
 	{SMB_CI_ENCRYPT_CIPHER, "encrypt_cipher", SCF_TYPE_ASTRING, 0},
+	{SMB_CI_NETLOGON_FLAGS, "netlogon_flags", SCF_TYPE_INTEGER, 0},
 
 	/* SMB_CI_MAX */
 };
--- a/usr/src/lib/smbsrv/libsmb/common/smb_info.c	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_info.c	Sat Jan 16 16:59:32 2021 +0000
@@ -20,7 +20,7 @@
  */
 /*
  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
  * Copyright 2020 RackTop Systems, Inc.
  */
 
@@ -723,3 +723,15 @@
 
 	return (h);
 }
+
+uint32_t
+smb_get_netlogon_flags(void)
+{
+	int64_t val;
+
+	if (smb_config_getnum(SMB_CI_NETLOGON_FLAGS, &val) != SMBD_SMF_OK)
+		return (SMB_PI_NETLOGON_FLAGS_DEFAULT);
+
+	/* These are flags, and we only use the lowest 32 bits */
+	return ((uint32_t)val);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkg/manifests/system-test-libmlrpctest.mf	Sat Jan 16 16:59:32 2021 +0000
@@ -0,0 +1,46 @@
+#
+# 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.
+#
+
+set name=pkg.fmri value=pkg:/system/test/libmlrpctest@$(PKGVERS)
+set name=pkg.description value="Unit Tests for libmlrpc and its consumers"
+set name=pkg.summary value="Libmlrpc Unit Test Suite"
+set name=info.classification \
+    value=org.opensolaris.category.2008:Development/System
+set name=variant.arch value=$(ARCH)
+dir path=opt/libmlrpc-tests
+dir path=opt/libmlrpc-tests/bin
+dir path=opt/libmlrpc-tests/cfg
+dir path=opt/libmlrpc-tests/runfiles
+dir path=opt/libmlrpc-tests/tests
+dir path=opt/libmlrpc-tests/tests/netrlogon
+dir path=opt/libmlrpc-tests/tests/netrlogon/krb5_pac_tests
+dir path=opt/libmlrpc-tests/tests/netrlogon/samlogon_tests
+file path=opt/libmlrpc-tests/README mode=0444
+file path=opt/libmlrpc-tests/bin/libmlrpctest mode=0555
+file path=opt/libmlrpc-tests/cfg/krb5_pac.config mode=0644
+file path=opt/libmlrpc-tests/cfg/samlogon.config mode=0644
+file path=opt/libmlrpc-tests/runfiles/default.run mode=0444
+file path=opt/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/krb5_pac.bin \
+    mode=0444
+file path=opt/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/krb5_pac_decode \
+    mode=0555
+file path=opt/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/run_krb5_pac_tests \
+    mode=0555
+file path=opt/libmlrpc-tests/tests/netrlogon/samlogon_tests/run_samlogon_tests \
+    mode=0555
+file path=opt/libmlrpc-tests/tests/netrlogon/samlogon_tests/samlogon mode=0555
+license lic_CDDL license=lic_CDDL
+depend fmri=service/file-system/smb type=require
+depend fmri=system/test/testrunner type=require
--- a/usr/src/test/Makefile	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/test/Makefile	Sat Jan 16 16:59:32 2021 +0000
@@ -13,6 +13,7 @@
 # Copyright (c) 2012 by Delphix. All rights reserved.
 # Copyright 2014 Garrett D'Amore <garrett@damore.org>
 # Copyright 2019 Joyent, Inc.
+# Copyright 2020 Tintri by DDN, Inc. All rights reserved.
 #
 
 .PARALLEL: $(SUBDIRS)
@@ -21,6 +22,7 @@
 	crypto-tests \
 	elf-tests \
 	libc-tests \
+	libmlrpc-tests \
 	net-tests \
 	os-tests \
 	smbclient-tests \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/libmlrpc-tests/Makefile	Sat Jan 16 16:59:32 2021 +0000
@@ -0,0 +1,20 @@
+#
+# 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.
+#
+
+.PARALLEL: $(SUBDIRS)
+
+SUBDIRS = cfg cmd runfiles tests doc
+
+include $(SRC)/test/Makefile.com
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/libmlrpc-tests/cfg/Makefile	Sat Jan 16 16:59:32 2021 +0000
@@ -0,0 +1,39 @@
+#
+# 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 2019 Joyent, Inc.
+# Copyright 2020 Tintri by DDN, Inc. All rights reserved.
+#
+
+include $(SRC)/Makefile.master
+
+CFGS = samlogon.config krb5_pac.config
+ROOTOPTPKG = $(ROOT)/opt/libmlrpc-tests
+ROOTOPTPKGCFG = $(ROOT)/opt/libmlrpc-tests/cfg
+ROOTOPTPKGDIRS = $(ROOTOPTPKG) $(ROOTOPTPKGCFG)
+FILES = $(CFGS:%=$(ROOTOPTPKGCFG)/%)
+$(FILES) := FILEMODE = 0644
+
+include $(SRC)/test/Makefile.com
+
+all: $(CFGS)
+
+install: $(ROOTOPTPKG) $(ROOTOPTPKGCFG) $(FILES)
+
+clobber: clean
+	$(RM) $(FILES)
+
+$(ROOTOPTPKGDIRS):
+	$(INS.dir)
+
+$(ROOTOPTPKGCFG)/%: % $(ROOTOPTPKGDIRS)
+	$(INS.file)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/libmlrpc-tests/cfg/krb5_pac.config	Sat Jan 16 16:59:32 2021 +0000
@@ -0,0 +1,21 @@
+#!/usr/bin/ksh
+#
+# 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.
+#
+
+#
+# A binary file containing the PAC_LOGON_INFO from a decrypted Kerberos ticket.
+# This can be exported from the AD-Win2k-PAC (including the MES header)
+# using Wireshark.
+#
+export KRB5_PAC_BIN="$MLRPC_TESTS/tests/netrlogon/krb5_pac_tests/krb5_pac.bin"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/libmlrpc-tests/cfg/samlogon.config	Sat Jan 16 16:59:32 2021 +0000
@@ -0,0 +1,31 @@
+#!/usr/bin/ksh
+#
+# 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.
+#
+
+# The NETBIOS domain name to which the server is joined.
+export NETBIOS_DOMAIN="DOMAIN"
+# An FQDN of a dc in the domain.
+export DC_FQDN="dc.domain.com"
+# The authenticating user.
+export USERNAME="user"
+# The name of the computer from which the user is authenticating.
+export CLIENT_COMPUTER="client1"
+
+#
+# Binary files exported from Wireshark, containing parts of a
+# NetrSamLogon(Ex) request.
+#
+export CHALLENGE_FILE=/path/to/challenge.bin
+export NT_RESPONSE_FILE=/path/to/nt_file.bin
+export LM_RESPONSE_FILE=/path/to/lm_file.bin
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/libmlrpc-tests/cmd/Makefile	Sat Jan 16 16:59:32 2021 +0000
@@ -0,0 +1,40 @@
+#
+# 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 $(SRC)/Makefile.master
+include $(SRC)/test/Makefile.com
+
+ROOTOPTPKG = $(ROOT)/opt/libmlrpc-tests
+ROOTBIN = $(ROOTOPTPKG)/bin
+
+PROGS = libmlrpctest
+
+CMDS = $(PROGS:%=$(ROOTBIN)/%)
+$(CMDS) := FILEMODE = 0555
+
+all lint clean clobber:
+
+install: $(CMDS)
+
+$(CMDS): $(ROOTBIN)
+
+$(ROOTBIN):
+	$(INS.dir)
+
+$(ROOTBIN)/%: %
+	$(INS.file)
+
+$(ROOTBIN)/%: %.ksh
+	$(INS.rename)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/libmlrpc-tests/cmd/libmlrpctest.ksh	Sat Jan 16 16:59:32 2021 +0000
@@ -0,0 +1,53 @@
+#!/usr/bin/ksh
+
+#
+# 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 (c) 2012 by Delphix. All rights reserved.
+# Copyright 2020 Tintri by DDN, Inc. All rights reserved.
+#
+
+export MLRPC_TESTS="/opt/libmlrpc-tests"
+runner="/opt/test-runner/bin/run"
+
+function fail
+{
+	echo $1
+	exit ${2:-1}
+}
+
+function find_runfile
+{
+	typeset distro=
+	if [[ -f $MLRPC_TESTS/runfiles/default.run ]]; then
+		distro=default
+	fi
+
+	[[ -n $distro ]] && echo $MLRPC_TESTS/runfiles/$distro.run
+}
+
+while getopts c: c; do
+	case $c in
+	'c')
+		runfile=$OPTARG
+		[[ -f $runfile ]] || fail "Cannot read file: $runfile"
+		;;
+	esac
+done
+shift $((OPTIND - 1))
+
+[[ -z $runfile ]] && runfile=$(find_runfile)
+[[ -z $runfile ]] && fail "Couldn't determine distro"
+
+$runner -c $runfile
+
+exit $?
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/libmlrpc-tests/doc/Makefile	Sat Jan 16 16:59:32 2021 +0000
@@ -0,0 +1,36 @@
+#
+# 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 (c) 2012 by Delphix. All rights reserved.
+# Copyright 2020 Tintri by DDN, Inc. All rights reserved.
+#
+
+include $(SRC)/Makefile.master
+
+READMES = README
+
+ROOTOPTPKG = $(ROOT)/opt/libmlrpc-tests
+
+FILES = $(READMES:%=$(ROOTOPTPKG)/%)
+$(FILES) := FILEMODE = 0444
+
+all: $(READMES)
+
+install: $(ROOTOPTPKG) $(FILES)
+
+clean lint clobber:
+
+$(ROOTOPTPKG):
+	$(INS.dir)
+
+$(ROOTOPTPKG)/%: %
+	$(INS.file)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/libmlrpc-tests/doc/README	Sat Jan 16 16:59:32 2021 +0000
@@ -0,0 +1,77 @@
+#
+# 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 (c) 2012 by Delphix. All rights reserved.
+# Copyright 2020 Tintri by DDN, Inc. All rights reserved.
+#
+
+libmlrpc Unit Test Suite README
+
+1. Building and installing this Unit Test Suite
+2. Running this Unit Test Suite
+3. Test results
+
+--------------------------------------------------------------------------------
+
+1. Building and installing this Unit Test Suite
+
+This Test Suite runs under the testrunner framework (which can be installed
+as pkg:/system/test/testrunner). To build both this Unit Test Suite and the
+testrunner without running a full nightly:
+
+	build_machine$ bldenv [-d] <your_env_file>
+	build_machine$ cd $SRC/test
+	build_machine$ dmake install
+	build_machine$ cd $SRC/pkg
+	build_machine$ dmake install
+
+Then set the publisher on the test machine to point to your repository and
+install the Utils Unit Test Suite.
+
+	test_machine# pkg install pkg:/system/test/libmlrpctest
+
+Note, the framework will be installed automatically, as this test suite
+depends on it.
+
+2. Running this Unit Test Suite
+
+The pre-requisites for running the this Unit Test Suite are:
+	- A non-root user with the ability to sudo(1M) to root without a
+	  password or the root user must run the test.
+	    (The samlogon test requires a user with 'solaris.smf.read.smb'
+	    authorization, such as root.)
+	- The libmlrpc library must be installed.
+	- The system must be joined to the domain.
+	- Certain information must be collected from a packet capture performing
+	    NTLM authentication against the system using a domain user, all from
+	    the NetrSamLogon(Ex) request:
+		- IDENTITY_INFO/Domain
+		- IDENTITY_INFO/Acct Name
+		- IDENTITY_INFO/Wkst Name
+		- three binary files, exported from Wireshark:
+		  1. NETWORK_INFO/Challenge (8 bytes)
+		  2. First NETWORK_INFO/'LM Chal resp'/Bytes array/'LM Chal resp'
+		      (variable - the first is NT)
+		  3. Second NETWORK_INFO/'LM Chal resp'/Bytes array/'LM Chal resp'
+		      (variable - the second is LM)
+	- Enter configuration data in /opt/libmlrpc-tests/cfg/samlogon.config.
+
+Once the pre-requisites are satisfied, simply run the script:
+
+	test_machine$ /opt/libmlrpc-tests/bin/libmlrpctest
+
+3. Test results
+
+While the Unit Test Suite is running, one informational line is printed at
+the end of each test, and a results summary is printed at the end of the run.
+The results summary includes the location of the complete logs, which is of the
+form /var/tmp/test_results/<ISO 8601 date>.
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/libmlrpc-tests/runfiles/Makefile	Sat Jan 16 16:59:32 2021 +0000
@@ -0,0 +1,39 @@
+#
+# 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 (c) 2012 by Delphix. All rights reserved.
+# Copyright 2020 Tintri by DDN, Inc. All rights reserved.
+#
+
+include $(SRC)/Makefile.master
+
+SRCS = default.run
+
+ROOTOPTPKG = $(ROOT)/opt/libmlrpc-tests
+RUNFILES = $(ROOTOPTPKG)/runfiles
+
+CMDS = $(SRCS:%=$(RUNFILES)/%)
+$(CMDS) := FILEMODE = 0444
+
+all: $(SRCS)
+
+install: $(CMDS)
+
+clean lint clobber:
+
+$(CMDS): $(RUNFILES) $(SRCS)
+
+$(RUNFILES):
+	$(INS.dir)
+
+$(RUNFILES)/%: %
+	$(INS.file)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/libmlrpc-tests/runfiles/default.run	Sat Jan 16 16:59:32 2021 +0000
@@ -0,0 +1,29 @@
+#
+# 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.
+#
+
+[DEFAULT]
+pre =
+verbose = False
+quiet = False
+timeout = 60
+post =
+outputdir = /var/tmp/test_results
+
+[/opt/libmlrpc-tests/tests/netrlogon/krb5_pac_tests]
+tests = ['run_krb5_pac_tests']
+
+[/opt/libmlrpc-tests/tests/netrlogon/samlogon_tests]
+user = root
+tests = ['run_samlogon_tests']
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/libmlrpc-tests/tests/Makefile	Sat Jan 16 16:59:32 2021 +0000
@@ -0,0 +1,43 @@
+#
+# 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.
+#
+
+SUBDIRS = \
+	netrlogon
+
+ROOTOPTDIR = $(ROOT)/opt/libmlrpc-tests/tests
+
+all     :=      TARGET = all
+install :=      TARGET = install
+clean   :=      TARGET = clean
+clobber :=      TARGET = clobber
+lint    :=      TARGET = lint
+
+.KEEP_STATE:
+
+install: $(SUBDIRS)
+
+all: $(SUBDIRS)
+
+clean lint: $(SUBDIRS)
+
+$(ROOTOPTDIR):
+	$(INS.dir)
+
+clobber: $(SUBDIRS)
+
+$(SUBDIRS): FRC
+	@cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/libmlrpc-tests/tests/Makefile.com	Sat Jan 16 16:59:32 2021 +0000
@@ -0,0 +1,75 @@
+#
+# 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 (c) 2012 by Delphix. All rights reserved.
+# Copyright 2014 Garrett D'Amore <garrett@damore.org>
+# Copyright 2020 Tintri by DDN, Inc. All rights reserved.
+#
+
+include $(SRC)/Makefile.master
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/test/Makefile.com
+
+#
+# Note: NDR currently is only supported in 32-bit programs.
+#
+OBJS = $(PROG).o util_common.o
+SRCS = $(PROG).c $(TESTCOMMONDIR)/util_common.c
+
+CSTD = $(CSTD_GNU99)
+CPPFLAGS += -I$(TESTCOMMONDIR)
+
+ROOTOPTPKG = $(ROOT)/opt/libmlrpc-tests
+TESTDIR = $(ROOTOPTPKG)/tests/$(TESTSUBDIR)
+
+CMDS = $(PROG:%=$(TESTDIR)/%) $(KSHPROG:%=$(TESTDIR)/%)
+$(CMDS) := FILEMODE = 0555
+
+BINS = $(BINFILES:%=$(TESTDIR)/%)
+$(BINS) := FILEMODE = 0444
+
+all: $(PROG) $(KSHPROG) $(SUBDIRS)
+
+$(PROG): $(OBJS)
+	$(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+	$(POST_PROCESS)
+
+$(KSHPROG): $(KSHPROG).ksh
+	$(RM) $@
+	$(CP) $(KSHPROG).ksh $(@)
+	$(CHMOD) +x $@
+
+%.o: %.c
+	$(COMPILE.c) -o $@ $(CFLAGS_$(MACH)) $<
+
+%.o: $(TESTCOMMONDIR)/%.c
+	$(COMPILE.c) -o $@ $(CFLAGS_$(MACH)) $<
+
+install: $(SUBDIRS) $(CMDS) $(BINS)
+
+lint: lint_SRCS
+
+clobber: clean
+	-$(RM) $(PROG) $(KSHPROG)
+
+clean:
+	-$(RM) $(OBJS)
+
+$(CMDS): $(TESTDIR) $(PROG) $(KSHPROG)
+
+$(BINS): $(TESTDIR)
+
+$(TESTDIR):
+	$(INS.dir)
+
+$(TESTDIR)/%: %
+	$(INS.file)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/libmlrpc-tests/tests/common/util_common.c	Sat Jan 16 16:59:32 2021 +0000
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+/*
+ * Common utilities for libmlrpc tests.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+uchar_t *
+read_buf_from_file(char *file, uint32_t *size)
+{
+	struct stat stats;
+	uchar_t *buf;
+	FILE *fp;
+	size_t nread;
+	int rc;
+
+	errno = 0;
+	rc = stat(file, &stats);
+
+	if (rc < 0) {
+		fprintf(stderr, "stat failed with rc %d:\n", rc);
+		perror(file);
+		return (NULL);
+	}
+
+	buf = malloc(stats.st_size);
+
+	if (buf == NULL) {
+		fprintf(stderr, "couldn't allocate buffer\n");
+		return (NULL);
+	}
+	errno = 0;
+	fp = fopen(file, "r");
+	if (fp == NULL) {
+		fprintf(stderr, "fopen failed to open file:\n");
+		perror(file);
+		free(buf);
+		return (NULL);
+	}
+
+	errno = 0;
+	nread = fread(buf, 1, stats.st_size, fp);
+	if (nread == EOF && errno != 0) {
+		fprintf(stderr, "fread failed:\n");
+		perror(file);
+		free(buf);
+		return (NULL);
+	}
+
+	(void) fclose(fp);
+	if (nread == EOF) {
+		free(buf);
+		buf = NULL;
+	}
+	*size = nread;
+	return (buf);
+}
+
+/*
+ * smb_token_log() outputs to syslog. The library defines syslog to be
+ * smb_syslog, which it defines as NODIRECT to allow fksmbd to provide
+ * its own version. We use that to redirect syslog to stderr, so that
+ * we can print the token output to a useful location.
+ */
+void
+smb_syslog(int pri, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	(void) vfprintf(stderr, fmt, ap);
+	va_end(ap);
+	fprintf(stderr, "\n");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/libmlrpc-tests/tests/common/util_common.h	Sat Jan 16 16:59:32 2021 +0000
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#ifndef _UTIL_COMMON_H
+#define	_UTIL_COMMON_H
+
+/*
+ * Common utilities for libmlrpc tests.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+uchar_t *read_buf_from_file(char *, uint32_t *);
+void smb_syslog(int, const char *, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _UTIL_COMMON_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/libmlrpc-tests/tests/netrlogon/Makefile	Sat Jan 16 16:59:32 2021 +0000
@@ -0,0 +1,20 @@
+#
+# 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.
+#
+
+.PARALLEL: $(SUBDIRS)
+
+SUBDIRS = samlogon_tests krb5_pac_tests
+
+include $(SRC)/test/Makefile.com
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/Makefile	Sat Jan 16 16:59:32 2021 +0000
@@ -0,0 +1,29 @@
+#
+# 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 $(SRC)/Makefile.master
+
+TESTSUBDIR = netrlogon/krb5_pac_tests
+TESTCOMMONDIR = ../../common
+PROG = krb5_pac_decode
+KSHPROG = run_krb5_pac_tests
+BINFILES = krb5_pac.bin
+
+include ../../Makefile.com
+
+LDFLAGS += -R/usr/lib/smbsrv
+LDLIBS += -L$(ROOT)/usr/lib/smbsrv
+LDLIBS += -lmlsvc -lsmb
+CPPFLAGS += -Dsyslog=smb_syslog
Binary file usr/src/test/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/krb5_pac.bin has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/krb5_pac_decode.c	Sat Jan 16 16:59:32 2021 +0000
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+/*
+ * Test the ability to decode PAC data from AD Kerberos tickets.
+ */
+
+#include <smbsrv/libmlsvc.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <util_common.h>
+
+enum KPAC_RC {
+	KPC_SUCCESS = 0,
+	KPC_ARGC,
+	KPC_PAC_FILE,
+	KPC_TOKEN_ALLOC,
+	KPC_DECODE_PAC
+};
+
+int
+main(int argc, char *argv[])
+{
+	char *pac_file;
+	uchar_t *pac_buf;
+	size_t buflen;
+	smb_token_t *token;
+	uint32_t status;
+
+	if (argc < 2) {
+		fprintf(stderr, "usage: %s <Binary PAC File>\n", argv[0]);
+		return (-KPC_ARGC);
+	}
+
+	pac_file = argv[1];
+
+	pac_buf = read_buf_from_file(pac_file, &buflen);
+
+	if (pac_buf == NULL) {
+		fprintf(stderr, "failed to read pac data\n");
+		return (-KPC_PAC_FILE);
+	}
+
+	token = calloc(1, sizeof (*token));
+	if (token == NULL) {
+		fprintf(stderr, "failed to allocate token\n");
+		return (-KPC_TOKEN_ALLOC);
+	}
+
+	/* Initialize only those bits on which smb_decode_krb5_pac depends */
+	(void) smb_lgrp_start();
+
+	status = smb_decode_krb5_pac(token, (char *)pac_buf, buflen);
+	if (status != 0) {
+		fprintf(stderr, "smb_decode_krb5_pac failed with 0x%x\n",
+		    status);
+		return (-KPC_DECODE_PAC);
+	}
+
+	smb_token_log(token);
+
+	return (KPC_SUCCESS);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/run_krb5_pac_tests.ksh	Sat Jan 16 16:59:32 2021 +0000
@@ -0,0 +1,26 @@
+#!/usr/bin/ksh
+
+#
+# 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.
+#
+
+if [[ -z $MLRPC_TESTS ]]; then
+	echo "MLRPC_TESTS not set" >&2
+	exit 1
+fi
+
+. $MLRPC_TESTS/cfg/krb5_pac.config
+
+$MLRPC_TESTS/tests/netrlogon/krb5_pac_tests/krb5_pac_decode $KRB5_PAC_BIN
+exit $?
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/libmlrpc-tests/tests/netrlogon/samlogon_tests/Makefile	Sat Jan 16 16:59:32 2021 +0000
@@ -0,0 +1,28 @@
+#
+# 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 $(SRC)/Makefile.master
+
+TESTSUBDIR = netrlogon/samlogon_tests
+TESTCOMMONDIR = ../../common
+PROG = samlogon
+KSHPROG = run_samlogon_tests
+
+include ../../Makefile.com
+
+LDFLAGS += -R/usr/lib/smbsrv
+LDLIBS += -L$(ROOT)/usr/lib/smbsrv
+LDLIBS += -lmlsvc -lsmb
+CPPFLAGS += -Dsyslog=smb_syslog
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/libmlrpc-tests/tests/netrlogon/samlogon_tests/run_samlogon_tests.ksh	Sat Jan 16 16:59:32 2021 +0000
@@ -0,0 +1,28 @@
+#!/usr/bin/ksh
+
+#
+# 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.
+#
+
+if [[ -z $MLRPC_TESTS ]]; then
+	echo "MLRPC_TESTS not set" >&2
+	exit 1
+fi
+
+. $MLRPC_TESTS/cfg/samlogon.config
+
+$MLRPC_TESTS/tests/netrlogon/samlogon_tests/samlogon $NETBIOS_DOMAIN $DC_FQDN \
+    $USERNAME $CLIENT_COMPUTER $CHALLENGE_FILE $NT_RESPONSE_FILE \
+    $LM_RESPONSE_FILE
+exit $?
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/libmlrpc-tests/tests/netrlogon/samlogon_tests/samlogon.c	Sat Jan 16 16:59:32 2021 +0000
@@ -0,0 +1,341 @@
+/*
+ * 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.
+ */
+
+/*
+ * Test NetrSamLogon and NetrSamLogonEx, uses for NTLM pass-thru auth.
+ */
+
+#include <smbsrv/libmlsvc.h>
+#include <smbsrv/netrauth.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <util_common.h>
+
+extern void netr_initialize(void);
+extern uint32_t netlogon_logon(smb_logon_t *, smb_token_t *, smb_domainex_t *);
+
+boolean_t
+compare_tokens(const smb_token_t *src, const smb_token_t *dst)
+{
+	int i;
+	const smb_ids_t *src_wgrps, *dst_wgrps;
+	smb_id_t *src_grp, *dst_grp;
+	char src_sid[SMB_SID_STRSZ] = "NULL", dst_sid[SMB_SID_STRSZ] = "NULL";
+
+	if (strcmp(src->tkn_domain_name, dst->tkn_domain_name) != 0) {
+		fprintf(stderr, "src domain %s does not match dst %s\n",
+		    src->tkn_domain_name, dst->tkn_domain_name);
+		return (B_FALSE);
+	}
+
+	if (strcmp(src->tkn_account_name, dst->tkn_account_name) != 0) {
+		fprintf(stderr, "src account %s does not match dst %s\n",
+		    src->tkn_account_name, dst->tkn_account_name);
+		return (B_FALSE);
+	}
+
+	if (src->tkn_user.i_attrs != dst->tkn_user.i_attrs) {
+		fprintf(stderr, "src attrs 0x%x does not match dst 0x%x\n",
+		    src->tkn_user.i_attrs, dst->tkn_user.i_attrs);
+		return (B_FALSE);
+	}
+
+	if (!smb_sid_cmp(src->tkn_user.i_sid, dst->tkn_user.i_sid)) {
+		smb_sid_tostr(src->tkn_user.i_sid, src_sid);
+		smb_sid_tostr(dst->tkn_user.i_sid, dst_sid);
+		fprintf(stderr, "src usersid %s does not match dst %s\n",
+		    src_sid, dst_sid);
+		return (B_FALSE);
+	}
+
+	/* tkn_owner can be NULL if we haven't called smb_token_setup_common */
+	if (src->tkn_owner.i_sid != dst->tkn_owner.i_sid &&
+	    !smb_sid_cmp(src->tkn_owner.i_sid, dst->tkn_owner.i_sid)) {
+		smb_sid_tostr(src->tkn_owner.i_sid, src_sid);
+		smb_sid_tostr(dst->tkn_owner.i_sid, dst_sid);
+		fprintf(stderr, "src ownersid %s does not match dst %s\n",
+		    src_sid, dst_sid);
+		return (B_FALSE);
+	}
+
+	if (!smb_sid_cmp(src->tkn_primary_grp.i_sid,
+	    dst->tkn_primary_grp.i_sid)) {
+		smb_sid_tostr(src->tkn_primary_grp.i_sid, src_sid);
+		smb_sid_tostr(dst->tkn_primary_grp.i_sid, dst_sid);
+		fprintf(stderr, "src primarysid %s does not match dst %s\n",
+		    src_sid, dst_sid);
+		return (B_FALSE);
+	}
+
+	src_wgrps = &src->tkn_win_grps;
+	dst_wgrps = &dst->tkn_win_grps;
+
+	if ((src_wgrps->i_ids == NULL && dst_wgrps->i_ids != NULL) ||
+	    (src_wgrps->i_ids != NULL && dst_wgrps->i_ids == NULL)) {
+		fprintf(stderr,
+		    "src wingrp nullness 0x%p does not match dst 0x%p\n",
+		    src_wgrps->i_ids, dst_wgrps->i_ids);
+		return (B_FALSE);
+	}
+
+	if (src_wgrps->i_ids != NULL) {
+		src_grp = &src_wgrps->i_ids[0];
+		dst_grp = &dst_wgrps->i_ids[0];
+		if (src_wgrps->i_cnt != dst_wgrps->i_cnt) {
+			fprintf(stderr,
+			    "src wingrp count %d does not match dst %d\n",
+			    src_wgrps->i_cnt, dst_wgrps->i_cnt);
+			return (B_FALSE);
+		}
+
+		for (i = 0; i < src_wgrps->i_cnt; i++, src_grp++, dst_grp++) {
+			if ((src_grp->i_sid == NULL &&
+			    dst_grp->i_sid != NULL) ||
+			    (src_grp->i_sid != NULL &&
+			    dst_grp->i_sid == NULL)) {
+				fprintf(stderr,
+				    "src wgrp %d nullness 0x%p does not "
+				    "match dst 0x%p\n",
+				    i, src_grp->i_sid, dst_grp->i_sid);
+				return (B_FALSE);
+			}
+
+
+			if (src_grp->i_sid != NULL &&
+			    !smb_sid_cmp(src_grp->i_sid, dst_grp->i_sid)) {
+				smb_sid_tostr(src_grp->i_sid, src_sid);
+				smb_sid_tostr(dst_grp->i_sid, dst_sid);
+				fprintf(stderr, "src wingrp %d sid %s "
+				    "does not match dst %s\n",
+				    i, src_sid, dst_sid);
+				return (B_FALSE);
+			}
+		}
+	}
+
+	if ((src->tkn_posix_grps == NULL && dst->tkn_posix_grps != NULL) ||
+	    (src->tkn_posix_grps != NULL && dst->tkn_posix_grps == NULL)) {
+		fprintf(stderr, "src pgrp nullness 0x%p does not match "
+		    "dst 0x%p\n",
+		    src->tkn_posix_grps, dst->tkn_posix_grps);
+		return (B_FALSE);
+	}
+
+	if (src->tkn_posix_grps != NULL) {
+		if (src->tkn_posix_grps->pg_ngrps !=
+		    dst->tkn_posix_grps->pg_ngrps) {
+			fprintf(stderr,
+			    "src pgrp count %d does not match dst %d\n",
+			    src->tkn_posix_grps->pg_ngrps,
+			    dst->tkn_posix_grps->pg_ngrps);
+			return (B_FALSE);
+		}
+
+		for (i = 0; i < src->tkn_posix_grps->pg_ngrps; i++) {
+			if (src->tkn_posix_grps->pg_grps[i] !=
+			    dst->tkn_posix_grps->pg_grps[i]) {
+				fprintf(stderr,
+				    "src pgrp num %d %d does not match "
+				    "dst %d\n", i,
+				    src->tkn_posix_grps->pg_grps[i],
+				    dst->tkn_posix_grps->pg_grps[i]);
+				return (B_FALSE);
+			}
+		}
+	}
+
+	return (B_TRUE);
+}
+
+enum SAMLOGON_RC {
+	SL_SUCCESS = 0,
+	SL_ARGC,
+	SL_DC_FQDN,
+	SL_NB_DOMAIN,
+	SL_CHALLENGE,
+	SL_NT_PASS,
+	SL_LM_PASS,
+	SL_TOKEN_ALLOC,
+	SL_NETLOGON,
+	SL_TOKEN_COMP,
+	SL_NETLOGON_LOOP,
+	SL_NETLOGON_SAMLOGON,
+	SL_NETLOGON_NOVERIFY
+};
+
+int
+main(int argc, char *argv[])
+{
+	smb_logon_t user_info = {
+		.lg_secmode = SMB_SECMODE_DOMAIN,
+		.lg_domain_type = SMB_DOMAIN_PRIMARY,
+		.lg_level = NETR_NETWORK_LOGON
+	};
+	smb_token_t *token = NULL;
+	smb_token_t cmp_token;
+	smb_domainex_t di = {0};
+	char *nb_domain, *dc_name, *user_name, *workstation, *chall_file;
+	char *nt_file, *lm_file;
+	uint32_t status;
+	int i;
+
+	if (argc < 8) {
+		fprintf(stderr, "usage: %s <NETBIOS domain> <DC FQDN> "
+		    "<user name> "
+		    "<client computer name> <Binary Challenge File> "
+		    "<Binary NT response file> <Binary LM response file>\n",
+		    argv[0]);
+		return (-SL_ARGC);
+	}
+
+	nb_domain = argv[1];
+	dc_name = argv[2];
+	user_name = argv[3];
+	workstation = argv[4];
+	chall_file = argv[5];
+	nt_file = argv[6];
+	lm_file = argv[7];
+
+	if (strlcpy(di.d_dci.dc_name, dc_name, sizeof (di.d_dci.dc_name)) >=
+	    sizeof (di.d_dci.dc_name)) {
+		fprintf(stderr, "DC FQDN %s is too long\n", dc_name);
+		return (-SL_DC_FQDN);
+	}
+	if (strlcpy(di.d_primary.di_nbname, nb_domain,
+	    sizeof (di.d_primary.di_nbname)) >=
+	    sizeof (di.d_primary.di_nbname)) {
+		fprintf(stderr, "Netbios Domain %s is too long\n", nb_domain);
+		return (-SL_NB_DOMAIN);
+	}
+
+	user_info.lg_domain = nb_domain;
+	user_info.lg_e_domain = user_info.lg_domain;
+	user_info.lg_username = user_name;
+	user_info.lg_workstation = workstation;
+
+	user_info.lg_challenge_key.val =
+	    read_buf_from_file(chall_file, &user_info.lg_challenge_key.len);
+	if (user_info.lg_challenge_key.val == NULL) {
+		fprintf(stderr, "failed to get challenge\n");
+		return (-SL_CHALLENGE);
+	}
+
+	user_info.lg_nt_password.val =
+	    read_buf_from_file(nt_file, &user_info.lg_nt_password.len);
+	if (user_info.lg_nt_password.val == NULL) {
+		fprintf(stderr, "failed to get NT pass\n");
+		return (-SL_NT_PASS);
+	}
+
+	user_info.lg_lm_password.val =
+	    read_buf_from_file(lm_file, &user_info.lg_lm_password.len);
+	if (user_info.lg_lm_password.val == NULL) {
+		fprintf(stderr, "failed to get LM pass\n");
+		return (-SL_LM_PASS);
+	}
+
+	/* Initialize only those bits on which netlogon_logon depends */
+	(void) smb_lgrp_start();
+	smb_ipc_init();
+	netr_initialize();
+
+	token = calloc(1, sizeof (*token));
+	if (token == NULL) {
+		fprintf(stderr, "failed to allocate token\n");
+		return (-SL_TOKEN_ALLOC);
+	}
+	status = netlogon_logon(&user_info, token, &di);
+
+	if (status != NT_STATUS_SUCCESS) {
+		fprintf(stderr, "netlogon_logon failed: 0x%x\n", status);
+		return (-SL_NETLOGON);
+	}
+	smb_token_log(token);
+
+	/* struct copy */
+	cmp_token = *token;
+
+	for (i = 0; i < 10; i++) {
+		token = calloc(1, sizeof (*token));
+		if (token == NULL) {
+			fprintf(stderr, "iter %d: failed to allocate token\n",
+			    i);
+			return (-SL_TOKEN_ALLOC);
+		}
+		status = netlogon_logon(&user_info, token, &di);
+
+		if (status != NT_STATUS_SUCCESS) {
+			fprintf(stderr,
+			    "iter %d: netlogon_logon failed: 0x%x\n",
+			    i, status);
+			return (-SL_NETLOGON_LOOP);
+		}
+		if (!compare_tokens(&cmp_token, token)) {
+			fprintf(stderr, "iter %d: tokens didn't match\n", i);
+			smb_token_log(token);
+			return (-SL_TOKEN_COMP);
+		}
+		if (i != 9)
+			smb_token_destroy(token);
+	}
+	smb_token_log(token);
+	smb_token_destroy(token);
+
+	token = calloc(1, sizeof (*token));
+	if (token == NULL) {
+		fprintf(stderr, "failed to allocate token\n");
+		return (-SL_TOKEN_ALLOC);
+	}
+
+	/* Turn off SamLogonEx */
+	netlogon_init_global(0x00000004);
+	status = netlogon_logon(&user_info, token, &di);
+	if (status != NT_STATUS_SUCCESS) {
+		fprintf(stderr, "NoSamLogonEx: netlogon_logon failed: 0x%x\n",
+		    status);
+		return (-SL_NETLOGON_SAMLOGON);
+	}
+	smb_token_log(token);
+	if (!compare_tokens(&cmp_token, token)) {
+		fprintf(stderr, "tokens didn't match\n");
+		return (-SL_TOKEN_COMP);
+	}
+	smb_token_destroy(token);
+
+	token = calloc(1, sizeof (*token));
+	if (token == NULL) {
+		fprintf(stderr, "failed to allocate token\n");
+		return (-SL_TOKEN_ALLOC);
+	}
+
+	/* Don't verify responses */
+	netlogon_init_global(0x00000002);
+	status = netlogon_logon(&user_info, token, &di);
+
+	if (status != NT_STATUS_SUCCESS) {
+		fprintf(stderr, "NoVerify: netlogon_logon failed: 0x%x\n",
+		    status);
+		return (-SL_NETLOGON_NOVERIFY);
+	}
+	smb_token_log(token);
+
+	if (!compare_tokens(&cmp_token, token)) {
+		fprintf(stderr, "tokens didn't match\n");
+		return (-SL_TOKEN_COMP);
+	}
+	smb_token_destroy(token);
+	return (SL_SUCCESS);
+}
--- a/usr/src/tools/ndrgen/ndr_anal.c	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/tools/ndrgen/ndr_anal.c	Sat Jan 16 16:59:32 2021 +0000
@@ -24,7 +24,9 @@
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
+/*
+ * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
+ */
 
 #include <strings.h>
 #include <string.h>
@@ -1003,6 +1005,7 @@
 
 	advice->a_reference = find_advice(advice_list, REFERENCE_KW);
 	advice->a_align = find_advice(advice_list, ALIGN_KW);
+	advice->a_fake = find_advice(advice_list, FAKE_KW);
 }
 
 static ndr_node_t *
--- a/usr/src/tools/ndrgen/ndr_gen.c	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/tools/ndrgen/ndr_gen.c	Sat Jan 16 16:59:32 2021 +0000
@@ -24,6 +24,10 @@
  * Use is subject to license terms.
  */
 
+/*
+ * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
+ */
+
 #include <string.h>
 #include "ndrgen.h"
 #include "y.tab.h"
@@ -528,6 +532,9 @@
 	if (ti->is_conformant)
 		(void) strlcat(flags, "|NDR_F_CONFORMANT", NDLBUFSZ);
 
+	if (ti->advice.a_fake)
+		(void) strlcat(flags, "|NDR_F_FAKE", NDLBUFSZ);
+
 	if (ti->type_op == STRUCT_KW) {
 		if (ti->advice.a_operation)
 			(void) strlcat(flags, "|NDR_F_OPERATION", NDLBUFSZ);
--- a/usr/src/tools/ndrgen/ndr_lex.c	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/tools/ndrgen/ndr_lex.c	Sat Jan 16 16:59:32 2021 +0000
@@ -24,6 +24,10 @@
  * Use is subject to license terms.
  */
 
+/*
+ * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
+ */
+
 #include <errno.h>
 #include <stdarg.h>
 #include "ndrgen.h"
@@ -117,6 +121,7 @@
 
 	{ "transmit_as", TRANSMIT_AS_KW, 0 },
 	{ "arg_is",	ARG_IS_KW,	0 },
+	{ "fake",	FAKE_KW,	0 },
 
 	{ "char",	BASIC_TYPE,	1 },
 	{ "uchar",	BASIC_TYPE,	1 },
--- a/usr/src/tools/ndrgen/ndr_parse.y	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/tools/ndrgen/ndr_parse.y	Sat Jan 16 16:59:32 2021 +0000
@@ -25,6 +25,10 @@
  * Use is subject to license terms.
  */
 
+/*
+ * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
+ */
+
 #include "ndrgen.h"
 
 typedef struct node *node_ptr;
@@ -39,7 +43,7 @@
 %token INTERFACE_KW UUID_KW _NO_REORDER_KW EXTERN_KW
 %token SIZE_IS_KW LENGTH_IS_KW STRING_KW REFERENCE_KW
 %token CASE_KW DEFAULT_KW SWITCH_IS_KW
-%token TRANSMIT_AS_KW ARG_IS_KW 
+%token TRANSMIT_AS_KW ARG_IS_KW FAKE_KW
 
 /* composite keywords */
 %token BASIC_TYPE TYPENAME
@@ -112,6 +116,7 @@
 	|	OPERATION_KW LP arg RP	={ $$ = n_cons (OPERATION_KW, $3); }
 	|	ALIGN_KW LP arg RP	={ $$ = n_cons (ALIGN_KW, $3); }
 	|	STRING_KW		={ $$ = n_cons (STRING_KW); }
+	|	FAKE_KW			={ $$ = n_cons (FAKE_KW); }
 
 	|	SIZE_IS_KW LP arg RP
 				={ $$ = n_cons (SIZE_IS_KW, $3, $3, $3); }
--- a/usr/src/tools/ndrgen/ndr_print.c	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/tools/ndrgen/ndr_print.c	Sat Jan 16 16:59:32 2021 +0000
@@ -24,6 +24,10 @@
  * Use is subject to license terms.
  */
 
+/*
+ * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
+ */
+
 #include "ndrgen.h"
 #include "y.tab.h"
 
@@ -51,6 +55,7 @@
 
 	switch (np->label) {
 	case ALIGN_KW:		nm = "align";		break;
+	case FAKE_KW:		nm = "fake";		break;
 	case STRUCT_KW:		nm = "struct";		break;
 	case UNION_KW:		nm = "union";		break;
 	case TYPEDEF_KW:	nm = "typedef";		break;
@@ -104,6 +109,7 @@
 	case DEFAULT_KW:
 	case _NO_REORDER_KW:
 	case EXTERN_KW:
+	case FAKE_KW:
 		(void) printf("%s", nm);
 		break;
 
--- a/usr/src/tools/ndrgen/ndrgen.h	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/tools/ndrgen/ndrgen.h	Sat Jan 16 16:59:32 2021 +0000
@@ -24,6 +24,10 @@
  * Use is subject to license terms.
  */
 
+/*
+ * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
+ */
+
 #ifndef _NDRGEN_H
 #define	_NDRGEN_H
 
@@ -97,7 +101,7 @@
 #define	NDLBUFSZ	100
 
 /* This makes certain things much easier */
-#define	N_ADVICE	18
+#define	N_ADVICE	19
 
 typedef struct advice {
 	struct node		*a_nodes[N_ADVICE];
@@ -130,6 +134,7 @@
 #define	a_extern	a_nodes[14]
 #define	a_reference	a_nodes[15]
 #define	a_align		a_nodes[16]
+#define	a_fake		a_nodes[17]
 } ndr_advice_t;
 
 typedef struct typeinfo {
--- a/usr/src/uts/common/fs/smbsrv/smb_common_open.c	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/uts/common/fs/smbsrv/smb_common_open.c	Sat Jan 16 16:59:32 2021 +0000
@@ -22,6 +22,7 @@
 /*
  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
+ * Copyright 2020 Nexenta by DDN, Inc. All rights reserved.
  */
 
 /*
@@ -253,6 +254,7 @@
 	smb_node_t	*fnode = NULL;
 	smb_node_t	*dnode = NULL;
 	smb_node_t	*cur_node = NULL;
+	smb_node_t	*tmp_node = NULL;
 	smb_arg_open_t	*op = &sr->sr_open;
 	smb_pathname_t	*pn = &op->fqi.fq_path;
 	smb_ofile_t	*of = NULL;
@@ -269,6 +271,7 @@
 	uint16_t	tree_fid = 0;
 	boolean_t	created = B_FALSE;
 	boolean_t	last_comp_found = B_FALSE;
+	boolean_t	stream_found = B_FALSE;
 	boolean_t	opening_incr = B_FALSE;
 	boolean_t	dnode_held = B_FALSE;
 	boolean_t	dnode_wlock = B_FALSE;
@@ -278,6 +281,7 @@
 	boolean_t	did_open = B_FALSE;
 	boolean_t	did_break_handle = B_FALSE;
 	boolean_t	did_cleanup_orphans = B_FALSE;
+	char		*sname = NULL;
 
 	/* Get out now if we've been cancelled. */
 	mutex_enter(&sr->sr_mutex);
@@ -418,9 +422,13 @@
 	if ((op->desired_access & ~FILE_READ_ATTRIBUTES) == DELETE)
 		lookup_flags &= ~SMB_FOLLOW_LINKS;
 
-	rc = smb_fsop_lookup_name(sr, zone_kcred(), lookup_flags,
+	/*
+	 * Lookup *just* the file portion of the name.
+	 * Returns stream name in sname, which this allocates
+	 */
+	rc = smb_fsop_lookup_file(sr, zone_kcred(), lookup_flags,
 	    sr->tid_tree->t_snode, op->fqi.fq_dnode, op->fqi.fq_last_comp,
-	    &op->fqi.fq_fnode);
+	    &sname, &op->fqi.fq_fnode);
 
 	if (rc == 0) {
 		last_comp_found = B_TRUE;
@@ -449,9 +457,6 @@
 
 	if (last_comp_found) {
 
-		smb_node_unlock(dnode);
-		dnode_wlock = B_FALSE;
-
 		fnode = op->fqi.fq_fnode;
 		dnode = op->fqi.fq_dnode;
 
@@ -468,8 +473,9 @@
 		 *   it must NOT be (required by Lotus Notes)
 		 * - the target is NOT a directory and client requires that
 		 *   it MUST be.
+		 * Streams are never directories.
 		 */
-		if (smb_node_is_dir(fnode)) {
+		if (smb_node_is_dir(fnode) && sname == NULL) {
 			if (op->create_options & FILE_NON_DIRECTORY_FILE) {
 				status = NT_STATUS_FILE_IS_A_DIRECTORY;
 				goto errout;
@@ -482,20 +488,81 @@
 			}
 		}
 
-		/*
-		 * No more open should be accepted when "Delete on close"
-		 * flag is set.
-		 */
-		if (fnode->flags & NODE_FLAGS_DELETE_ON_CLOSE) {
-			status = NT_STATUS_DELETE_PENDING;
-			goto errout;
+		/* If we're given a stream name, look it up now */
+		if (sname != NULL) {
+			tmp_node = fnode;
+			rc = smb_fsop_lookup_stream(sr, zone_kcred(),
+			    lookup_flags, sr->tid_tree->t_snode, fnode, sname,
+			    &fnode);
+		} else {
+			rc = 0;
 		}
 
-		/*
-		 * Specified file already exists so the operation should fail.
-		 */
-		if (op->create_disposition == FILE_CREATE) {
-			status = NT_STATUS_OBJECT_NAME_COLLISION;
+		if (rc == 0) { /* Stream Exists (including unnamed stream) */
+			stream_found = B_TRUE;
+			smb_node_unlock(dnode);
+			dnode_wlock = B_FALSE;
+
+			if (tmp_node != NULL)
+				smb_node_release(tmp_node);
+
+			/*
+			 * No more open should be accepted when
+			 * "Delete on close" flag is set.
+			 */
+			if (fnode->flags & NODE_FLAGS_DELETE_ON_CLOSE) {
+				status = NT_STATUS_DELETE_PENDING;
+				goto errout;
+			}
+
+			/*
+			 * Specified file already exists
+			 * so the operation should fail.
+			 */
+			if (op->create_disposition == FILE_CREATE) {
+				status = NT_STATUS_OBJECT_NAME_COLLISION;
+				goto errout;
+			}
+
+			if ((op->create_disposition == FILE_SUPERSEDE) ||
+			    (op->create_disposition == FILE_OVERWRITE_IF) ||
+			    (op->create_disposition == FILE_OVERWRITE)) {
+
+				if (sname == NULL) {
+					if (!smb_sattr_check(
+					    op->fqi.fq_fattr.sa_dosattr,
+					    op->dattr)) {
+						status =
+						    NT_STATUS_ACCESS_DENIED;
+						goto errout;
+					}
+					op->desired_access |=
+					    FILE_WRITE_ATTRIBUTES;
+				}
+
+				if (smb_node_is_dir(fnode)) {
+					status = NT_STATUS_ACCESS_DENIED;
+					goto errout;
+				}
+			}
+
+			/* MS-FSA 2.1.5.1.2 */
+			if (op->create_disposition == FILE_SUPERSEDE)
+				op->desired_access |= DELETE;
+			if ((op->create_disposition == FILE_OVERWRITE_IF) ||
+			    (op->create_disposition == FILE_OVERWRITE))
+				op->desired_access |= FILE_WRITE_DATA;
+		} else if (rc == ENOENT) { /* File Exists, but Stream doesn't */
+			if (op->create_disposition == FILE_OPEN ||
+			    op->create_disposition == FILE_OVERWRITE) {
+				status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+				goto errout;
+			}
+
+			op->desired_access |= FILE_WRITE_DATA;
+		} else { /* Error looking up stream */
+			status = smb_errno2status(rc);
+			fnode = tmp_node;
 			goto errout;
 		}
 
@@ -520,29 +587,6 @@
 			}
 		}
 
-		if ((op->create_disposition == FILE_SUPERSEDE) ||
-		    (op->create_disposition == FILE_OVERWRITE_IF) ||
-		    (op->create_disposition == FILE_OVERWRITE)) {
-
-			if (!smb_sattr_check(op->fqi.fq_fattr.sa_dosattr,
-			    op->dattr)) {
-				status = NT_STATUS_ACCESS_DENIED;
-				goto errout;
-			}
-
-			if (smb_node_is_dir(fnode)) {
-				status = NT_STATUS_ACCESS_DENIED;
-				goto errout;
-			}
-		}
-
-		/* MS-FSA 2.1.5.1.2 */
-		if (op->create_disposition == FILE_SUPERSEDE)
-			op->desired_access |= DELETE;
-		if ((op->create_disposition == FILE_OVERWRITE_IF) ||
-		    (op->create_disposition == FILE_OVERWRITE))
-			op->desired_access |= FILE_WRITE_DATA;
-
 		/* Dataset roots can't be deleted, so don't set DOC */
 		if ((op->create_options & FILE_DELETE_ON_CLOSE) != 0 &&
 		    (fnode->flags & NODE_FLAGS_VFSROOT) != 0) {
@@ -552,6 +596,7 @@
 
 		status = smb_fsop_access(sr, sr->user_cr, fnode,
 		    op->desired_access);
+
 		if (status != NT_STATUS_SUCCESS)
 			goto errout;
 
@@ -575,6 +620,31 @@
 		if ((op->desired_access & FILE_DATA_ALL) != 0)
 			op->desired_access |= FILE_READ_ATTRIBUTES;
 
+		/* If the stream didn't exist, create it now */
+		if (!stream_found) {
+			smb_node_t *tmp_node = fnode;
+
+			bzero(&new_attr, sizeof (new_attr));
+			new_attr.sa_vattr.va_type = VREG;
+			new_attr.sa_vattr.va_mode = S_IRUSR;
+			new_attr.sa_mask |= SMB_AT_TYPE | SMB_AT_MODE;
+
+			rc = smb_fsop_create_stream(sr, sr->user_cr, dnode,
+			    fnode, sname, lookup_flags, &new_attr, &fnode);
+			smb_node_release(tmp_node);
+
+			if (rc != 0) {
+				status = smb_errno2status(rc);
+				fnode_held = B_FALSE;
+				goto errout;
+			}
+			op->action_taken = SMB_OACT_CREATED;
+			created = B_TRUE;
+
+			smb_node_unlock(dnode);
+			dnode_wlock = B_FALSE;
+		}
+
 		/*
 		 * Oplock break is done prior to sharing checks as the break
 		 * may cause other clients to close the file which would
@@ -593,6 +663,24 @@
 		smb_node_inc_opening_count(fnode);
 		opening_incr = B_TRUE;
 
+		if (!stream_found) {
+			/*
+			 * Stake our Share Access claim.
+			 */
+			smb_node_wrlock(fnode);
+			fnode_wlock = B_TRUE;
+
+			status = smb_fsop_shrlock(sr->user_cr, fnode, uniq_fid,
+			    op->desired_access, op->share_access);
+			if (status != 0)
+				goto errout;
+
+			fnode_shrlk = B_TRUE;
+			smb_node_unlock(fnode);
+			fnode_wlock = B_FALSE;
+			goto stream_created;
+		}
+
 		/*
 		 * XXX Supposed to do share access checks next.
 		 * [MS-FSA] describes that as part of access check:
@@ -780,11 +868,20 @@
 		case FILE_SUPERSEDE:
 		case FILE_OVERWRITE_IF:
 		case FILE_OVERWRITE:
-			op->dattr |= FILE_ATTRIBUTE_ARCHIVE;
-			/* Don't apply readonly until smb_set_open_attributes */
-			if (op->dattr & FILE_ATTRIBUTE_READONLY) {
-				op->dattr &= ~FILE_ATTRIBUTE_READONLY;
-				op->created_readonly = B_TRUE;
+			bzero(&new_attr, sizeof (new_attr));
+			if (sname == NULL) {
+				op->dattr |= FILE_ATTRIBUTE_ARCHIVE;
+				/*
+				 * Don't apply readonly until
+				 * smb_set_open_attributes
+				 */
+				if (op->dattr & FILE_ATTRIBUTE_READONLY) {
+					op->dattr &= ~FILE_ATTRIBUTE_READONLY;
+					op->created_readonly = B_TRUE;
+				}
+				new_attr.sa_dosattr = op->dattr;
+			} else {
+				new_attr.sa_dosattr = FILE_ATTRIBUTE_ARCHIVE;
 			}
 
 			/*
@@ -793,8 +890,6 @@
 			 * after we have an ofile.  See:
 			 * smb_set_open_attributes
 			 */
-			bzero(&new_attr, sizeof (new_attr));
-			new_attr.sa_dosattr = op->dattr;
 			new_attr.sa_vattr.va_size = 0;
 			new_attr.sa_mask = SMB_AT_DOSATTR | SMB_AT_SIZE;
 			rc = smb_fsop_setattr(sr, sr->user_cr, fnode,
@@ -844,6 +939,12 @@
 			goto errout;
 		}
 
+		if ((op->desired_access & ACCESS_SYSTEM_SECURITY) != 0 &&
+		    !smb_user_has_security_priv(sr->uid_user, sr->user_cr)) {
+			status = NT_STATUS_ACCESS_DENIED;
+			goto errout;
+		}
+
 		if (pn->pn_fname && smb_is_invalid_filename(pn->pn_fname)) {
 			status = NT_STATUS_OBJECT_NAME_INVALID;
 			goto errout;
@@ -982,6 +1083,7 @@
 		(void) smb_oplock_break_PARENT(dnode, of);
 	}
 
+stream_created:
 	/*
 	 * We might have blocked in smb_oplock_break_OPEN long enough
 	 * so a tree disconnect might have happened.  In that case,
@@ -1061,6 +1163,8 @@
 	 * how that happens is protocol-specific.
 	 */
 
+	if (sname != NULL)
+		kmem_free(sname, MAXNAMELEN);
 	if (fnode_wlock)
 		smb_node_unlock(fnode);
 	if (opening_incr)
@@ -1091,6 +1195,8 @@
 		smb_delete_new_object(sr);
 	}
 
+	if (sname != NULL)
+		kmem_free(sname, MAXNAMELEN);
 	if (fnode_wlock)
 		smb_node_unlock(fnode);
 	if (opening_incr)
--- a/usr/src/uts/common/fs/smbsrv/smb_fsops.c	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/uts/common/fs/smbsrv/smb_fsops.c	Sat Jan 16 16:59:32 2021 +0000
@@ -35,8 +35,8 @@
 
 extern caller_context_t smb_ct;
 
-static int smb_fsop_create_stream(smb_request_t *, cred_t *, smb_node_t *,
-    char *, char *, int, smb_attr_t *, smb_node_t **);
+static int smb_fsop_create_file_with_stream(smb_request_t *, cred_t *,
+    smb_node_t *, char *, char *, int, smb_attr_t *, smb_node_t **);
 
 static int smb_fsop_create_file(smb_request_t *, cred_t *, smb_node_t *,
     char *, int, smb_attr_t *, smb_node_t **);
@@ -136,6 +136,7 @@
 	boolean_t is_dir;
 
 	ASSERT(fs_sd);
+	ASSERT(ret_snode != NULL);
 
 	if (SMB_TREE_IS_CASEINSENSITIVE(sr))
 		flags = SMB_IGNORE_CASE;
@@ -319,7 +320,7 @@
 		sname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
 		smb_stream_parse_name(name, fname, sname);
 
-		rc = smb_fsop_create_stream(sr, cr, dnode,
+		rc = smb_fsop_create_file_with_stream(sr, cr, dnode,
 		    fname, sname, flags, attr, ret_snode);
 
 		kmem_free(fname, MAXNAMELEN);
@@ -348,17 +349,71 @@
 
 
 /*
- * smb_fsop_create_stream
+ * smb_fsop_create_file_with_stream
  *
- * Create NTFS named stream file (sname) on unnamed stream
- * file (fname), creating the unnamed stream file if it
+ * Create named stream (sname) on file (fname), creating the file if it
  * doesn't exist.
- * If we created the unnamed stream file and then creation
- * of the named stream file fails, we delete the unnamed stream.
+ * If we created the file and then creation of the named stream fails,
+ * we delete the file.
  * Since we use the real file name for the smb_vop_remove we
  * clear the SMB_IGNORE_CASE flag to ensure a case sensitive
  * match.
  *
+ * Note that some stream "types" are "restricted" and only
+ * internal callers (cr == kcred) can create those.
+ */
+static int
+smb_fsop_create_file_with_stream(smb_request_t *sr, cred_t *cr,
+    smb_node_t *dnode, char *fname, char *sname, int flags,
+    smb_attr_t *attr, smb_node_t **ret_snode)
+{
+	smb_node_t	*fnode;
+	cred_t		*kcr = zone_kcred();
+	int		rc = 0;
+	boolean_t	fcreate = B_FALSE;
+
+	ASSERT(ret_snode != NULL);
+
+	if (cr != kcr && smb_strname_restricted(sname))
+		return (EACCES);
+
+	/* Look up / create the unnamed stream, fname */
+	rc = smb_fsop_lookup(sr, cr, flags | SMB_FOLLOW_LINKS,
+	    sr->tid_tree->t_snode, dnode, fname, &fnode);
+	if (rc == 0) {
+		if (smb_fsop_access(sr, sr->user_cr, fnode,
+		    sr->sr_open.desired_access) != 0) {
+			smb_node_release(fnode);
+			rc = EACCES;
+		}
+	} else if (rc == ENOENT) {
+		fcreate = B_TRUE;
+		rc = smb_fsop_create_file(sr, cr, dnode, fname, flags,
+		    attr, &fnode);
+	}
+	if (rc != 0)
+		return (rc);
+
+	rc = smb_fsop_create_stream(sr, cr, dnode, fnode, sname, flags, attr,
+	    ret_snode);
+
+	if (rc != 0) {
+		if (fcreate) {
+			flags &= ~SMB_IGNORE_CASE;
+			(void) smb_vop_remove(dnode->vp,
+			    fnode->od_name, flags, cr);
+		}
+	}
+
+	smb_node_release(fnode);
+	return (rc);
+}
+
+/*
+ * smb_fsop_create_stream
+ *
+ * Create named stream (sname) on existing file (fnode).
+ *
  * The second parameter of smb_vop_setattr() is set to
  * NULL, even though an unnamed stream exists.  This is
  * because we want to set the UID and GID on the named
@@ -368,54 +423,33 @@
  * Note that some stream "types" are "restricted" and only
  * internal callers (cr == kcred) can create those.
  */
-static int
+int
 smb_fsop_create_stream(smb_request_t *sr, cred_t *cr,
-    smb_node_t *dnode, char *fname, char *sname, int flags,
+    smb_node_t *dnode, smb_node_t *fnode, char *sname, int flags,
     smb_attr_t *attr, smb_node_t **ret_snode)
 {
 	smb_attr_t	fattr;
-	smb_node_t	*fnode;
 	vnode_t		*xattrdvp;
 	vnode_t		*vp;
 	cred_t		*kcr = zone_kcred();
 	int		rc = 0;
-	boolean_t	fcreate = B_FALSE;
+
+	ASSERT(ret_snode != NULL);
 
 	if (cr != kcr && smb_strname_restricted(sname))
 		return (EACCES);
 
-	/* Look up / create the unnamed stream, fname */
-	rc = smb_fsop_lookup(sr, cr, flags | SMB_FOLLOW_LINKS,
-	    sr->tid_tree->t_snode, dnode, fname, &fnode);
-	if (rc == 0) {
-		if (smb_fsop_access(sr, sr->user_cr, fnode,
-		    sr->sr_open.desired_access) != 0)
-			rc = EACCES;
-	} else if (rc == ENOENT) {
-		fcreate = B_TRUE;
-		rc = smb_fsop_create_file(sr, cr, dnode, fname, flags,
-		    attr, &fnode);
-	}
-	if (rc != 0)
-		return (rc);
-
+	bzero(&fattr, sizeof (fattr));
 	fattr.sa_mask = SMB_AT_UID | SMB_AT_GID;
 	rc = smb_vop_getattr(fnode->vp, NULL, &fattr, 0, kcr);
 
 	if (rc == 0) {
 		/* create the named stream, sname */
-		rc = smb_vop_stream_create(fnode->vp, sname, attr,
-		    &vp, &xattrdvp, flags, cr);
+		rc = smb_vop_stream_create(fnode->vp, sname,
+		    attr, &vp, &xattrdvp, flags, cr);
 	}
-	if (rc != 0) {
-		if (fcreate) {
-			flags &= ~SMB_IGNORE_CASE;
-			(void) smb_vop_remove(dnode->vp,
-			    fnode->od_name, flags, cr);
-		}
-		smb_node_release(fnode);
+	if (rc != 0)
 		return (rc);
-	}
 
 	attr->sa_vattr.va_uid = fattr.sa_vattr.va_uid;
 	attr->sa_vattr.va_gid = fattr.sa_vattr.va_gid;
@@ -423,14 +457,14 @@
 
 	rc = smb_vop_setattr(vp, NULL, attr, 0, kcr);
 	if (rc != 0) {
-		smb_node_release(fnode);
+		VN_RELE(xattrdvp);
+		VN_RELE(vp);
 		return (rc);
 	}
 
 	*ret_snode = smb_stream_node_lookup(sr, cr, fnode, xattrdvp,
 	    vp, sname);
 
-	smb_node_release(fnode);
 	VN_RELE(xattrdvp);
 	VN_RELE(vp);
 
@@ -440,7 +474,7 @@
 	/* notify change to the unnamed stream */
 	if (rc == 0)
 		smb_node_notify_change(dnode,
-		    FILE_ACTION_ADDED_STREAM, fname);
+		    FILE_ACTION_ADDED_STREAM, fnode->od_name);
 
 	return (rc);
 }
@@ -457,6 +491,8 @@
 	vnode_t		*vp;
 	int		rc;
 
+	ASSERT(ret_snode != NULL);
+
 #ifdef	_KERNEL
 	smb_fssd_t	fs_sd;
 	uint32_t	secinfo;
@@ -470,6 +506,10 @@
 		 */
 		secinfo = smb_sd_get_secinfo(op->sd);
 
+		if ((secinfo & SMB_SACL_SECINFO) != 0 &&
+		    !smb_user_has_security_priv(sr->uid_user, cr))
+			return (NT_STATUS_PRIVILEGE_NOT_HELD);
+
 		smb_fssd_init(&fs_sd, secinfo, 0);
 
 		status = smb_sd_tofs(op->sd, &fs_sd);
@@ -616,6 +656,10 @@
 		 */
 		secinfo = smb_sd_get_secinfo(op->sd);
 
+		if ((secinfo & SMB_SACL_SECINFO) != 0 &&
+		    !smb_user_has_security_priv(sr->uid_user, cr))
+			return (NT_STATUS_PRIVILEGE_NOT_HELD);
+
 		smb_fssd_init(&fs_sd, secinfo, SMB_FSSD_FLAGS_DIR);
 
 		status = smb_sd_tofs(op->sd, &fs_sd);
@@ -1699,10 +1743,7 @@
 		 * it's not part of DACL. It's only granted via proper
 		 * privileges.
 		 */
-		if ((sr->uid_user->u_privileges &
-		    (SMB_USER_PRIV_BACKUP |
-		    SMB_USER_PRIV_RESTORE |
-		    SMB_USER_PRIV_SECURITY)) == 0)
+		if (!smb_user_has_security_priv(sr->uid_user, cr))
 			return (NT_STATUS_PRIVILEGE_NOT_HELD);
 
 		faccess &= ~ACCESS_SYSTEM_SECURITY;
@@ -1744,9 +1785,13 @@
 /*
  * smb_fsop_lookup_name()
  *
+ * Lookup both the file and stream specified in 'name'.
  * If name indicates that the file is a stream file, perform
  * stream specific lookup, otherwise call smb_fsop_lookup.
  *
+ * On success, returns the found node in *ret_snode. This will be either a named
+ * or unnamed stream node, depending on the name specified.
+ *
  * Return an error if the looked-up file is in outside the tree.
  * (Required when invoked from open path.)
  *
@@ -1768,18 +1813,64 @@
     char	*name,
     smb_node_t	**ret_snode)
 {
-	smb_node_t	*fnode;
-	vnode_t		*xattrdirvp;
-	vnode_t		*vp;
-	char		*od_name;
+	char *sname = NULL;
+	int rc;
+	smb_node_t *tmp_node;
+
+	ASSERT(ret_snode != NULL);
+
+	rc = smb_fsop_lookup_file(sr, cr, flags, root_node, dnode, name,
+	    &sname, ret_snode);
+
+	if (rc != 0 || sname == NULL)
+		return (rc);
+
+	tmp_node = *ret_snode;
+	rc = smb_fsop_lookup_stream(sr, cr, flags, root_node, tmp_node, sname,
+	    ret_snode);
+	kmem_free(sname, MAXNAMELEN);
+	smb_node_release(tmp_node);
+
+	return (rc);
+}
+
+/*
+ * smb_fsop_lookup_file()
+ *
+ * Look up of the file portion of 'name'. If a Stream is specified,
+ * return the stream name in 'sname', which this allocates.
+ * The caller must free 'sname'.
+ *
+ * Return an error if the looked-up file is outside the tree.
+ * (Required when invoked from open path.)
+ *
+ * Case sensitivity flags (SMB_IGNORE_CASE, SMB_CASE_SENSITIVE):
+ * if SMB_CASE_SENSITIVE is set, the SMB_IGNORE_CASE flag will NOT be set
+ * based on the tree's case sensitivity. However, if the SMB_IGNORE_CASE
+ * flag is set in the flags value passed as a parameter, a case insensitive
+ * lookup WILL be done (regardless of whether SMB_CASE_SENSITIVE is set
+ * or not).
+ */
+
+int
+smb_fsop_lookup_file(
+    smb_request_t *sr,
+    cred_t	*cr,
+    int		flags,
+    smb_node_t	*root_node,
+    smb_node_t	*dnode,
+    char	*name,
+    char	**sname,
+    smb_node_t	**ret_snode)
+{
 	char		*fname;
-	char		*sname;
 	int		rc;
 
 	ASSERT(cr);
 	ASSERT(dnode);
 	ASSERT(dnode->n_magic == SMB_NODE_MAGIC);
 	ASSERT(dnode->n_state != SMB_NODE_STATE_DESTROYING);
+	ASSERT(ret_snode != NULL);
 
 	/*
 	 * The following check is required for streams processing, below
@@ -1790,11 +1881,11 @@
 			flags |= SMB_IGNORE_CASE;
 	}
 
-	fname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
-	sname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
-
+	*sname = NULL;
 	if (smb_is_stream_name(name)) {
-		smb_stream_parse_name(name, fname, sname);
+		*sname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
+		fname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
+		smb_stream_parse_name(name, fname, *sname);
 
 		/*
 		 * Look up the unnamed stream (i.e. fname).
@@ -1802,49 +1893,8 @@
 		 * as well as any link target.
 		 */
 		rc = smb_fsop_lookup(sr, cr, flags, root_node, dnode,
-		    fname, &fnode);
-
-		if (rc != 0) {
-			kmem_free(fname, MAXNAMELEN);
-			kmem_free(sname, MAXNAMELEN);
-			return (rc);
-		}
-
-		od_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
-
-		/*
-		 * od_name is the on-disk name of the stream, except
-		 * without the prepended stream prefix (SMB_STREAM_PREFIX)
-		 */
-
-		/*
-		 * XXX
-		 * What permissions NTFS requires for stream lookup if any?
-		 */
-		rc = smb_vop_stream_lookup(fnode->vp, sname, &vp, od_name,
-		    &xattrdirvp, flags, root_node->vp, cr);
-
-		if (rc != 0) {
-			smb_node_release(fnode);
-			kmem_free(fname, MAXNAMELEN);
-			kmem_free(sname, MAXNAMELEN);
-			kmem_free(od_name, MAXNAMELEN);
-			return (rc);
-		}
-
-		*ret_snode = smb_stream_node_lookup(sr, cr, fnode, xattrdirvp,
-		    vp, od_name);
-
-		kmem_free(od_name, MAXNAMELEN);
-		smb_node_release(fnode);
-		VN_RELE(xattrdirvp);
-		VN_RELE(vp);
-
-		if (*ret_snode == NULL) {
-			kmem_free(fname, MAXNAMELEN);
-			kmem_free(sname, MAXNAMELEN);
-			return (ENOMEM);
-		}
+		    fname, ret_snode);
+		kmem_free(fname, MAXNAMELEN);
 	} else {
 		rc = smb_fsop_lookup(sr, cr, flags, root_node, dnode, name,
 		    ret_snode);
@@ -1859,8 +1909,66 @@
 		}
 	}
 
-	kmem_free(fname, MAXNAMELEN);
-	kmem_free(sname, MAXNAMELEN);
+	if (rc != 0 && *sname != NULL) {
+		kmem_free(*sname, MAXNAMELEN);
+		*sname = NULL;
+	}
+	return (rc);
+}
+
+/*
+ * smb_fsop_lookup_stream
+ *
+ * The file exists, see if the stream exists.
+ */
+int
+smb_fsop_lookup_stream(
+    smb_request_t *sr,
+    cred_t *cr,
+    int flags,
+    smb_node_t *root_node,
+    smb_node_t *fnode,
+    char *sname,
+    smb_node_t **ret_snode)
+{
+	char		*od_name;
+	vnode_t		*xattrdirvp;
+	vnode_t		*vp;
+	int rc;
+
+	/*
+	 * The following check is required for streams processing, below
+	 */
+
+	if (!(flags & SMB_CASE_SENSITIVE)) {
+		if (SMB_TREE_IS_CASEINSENSITIVE(sr))
+			flags |= SMB_IGNORE_CASE;
+	}
+
+	od_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
+
+	/*
+	 * od_name is the on-disk name of the stream, except
+	 * without the prepended stream prefix (SMB_STREAM_PREFIX)
+	 */
+
+	rc = smb_vop_stream_lookup(fnode->vp, sname, &vp, od_name,
+	    &xattrdirvp, flags, root_node->vp, cr);
+
+	if (rc != 0) {
+		kmem_free(od_name, MAXNAMELEN);
+		return (rc);
+	}
+
+	*ret_snode = smb_stream_node_lookup(sr, cr, fnode, xattrdirvp,
+	    vp, od_name);
+
+	kmem_free(od_name, MAXNAMELEN);
+	VN_RELE(xattrdirvp);
+	VN_RELE(vp);
+
+	if (*ret_snode == NULL)
+		return (ENOMEM);
 
 	return (rc);
 }
--- a/usr/src/uts/common/fs/smbsrv/smb_node.c	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/uts/common/fs/smbsrv/smb_node.c	Sat Jan 16 16:59:32 2021 +0000
@@ -1479,6 +1479,7 @@
 	int rc;
 	uint_t times_mask;
 	smb_attr_t tmp_attr;
+	smb_node_t *unnamed_node;
 
 	SMB_NODE_VALID(node);
 
@@ -1607,6 +1608,13 @@
 		    FILE_ACTION_MODIFIED, node->od_name);
 	}
 
+	if ((unnamed_node = SMB_IS_STREAM(node)) != NULL) {
+		ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC);
+		ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING);
+		smb_node_notify_change(node->n_dnode,
+		    FILE_ACTION_MODIFIED_STREAM, node->od_name);
+	}
+
 	return (0);
 }
 
--- a/usr/src/uts/common/fs/smbsrv/smb_user.c	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/uts/common/fs/smbsrv/smb_user.c	Sat Jan 16 16:59:32 2021 +0000
@@ -205,6 +205,8 @@
 #include <sys/types.h>
 #include <sys/sid.h>
 #include <sys/priv_names.h>
+#include <sys/priv.h>
+#include <sys/policy.h>
 #include <smbsrv/smb_kproto.h>
 #include <smbsrv/smb_door.h>
 
@@ -831,6 +833,45 @@
 #endif	/* _KERNEL */
 
 /*
+ * Determines whether a user can be granted ACCESS_SYSTEM_SECURITY
+ */
+boolean_t
+smb_user_has_security_priv(smb_user_t *user, cred_t *cr)
+{
+	/* Need SeSecurityPrivilege to get/set SACL */
+	if ((user->u_privileges & SMB_USER_PRIV_SECURITY) != 0)
+		return (B_TRUE);
+
+#ifdef _KERNEL
+	/*
+	 * ACCESS_SYSTEM_SECURITY is also granted if the file is opened with
+	 * BACKUP/RESTORE intent by a user with BACKUP/RESTORE privilege,
+	 * which means we'll be using u_privcred.
+	 *
+	 * We translate BACKUP as DAC_READ and RESTORE as DAC_WRITE,
+	 * to account for our various SMB_USER_* privileges.
+	 */
+	if (PRIV_POLICY_ONLY(cr,
+	    priv_getbyname(PRIV_FILE_DAC_READ, 0), B_FALSE) ||
+	    PRIV_POLICY_ONLY(cr,
+	    priv_getbyname(PRIV_FILE_DAC_WRITE, 0), B_FALSE))
+		return (B_TRUE);
+#else
+	/*
+	 * No "real" privileges in fksmbsrv, so use the SMB privs instead.
+	 */
+	if ((user->u_privileges &
+	    (SMB_USER_PRIV_BACKUP |
+	    SMB_USER_PRIV_RESTORE |
+	    SMB_USER_PRIV_READ_FILE |
+	    SMB_USER_PRIV_WRITE_FILE)) != 0)
+		return (B_TRUE);
+#endif
+
+	return (B_FALSE);
+}
+
+/*
  * Private function to support smb_user_enum.
  */
 static int
--- a/usr/src/uts/common/smbsrv/ndl/netlogon.ndl	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/uts/common/smbsrv/ndl/netlogon.ndl	Sat Jan 16 16:59:32 2021 +0000
@@ -22,7 +22,7 @@
  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  *
- * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
  */
 
 #ifndef _MLSVC_NETR_NDL_
@@ -71,6 +71,8 @@
 #define NETR_OPNUM_LogonGetDomainInfo		0x1D
 #define NETR_OPNUM_ServerPasswordSet2		0x1E
 
+#define	NETR_OPNUM_SamLogonEx			0x27
+
 /*
  * This is not a real NETR OPNUM.  It's used to unpack the
  * struct krb5_validation_info found in the Kerberos PAC.
@@ -302,6 +304,7 @@
 };
 
 
+FAKE
 struct netr_login_info {
 	WORD logon_level;
 	WORD switch_value;
@@ -410,6 +413,19 @@
 	OUT		DWORD status;
 };
 
+ALIGN(2)
+OPERATION(NETR_OPNUM_SamLogonEx)
+struct netr_SamLogonEx {
+	IN		LPTSTR servername;
+	IN		LPTSTR hostname;
+	IN		struct netr_login_info logon_info;
+	INOUT	WORD validation_level;
+  SWITCH(validation_level)
+	OUT		union netr_validation_u ru;
+	OUT		DWORD authoritative;
+	INOUT		DWORD extra_flags;
+	OUT		DWORD status;
+};
 
 /*
  ***********************************************************************
@@ -442,6 +458,8 @@
 		struct netr_ServerAuthenticate2		ServerAuthenticate2;
 	CASE(NETR_OPNUM_SamLogon)
 		struct netr_SamLogon			SamLogon;
+	CASE(NETR_OPNUM_SamLogonEx)
+		struct netr_SamLogonEx			SamLogonEx;
 	CASE(NETR_OPNUM_SamLogoff)
 		struct netr_SamLogoff			SamLogoff;
 	CASE(NETR_OPNUM_ServerPasswordSet)
--- a/usr/src/uts/common/smbsrv/netrauth.h	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/uts/common/smbsrv/netrauth.h	Sat Jan 16 16:59:32 2021 +0000
@@ -22,7 +22,7 @@
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  *
- * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
  */
 
 #ifndef _SMBSRV_NETRAUTH_H
@@ -51,8 +51,61 @@
 /*
  * Negotiation flags for challenge/response authentication.
  */
-#define	NETR_NEGOTIATE_BASE_FLAGS		0x000001FF
-#define	NETR_NEGOTIATE_STRONGKEY_FLAG		0x00004000
+#define	NETR_NEGO_UNUSED_A_FLAG			0x00000001
+#define	NETR_NEGO_BDC_UPDATE_FLAG		0x00000002
+#define	NETR_NEGO_RC4_ENCRYPT_FLAG		0x00000004
+#define	NETR_NEGO_UNUSED_D_FLAG			0x00000008
+
+#define	NETR_NEGO_BDC_CHANGELOG_FLAG		0x00000010
+#define	NETR_NEGO_DC_RESTART_SYNC_FLAG		0x00000020
+#define	NETR_NEGO_VALID2_NOTREQUIRED_FLAG	0x00000040
+#define	NETR_NEGO_OPNUM17_FLAG			0x00000080
+
+#define	NETR_NEGO_PWCHANGE_REFUSE_FLAG		0x00000100
+#define	NETR_NEGO_OPNUM32_FLAG			0x00000200
+#define	NETR_NEGO_GENERIC_PASSTHRU_FLAG		0x00000400
+#define	NETR_NEGO_CONCURRENT_RPC_FLAG		0x00000800
+
+#define	NETR_NEGO_AVOID_USERDB_REPL_FLAG	0x00001000
+#define	NETR_NEGO_AVOID_SECURITYDB_REPL_FLAG	0x00002000
+#define	NETR_NEGO_STRONGKEY_FLAG		0x00004000
+#define	NETR_NEGO_TRANSITIVE_TRUSTFLAG		0x00008000
+
+#define	NETR_NEGO_UNUSED_Q_FLAG			0x00010000
+#define	NETR_NEGO_PASSWORDSET2_FLAG		0x00020000
+#define	NETR_NEGO_GETDOMAININFO_FLAG		0x00040000
+#define	NETR_NEGO_CROSS_FOREST_TRUST_FLAG	0x00080000
+
+#define	NETR_NEGO_IGNORE_NT4EMULATOR_FLAG	0x00100000
+#define	NETR_NEGO_RODC_PASSTHRU_FLAG		0x00200000
+#define	NETR_NEGO_UNDEFINED_9_FLAG		0x00400000
+#define	NETR_NEGO_UNDEFINED_8_FLAG		0x00800000
+
+#define	NETR_NEGO_AES_SHA2_FLAG			0x01000000
+#define	NETR_NEGO_UNDEFINED_6_FLAG		0x02000000
+#define	NETR_NEGO_UNDEFINED_5_FLAG		0x04000000
+#define	NETR_NEGO_UNDEFINED_4_FLAG		0x08000000
+
+#define	NETR_NEGO_UNDEFINED_3_FLAG		0x10000000
+#define	NETR_NEGO_UNUSED_X_FLAG			0x20000000
+#define	NETR_NEGO_SECURE_RPC_FLAG		0x40000000
+#define	NETR_NEGO_UNDEFINED_0_FLAG		0x80000000
+
+/*
+ * TODO: This needs review - some of these are inappropriate.
+ * I.E. BDC_UPDATE, BDC_CHANGELOG, and DC_RESTART_SYNC are
+ * server-to-server only, but we implement a client.
+ */
+#define	NETR_NEGO_BASE_FLAGS	(		\
+	NETR_NEGO_UNUSED_A_FLAG	|		\
+	NETR_NEGO_BDC_UPDATE_FLAG |		\
+	NETR_NEGO_RC4_ENCRYPT_FLAG |		\
+	NETR_NEGO_UNUSED_D_FLAG |		\
+	NETR_NEGO_BDC_CHANGELOG_FLAG |		\
+	NETR_NEGO_DC_RESTART_SYNC_FLAG |	\
+	NETR_NEGO_VALID2_NOTREQUIRED_FLAG |	\
+	NETR_NEGO_OPNUM17_FLAG |		\
+	NETR_NEGO_PWCHANGE_REFUSE_FLAG)		\
 
 #define	NETR_SESSKEY64_SZ			8
 #define	NETR_SESSKEY128_SZ			16
@@ -123,6 +176,8 @@
 	DWORD flags;
 	char server[MAXHOSTNAMELEN];		/* Current DC, FQDN */
 	char hostname[NETBIOS_NAME_SZ * 2];	/* local "flat" name */
+	char nb_domain[NETBIOS_NAME_SZ * 2];	/* Current NetBios domain */
+	char fqdn_domain[MAXHOSTNAMELEN];	/* Current Domain */
 	netr_cred_t client_challenge;
 	netr_cred_t server_challenge;
 	netr_cred_t client_credential;
@@ -130,6 +185,10 @@
 	netr_session_key_t session_key;
 	BYTE password[NETR_MACHINE_ACCT_PASSWD_MAX];
 	time_t timestamp;
+	uint64_t clh_seqnum; /* Client SequenceNumber for Netlogon SSP */
+	DWORD nego_flags; /* Negotiated flags returned from ServerAuthenciate */
+	boolean_t use_secure_rpc; /* Use "SecureRPC" (RPC-level auth) */
+	boolean_t use_logon_ex; /* Use SamLogonEx (instead of SamLogon) */
 } netr_info_t;
 
 /*
@@ -138,8 +197,10 @@
 int netr_gen_skey64(netr_info_t *);
 int netr_gen_skey128(netr_info_t *);
 
-int netr_gen_credentials(BYTE *, netr_cred_t *, DWORD, netr_cred_t *);
+int netr_gen_credentials(BYTE *, netr_cred_t *, DWORD, netr_cred_t *,
+    boolean_t);
 
+void netlogon_init_global(uint32_t);
 
 #define	NETR_A2H(c) (isdigit(c)) ? ((c) - '0') : ((c) - 'A' + 10)
 
--- a/usr/src/uts/common/smbsrv/smb_fsops.h	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/uts/common/smbsrv/smb_fsops.h	Sat Jan 16 16:59:32 2021 +0000
@@ -54,6 +54,10 @@
 int smb_fsop_create(smb_request_t *, cred_t *, smb_node_t *,
     char *, smb_attr_t *, smb_node_t **);
 
+int
+smb_fsop_create_stream(smb_request_t *, cred_t *, smb_node_t *, smb_node_t *,
+    char *, int, smb_attr_t *, smb_node_t **);
+
 int smb_fsop_mkdir(smb_request_t *, cred_t *, smb_node_t *,
     char *, smb_attr_t *, smb_node_t **);
 
@@ -93,12 +97,18 @@
 
 void smb_fsop_eaccess(smb_request_t *, cred_t *, smb_node_t *, uint32_t *);
 
+int smb_fsop_lookup_file(smb_request_t *, cred_t *, int,
+    smb_node_t *, smb_node_t *, char *, char **, smb_node_t **);
+
 int smb_fsop_lookup_name(smb_request_t *, cred_t *, int,
     smb_node_t *, smb_node_t *, char *, smb_node_t **);
 
 int smb_fsop_lookup(smb_request_t *, cred_t *, int,
     smb_node_t *, smb_node_t *, char *, smb_node_t **);
 
+int smb_fsop_lookup_stream(smb_request_t *, cred_t *, int, smb_node_t *,
+    smb_node_t *, char *, smb_node_t **);
+
 int smb_fsop_commit(smb_request_t *, cred_t *, smb_node_t *);
 
 int smb_fsop_aclread(smb_request_t *, cred_t *, smb_node_t *, smb_fssd_t *);
--- a/usr/src/uts/common/smbsrv/smb_kproto.h	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/uts/common/smbsrv/smb_kproto.h	Sat Jan 16 16:59:32 2021 +0000
@@ -739,6 +739,7 @@
 cred_t *smb_kcred_create(void);
 void smb_user_setcred(smb_user_t *, cred_t *, uint32_t);
 boolean_t smb_is_same_user(cred_t *, cred_t *);
+boolean_t smb_user_has_security_priv(smb_user_t *, cred_t *);
 
 /*
  * SMB tree functions (file smb_tree.c)
--- a/usr/src/uts/common/smbsrv/smbinfo.h	Thu Jan 14 17:27:56 2021 +0100
+++ b/usr/src/uts/common/smbsrv/smbinfo.h	Sat Jan 16 16:59:32 2021 +0000
@@ -20,7 +20,7 @@
  */
 /*
  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
  * Copyright 2020 RackTop Systems, Inc.
  */
 
@@ -120,6 +120,8 @@
 #define	SMB_PI_MAXIMUM_CREDITS_DEF	1000
 #define	SMB_PI_MAXIMUM_CREDITS_MAX	1024
 
+#define	SMB_PI_NETLOGON_FLAGS_DEFAULT	0x00000000
+
 /*
  * sv_size is used by the RPC services and should be set to
  * sizeof (smb_version_t).