summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorMatt Barden <mbarden@tintri.com>2020-09-24 15:01:37 -0400
committerGordon Ross <gordon.ross@tintri.com>2021-01-30 14:02:54 -0500
commitce8560eeb961d528e27685fcdd2ffb03e9478dbf (patch)
tree0fdb2e242a356995912775b3d6b765e22af10a4b /usr/src
parent9e3ab9e9117808af4e738ea3ac45888be11e4045 (diff)
downloadillumos-joyent-ce8560eeb961d528e27685fcdd2ffb03e9478dbf.tar.gz
13169 CVE-2020-1472 (ZeroLogon) and SMB authentication
Reviewed by: Joyce McIntosh <jmcintosh@tintri.com> Reviewed by: Evan Layton <elayton@tintri.com> Reviewed by: Gordon Ross <gordon.ross@tintri.com> Reviewed by: Prashanth Badari <prbadari@tintri.com> Reviewed by: Andy Fiddaman <andy@omnios.org> Reviewed by: C Fraire <cfraire@me.com> Approved by: Robert Mustacchi <rm@fingolfin.org>
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/cmd/smbsrv/smbd/server.xml3
-rw-r--r--usr/src/lib/libmlrpc/Makefile.com3
-rw-r--r--usr/src/lib/libmlrpc/common/libmlrpc.h83
-rw-r--r--usr/src/lib/libmlrpc/common/mapfile-vers3
-rw-r--r--usr/src/lib/libmlrpc/common/mlrpc_clh.c23
-rw-r--r--usr/src/lib/libmlrpc/common/ndr.h57
-rw-r--r--usr/src/lib/libmlrpc/common/ndr_auth.c171
-rw-r--r--usr/src/lib/libmlrpc/common/ndr_client.c158
-rw-r--r--usr/src/lib/libmlrpc/common/ndr_marshal.c314
-rw-r--r--usr/src/lib/libmlrpc/common/ndr_ops.c8
-rw-r--r--usr/src/lib/libmlrpc/common/ndr_process.c71
-rw-r--r--usr/src/lib/libmlrpc/common/ndrtypes.ndl4
-rw-r--r--usr/src/lib/libmlrpc/common/rpcpdu.ndl41
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/Makefile.com8
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h4
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers5
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h7
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c49
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/mlsvc_netr.c52
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c213
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c249
-rw-r--r--usr/src/lib/smbsrv/libmlsvc/common/netr_ssp.c494
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/libsmb.h6
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/mapfile-vers3
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_cfg.c3
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_info.c14
-rw-r--r--usr/src/pkg/manifests/system-test-libmlrpctest.mf46
-rw-r--r--usr/src/test/Makefile2
-rw-r--r--usr/src/test/libmlrpc-tests/Makefile20
-rw-r--r--usr/src/test/libmlrpc-tests/cfg/Makefile39
-rw-r--r--usr/src/test/libmlrpc-tests/cfg/krb5_pac.config21
-rw-r--r--usr/src/test/libmlrpc-tests/cfg/samlogon.config31
-rw-r--r--usr/src/test/libmlrpc-tests/cmd/Makefile40
-rw-r--r--usr/src/test/libmlrpc-tests/cmd/libmlrpctest.ksh53
-rw-r--r--usr/src/test/libmlrpc-tests/doc/Makefile36
-rw-r--r--usr/src/test/libmlrpc-tests/doc/README77
-rw-r--r--usr/src/test/libmlrpc-tests/runfiles/Makefile39
-rw-r--r--usr/src/test/libmlrpc-tests/runfiles/default.run29
-rw-r--r--usr/src/test/libmlrpc-tests/tests/Makefile43
-rw-r--r--usr/src/test/libmlrpc-tests/tests/Makefile.com75
-rw-r--r--usr/src/test/libmlrpc-tests/tests/common/util_common.c93
-rw-r--r--usr/src/test/libmlrpc-tests/tests/common/util_common.h34
-rw-r--r--usr/src/test/libmlrpc-tests/tests/netrlogon/Makefile20
-rw-r--r--usr/src/test/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/Makefile29
-rw-r--r--usr/src/test/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/krb5_pac.binbin0 -> 6856 bytes
-rw-r--r--usr/src/test/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/krb5_pac_decode.c76
-rw-r--r--usr/src/test/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/run_krb5_pac_tests.ksh26
-rw-r--r--usr/src/test/libmlrpc-tests/tests/netrlogon/samlogon_tests/Makefile28
-rw-r--r--usr/src/test/libmlrpc-tests/tests/netrlogon/samlogon_tests/run_samlogon_tests.ksh28
-rw-r--r--usr/src/test/libmlrpc-tests/tests/netrlogon/samlogon_tests/samlogon.c341
-rw-r--r--usr/src/tools/ndrgen/ndr_anal.c5
-rw-r--r--usr/src/tools/ndrgen/ndr_gen.c7
-rw-r--r--usr/src/tools/ndrgen/ndr_lex.c5
-rw-r--r--usr/src/tools/ndrgen/ndr_parse.y7
-rw-r--r--usr/src/tools/ndrgen/ndr_print.c8
-rw-r--r--usr/src/tools/ndrgen/ndrgen.h7
-rw-r--r--usr/src/uts/common/smbsrv/ndl/netlogon.ndl20
-rw-r--r--usr/src/uts/common/smbsrv/netrauth.h69
-rw-r--r--usr/src/uts/common/smbsrv/smbinfo.h4
59 files changed, 3182 insertions, 222 deletions
diff --git a/usr/src/cmd/smbsrv/smbd/server.xml b/usr/src/cmd/smbsrv/smbd/server.xml
index 377d6e8072..aff42913ac 100644
--- a/usr/src/cmd/smbsrv/smbd/server.xml
+++ b/usr/src/cmd/smbsrv/smbd/server.xml
@@ -24,6 +24,7 @@ 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 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 @@ file.
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 -->
diff --git a/usr/src/lib/libmlrpc/Makefile.com b/usr/src/lib/libmlrpc/Makefile.com
index 700a8c8968..8e289594cc 100644
--- a/usr/src/lib/libmlrpc/Makefile.com
+++ b/usr/src/lib/libmlrpc/Makefile.com
@@ -23,7 +23,7 @@
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+# Copyright 2020 Tintri by DDN, Inc. All rights reserved.
#
LIBRARY = libmlrpc.a
@@ -31,6 +31,7 @@ VERS = .2
OBJS_COMMON = \
mlrpc_clh.o \
+ ndr_auth.o \
ndr_client.o \
ndr_heap.o \
ndr_marshal.o \
diff --git a/usr/src/lib/libmlrpc/common/libmlrpc.h b/usr/src/lib/libmlrpc/common/libmlrpc.h
index d020532cbe..6b195e4c26 100644
--- a/usr/src/lib/libmlrpc/common/libmlrpc.h
+++ b/usr/src/lib/libmlrpc/common/libmlrpc.h
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
*/
#ifndef _LIBMLRPC_H
@@ -117,6 +117,7 @@ extern "C" {
/* Fake PTYPE DRC discriminators */
#define NDR_DRC_PTYPE_RPCHDR(DRC) ((DRC) | 0x00FF)
#define NDR_DRC_PTYPE_API(DRC) ((DRC) | 0x00AA)
+#define NDR_DRC_PTYPE_SEC(DRC) ((DRC) | 0x00CC)
/* DRC Recognizers */
#define NDR_DRC_IS_OK(DRC) (((DRC) & NDR_DRC_MASK_SPECIFIER) == 0)
@@ -154,6 +155,10 @@ extern "C" {
#define NDR_DRC_FAULT_PARAM_2_UNIMPLEMENTED 0xD200
#define NDR_DRC_FAULT_PARAM_3_INVALID 0xC300
#define NDR_DRC_FAULT_PARAM_3_UNIMPLEMENTED 0xD300
+#define NDR_DRC_FAULT_PARAM_4_INVALID 0xC400
+#define NDR_DRC_FAULT_PARAM_4_UNIMPLEMENTED 0xD400
+#define NDR_DRC_FAULT_PARAM_5_INVALID 0xC500
+#define NDR_DRC_FAULT_PARAM_5_UNIMPLEMENTED 0xD500
#define NDR_DRC_FAULT_OUT_OF_MEMORY 0xF000
@@ -179,6 +184,32 @@ extern "C" {
#define NDR_DRC_FAULT_API_BIND_NO_SLOTS 0x91AA /* RESOURCE_1 */
#define NDR_DRC_FAULT_API_OPNUM_INVALID 0xC1AA /* PARAM_1_INVALID */
+/* Secure RPC and SSPs */
+#define NDR_DRC_FAULT_SEC_TYPE_UNIMPLEMENTED \
+ NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_PARAM_0_UNIMPLEMENTED)
+#define NDR_DRC_FAULT_SEC_LEVEL_UNIMPLEMENTED \
+ NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_PARAM_1_UNIMPLEMENTED)
+#define NDR_DRC_FAULT_SEC_SSP_FAILED \
+ NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_RESOURCE_1)
+#define NDR_DRC_FAULT_SEC_ENCODE_TOO_BIG \
+ NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_ENCODE_TOO_BIG)
+#define NDR_DRC_FAULT_SEC_AUTH_LENGTH_INVALID \
+ NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_PARAM_2_INVALID)
+#define NDR_DRC_FAULT_SEC_AUTH_TYPE_INVALID \
+ NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_PARAM_0_INVALID)
+#define NDR_DRC_FAULT_SEC_AUTH_LEVEL_INVALID \
+ NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_PARAM_1_INVALID)
+#define NDR_DRC_FAULT_SEC_OUT_OF_MEMORY \
+ NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_OUT_OF_MEMORY)
+#define NDR_DRC_FAULT_SEC_ENCODE_FAILED \
+ NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_ENCODE_FAILED)
+#define NDR_DRC_FAULT_SEC_META_INVALID \
+ NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_PARAM_3_INVALID)
+#define NDR_DRC_FAULT_SEC_SEQNUM_INVALID \
+ NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_PARAM_4_INVALID)
+#define NDR_DRC_FAULT_SEC_SIG_INVALID \
+ NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_PARAM_5_INVALID)
+
struct ndr_xa;
struct ndr_client;
@@ -224,12 +255,12 @@ typedef struct ndr_service {
* conn->binding_pool, N_BINDING_POOL);
*/
typedef struct ndr_binding {
- struct ndr_binding *next;
+ struct ndr_binding *next;
ndr_p_context_id_t p_cont_id;
unsigned char which_side;
struct ndr_client *clnt;
ndr_service_t *service;
- void *instance_specific;
+ void *instance_specific;
} ndr_binding_t;
#define NDR_BIND_SIDE_CLIENT 1
@@ -422,14 +453,39 @@ typedef struct ndr_xa {
unsigned short opnum;
ndr_stream_t recv_nds;
ndr_hdr_t recv_hdr;
+ ndr_sec_t recv_auth;
ndr_stream_t send_nds;
ndr_hdr_t send_hdr;
+ ndr_sec_t send_auth;
ndr_binding_t *binding; /* what we're using */
ndr_binding_t *binding_list; /* from connection */
ndr_heap_t *heap;
ndr_pipe_t *pipe;
} ndr_xa_t;
+typedef struct ndr_auth_ops {
+ int (*nao_init)(void *, ndr_xa_t *);
+ int (*nao_recv)(void *, ndr_xa_t *);
+ int (*nao_sign)(void *, ndr_xa_t *);
+ int (*nao_verify)(void *, ndr_xa_t *, boolean_t);
+} ndr_auth_ops_t;
+
+/*
+ * A client provides this structure during bind to indicate
+ * that the RPC runtime should use "Secure RPC" (RPC-level auth).
+ *
+ * Currently, only NETLOGON uses this, and only NETLOGON-based
+ * Integrity protection is supported.
+ */
+typedef struct ndr_auth_ctx {
+ ndr_auth_ops_t auth_ops;
+ void *auth_ctx; /* SSP-specific context */
+ uint32_t auth_context_id;
+ uint8_t auth_type;
+ uint8_t auth_level;
+ boolean_t auth_verify_resp;
+} ndr_auth_ctx_t;
+
/*
* 20-byte opaque id used by various RPC services.
*/
@@ -459,6 +515,8 @@ typedef struct ndr_client {
uint32_t next_call_id;
unsigned next_p_cont_id;
+
+ ndr_auth_ctx_t auth_ctx;
} ndr_client_t;
typedef struct ndr_handle {
@@ -507,6 +565,19 @@ void ndr_remove_frag_hdr(ndr_stream_t *);
void ndr_show_hdr(ndr_common_header_t *);
unsigned ndr_bind_ack_hdr_size(ndr_xa_t *);
unsigned ndr_alter_context_rsp_hdr_size(void);
+int ndr_decode_pdu_auth(ndr_xa_t *);
+int ndr_encode_pdu_auth(ndr_xa_t *);
+void ndr_show_auth(ndr_sec_t *);
+
+/*
+ * MS-RPCE "Secure RPC" (RPC-level auth).
+ * These call the functions in ndr_auth_ops_t, which should be
+ * GSSAPI (or equivalent) calls.
+ */
+int ndr_add_sec_context(ndr_auth_ctx_t *, ndr_xa_t *);
+int ndr_recv_sec_context(ndr_auth_ctx_t *, ndr_xa_t *);
+int ndr_add_auth(ndr_auth_ctx_t *, ndr_xa_t *);
+int ndr_check_auth(ndr_auth_ctx_t *, ndr_xa_t *);
/* ndr_server.c */
void ndr_pipe_worker(ndr_pipe_t *);
@@ -542,7 +613,10 @@ ssize_t ndr_uiomove(caddr_t, size_t, enum uio_rw, struct uio *);
* level (bind) handle is released, we close the connection.
*
* There are some places in libmlsvc where the code assumes that the
- * handle member is first in this struct. careful
+ * handle member is first in this struct. Careful!
+ *
+ * Note that this entire structure is bzero()'d once the ndr_client_t
+ * has been created.
*/
typedef struct mlrpc_handle {
ndr_hdid_t handle; /* keep first */
@@ -550,6 +624,7 @@ typedef struct mlrpc_handle {
} mlrpc_handle_t;
int mlrpc_clh_create(mlrpc_handle_t *, void *);
+uint32_t mlrpc_clh_set_auth(mlrpc_handle_t *, ndr_auth_ctx_t *);
uint32_t mlrpc_clh_bind(mlrpc_handle_t *, ndr_service_t *);
void mlrpc_clh_unbind(mlrpc_handle_t *);
void *mlrpc_clh_free(mlrpc_handle_t *);
diff --git a/usr/src/lib/libmlrpc/common/mapfile-vers b/usr/src/lib/libmlrpc/common/mapfile-vers
index 241d8309d0..21fee6d1ca 100644
--- a/usr/src/lib/libmlrpc/common/mapfile-vers
+++ b/usr/src/lib/libmlrpc/common/mapfile-vers
@@ -20,7 +20,7 @@
#
#
# Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
-# Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+# Copyright 2020 Tintri by DDN, Inc. All rights reserved.
#
#
@@ -45,6 +45,7 @@ SYMBOL_VERSION SUNWprivate {
mlrpc_clh_bind;
mlrpc_clh_create;
mlrpc_clh_free;
+ mlrpc_clh_set_auth;
mlrpc_clh_unbind;
# Allow debug/test programs to provide these.
diff --git a/usr/src/lib/libmlrpc/common/mlrpc_clh.c b/usr/src/lib/libmlrpc/common/mlrpc_clh.c
index 72c051d675..2810ab4544 100644
--- a/usr/src/lib/libmlrpc/common/mlrpc_clh.c
+++ b/usr/src/lib/libmlrpc/common/mlrpc_clh.c
@@ -23,7 +23,7 @@
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
- * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
*/
/*
@@ -71,9 +71,8 @@ mlrpc_clh_create(mlrpc_handle_t *handle, void *ctx)
/*
* Allocate...
*/
- if ((clnt = malloc(sizeof (*clnt))) == NULL)
+ if ((clnt = calloc(1, sizeof (*clnt))) == NULL)
return (ENOMEM);
- bzero(clnt, sizeof (*clnt));
clnt->xa_fd = -1;
@@ -108,6 +107,24 @@ nomem:
return (ENOMEM);
}
+/*
+ * Set up this handle to perform RPC-level authentication.
+ */
+uint32_t
+mlrpc_clh_set_auth(mlrpc_handle_t *handle, ndr_auth_ctx_t *auth_ctx)
+{
+ ndr_client_t *clnt = NULL;
+
+ if ((clnt = handle->clnt) == NULL)
+ return (NT_STATUS_INTERNAL_ERROR);
+
+ if (auth_ctx != NULL) {
+ /* struct copy */
+ clnt->auth_ctx = *auth_ctx;
+ }
+
+ return (NT_STATUS_SUCCESS);
+}
/*
* This call must be made to initialize an RPC client structure and bind
diff --git a/usr/src/lib/libmlrpc/common/ndr.h b/usr/src/lib/libmlrpc/common/ndr.h
index f6af9a22cf..0ed7010f18 100644
--- a/usr/src/lib/libmlrpc/common/ndr.h
+++ b/usr/src/lib/libmlrpc/common/ndr.h
@@ -22,7 +22,7 @@
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
- * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
*/
#ifndef _SMBSRV_NDR_H
@@ -145,6 +145,7 @@ extern "C" {
#define NDR_F_INTERFACE 0x0700 /* type is a union, special */
#define NDR_F_CONFORMANT 0x1000 /* struct conforming (var-size tail) */
#define NDR_F_VARYING 0x2000 /* not implemented */
+#define NDR_F_FAKE 0x4000 /* not a real struct */
struct ndr_heap;
struct ndr_stream;
@@ -230,11 +231,43 @@ typedef struct ndr_stream_ops {
#define NDS_RESET(NDS) (*(NDS)->ndo->ndo_reset)(NDS)
#define NDS_DESTRUCT(NDS) (*(NDS)->ndo->ndo_destruct)(NDS)
+/*
+ * The ndr_stream_t tracks the state for a particular RPC call or response.
+ * A single call/response may be transmitted as multiple fragments, where
+ * each fragment has its own header and sec_trailer. For the layout of
+ * a fragment, see the comment at the top of ndr_marshal.c.
+ *
+ * pdu_base_addr is the buffer in which marshalled/received data is stored.
+ *
+ * pdu_base_offset is pdu_base_addr with a different type.
+ *
+ * pdu_max_size is the size of the buffer in pdu_base_addr.
+ *
+ * pdu_size represents the total amount of data stored in pdu_base_addr.
+ * It is typically less than pdu_max_size, and may contain more than one
+ * fragment (when reconstructing fragmented calls/responses, for example).
+ *
+ * pdu_body_offset points to the offset from pdu_base_addr of the PDU body
+ * of the currently encoded/decoded fragment.
+ *
+ * pdu_body_size is the size of the PDU body in the currently encoded/decoded
+ * fragment.
+ * For what constitutes the PDU Body, see ndr_marshal.c.
+ *
+ * pdu_hdr_size is the size of the header for the currently encoded/decoded
+ * fragment.
+ *
+ * pdu_scan_offset points to the next valid byte in pdu_base_addr
+ * (the next free space for encode, and the next unread byte for decode).
+ */
typedef struct ndr_stream {
unsigned long pdu_size;
unsigned long pdu_max_size;
unsigned long pdu_base_offset;
unsigned long pdu_scan_offset;
+ unsigned long pdu_body_offset;
+ unsigned long pdu_body_size;
+ unsigned long pdu_hdr_size;
unsigned char *pdu_base_addr;
ndr_stream_ops_t *ndo;
@@ -325,6 +358,22 @@ typedef struct ndr_stream {
#define NDR_DIR_IS_IN (encl_ref->stream->dir == NDR_DIR_IN)
#define NDR_DIR_IS_OUT (encl_ref->stream->dir == NDR_DIR_OUT)
+#define NDR_MEMBER_PTR_WITH_ARG(TYPE, MEMBER, OFFSET, \
+ ARGFLAGS, ARGMEM, ARGVAL) { \
+ myref.pdu_offset = encl_ref->pdu_offset + (OFFSET); \
+ myref.name = MEMBER_STR(MEMBER); \
+ myref.datum = (char *)val->MEMBER; \
+ myref.inner_flags = ARGFLAGS; \
+ myref.ti = &ndt_##TYPE; \
+ myref.ARGMEM = ARGVAL; \
+ if (!ndr_inner(&myref)) \
+ return (0); \
+ }
+
+#define NDR_MEMBER_PTR_WITH_DIMENSION(TYPE, MEMBER, OFFSET, SIZE_IS) \
+ NDR_MEMBER_PTR_WITH_ARG(TYPE, MEMBER, OFFSET, \
+ NDR_F_DIMENSION_IS, dimension_is, SIZE_IS)
+
#define NDR_MEMBER_WITH_ARG(TYPE, MEMBER, OFFSET, \
ARGFLAGS, ARGMEM, ARGVAL) { \
myref.pdu_offset = encl_ref->pdu_offset + (OFFSET); \
@@ -374,16 +423,16 @@ typedef struct ndr_stream {
return (0); \
}
-#define NDR_TOPMOST_MEMBER(TYPE, MEMBER) \
+#define NDR_TOPMOST_MEMBER(TYPE, MEMBER) \
NDR_TOPMOST_MEMBER_WITH_ARG(TYPE, MEMBER, \
NDR_F_NONE, size_is, 0)
#define NDR_TOPMOST_MEMBER_ARR_WITH_SIZE_IS(TYPE, MEMBER, SIZE_IS) \
- NDR_TOPMOST_MEMBER_WITH_ARG(TYPE, MEMBER, \
+ NDR_TOPMOST_MEMBER_WITH_ARG(TYPE, MEMBER, \
NDR_F_SIZE_IS, size_is, SIZE_IS)
#define NDR_TOPMOST_MEMBER_ARR_WITH_DIMENSION(TYPE, MEMBER, SIZE_IS) \
- NDR_TOPMOST_MEMBER_WITH_ARG(TYPE, MEMBER, \
+ NDR_TOPMOST_MEMBER_WITH_ARG(TYPE, MEMBER, \
NDR_F_DIMENSION_IS, dimension_is, SIZE_IS)
#define NDR_TOPMOST_MEMBER_PTR_WITH_SIZE_IS(TYPE, MEMBER, SIZE_IS) \
diff --git a/usr/src/lib/libmlrpc/common/ndr_auth.c b/usr/src/lib/libmlrpc/common/ndr_auth.c
new file mode 100644
index 0000000000..6976f2b908
--- /dev/null
+++ b/usr/src/lib/libmlrpc/common/ndr_auth.c
@@ -0,0 +1,171 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2020 Tintri by DDN, Inc. All Rights Reserved.
+ */
+
+#include <libmlrpc.h>
+#include <sys/sysmacros.h>
+#include <strings.h>
+
+/*
+ * Initializes the sec_trailer (ndr_sec_t).
+ * The actual token is allocated and set later (in the SSP).
+ */
+int
+ndr_add_auth_token(ndr_auth_ctx_t *ctx, ndr_xa_t *mxa)
+{
+ ndr_stream_t *nds = &mxa->send_nds;
+ ndr_sec_t *secp = &mxa->send_auth;
+
+ secp->auth_type = ctx->auth_type;
+ secp->auth_level = ctx->auth_level;
+ secp->auth_rsvd = 0;
+
+ /*
+ * [MS-RPCE] 2.2.2.12 "Authentication Tokens"
+ * auth_pad_len aligns the packet to 16 bytes.
+ */
+ secp->auth_pad_len = P2ROUNDUP(nds->pdu_scan_offset, 16) -
+ nds->pdu_scan_offset;
+ if (NDS_PAD_PDU(nds, nds->pdu_scan_offset,
+ secp->auth_pad_len, NULL) == 0)
+ return (NDR_DRC_FAULT_SEC_ENCODE_TOO_BIG);
+
+ /* PAD_PDU doesn't adjust scan_offset */
+ nds->pdu_scan_offset += secp->auth_pad_len;
+ nds->pdu_body_size = nds->pdu_scan_offset -
+ nds->pdu_body_offset;
+
+ secp->auth_context_id = ctx->auth_context_id;
+ return (NDR_DRC_OK);
+}
+
+/*
+ * Does gss_init_sec_context (or equivalent) and creates
+ * the sec_trailer and the auth token.
+ *
+ * Used during binds (and alter context).
+ *
+ * Currently, only NETLOGON auth with Integrity protection is implemented.
+ */
+int
+ndr_add_sec_context(ndr_auth_ctx_t *ctx, ndr_xa_t *mxa)
+{
+ int rc;
+
+ if (ctx->auth_level == NDR_C_AUTHN_NONE ||
+ ctx->auth_type == NDR_C_AUTHN_NONE)
+ return (NDR_DRC_OK);
+
+ if (ctx->auth_type != NDR_C_AUTHN_GSS_NETLOGON)
+ return (NDR_DRC_FAULT_SEC_TYPE_UNIMPLEMENTED);
+
+ if (ctx->auth_level != NDR_C_AUTHN_LEVEL_PKT_INTEGRITY)
+ return (NDR_DRC_FAULT_SEC_LEVEL_UNIMPLEMENTED);
+
+ if ((rc = ndr_add_auth_token(ctx, mxa)) != 0)
+ return (rc);
+
+ return (ctx->auth_ops.nao_init(ctx->auth_ctx, mxa));
+}
+
+/*
+ * Does response-side gss_init_sec_context (or equivalent) and validates
+ * the sec_trailer and the auth token.
+ *
+ * Used during bind (and alter context) ACKs.
+ */
+int
+ndr_recv_sec_context(ndr_auth_ctx_t *ctx, ndr_xa_t *mxa)
+{
+ ndr_sec_t *bind_secp = &mxa->send_auth;
+ ndr_sec_t *ack_secp = &mxa->recv_auth;
+
+ if (ctx->auth_level == NDR_C_AUTHN_NONE ||
+ ctx->auth_type == NDR_C_AUTHN_NONE) {
+ if (mxa->recv_hdr.common_hdr.auth_length != 0)
+ return (NDR_DRC_FAULT_SEC_AUTH_LENGTH_INVALID);
+ return (NDR_DRC_OK);
+ } else if (mxa->recv_hdr.common_hdr.auth_length == 0) {
+ return (NDR_DRC_FAULT_SEC_AUTH_LENGTH_INVALID);
+ }
+
+ if (bind_secp->auth_type != ack_secp->auth_type)
+ return (NDR_DRC_FAULT_SEC_AUTH_TYPE_INVALID);
+ if (bind_secp->auth_level != ack_secp->auth_level)
+ return (NDR_DRC_FAULT_SEC_AUTH_LEVEL_INVALID);
+
+ return (ctx->auth_ops.nao_recv(ctx->auth_ctx, mxa));
+}
+
+/*
+ * Does gss_MICEx (or equivalent) and creates
+ * the sec_trailer and the auth token.
+ *
+ * Used upon sending a request (client)/response (server) packet.
+ */
+int
+ndr_add_auth(ndr_auth_ctx_t *ctx, ndr_xa_t *mxa)
+{
+ int rc;
+
+ if (ctx->auth_level == NDR_C_AUTHN_NONE ||
+ ctx->auth_type == NDR_C_AUTHN_NONE)
+ return (NDR_DRC_OK);
+
+ if (ctx->auth_type != NDR_C_AUTHN_GSS_NETLOGON)
+ return (NDR_DRC_FAULT_SEC_TYPE_UNIMPLEMENTED);
+
+ if (ctx->auth_level != NDR_C_AUTHN_LEVEL_PKT_INTEGRITY)
+ return (NDR_DRC_FAULT_SEC_LEVEL_UNIMPLEMENTED);
+
+ if ((rc = ndr_add_auth_token(ctx, mxa)) != 0)
+ return (rc);
+
+ return (ctx->auth_ops.nao_sign(ctx->auth_ctx, mxa));
+}
+
+/*
+ * Does gss_VerifyMICEx (or equivalent) and validates
+ * the sec_trailer and the auth token.
+ *
+ * Used upon receiving a request (server)/response (client) packet.
+ *
+ * If auth_verify_resp is B_FALSE, this doesn't verify responses (but
+ * the SSP may still have side-effects).
+ */
+int
+ndr_check_auth(ndr_auth_ctx_t *ctx, ndr_xa_t *mxa)
+{
+ ndr_sec_t *secp = &mxa->recv_auth;
+
+ if (ctx->auth_level == NDR_C_AUTHN_NONE ||
+ ctx->auth_type == NDR_C_AUTHN_NONE) {
+ if (mxa->recv_hdr.common_hdr.auth_length != 0)
+ return (NDR_DRC_FAULT_SEC_AUTH_LENGTH_INVALID);
+ return (NDR_DRC_OK);
+ } else if (mxa->recv_hdr.common_hdr.auth_length == 0) {
+ return (NDR_DRC_FAULT_SEC_AUTH_LENGTH_INVALID);
+ }
+
+ if (ctx->auth_type != secp->auth_type ||
+ ctx->auth_type != NDR_C_AUTHN_GSS_NETLOGON)
+ return (NDR_DRC_FAULT_SEC_AUTH_TYPE_INVALID);
+
+ if (ctx->auth_level != secp->auth_level ||
+ ctx->auth_level != NDR_C_AUTHN_LEVEL_PKT_INTEGRITY)
+ return (NDR_DRC_FAULT_SEC_AUTH_LEVEL_INVALID);
+
+ return (ctx->auth_ops.nao_verify(ctx->auth_ctx, mxa,
+ ctx->auth_verify_resp));
+}
diff --git a/usr/src/lib/libmlrpc/common/ndr_client.c b/usr/src/lib/libmlrpc/common/ndr_client.c
index 56cc1847ef..7dbbfed83b 100644
--- a/usr/src/lib/libmlrpc/common/ndr_client.c
+++ b/usr/src/lib/libmlrpc/common/ndr_client.c
@@ -22,7 +22,7 @@
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
- * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
*/
#include <sys/errno.h>
@@ -45,6 +45,7 @@ ndr_clnt_bind(ndr_client_t *clnt, ndr_service_t *msvc,
ndr_binding_t *mbind;
ndr_xa_t mxa;
ndr_bind_hdr_t *bhdr;
+ ndr_common_header_t *hdr;
ndr_p_cont_elem_t *pce;
ndr_bind_ack_hdr_t *bahdr;
ndr_p_result_t *pre;
@@ -59,8 +60,8 @@ ndr_clnt_bind(ndr_client_t *clnt, ndr_service_t *msvc,
ndr_clnt_init_hdr(clnt, &mxa);
bhdr = &mxa.send_hdr.bind_hdr;
- bhdr->common_hdr.ptype = NDR_PTYPE_BIND;
- bhdr->common_hdr.frag_length = sizeof (*bhdr);
+ hdr = &mxa.send_hdr.bind_hdr.common_hdr;
+ hdr->ptype = NDR_PTYPE_BIND;
bhdr->max_xmit_frag = NDR_DEFAULT_FRAGSZ;
bhdr->max_recv_frag = NDR_DEFAULT_FRAGSZ;
bhdr->assoc_group_id = 0;
@@ -89,6 +90,30 @@ ndr_clnt_bind(ndr_client_t *clnt, ndr_service_t *msvc,
if ((*clnt->xa_init)(clnt, &mxa) < 0)
return (NDR_DRC_FAULT_OUT_OF_MEMORY);
+ /* Reserve room for hdr */
+ mxa.send_nds.pdu_scan_offset = sizeof (*bhdr);
+
+ /* GSS_Init_sec_context */
+ rc = ndr_add_sec_context(&clnt->auth_ctx, &mxa);
+ if (NDR_DRC_IS_FAULT(rc))
+ goto fault_exit;
+
+ rc = ndr_encode_pdu_auth(&mxa);
+ if (NDR_DRC_IS_FAULT(rc))
+ goto fault_exit;
+
+ /*
+ * If we have auth data, then pdu_size has been initialized.
+ * Otherwise, it hasn't.
+ */
+ if (hdr->auth_length == 0)
+ hdr->frag_length = sizeof (*bhdr);
+ else
+ hdr->frag_length = mxa.send_nds.pdu_size;
+
+ /* Reset scan_offset to header */
+ mxa.send_nds.pdu_scan_offset = 0;
+
rc = ndr_encode_pdu_hdr(&mxa);
if (NDR_DRC_IS_FAULT(rc))
goto fault_exit;
@@ -102,21 +127,36 @@ ndr_clnt_bind(ndr_client_t *clnt, ndr_service_t *msvc,
if (NDR_DRC_IS_FAULT(rc))
goto fault_exit;
- /* done with buffers */
- (*clnt->xa_destruct)(clnt, &mxa);
+ rc = ndr_decode_pdu_auth(&mxa);
+ if (NDR_DRC_IS_FAULT(rc))
+ goto fault_exit;
bahdr = &mxa.recv_hdr.bind_ack_hdr;
- if (mxa.ptype != NDR_PTYPE_BIND_ACK)
- return (NDR_DRC_FAULT_RECEIVED_MALFORMED);
+ if (mxa.ptype != NDR_PTYPE_BIND_ACK) {
+ rc = NDR_DRC_FAULT_RECEIVED_MALFORMED;
+ goto fault_exit;
+ }
- if (bahdr->p_result_list.n_results != 1)
- return (NDR_DRC_FAULT_RECEIVED_MALFORMED);
+ if (bahdr->p_result_list.n_results != 1) {
+ rc = NDR_DRC_FAULT_RECEIVED_MALFORMED;
+ goto fault_exit;
+ }
pre = &bahdr->p_result_list.p_results[0];
- if (pre->result != NDR_PCDR_ACCEPTANCE)
- return (NDR_DRC_FAULT_RECEIVED_MALFORMED);
+ if (pre->result != NDR_PCDR_ACCEPTANCE) {
+ rc = NDR_DRC_FAULT_RECEIVED_MALFORMED;
+ goto fault_exit;
+ }
+
+ /* GSS_init_sec_context 2 */
+ rc = ndr_recv_sec_context(&clnt->auth_ctx, &mxa);
+ if (NDR_DRC_IS_FAULT(rc))
+ goto fault_exit;
+
+ /* done with buffers */
+ (*clnt->xa_destruct)(clnt, &mxa);
mbind->p_cont_id = pce->p_cont_id;
mbind->which_side = NDR_BIND_SIDE_CLIENT;
@@ -139,7 +179,7 @@ ndr_clnt_call(ndr_binding_t *mbind, int opnum, void *params)
ndr_xa_t mxa;
ndr_request_hdr_t *reqhdr;
ndr_common_header_t *rsphdr;
- unsigned long recv_pdu_scan_offset;
+ unsigned long recv_pdu_scan_offset, recv_pdu_size;
int rc;
bzero(&mxa, sizeof (mxa));
@@ -160,20 +200,36 @@ ndr_clnt_call(ndr_binding_t *mbind, int opnum, void *params)
/* Reserve room for hdr */
mxa.send_nds.pdu_scan_offset = sizeof (*reqhdr);
+ /* pdu_scan_offset now points to start of stub */
+ mxa.send_nds.pdu_body_offset = mxa.send_nds.pdu_scan_offset;
rc = ndr_encode_call(&mxa, params);
if (!NDR_DRC_IS_OK(rc))
goto fault_exit;
- mxa.send_nds.pdu_scan_offset = 0;
+ /*
+ * With the Stub data encoded, calculate the alloc_hint
+ * before we add padding or auth data.
+ */
+ reqhdr->alloc_hint = mxa.send_nds.pdu_size -
+ sizeof (ndr_request_hdr_t);
+
+ /* GSS_WrapEx/VerifyMICEx */
+ rc = ndr_add_auth(&clnt->auth_ctx, &mxa);
+ if (NDR_DRC_IS_FAULT(rc))
+ goto fault_exit;
+
+ rc = ndr_encode_pdu_auth(&mxa);
+ if (NDR_DRC_IS_FAULT(rc))
+ goto fault_exit;
/*
* Now we have the PDU size, we need to set up the
- * frag_length and calculate the alloc_hint.
+ * frag_length.
+ * Also reset pdu_scan_offset to header.
*/
mxa.send_hdr.common_hdr.frag_length = mxa.send_nds.pdu_size;
- reqhdr->alloc_hint = mxa.send_nds.pdu_size -
- sizeof (ndr_request_hdr_t);
+ mxa.send_nds.pdu_scan_offset = 0;
rc = ndr_encode_pdu_hdr(&mxa);
if (NDR_DRC_IS_FAULT(rc))
@@ -192,26 +248,44 @@ ndr_clnt_call(ndr_binding_t *mbind, int opnum, void *params)
goto fault_exit;
}
+ rc = ndr_decode_pdu_auth(&mxa);
+ if (NDR_DRC_IS_FAULT(rc))
+ goto fault_exit;
+
+ rc = ndr_check_auth(&clnt->auth_ctx, &mxa);
+ if (NDR_DRC_IS_FAULT(rc))
+ goto fault_exit;
+
rsphdr = &mxa.recv_hdr.common_hdr;
if (!NDR_IS_LAST_FRAG(rsphdr->pfc_flags)) {
/*
* This is a multi-fragment response.
- * Preserve the current scan offset while getting
+ * Preserve the current body offset while getting
* fragments so that we can continue afterward
* as if we had received the entire response as
* a single PDU.
+ *
+ * GROW_PDU trashes pdu_size; reset it afterwards.
*/
+ recv_pdu_size = mxa.recv_nds.pdu_size;
(void) NDS_GROW_PDU(&mxa.recv_nds, NDR_MULTI_FRAGSZ, NULL);
- recv_pdu_scan_offset = mxa.recv_nds.pdu_scan_offset;
- mxa.recv_nds.pdu_scan_offset = rsphdr->frag_length;
- mxa.recv_nds.pdu_size = rsphdr->frag_length;
+ /*
+ * pdu_scan_offset needs to be the first byte after the first
+ * fragment in pdu_base_addr (minus the sec_trailer).
+ *
+ * pdu_size needs to be all of the (usable) data we've
+ * received thus far.
+ */
+ recv_pdu_scan_offset = mxa.recv_nds.pdu_body_offset;
+ mxa.recv_nds.pdu_scan_offset = mxa.recv_nds.pdu_body_offset +
+ mxa.recv_nds.pdu_body_size - mxa.recv_auth.auth_pad_len;
+ mxa.recv_nds.pdu_size = recv_pdu_size;
- if (ndr_clnt_get_frags(clnt, &mxa) < 0) {
- rc = NDR_DRC_FAULT_RECEIVED_MALFORMED;
+ rc = ndr_clnt_get_frags(clnt, &mxa);
+ if (NDR_DRC_IS_FAULT(rc))
goto fault_exit;
- }
mxa.recv_nds.pdu_scan_offset = recv_pdu_scan_offset;
}
@@ -225,6 +299,15 @@ ndr_clnt_call(ndr_binding_t *mbind, int opnum, void *params)
return (NDR_DRC_OK);
fault_exit:
+ ndr_show_hdr(&mxa.send_hdr.common_hdr);
+ nds_show_state(&mxa.send_nds);
+ if (mxa.send_hdr.common_hdr.auth_length != 0)
+ ndr_show_auth(&mxa.send_auth);
+
+ ndr_show_hdr(&mxa.recv_hdr.common_hdr);
+ nds_show_state(&mxa.recv_nds);
+ if (mxa.recv_hdr.common_hdr.auth_length != 0)
+ ndr_show_auth(&mxa.recv_auth);
(*clnt->xa_destruct)(clnt, &mxa);
return (rc);
}
@@ -270,23 +353,44 @@ ndr_clnt_get_frags(ndr_client_t *clnt, ndr_xa_t *mxa)
ndr_common_header_t hdr;
int frag_size;
int last_frag;
+ int rc;
do {
if (ndr_clnt_get_frag(clnt, mxa, &hdr) < 0) {
nds_show_state(nds);
- return (-1);
+ return (NDR_DRC_FAULT_RECEIVED_RUNT);
}
last_frag = NDR_IS_LAST_FRAG(hdr.pfc_flags);
frag_size = hdr.frag_length;
+ /*
+ * ndr_clnt_get_frag() doesn't change pdu_scan_offset.
+ */
if (frag_size > (nds->pdu_size - nds->pdu_scan_offset)) {
nds_show_state(nds);
- return (-1);
+ return (NDR_DRC_FAULT_RECEIVED_MALFORMED);
}
+ if (hdr.auth_length != 0 && hdr.auth_length >
+ (hdr.frag_length - nds->pdu_hdr_size - SEC_TRAILER_SIZE))
+ return (NDR_DRC_FAULT_RECEIVED_MALFORMED);
+
+ rc = ndr_decode_pdu_auth(mxa);
+ if (NDR_DRC_IS_FAULT(rc))
+ return (rc);
+
+ rc = ndr_check_auth(&clnt->auth_ctx, mxa);
+ if (NDR_DRC_IS_FAULT(rc))
+ return (rc);
+
+ /*
+ * Headers, Auth Padding, and auth data shouldn't be kept
+ * from fragments.
+ */
ndr_remove_frag_hdr(nds);
- nds->pdu_scan_offset += frag_size - NDR_RSP_HDR_SIZE;
+ nds->pdu_scan_offset +=
+ nds->pdu_body_size - mxa->recv_auth.auth_pad_len;
} while (!last_frag);
return (0);
@@ -307,7 +411,7 @@ ndr_clnt_get_frag(ndr_client_t *clnt, ndr_xa_t *mxa, ndr_common_header_t *hdr)
available = nds->pdu_size - nds->pdu_scan_offset;
while (available < NDR_RSP_HDR_SIZE) {
- if ((nbytes += (*clnt->xa_read)(clnt, mxa)) <= 0)
+ if ((nbytes = (*clnt->xa_read)(clnt, mxa)) <= 0)
return (-1);
available += nbytes;
}
diff --git a/usr/src/lib/libmlrpc/common/ndr_marshal.c b/usr/src/lib/libmlrpc/common/ndr_marshal.c
index 4d34030d2a..33345aa3dd 100644
--- a/usr/src/lib/libmlrpc/common/ndr_marshal.c
+++ b/usr/src/lib/libmlrpc/common/ndr_marshal.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
*/
#include <assert.h>
@@ -38,18 +38,40 @@ static const int ndr_native_byte_order = NDR_REPLAB_INTG_LITTLE_ENDIAN;
static int ndr_decode_hdr_common(ndr_stream_t *, ndr_common_header_t *);
static int ndr_decode_pac_hdr(ndr_stream_t *, ndr_pac_hdr_t *);
+/*
+ * This is the layout of an RPC PDU, as shown in
+ * [MS-RPCE] 2.2.2.13 "Verification Trailer".
+ *
+ * +-------------------------------+
+ * | PDU Header |
+ * +-------------------------------+ ====
+ * | Stub Data |
+ * +-------------------------------+ PDU
+ * | Stub Padding Octets |
+ * +-------------------------------+ Body
+ * | Verification Trailer |
+ * +-------------------------------+ Here
+ * | Authentication Padding |
+ * +-------------------------------+ ====
+ * | sec_trailer |
+ * +-------------------------------+
+ * | Authentication Token |
+ * +-------------------------------+
+ *
+ * We don't use the "Verification Trailer" for anything yet.
+ * sec_trailer and Authentication Token are for Secure RPC,
+ * and are collectively the 'auth_verifier_co' in DCERPC.
+ *
+ * Each fragment of a multi-fragment response has a unique
+ * header and, if authentication was requested, a unique
+ * sec_trailer.
+ */
+
static int
-ndr_encode_decode_common(ndr_stream_t *nds, unsigned opnum,
- ndr_typeinfo_t *ti, void *datum)
+ndr_convert_nds_error(ndr_stream_t *nds)
{
int rc;
- /*
- * Perform the (un)marshalling
- */
- if (ndo_operation(nds, ti, opnum, datum))
- return (NDR_DRC_OK);
-
switch (nds->error) {
case NDR_ERR_MALLOC_FAILED:
rc = NDR_DRC_FAULT_OUT_OF_MEMORY;
@@ -78,6 +100,31 @@ ndr_encode_decode_common(ndr_stream_t *nds, unsigned opnum,
return (rc);
}
+static int
+ndr_encode_decode_common(ndr_stream_t *nds, unsigned opnum,
+ ndr_typeinfo_t *ti, void *datum)
+{
+ /*
+ * Perform the (un)marshalling
+ */
+ if (ndo_operation(nds, ti, opnum, datum))
+ return (NDR_DRC_OK);
+
+ return (ndr_convert_nds_error(nds));
+}
+
+static int
+ndr_encode_decode_type(ndr_stream_t *nds, ndr_typeinfo_t *ti, void *datum)
+{
+ /*
+ * Perform the (un)marshalling
+ */
+ if (ndo_process(nds, ti, datum))
+ return (NDR_DRC_OK);
+
+ return (ndr_convert_nds_error(nds));
+}
+
ndr_buf_t *
ndr_buf_init(ndr_typeinfo_t *ti)
{
@@ -115,7 +162,7 @@ ndr_buf_fini(ndr_buf_t *nbuf)
*
* pac_info_t info;
*
- * if ((nbuf = ndr_buf_init(&TYPEINFO(ndr_pac)) != NULL) {
+ * if ((nbuf = ndr_buf_init(&TYPEINFO(ndr_pac)) != NULL) {
* rc = ndr_decode_buf(nbuf, opnum, data, datalen, &info);
* ...
* ndr_buf_fini(nbuf);
@@ -145,6 +192,7 @@ ndr_buf_decode(ndr_buf_t *nbuf, unsigned hdr_type, unsigned opnum,
return (rc);
bcopy(data, nbuf->nb_nds.pdu_base_addr, datalen);
+ nbuf->nb_nds.pdu_size = datalen;
switch (hdr_type) {
case NDR_PTYPE_COMMON:
@@ -252,7 +300,9 @@ ndr_decode_pdu_hdr(ndr_xa_t *mxa)
ndr_common_header_t *hdr = &mxa->recv_hdr.common_hdr;
ndr_stream_t *nds = &mxa->recv_nds;
int rc;
+ ulong_t saved_offset;
+ saved_offset = nds->pdu_scan_offset;
rc = ndr_decode_hdr_common(nds, hdr);
if (NDR_DRC_IS_FAULT(rc))
return (rc);
@@ -264,6 +314,16 @@ ndr_decode_pdu_hdr(ndr_xa_t *mxa)
return (NDR_DRC_FAULT_RPCHDR_DECODE_FAILED);
mxa->ptype = hdr->ptype;
+ /* pdu_scan_offset now points to (this fragment's) stub data */
+ nds->pdu_body_offset = nds->pdu_scan_offset;
+ nds->pdu_hdr_size = nds->pdu_scan_offset - saved_offset;
+ nds->pdu_body_size = hdr->frag_length - hdr->auth_length -
+ nds->pdu_hdr_size -
+ ((hdr->auth_length != 0) ? SEC_TRAILER_SIZE : 0);
+
+ if (hdr->auth_length != 0 && hdr->auth_length >
+ (hdr->frag_length - nds->pdu_hdr_size - SEC_TRAILER_SIZE))
+ return (NDR_DRC_FAULT_RECEIVED_MALFORMED);
return (NDR_DRC_OK);
}
@@ -274,6 +334,7 @@ ndr_decode_hdr_common(ndr_stream_t *nds, ndr_common_header_t *hdr)
int rc;
int charset;
int byte_order;
+ ulong_t saved_offset;
if (nds->m_op != NDR_M_OP_UNMARSHALL)
return (NDR_DRC_FAULT_RPCHDR_MODE_MISMATCH);
@@ -281,8 +342,8 @@ ndr_decode_hdr_common(ndr_stream_t *nds, ndr_common_header_t *hdr)
/*
* All PDU headers are at least this big
*/
- rc = NDS_GROW_PDU(nds, sizeof (ndr_common_header_t), 0);
- if (!rc)
+ saved_offset = nds->pdu_scan_offset;
+ if ((nds->pdu_size - saved_offset) < sizeof (ndr_common_header_t))
return (NDR_DRC_FAULT_RPCHDR_RECEIVED_RUNT);
/*
@@ -315,6 +376,8 @@ ndr_decode_hdr_common(ndr_stream_t *nds, ndr_common_header_t *hdr)
rc = ndr_encode_decode_common(nds, ptype, &TYPEINFO(ndr_hdr), hdr);
+ if (hdr->frag_length > (nds->pdu_size - saved_offset))
+ rc = NDR_DRC_FAULT_RECEIVED_MALFORMED;
return (NDR_DRC_PTYPE_RPCHDR(rc));
}
@@ -329,8 +392,7 @@ ndr_decode_pac_hdr(ndr_stream_t *nds, ndr_pac_hdr_t *hdr)
/*
* All PDU headers are at least this big
*/
- rc = NDS_GROW_PDU(nds, sizeof (ndr_pac_hdr_t), 0);
- if (!rc)
+ if ((nds->pdu_size - nds->pdu_scan_offset) < sizeof (ndr_pac_hdr_t))
return (NDR_DRC_FAULT_RPCHDR_RECEIVED_RUNT);
/*
@@ -388,6 +450,13 @@ ndr_decode_frag_hdr(ndr_stream_t *nds, ndr_common_header_t *hdr)
sizeof (WORD));
nds_bswap(&tmp->call_id, &hdr->call_id, sizeof (DWORD));
}
+
+ /* pdu_scan_offset points to byte 0 of this fragment */
+ nds->pdu_hdr_size = NDR_RSP_HDR_SIZE;
+ nds->pdu_body_offset = nds->pdu_scan_offset + nds->pdu_hdr_size;
+ nds->pdu_body_size = hdr->frag_length - hdr->auth_length -
+ nds->pdu_hdr_size -
+ ((hdr->auth_length != 0) ? SEC_TRAILER_SIZE : 0);
}
/*
@@ -418,7 +487,10 @@ ndr_remove_frag_hdr(ndr_stream_t *nds)
data = hdr + NDR_RSP_HDR_SIZE;
nbytes = nds->pdu_size - nds->pdu_scan_offset - NDR_RSP_HDR_SIZE;
- bcopy(data, hdr, nbytes);
+ /*
+ * Move all of the data after the header back to where the header began.
+ */
+ memmove(hdr, data, nbytes);
nds->pdu_size -= NDR_RSP_HDR_SIZE;
}
@@ -442,9 +514,24 @@ ndr_show_hdr(ndr_common_header_t *hdr)
fragtype = "intermediate";
ndo_printf(NULL, NULL,
- "ndr hdr: %d.%d ptype=%d, %s frag (flags=0x%08x) len=%d",
+ "ndr hdr: %d.%d ptype=%d, %s frag (flags=0x%08x) len=%d "
+ "auth_len=%d",
hdr->rpc_vers, hdr->rpc_vers_minor, hdr->ptype,
- fragtype, hdr->pfc_flags, hdr->frag_length);
+ fragtype, hdr->pfc_flags, hdr->frag_length, hdr->auth_length);
+}
+
+void
+ndr_show_auth(ndr_sec_t *auth)
+{
+ if (auth == NULL) {
+ ndo_printf(NULL, NULL, "ndr auth: <null>");
+ return;
+ }
+
+ ndo_printf(NULL, NULL,
+ "ndr auth: type=0x%x, level=0x%x, pad_len=%d, ctx_id=%d",
+ auth->auth_type, auth->auth_level, auth->auth_pad_len,
+ auth->auth_context_id);
}
int
@@ -646,3 +733,196 @@ ndr_alter_context_rsp_hdr_size(void)
offset += sizeof (ndr_p_result_list_t);
return (offset);
}
+
+/*
+ * This is a hand-coded (un)marshalling routine for auth_verifier_co
+ * (aka ndr_sec_t).
+ *
+ * We need to pretend this structure isn't variably sized, until ndrgen
+ * has been modified to support variable-sized arrays.
+ * Here, we only account for the fixed-size members (8 bytes), plus
+ * a pointer for the C structure.
+ *
+ * We then convert between a pointer to the auth token (auth_value,
+ * allocated here during unmarshall) and a flat, 'fixed'-sized array.
+ */
+
+int ndr__auth_verifier_co(ndr_ref_t *encl_ref);
+ndr_typeinfo_t ndt__auth_verifier_co = {
+ 1, /* NDR version */
+ 3, /* alignment */
+ NDR_F_STRUCT, /* flags */
+ ndr__auth_verifier_co, /* ndr_func */
+ 8, /* pdu_size_fixed_part */
+ 0, /* pdu_size_variable_part */
+ 8 + sizeof (void *), /* c_size_fixed_part */
+ 0, /* c_size_variable_part */
+};
+
+/*
+ * [_no_reorder]
+ */
+int
+ndr__auth_verifier_co(ndr_ref_t *encl_ref)
+{
+ ndr_stream_t *nds = encl_ref->stream;
+ ndr_xa_t *mxa = /*LINTED E_BAD_PTR_CAST_ALIGN*/
+ (ndr_xa_t *)encl_ref->datum;
+ ndr_common_header_t *hdr;
+ ndr_ref_t myref;
+ ndr_sec_t *val;
+
+ /*
+ * Assumes scan_offset points to the end of PDU body.
+ * (That's base + frag_len - auth_len - SEC_TRAILER_SIZE)
+ *
+ * At some point, NDRGEN could use struct initializers instead of
+ * bzero() + initialization.
+ */
+ bzero(&myref, sizeof (myref));
+ myref.enclosing = encl_ref;
+ myref.stream = encl_ref->stream;
+
+ switch (nds->m_op) {
+ case NDR_M_OP_MARSHALL:
+ val = &mxa->send_auth;
+ hdr = &mxa->send_hdr.common_hdr;
+ break;
+
+ case NDR_M_OP_UNMARSHALL:
+ val = &mxa->recv_auth;
+ hdr = &mxa->recv_hdr.common_hdr;
+ val->auth_value = (uchar_t *)NDS_MALLOC(nds, hdr->auth_length,
+ encl_ref);
+ break;
+
+ default:
+ NDR_SET_ERROR(encl_ref, NDR_ERR_M_OP_INVALID);
+ return (0);
+ }
+
+ /*
+ * ndr_topmost() can't account for auth_length (pdu_scan/end_offset).
+ * This would only matter if any of this struct's members
+ * are treated as 'outer' constructs, but they aren't.
+ */
+ encl_ref->pdu_end_offset += hdr->auth_length;
+ nds->pdu_scan_offset += hdr->auth_length;
+
+ NDR_MEMBER(_uchar, auth_type, 0UL);
+ NDR_MEMBER(_uchar, auth_level, 1UL);
+ NDR_MEMBER(_uchar, auth_pad_len, 2UL);
+ NDR_MEMBER(_uchar, auth_rsvd, 3UL);
+ NDR_MEMBER(_ulong, auth_context_id, 4UL);
+
+ NDR_MEMBER_PTR_WITH_DIMENSION(_uchar, auth_value, 8UL,
+ hdr->auth_length);
+
+ return (1);
+}
+
+int
+ndr_encode_pdu_auth(ndr_xa_t *mxa)
+{
+ ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
+ ndr_stream_t *nds = &mxa->send_nds;
+ int rc;
+ ulong_t want_size;
+
+ if (nds->m_op != NDR_M_OP_MARSHALL)
+ return (NDR_DRC_FAULT_MODE_MISMATCH);
+
+ if (hdr->auth_length == 0)
+ return (NDR_DRC_OK);
+
+ want_size = nds->pdu_scan_offset + hdr->auth_length + SEC_TRAILER_SIZE;
+
+ /*
+ * Make sure we have space for the sec trailer - the marshaller
+ * doesn't know how large the auth token is.
+ * Note: ndr_add_auth_token() has already added padding.
+ *
+ * NDS_GROW_PDU will adjust pdu_size for us.
+ */
+ if (nds->pdu_max_size < want_size) {
+ if (NDS_GROW_PDU(nds, want_size, NULL) == 0)
+ return (NDR_DRC_FAULT_ENCODE_TOO_BIG);
+ } else {
+ nds->pdu_size = want_size;
+ }
+ rc = ndr_encode_decode_type(nds, &TYPEINFO(auth_verifier_co),
+ mxa);
+
+ return (rc);
+}
+
+int
+ndr_decode_pdu_auth(ndr_xa_t *mxa)
+{
+ ndr_common_header_t *hdr = &mxa->recv_hdr.common_hdr;
+ ndr_stream_t *nds = &mxa->recv_nds;
+ ndr_sec_t *auth = &mxa->recv_auth;
+ int rc;
+ ulong_t saved_offset;
+ size_t auth_size;
+
+ if (nds->m_op != NDR_M_OP_UNMARSHALL)
+ return (NDR_DRC_FAULT_MODE_MISMATCH);
+
+ mxa->recv_auth.auth_pad_len = 0;
+ if (hdr->auth_length == 0)
+ return (NDR_DRC_OK);
+
+ /*
+ * Save the current offset, and skip to the sec_trailer.
+ * That's located after the (fragment of) stub data and the auth
+ * pad bytes (collectively the 'PDU Body').
+ */
+ saved_offset = nds->pdu_scan_offset;
+ nds->pdu_scan_offset = nds->pdu_body_offset + nds->pdu_body_size;
+
+ /* auth_length is all of the data after the sec_trailer */
+ if (hdr->auth_length >
+ (nds->pdu_size - nds->pdu_scan_offset - SEC_TRAILER_SIZE)) {
+ nds->pdu_scan_offset = saved_offset;
+ return (NDR_DRC_FAULT_RECEIVED_MALFORMED);
+ }
+
+ rc = ndr_encode_decode_type(nds, &TYPEINFO(auth_verifier_co),
+ mxa);
+
+ /*
+ * Reset the scan_offset for call decode processing.
+ * If we were successful, remove the sec trailer and padding
+ * from size accounting.
+ */
+ if (auth->auth_pad_len > nds->pdu_body_size)
+ rc = NDR_DRC_FAULT_RECEIVED_MALFORMED;
+ else if (rc == NDR_DRC_OK) {
+ auth_size = hdr->auth_length + SEC_TRAILER_SIZE +
+ auth->auth_pad_len;
+
+ /*
+ * After the authenticator has been decoded,
+ * pdu_scan_offset points to just after the auth token,
+ * which is the end of the fragment.
+ *
+ * If there's no data after the authenticator, then we
+ * just remove the authenticator from size accounting.
+ * Otherwise, need to memmove() all of that data back to after
+ * the stub data. The data we move starts at the beginning of
+ * the next fragment.
+ */
+ if (nds->pdu_size > nds->pdu_scan_offset) {
+ uchar_t *next_frag_ptr = nds->pdu_base_addr +
+ nds->pdu_scan_offset;
+
+ memmove(next_frag_ptr - auth_size, next_frag_ptr,
+ nds->pdu_size - nds->pdu_scan_offset);
+ }
+
+ nds->pdu_size -= auth_size;
+ }
+ nds->pdu_scan_offset = saved_offset;
+ return (rc);
+}
diff --git a/usr/src/lib/libmlrpc/common/ndr_ops.c b/usr/src/lib/libmlrpc/common/ndr_ops.c
index 4ca1cb6295..7716ddd7b0 100644
--- a/usr/src/lib/libmlrpc/common/ndr_ops.c
+++ b/usr/src/lib/libmlrpc/common/ndr_ops.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
*/
/*
@@ -186,9 +186,11 @@ nds_show_state(ndr_stream_t *nds)
return;
}
- ndo_printf(NULL, NULL, "nds: base=0x%x, size=%d, max=%d, scan=%d",
+ ndo_printf(NULL, NULL, "nds: base=0x%x, size=%d, max=%d, scan=%d, "
+ "hdr_size=%d, body_size=%d, body_offset=%d",
nds->pdu_base_offset, nds->pdu_size, nds->pdu_max_size,
- nds->pdu_scan_offset);
+ nds->pdu_scan_offset, nds->pdu_hdr_size, nds->pdu_body_size,
+ nds->pdu_body_offset);
}
/*
diff --git a/usr/src/lib/libmlrpc/common/ndr_process.c b/usr/src/lib/libmlrpc/common/ndr_process.c
index 4b2ea938e8..c39380f367 100644
--- a/usr/src/lib/libmlrpc/common/ndr_process.c
+++ b/usr/src/lib/libmlrpc/common/ndr_process.c
@@ -23,7 +23,7 @@
* Use is subject to license terms.
*
* Copyright 2012 Milan Jurik. All rights reserved.
- * Copyright 2019 Nexenta by DDN, Inc. All rights reserved.
+ * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
*/
/*
@@ -1474,7 +1474,7 @@ ndr_outer_align(ndr_ref_t *outer_ref)
n_pad = NDR_ALIGN4(nds->pdu_scan_offset);
}
- if (n_pad == 0)
+ if ((outer_ref->ti->type_flags & NDR_F_FAKE) != 0 || n_pad == 0)
return (1); /* already aligned, often the case */
if (!ndr_outer_grow(outer_ref, n_pad))
@@ -1544,6 +1544,57 @@ ndr_outer_grow(ndr_ref_t *outer_ref, unsigned n_total)
}
/*
+ * Some 'outer' constructs incorrectly align the entire construct
+ * on a 4-byte boundary, when each of its members needs to be
+ * aligned separately. This function handles aligning 'inner'
+ * members on their natural alignment boundary.
+ *
+ * NOTE: This assumes it is not being used for headers.
+ * Headers present some unique concerns that this may not
+ * adequately address (e.g. reserved space, pdu_body_offset).
+ */
+int
+ndr_inner_align(ndr_ref_t *arg_ref)
+{
+ ndr_stream_t *nds = arg_ref->stream;
+ int rc;
+ unsigned n_pad;
+
+ n_pad = ((arg_ref->ti->alignment + 1) - arg_ref->pdu_offset) &
+ arg_ref->ti->alignment;
+
+ if (n_pad == 0)
+ return (1); /* already aligned, often the case */
+
+ if (!ndr_outer_grow(arg_ref->enclosing, n_pad))
+ return (0); /* error already set */
+
+ switch (nds->m_op) {
+ case NDR_M_OP_MARSHALL:
+ rc = NDS_PAD_PDU(nds, arg_ref->pdu_offset, n_pad, arg_ref);
+ if (!rc) {
+ NDR_SET_ERROR(arg_ref, NDR_ERR_PAD_FAILED);
+ return (0);
+ }
+ break;
+
+ case NDR_M_OP_UNMARSHALL:
+ break;
+
+ default:
+ NDR_SET_ERROR(arg_ref, NDR_ERR_M_OP_INVALID);
+ return (0);
+ }
+
+ /* All current and future offsets need to advance */
+ arg_ref->enclosing->pdu_offset += n_pad;
+ arg_ref->pdu_offset += n_pad;
+ /* ndr_outer_grow changed pdu_end_offset */
+ nds->pdu_scan_offset += n_pad;
+ return (1);
+}
+
+/*
* INNER ELEMENTS
*
* The local datum (arg_ref->datum) already exists, there is no need to
@@ -1568,6 +1619,22 @@ ndr_inner(ndr_ref_t *arg_ref)
params = arg_ref->inner_flags & NDR_F_PARAMS_MASK;
+ /*
+ * Switched unions are meant to be converted to and from encapsulated
+ * structures on the wire. However, NDRGEN doesn't implement this.
+ * As a result, our interface definitions use 'fake' structures
+ * to represent switched unions.
+ * This causes the structure to be aligned on a struct (4 byte)
+ * boundary, with none of its members having separate alignment -
+ * but that's not correct. Each of its members has its own,
+ * natural alignment, and we need to honor that.
+ * That happens here.
+ */
+ if (arg_ref->enclosing != NULL &&
+ (arg_ref->enclosing->ti->type_flags & NDR_F_FAKE) != 0 &&
+ !ndr_inner_align(arg_ref))
+ return (0); /* error already set */
+
switch (params) {
case NDR_F_NONE:
if (is_union) {
diff --git a/usr/src/lib/libmlrpc/common/ndrtypes.ndl b/usr/src/lib/libmlrpc/common/ndrtypes.ndl
index b7d5fdb716..a5f0e3fd61 100644
--- a/usr/src/lib/libmlrpc/common/ndrtypes.ndl
+++ b/usr/src/lib/libmlrpc/common/ndrtypes.ndl
@@ -22,7 +22,7 @@
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
- * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
*/
#ifndef _NDRTYPES_NDL_
@@ -42,6 +42,7 @@
#define IN [in]
#define OUT [out]
#define INOUT [in out]
+#define FAKE [fake]
#define STRING [string]
#define SIZE_IS(X) [size_is(X)]
@@ -85,6 +86,7 @@
#define IN
#define OUT
#define INOUT
+#define FAKE
#define STRING
#define SIZE_IS(X)
diff --git a/usr/src/lib/libmlrpc/common/rpcpdu.ndl b/usr/src/lib/libmlrpc/common/rpcpdu.ndl
index f1ea15cf00..fae76738da 100644
--- a/usr/src/lib/libmlrpc/common/rpcpdu.ndl
+++ b/usr/src/lib/libmlrpc/common/rpcpdu.ndl
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
*/
#ifndef _RPCPDU_NDL_
@@ -132,7 +133,7 @@ typedef struct ndr_representation_label ndr_replab_t;
* MS-RPCE 2.2.2.3 PFC_SUPPORT_HEADER_SIGN
* For PDU types bind, bind_ack, alter_context and alter_context_resp,
* 0x04 means PFC_SUPPORT_HEADER_SIGN.
- * For other PDU types 0x04 means PFC_PENDING_CANCEL.
+ * For other PDU types 0x04 means PFC_PENDING_CANCEL.
*/
#define NDR_PFC_FIRST_FRAG 0x01 /* First fragment */
#define NDR_PFC_LAST_FRAG 0x02 /* Last framgent */
@@ -202,7 +203,7 @@ EXTERNTYPEINFO(ndr_common_header)
* One header per serialization stream: the header must be little endian.
* The filler must be set to 0xcccccccc during marshaling and ignored
* during unmarshaling.
- */
+ */
_NO_REORDER_
struct ndr_serialtype1_hdr {
BYTE version; /* 00:01 1 */
@@ -232,7 +233,7 @@ EXTERNTYPEINFO(ndr_serialtype1_priv_hdr)
* The header must be little endian.
* The endianinfo and reserved fields must be set to 0xcccccccc during
* marshaling and ignored during unmarshaling.
- */
+ */
_NO_REORDER_
struct ndr_serialtype2_hdr {
BYTE version; /* 00:01 1 */
@@ -362,7 +363,35 @@ EXTERNTYPEINFO(ndr_port_any)
#define NDR_USER_DATA_NOT_READABLE 6
#define NDR_NO_PSAP_AVAILABLE 7
#define NDR_AUTH_TYPE_NOT_RECOGNIZED 8
-#define NDR_INAVLID_CHECKSUM 9
+#define NDR_INVALID_CHECKSUM 9
+
+/*
+ * NDRGEN can't handle variable-length arrays, so we provide our own
+ * marshal/unmarshal routine, so that we can convert to and from
+ * our pointer and a fixed-size array.
+ *
+ * NOTE: MS-RPCE calls this a sec_trailer_t, so we use ndr_sec_t.
+ * auth_value should also be an ANY_SIZE_ARRAY, but NDRGEN can't handle
+ * variable-sized arrays.
+ */
+IMPORT_EXTERN
+_NO_REORDER_
+struct auth_verifier_co {
+ /* restore 16 byte alignment */
+ /* SIZE_IS(auth_pad_len) */
+ /* BYTE auth_pad[ANY_SIZE_ARRAY] */ /* align(16) */
+ BYTE auth_type; /* 00:01 */
+ BYTE auth_level; /* 01:01 */
+ BYTE auth_pad_len; /* 02:01 */
+ BYTE auth_rsvd; /* 03:01 */
+ DWORD auth_context_id; /* 04:04 */
+ /* SIZE_IS(hdr->auth_length) */
+ BYTE *auth_value; /* 08:* credentials */
+};
+typedef struct auth_verifier_co ndr_sec_t;
+EXTERNTYPEINFO(auth_verifier_co)
+
+#define SEC_TRAILER_SIZE 8
/*
* Alter Context PDU (0x0E)
@@ -376,7 +405,7 @@ struct ndr_alter_context_hdr {
WORD max_recv_frag; /* 18:02 ignored */
DWORD assoc_group_id; /* 20:04 ignored */
- /*
+ /*
* Presentation context list (see bind hdr comments).
*/
ndr_p_cont_list_t p_context_elem; /* 24: */
@@ -406,7 +435,7 @@ struct ndr_alter_context_rsp_hdr {
DWORD assoc_group_id; /* 20:04 ignored */
ndr_port_any_t sec_addr; /* 24:20 ignored */
- /*
+ /*
* Presentation context list (see bind hdr comments).
*/
ndr_p_result_list_t p_result_list; /* 44:nn */
diff --git a/usr/src/lib/smbsrv/libmlsvc/Makefile.com b/usr/src/lib/smbsrv/libmlsvc/Makefile.com
index a3c41456f4..35094ad060 100644
--- a/usr/src/lib/smbsrv/libmlsvc/Makefile.com
+++ b/usr/src/lib/smbsrv/libmlsvc/Makefile.com
@@ -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 @@ OBJS_COMMON = \
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)/common/smbsrv
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
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h b/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h
index 14cbf858c4..789192b3f0 100644
--- a/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h
+++ b/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h
@@ -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 @@ typedef struct mlrpc_handle mlsvc_handle_t;
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);
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers b/usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers
index 80317f002c..7750db2de1 100644
--- a/usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers
@@ -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 @@ SYMBOL_VERSION SUNWprivate {
mlsvc_init;
mlsvc_join;
mlsvc_netlogon;
+ netlogon_init_global;
+ netlogon_logon;
+ netr_initialize;
smb_autohome_add;
smb_autohome_remove;
smb_ddiscover_bad_dc;
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h
index 0aa45b0292..b4430f1bd5 100644
--- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h
@@ -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 spoolss_finalize(void);
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 *);
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c
index c8879837b1..20dea68442 100644
--- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c
@@ -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 @@ ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain,
}
/*
+ * 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 @@ ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain,
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.
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_netr.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_netr.c
index b27fc29cd2..850b5452b6 100644
--- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_netr.c
+++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_netr.c
@@ -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 @@ static ndr_service_t netr_service = {
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 @@ netr_s_SamLogoff(void *arg, ndr_xa_t *mxa)
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 @@ fixup_netr_SamLogon(struct netr_SamLogon *arg)
};
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);
+}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c b/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c
index 2b22744304..f56838303f 100644
--- a/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c
+++ b/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c
@@ -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 @@ static int netr_gen_password(BYTE *, BYTE *, BYTE *);
/*
* 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 @@ netr_info_t netr_global_info;
*
*/
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;
- netr_info = &netr_global_info;
- bzero(netr_info, sizeof (netr_info_t));
+ /*
+ * [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->flags |= flags;
+ netr_info = &netr_global_info;
+ 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;
- if ((rc = netr_server_req_challenge(netr_handle, netr_info)) == 0) {
- rc = netr_server_authenticate2(netr_handle, netr_info);
+ rc = smb_getfqdomainname(netr_info->fqdn_domain,
+ sizeof (netr_info->fqdn_domain));
+ if (rc != 0)
+ goto errout;
+
+ /*
+ * [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 @@ netlogon_auth(char *server, mlsvc_handle_t *netr_handle, DWORD flags)
}
}
+errout:
+ (void) netr_close(&netr_handle);
+
return ((rc) ? NT_STATUS_UNSUCCESSFUL : NT_STATUS_SUCCESS);
}
@@ -145,6 +261,34 @@ netr_open(char *server, char *domain, mlsvc_handle_t *netr_handle)
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 @@ netr_server_req_challenge(mlsvc_handle_t *netr_handle, netr_info_t *netr_info)
}
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 @@ netr_server_authenticate2(mlsvc_handle_t *netr_handle, netr_info_t *netr_info)
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 @@ netr_server_authenticate2(mlsvc_handle_t *netr_handle, netr_info_t *netr_info)
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 @@ netr_server_authenticate2(mlsvc_handle_t *netr_handle, netr_info_t *netr_info)
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 @@ netr_gen_skey64(netr_info_t *netr_info)
*/
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 @@ netr_gen_credentials(BYTE *session_key, netr_cred_t *challenge,
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);
}
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c b/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c
index 4e2cfc5518..e82722b257 100644
--- a/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c
+++ b/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c
@@ -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 @@ out:
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 @@ out:
* 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 @@ netlogon_logon(smb_logon_t *user_info, smb_token_t *token, smb_domainex_t *di)
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 @@ netlogon_logon(smb_logon_t *user_info, smb_token_t *token, smb_domainex_t *di)
}
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 @@ reauth:
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 @@ smb_netlogon_check(char *server, char *domain)
(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_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));
- 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;
- }
-
- 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 @@ uint32_t
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 @@ netr_server_samlogon(mlsvc_handle_t *netr_handle, netr_info_t *netr_info,
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_server_samlogon(mlsvc_handle_t *netr_handle, netr_info_t *netr_info,
}
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 @@ netr_server_samlogon(mlsvc_handle_t *netr_handle, netr_info_t *netr_info,
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 @@ netr_server_samlogon(mlsvc_handle_t *netr_handle, netr_info_t *netr_info,
* 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 @@ int
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 @@ netr_validate_chain(netr_info_t *netr_info, struct netr_authenticator *auth)
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) {
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/netr_ssp.c b/usr/src/lib/smbsrv/libmlsvc/common/netr_ssp.c
new file mode 100644
index 0000000000..8b342f455f
--- /dev/null
+++ b/usr/src/lib/smbsrv/libmlsvc/common/netr_ssp.c
@@ -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->fqdn_domain) * 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
+};
diff --git a/usr/src/lib/smbsrv/libsmb/common/libsmb.h b/usr/src/lib/smbsrv/libsmb/common/libsmb.h
index 362c15c294..5b6a41cbf0 100644
--- a/usr/src/lib/smbsrv/libsmb/common/libsmb.h
+++ b/usr/src/lib/smbsrv/libsmb/common/libsmb.h
@@ -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 @@ typedef enum {
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_chk_hostaccess(smb_inaddr_t *, char *);
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 @@ void libsmb_redirect_syslog(__FILE_TAG *fp, int priority);
#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
diff --git a/usr/src/lib/smbsrv/libsmb/common/mapfile-vers b/usr/src/lib/smbsrv/libsmb/common/mapfile-vers
index c8c5f3c4e2..ddb0698522 100644
--- a/usr/src/lib/smbsrv/libsmb/common/mapfile-vers
+++ b/usr/src/lib/smbsrv/libsmb/common/mapfile-vers
@@ -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 @@ SYMBOL_VERSION SUNWprivate {
smb_gen_random_passwd;
smb_get_dcinfo;
smb_get_nameservers;
+ smb_get_netlogon_flags;
smb_get_txid;
smb_getdataset;
smb_getdomainname;
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_cfg.c b/usr/src/lib/smbsrv/libsmb/common/smb_cfg.c
index f306476582..0dc291d95d 100644
--- a/usr/src/lib/smbsrv/libsmb/common/smb_cfg.c
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_cfg.c
@@ -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 @@ static smb_cfg_param_t smb_cfg_table[] =
{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 */
};
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_info.c b/usr/src/lib/smbsrv/libsmb/common/smb_info.c
index f95f0bb2ed..1cd213cc9a 100644
--- a/usr/src/lib/smbsrv/libsmb/common/smb_info.c
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_info.c
@@ -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 @@ smb_gethostbyaddr(const char *addr, int len, int type, int *err_num)
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);
+}
diff --git a/usr/src/pkg/manifests/system-test-libmlrpctest.mf b/usr/src/pkg/manifests/system-test-libmlrpctest.mf
new file mode 100644
index 0000000000..0999e3987e
--- /dev/null
+++ b/usr/src/pkg/manifests/system-test-libmlrpctest.mf
@@ -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
diff --git a/usr/src/test/Makefile b/usr/src/test/Makefile
index 9756f02ef7..0a6e367f20 100644
--- a/usr/src/test/Makefile
+++ b/usr/src/test/Makefile
@@ -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 @@ SUBDIRS = \
crypto-tests \
elf-tests \
libc-tests \
+ libmlrpc-tests \
net-tests \
os-tests \
smbclient-tests \
diff --git a/usr/src/test/libmlrpc-tests/Makefile b/usr/src/test/libmlrpc-tests/Makefile
new file mode 100644
index 0000000000..3c0890a74f
--- /dev/null
+++ b/usr/src/test/libmlrpc-tests/Makefile
@@ -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
diff --git a/usr/src/test/libmlrpc-tests/cfg/Makefile b/usr/src/test/libmlrpc-tests/cfg/Makefile
new file mode 100644
index 0000000000..85d38ba3bf
--- /dev/null
+++ b/usr/src/test/libmlrpc-tests/cfg/Makefile
@@ -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)
diff --git a/usr/src/test/libmlrpc-tests/cfg/krb5_pac.config b/usr/src/test/libmlrpc-tests/cfg/krb5_pac.config
new file mode 100644
index 0000000000..894f2439e1
--- /dev/null
+++ b/usr/src/test/libmlrpc-tests/cfg/krb5_pac.config
@@ -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"
diff --git a/usr/src/test/libmlrpc-tests/cfg/samlogon.config b/usr/src/test/libmlrpc-tests/cfg/samlogon.config
new file mode 100644
index 0000000000..39c351b69e
--- /dev/null
+++ b/usr/src/test/libmlrpc-tests/cfg/samlogon.config
@@ -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
diff --git a/usr/src/test/libmlrpc-tests/cmd/Makefile b/usr/src/test/libmlrpc-tests/cmd/Makefile
new file mode 100644
index 0000000000..2119c54e38
--- /dev/null
+++ b/usr/src/test/libmlrpc-tests/cmd/Makefile
@@ -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)
diff --git a/usr/src/test/libmlrpc-tests/cmd/libmlrpctest.ksh b/usr/src/test/libmlrpc-tests/cmd/libmlrpctest.ksh
new file mode 100644
index 0000000000..de2553e961
--- /dev/null
+++ b/usr/src/test/libmlrpc-tests/cmd/libmlrpctest.ksh
@@ -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 $?
diff --git a/usr/src/test/libmlrpc-tests/doc/Makefile b/usr/src/test/libmlrpc-tests/doc/Makefile
new file mode 100644
index 0000000000..74ebad02d4
--- /dev/null
+++ b/usr/src/test/libmlrpc-tests/doc/Makefile
@@ -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)
diff --git a/usr/src/test/libmlrpc-tests/doc/README b/usr/src/test/libmlrpc-tests/doc/README
new file mode 100644
index 0000000000..43412a4747
--- /dev/null
+++ b/usr/src/test/libmlrpc-tests/doc/README
@@ -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
diff --git a/usr/src/test/libmlrpc-tests/runfiles/Makefile b/usr/src/test/libmlrpc-tests/runfiles/Makefile
new file mode 100644
index 0000000000..0c64561f56
--- /dev/null
+++ b/usr/src/test/libmlrpc-tests/runfiles/Makefile
@@ -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)
diff --git a/usr/src/test/libmlrpc-tests/runfiles/default.run b/usr/src/test/libmlrpc-tests/runfiles/default.run
new file mode 100644
index 0000000000..9c2def8dfb
--- /dev/null
+++ b/usr/src/test/libmlrpc-tests/runfiles/default.run
@@ -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']
diff --git a/usr/src/test/libmlrpc-tests/tests/Makefile b/usr/src/test/libmlrpc-tests/tests/Makefile
new file mode 100644
index 0000000000..f75886950a
--- /dev/null
+++ b/usr/src/test/libmlrpc-tests/tests/Makefile
@@ -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:
diff --git a/usr/src/test/libmlrpc-tests/tests/Makefile.com b/usr/src/test/libmlrpc-tests/tests/Makefile.com
new file mode 100644
index 0000000000..e417f263d7
--- /dev/null
+++ b/usr/src/test/libmlrpc-tests/tests/Makefile.com
@@ -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)
diff --git a/usr/src/test/libmlrpc-tests/tests/common/util_common.c b/usr/src/test/libmlrpc-tests/tests/common/util_common.c
new file mode 100644
index 0000000000..d8e2212086
--- /dev/null
+++ b/usr/src/test/libmlrpc-tests/tests/common/util_common.c
@@ -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");
+}
diff --git a/usr/src/test/libmlrpc-tests/tests/common/util_common.h b/usr/src/test/libmlrpc-tests/tests/common/util_common.h
new file mode 100644
index 0000000000..fe9f1d70c0
--- /dev/null
+++ b/usr/src/test/libmlrpc-tests/tests/common/util_common.h
@@ -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 */
diff --git a/usr/src/test/libmlrpc-tests/tests/netrlogon/Makefile b/usr/src/test/libmlrpc-tests/tests/netrlogon/Makefile
new file mode 100644
index 0000000000..f6f3fa6b99
--- /dev/null
+++ b/usr/src/test/libmlrpc-tests/tests/netrlogon/Makefile
@@ -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
diff --git a/usr/src/test/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/Makefile b/usr/src/test/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/Makefile
new file mode 100644
index 0000000000..2096aed5b2
--- /dev/null
+++ b/usr/src/test/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/Makefile
@@ -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
diff --git a/usr/src/test/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/krb5_pac.bin b/usr/src/test/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/krb5_pac.bin
new file mode 100644
index 0000000000..29f736e18e
--- /dev/null
+++ b/usr/src/test/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/krb5_pac.bin
Binary files differ
diff --git a/usr/src/test/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/krb5_pac_decode.c b/usr/src/test/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/krb5_pac_decode.c
new file mode 100644
index 0000000000..cc4c6d94e9
--- /dev/null
+++ b/usr/src/test/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/krb5_pac_decode.c
@@ -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);
+}
diff --git a/usr/src/test/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/run_krb5_pac_tests.ksh b/usr/src/test/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/run_krb5_pac_tests.ksh
new file mode 100644
index 0000000000..04d8beb080
--- /dev/null
+++ b/usr/src/test/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/run_krb5_pac_tests.ksh
@@ -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 $?
diff --git a/usr/src/test/libmlrpc-tests/tests/netrlogon/samlogon_tests/Makefile b/usr/src/test/libmlrpc-tests/tests/netrlogon/samlogon_tests/Makefile
new file mode 100644
index 0000000000..231fb69fc2
--- /dev/null
+++ b/usr/src/test/libmlrpc-tests/tests/netrlogon/samlogon_tests/Makefile
@@ -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
diff --git a/usr/src/test/libmlrpc-tests/tests/netrlogon/samlogon_tests/run_samlogon_tests.ksh b/usr/src/test/libmlrpc-tests/tests/netrlogon/samlogon_tests/run_samlogon_tests.ksh
new file mode 100644
index 0000000000..a7087cd98d
--- /dev/null
+++ b/usr/src/test/libmlrpc-tests/tests/netrlogon/samlogon_tests/run_samlogon_tests.ksh
@@ -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 $?
diff --git a/usr/src/test/libmlrpc-tests/tests/netrlogon/samlogon_tests/samlogon.c b/usr/src/test/libmlrpc-tests/tests/netrlogon/samlogon_tests/samlogon.c
new file mode 100644
index 0000000000..c693ffdc08
--- /dev/null
+++ b/usr/src/test/libmlrpc-tests/tests/netrlogon/samlogon_tests/samlogon.c
@@ -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);
+}
diff --git a/usr/src/tools/ndrgen/ndr_anal.c b/usr/src/tools/ndrgen/ndr_anal.c
index 9328b90304..ff52db8c09 100644
--- a/usr/src/tools/ndrgen/ndr_anal.c
+++ b/usr/src/tools/ndrgen/ndr_anal.c
@@ -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 @@ determine_advice(ndr_advice_t *advice, ndr_node_t *advice_list)
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 *
diff --git a/usr/src/tools/ndrgen/ndr_gen.c b/usr/src/tools/ndrgen/ndr_gen.c
index e131002e1b..b6d0b3fc2b 100644
--- a/usr/src/tools/ndrgen/ndr_gen.c
+++ b/usr/src/tools/ndrgen/ndr_gen.c
@@ -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 @@ generate_typeinfo_typeinfo(ndr_typeinfo_t *ti, int is_static, char *fname_type)
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);
diff --git a/usr/src/tools/ndrgen/ndr_lex.c b/usr/src/tools/ndrgen/ndr_lex.c
index 5c6e07bc67..87b0cb12a7 100644
--- a/usr/src/tools/ndrgen/ndr_lex.c
+++ b/usr/src/tools/ndrgen/ndr_lex.c
@@ -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 @@ static ndr_keyword_t keywtable[] = {
{ "transmit_as", TRANSMIT_AS_KW, 0 },
{ "arg_is", ARG_IS_KW, 0 },
+ { "fake", FAKE_KW, 0 },
{ "char", BASIC_TYPE, 1 },
{ "uchar", BASIC_TYPE, 1 },
diff --git a/usr/src/tools/ndrgen/ndr_parse.y b/usr/src/tools/ndrgen/ndr_parse.y
index 88de5ace2a..f2dbac42aa 100644
--- a/usr/src/tools/ndrgen/ndr_parse.y
+++ b/usr/src/tools/ndrgen/ndr_parse.y
@@ -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 @@ typedef struct node *node_ptr;
%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 @@ adv_attr: IN_KW ={ $$ = n_cons (IN_KW); }
| 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); }
diff --git a/usr/src/tools/ndrgen/ndr_print.c b/usr/src/tools/ndrgen/ndr_print.c
index 4981677b8b..dafa3260c2 100644
--- a/usr/src/tools/ndrgen/ndr_print.c
+++ b/usr/src/tools/ndrgen/ndr_print.c
@@ -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 @@ print_node(ndr_node_t *np)
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 @@ print_node(ndr_node_t *np)
case DEFAULT_KW:
case _NO_REORDER_KW:
case EXTERN_KW:
+ case FAKE_KW:
(void) printf("%s", nm);
break;
@@ -193,7 +199,7 @@ print_node(ndr_node_t *np)
* Supports formats such as size_is(x) or size_is(x / 2). The supported
* operators are:
*
- * * / % + - & | ^
+ * * / % + - & | ^
*/
void
print_field_attr(ndr_node_t *np)
diff --git a/usr/src/tools/ndrgen/ndrgen.h b/usr/src/tools/ndrgen/ndrgen.h
index ac8f2d96ef..44149267b4 100644
--- a/usr/src/tools/ndrgen/ndrgen.h
+++ b/usr/src/tools/ndrgen/ndrgen.h
@@ -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 @@ typedef struct integer {
#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 @@ typedef struct advice {
#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 {
diff --git a/usr/src/uts/common/smbsrv/ndl/netlogon.ndl b/usr/src/uts/common/smbsrv/ndl/netlogon.ndl
index eded8f8415..718061e945 100644
--- a/usr/src/uts/common/smbsrv/ndl/netlogon.ndl
+++ b/usr/src/uts/common/smbsrv/ndl/netlogon.ndl
@@ -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 @@ union netr_logon_info_u {
};
+FAKE
struct netr_login_info {
WORD logon_level;
WORD switch_value;
@@ -410,6 +413,19 @@ struct netr_SamLogon {
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 @@ union netr_interface {
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)
diff --git a/usr/src/uts/common/smbsrv/netrauth.h b/usr/src/uts/common/smbsrv/netrauth.h
index b621e2da1a..b6ce17177c 100644
--- a/usr/src/uts/common/smbsrv/netrauth.h
+++ b/usr/src/uts/common/smbsrv/netrauth.h
@@ -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 @@ extern "C" {
/*
* 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 @@ typedef struct netr_info {
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 @@ typedef struct netr_info {
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 @@ typedef struct netr_info {
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)
diff --git a/usr/src/uts/common/smbsrv/smbinfo.h b/usr/src/uts/common/smbsrv/smbinfo.h
index 787f0f659d..238ee31c96 100644
--- a/usr/src/uts/common/smbsrv/smbinfo.h
+++ b/usr/src/uts/common/smbsrv/smbinfo.h
@@ -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 @@ extern "C" {
#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).