diff options
author | Matt Barden <mbarden@tintri.com> | 2020-09-24 15:01:37 -0400 |
---|---|---|
committer | Gordon Ross <gordon.ross@tintri.com> | 2021-01-30 14:02:54 -0500 |
commit | ce8560eeb961d528e27685fcdd2ffb03e9478dbf (patch) | |
tree | 0fdb2e242a356995912775b3d6b765e22af10a4b /usr/src | |
parent | 9e3ab9e9117808af4e738ea3ac45888be11e4045 (diff) | |
download | illumos-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')
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 Binary files differnew file mode 100644 index 0000000000..29f736e18e --- /dev/null +++ b/usr/src/test/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/krb5_pac.bin 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). |