Mercurial > illumos > git > illumos-omnios
changeset 23943:4fa9ed968314 default tip
Merge pull request #919 from hadfl/smb
SMB fixes
author | Andy Fiddaman <andy@omnios.org> |
---|---|
date | Sat, 16 Jan 2021 16:59:32 +0000 |
parents | 237362548d55 (current diff) 2459678edc5e (diff) |
children | |
files | usr/src/test/libmlrpc-tests/Makefile usr/src/test/libmlrpc-tests/cfg/Makefile usr/src/test/libmlrpc-tests/cmd/Makefile usr/src/test/libmlrpc-tests/cmd/libmlrpctest.ksh usr/src/test/libmlrpc-tests/doc/Makefile usr/src/test/libmlrpc-tests/runfiles/Makefile usr/src/test/libmlrpc-tests/tests/netrlogon/Makefile |
diffstat | 66 files changed, 3604 insertions(+), 369 deletions(-) [+] |
line wrap: on
line diff
--- a/exception_lists/copyright Thu Jan 14 17:27:56 2021 +0100 +++ b/exception_lists/copyright Sat Jan 16 16:59:32 2021 +0000 @@ -20,7 +20,7 @@ # # -# Copyright 2015 Nexenta Systems, Inc. All rights reserved. +# Copyright 2020 Tintri by DDN, Inc. All rights reserved. # Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright (c) 2011 by Delphix. All rights reserved. # Copyright 2018 OmniOS Community Edition (OmniOSce) Association. @@ -391,6 +391,7 @@ usr/src/lib/libsmbfs/smb/spnegoparse.[ch] usr/src/test/crypto-tests/tests/digest/data/*.rsp usr/src/test/crypto-tests/tests/digest/data/README +usr/src/test/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/krb5_pac.bin usr/src/test/util-tests/tests/dis/*/*.out usr/src/test/util-tests/tests/grep_xpg4/files/gout* usr/src/test/util-tests/tests/grep_xpg4/files/test*
--- a/usr/src/cmd/smbsrv/smbd/server.xml Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/cmd/smbsrv/smbd/server.xml Sat Jan 16 16:59:32 2021 +0000 @@ -22,8 +22,9 @@ CDDL HEADER END Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. +Copyright 2018 Nexenta Systems, Inc. All rights reserved. Copyright 2016 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org> -Copyright 2018 Nexenta Systems, Inc. All rights reserved. +Copyright 2020 Tintri by DDN, Inc. All rights reserved. Copyright 2020 RackTop Systems. NOTE: This service manifest is not editable; its contents will @@ -240,6 +241,8 @@ value='20' override='true'/> <propval name='maximum_credits' type='integer' value='1000' override='true'/> + <propval name='netlogon_flags' type='integer' + value='0' override='true'/> </property_group> <!-- SMB service-specific shares exec configuration defaults -->
--- a/usr/src/lib/libmlrpc/Makefile.com Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/lib/libmlrpc/Makefile.com Sat Jan 16 16:59:32 2021 +0000 @@ -23,7 +23,7 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# Copyright 2013 Nexenta Systems, Inc. All rights reserved. +# Copyright 2020 Tintri by DDN, Inc. All rights reserved. # LIBRARY = libmlrpc.a @@ -31,6 +31,7 @@ OBJS_COMMON = \ mlrpc_clh.o \ + ndr_auth.o \ ndr_client.o \ ndr_heap.o \ ndr_marshal.o \
--- a/usr/src/lib/libmlrpc/common/libmlrpc.h Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/lib/libmlrpc/common/libmlrpc.h Sat Jan 16 16:59:32 2021 +0000 @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ #ifndef _LIBMLRPC_H @@ -117,6 +117,7 @@ /* Fake PTYPE DRC discriminators */ #define NDR_DRC_PTYPE_RPCHDR(DRC) ((DRC) | 0x00FF) #define NDR_DRC_PTYPE_API(DRC) ((DRC) | 0x00AA) +#define NDR_DRC_PTYPE_SEC(DRC) ((DRC) | 0x00CC) /* DRC Recognizers */ #define NDR_DRC_IS_OK(DRC) (((DRC) & NDR_DRC_MASK_SPECIFIER) == 0) @@ -154,6 +155,10 @@ #define NDR_DRC_FAULT_PARAM_2_UNIMPLEMENTED 0xD200 #define NDR_DRC_FAULT_PARAM_3_INVALID 0xC300 #define NDR_DRC_FAULT_PARAM_3_UNIMPLEMENTED 0xD300 +#define NDR_DRC_FAULT_PARAM_4_INVALID 0xC400 +#define NDR_DRC_FAULT_PARAM_4_UNIMPLEMENTED 0xD400 +#define NDR_DRC_FAULT_PARAM_5_INVALID 0xC500 +#define NDR_DRC_FAULT_PARAM_5_UNIMPLEMENTED 0xD500 #define NDR_DRC_FAULT_OUT_OF_MEMORY 0xF000 @@ -179,6 +184,32 @@ #define NDR_DRC_FAULT_API_BIND_NO_SLOTS 0x91AA /* RESOURCE_1 */ #define NDR_DRC_FAULT_API_OPNUM_INVALID 0xC1AA /* PARAM_1_INVALID */ +/* Secure RPC and SSPs */ +#define NDR_DRC_FAULT_SEC_TYPE_UNIMPLEMENTED \ + NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_PARAM_0_UNIMPLEMENTED) +#define NDR_DRC_FAULT_SEC_LEVEL_UNIMPLEMENTED \ + NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_PARAM_1_UNIMPLEMENTED) +#define NDR_DRC_FAULT_SEC_SSP_FAILED \ + NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_RESOURCE_1) +#define NDR_DRC_FAULT_SEC_ENCODE_TOO_BIG \ + NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_ENCODE_TOO_BIG) +#define NDR_DRC_FAULT_SEC_AUTH_LENGTH_INVALID \ + NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_PARAM_2_INVALID) +#define NDR_DRC_FAULT_SEC_AUTH_TYPE_INVALID \ + NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_PARAM_0_INVALID) +#define NDR_DRC_FAULT_SEC_AUTH_LEVEL_INVALID \ + NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_PARAM_1_INVALID) +#define NDR_DRC_FAULT_SEC_OUT_OF_MEMORY \ + NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_OUT_OF_MEMORY) +#define NDR_DRC_FAULT_SEC_ENCODE_FAILED \ + NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_ENCODE_FAILED) +#define NDR_DRC_FAULT_SEC_META_INVALID \ + NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_PARAM_3_INVALID) +#define NDR_DRC_FAULT_SEC_SEQNUM_INVALID \ + NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_PARAM_4_INVALID) +#define NDR_DRC_FAULT_SEC_SIG_INVALID \ + NDR_DRC_PTYPE_SEC(NDR_DRC_FAULT_PARAM_5_INVALID) + struct ndr_xa; struct ndr_client; @@ -224,12 +255,12 @@ * conn->binding_pool, N_BINDING_POOL); */ typedef struct ndr_binding { - struct ndr_binding *next; + struct ndr_binding *next; ndr_p_context_id_t p_cont_id; unsigned char which_side; struct ndr_client *clnt; ndr_service_t *service; - void *instance_specific; + void *instance_specific; } ndr_binding_t; #define NDR_BIND_SIDE_CLIENT 1 @@ -422,14 +453,39 @@ unsigned short opnum; ndr_stream_t recv_nds; ndr_hdr_t recv_hdr; + ndr_sec_t recv_auth; ndr_stream_t send_nds; ndr_hdr_t send_hdr; + ndr_sec_t send_auth; ndr_binding_t *binding; /* what we're using */ ndr_binding_t *binding_list; /* from connection */ ndr_heap_t *heap; ndr_pipe_t *pipe; } ndr_xa_t; +typedef struct ndr_auth_ops { + int (*nao_init)(void *, ndr_xa_t *); + int (*nao_recv)(void *, ndr_xa_t *); + int (*nao_sign)(void *, ndr_xa_t *); + int (*nao_verify)(void *, ndr_xa_t *, boolean_t); +} ndr_auth_ops_t; + +/* + * A client provides this structure during bind to indicate + * that the RPC runtime should use "Secure RPC" (RPC-level auth). + * + * Currently, only NETLOGON uses this, and only NETLOGON-based + * Integrity protection is supported. + */ +typedef struct ndr_auth_ctx { + ndr_auth_ops_t auth_ops; + void *auth_ctx; /* SSP-specific context */ + uint32_t auth_context_id; + uint8_t auth_type; + uint8_t auth_level; + boolean_t auth_verify_resp; +} ndr_auth_ctx_t; + /* * 20-byte opaque id used by various RPC services. */ @@ -459,6 +515,8 @@ uint32_t next_call_id; unsigned next_p_cont_id; + + ndr_auth_ctx_t auth_ctx; } ndr_client_t; typedef struct ndr_handle { @@ -507,6 +565,19 @@ void ndr_show_hdr(ndr_common_header_t *); unsigned ndr_bind_ack_hdr_size(ndr_xa_t *); unsigned ndr_alter_context_rsp_hdr_size(void); +int ndr_decode_pdu_auth(ndr_xa_t *); +int ndr_encode_pdu_auth(ndr_xa_t *); +void ndr_show_auth(ndr_sec_t *); + +/* + * MS-RPCE "Secure RPC" (RPC-level auth). + * These call the functions in ndr_auth_ops_t, which should be + * GSSAPI (or equivalent) calls. + */ +int ndr_add_sec_context(ndr_auth_ctx_t *, ndr_xa_t *); +int ndr_recv_sec_context(ndr_auth_ctx_t *, ndr_xa_t *); +int ndr_add_auth(ndr_auth_ctx_t *, ndr_xa_t *); +int ndr_check_auth(ndr_auth_ctx_t *, ndr_xa_t *); /* ndr_server.c */ void ndr_pipe_worker(ndr_pipe_t *); @@ -542,7 +613,10 @@ * level (bind) handle is released, we close the connection. * * There are some places in libmlsvc where the code assumes that the - * handle member is first in this struct. careful + * handle member is first in this struct. Careful! + * + * Note that this entire structure is bzero()'d once the ndr_client_t + * has been created. */ typedef struct mlrpc_handle { ndr_hdid_t handle; /* keep first */ @@ -550,6 +624,7 @@ } mlrpc_handle_t; int mlrpc_clh_create(mlrpc_handle_t *, void *); +uint32_t mlrpc_clh_set_auth(mlrpc_handle_t *, ndr_auth_ctx_t *); uint32_t mlrpc_clh_bind(mlrpc_handle_t *, ndr_service_t *); void mlrpc_clh_unbind(mlrpc_handle_t *); void *mlrpc_clh_free(mlrpc_handle_t *);
--- a/usr/src/lib/libmlrpc/common/mapfile-vers Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/lib/libmlrpc/common/mapfile-vers Sat Jan 16 16:59:32 2021 +0000 @@ -20,7 +20,7 @@ # # # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. -# Copyright 2013 Nexenta Systems, Inc. All rights reserved. +# Copyright 2020 Tintri by DDN, Inc. All rights reserved. # # @@ -45,6 +45,7 @@ mlrpc_clh_bind; mlrpc_clh_create; mlrpc_clh_free; + mlrpc_clh_set_auth; mlrpc_clh_unbind; # Allow debug/test programs to provide these.
--- a/usr/src/lib/libmlrpc/common/mlrpc_clh.c Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/lib/libmlrpc/common/mlrpc_clh.c Sat Jan 16 16:59:32 2021 +0000 @@ -23,7 +23,7 @@ * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ /* @@ -71,9 +71,8 @@ /* * Allocate... */ - if ((clnt = malloc(sizeof (*clnt))) == NULL) + if ((clnt = calloc(1, sizeof (*clnt))) == NULL) return (ENOMEM); - bzero(clnt, sizeof (*clnt)); clnt->xa_fd = -1; @@ -108,6 +107,24 @@ return (ENOMEM); } +/* + * Set up this handle to perform RPC-level authentication. + */ +uint32_t +mlrpc_clh_set_auth(mlrpc_handle_t *handle, ndr_auth_ctx_t *auth_ctx) +{ + ndr_client_t *clnt = NULL; + + if ((clnt = handle->clnt) == NULL) + return (NT_STATUS_INTERNAL_ERROR); + + if (auth_ctx != NULL) { + /* struct copy */ + clnt->auth_ctx = *auth_ctx; + } + + return (NT_STATUS_SUCCESS); +} /* * This call must be made to initialize an RPC client structure and bind
--- a/usr/src/lib/libmlrpc/common/ndr.h Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/lib/libmlrpc/common/ndr.h Sat Jan 16 16:59:32 2021 +0000 @@ -22,7 +22,7 @@ * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ #ifndef _SMBSRV_NDR_H @@ -145,6 +145,7 @@ #define NDR_F_INTERFACE 0x0700 /* type is a union, special */ #define NDR_F_CONFORMANT 0x1000 /* struct conforming (var-size tail) */ #define NDR_F_VARYING 0x2000 /* not implemented */ +#define NDR_F_FAKE 0x4000 /* not a real struct */ struct ndr_heap; struct ndr_stream; @@ -230,11 +231,43 @@ #define NDS_RESET(NDS) (*(NDS)->ndo->ndo_reset)(NDS) #define NDS_DESTRUCT(NDS) (*(NDS)->ndo->ndo_destruct)(NDS) +/* + * The ndr_stream_t tracks the state for a particular RPC call or response. + * A single call/response may be transmitted as multiple fragments, where + * each fragment has its own header and sec_trailer. For the layout of + * a fragment, see the comment at the top of ndr_marshal.c. + * + * pdu_base_addr is the buffer in which marshalled/received data is stored. + * + * pdu_base_offset is pdu_base_addr with a different type. + * + * pdu_max_size is the size of the buffer in pdu_base_addr. + * + * pdu_size represents the total amount of data stored in pdu_base_addr. + * It is typically less than pdu_max_size, and may contain more than one + * fragment (when reconstructing fragmented calls/responses, for example). + * + * pdu_body_offset points to the offset from pdu_base_addr of the PDU body + * of the currently encoded/decoded fragment. + * + * pdu_body_size is the size of the PDU body in the currently encoded/decoded + * fragment. + * For what constitutes the PDU Body, see ndr_marshal.c. + * + * pdu_hdr_size is the size of the header for the currently encoded/decoded + * fragment. + * + * pdu_scan_offset points to the next valid byte in pdu_base_addr + * (the next free space for encode, and the next unread byte for decode). + */ typedef struct ndr_stream { unsigned long pdu_size; unsigned long pdu_max_size; unsigned long pdu_base_offset; unsigned long pdu_scan_offset; + unsigned long pdu_body_offset; + unsigned long pdu_body_size; + unsigned long pdu_hdr_size; unsigned char *pdu_base_addr; ndr_stream_ops_t *ndo; @@ -325,6 +358,22 @@ #define NDR_DIR_IS_IN (encl_ref->stream->dir == NDR_DIR_IN) #define NDR_DIR_IS_OUT (encl_ref->stream->dir == NDR_DIR_OUT) +#define NDR_MEMBER_PTR_WITH_ARG(TYPE, MEMBER, OFFSET, \ + ARGFLAGS, ARGMEM, ARGVAL) { \ + myref.pdu_offset = encl_ref->pdu_offset + (OFFSET); \ + myref.name = MEMBER_STR(MEMBER); \ + myref.datum = (char *)val->MEMBER; \ + myref.inner_flags = ARGFLAGS; \ + myref.ti = &ndt_##TYPE; \ + myref.ARGMEM = ARGVAL; \ + if (!ndr_inner(&myref)) \ + return (0); \ + } + +#define NDR_MEMBER_PTR_WITH_DIMENSION(TYPE, MEMBER, OFFSET, SIZE_IS) \ + NDR_MEMBER_PTR_WITH_ARG(TYPE, MEMBER, OFFSET, \ + NDR_F_DIMENSION_IS, dimension_is, SIZE_IS) + #define NDR_MEMBER_WITH_ARG(TYPE, MEMBER, OFFSET, \ ARGFLAGS, ARGMEM, ARGVAL) { \ myref.pdu_offset = encl_ref->pdu_offset + (OFFSET); \ @@ -374,16 +423,16 @@ return (0); \ } -#define NDR_TOPMOST_MEMBER(TYPE, MEMBER) \ +#define NDR_TOPMOST_MEMBER(TYPE, MEMBER) \ NDR_TOPMOST_MEMBER_WITH_ARG(TYPE, MEMBER, \ NDR_F_NONE, size_is, 0) #define NDR_TOPMOST_MEMBER_ARR_WITH_SIZE_IS(TYPE, MEMBER, SIZE_IS) \ - NDR_TOPMOST_MEMBER_WITH_ARG(TYPE, MEMBER, \ + NDR_TOPMOST_MEMBER_WITH_ARG(TYPE, MEMBER, \ NDR_F_SIZE_IS, size_is, SIZE_IS) #define NDR_TOPMOST_MEMBER_ARR_WITH_DIMENSION(TYPE, MEMBER, SIZE_IS) \ - NDR_TOPMOST_MEMBER_WITH_ARG(TYPE, MEMBER, \ + NDR_TOPMOST_MEMBER_WITH_ARG(TYPE, MEMBER, \ NDR_F_DIMENSION_IS, dimension_is, SIZE_IS) #define NDR_TOPMOST_MEMBER_PTR_WITH_SIZE_IS(TYPE, MEMBER, SIZE_IS) \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/lib/libmlrpc/common/ndr_auth.c Sat Jan 16 16:59:32 2021 +0000 @@ -0,0 +1,171 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2020 Tintri by DDN, Inc. All Rights Reserved. + */ + +#include <libmlrpc.h> +#include <sys/sysmacros.h> +#include <strings.h> + +/* + * Initializes the sec_trailer (ndr_sec_t). + * The actual token is allocated and set later (in the SSP). + */ +int +ndr_add_auth_token(ndr_auth_ctx_t *ctx, ndr_xa_t *mxa) +{ + ndr_stream_t *nds = &mxa->send_nds; + ndr_sec_t *secp = &mxa->send_auth; + + secp->auth_type = ctx->auth_type; + secp->auth_level = ctx->auth_level; + secp->auth_rsvd = 0; + + /* + * [MS-RPCE] 2.2.2.12 "Authentication Tokens" + * auth_pad_len aligns the packet to 16 bytes. + */ + secp->auth_pad_len = P2ROUNDUP(nds->pdu_scan_offset, 16) - + nds->pdu_scan_offset; + if (NDS_PAD_PDU(nds, nds->pdu_scan_offset, + secp->auth_pad_len, NULL) == 0) + return (NDR_DRC_FAULT_SEC_ENCODE_TOO_BIG); + + /* PAD_PDU doesn't adjust scan_offset */ + nds->pdu_scan_offset += secp->auth_pad_len; + nds->pdu_body_size = nds->pdu_scan_offset - + nds->pdu_body_offset; + + secp->auth_context_id = ctx->auth_context_id; + return (NDR_DRC_OK); +} + +/* + * Does gss_init_sec_context (or equivalent) and creates + * the sec_trailer and the auth token. + * + * Used during binds (and alter context). + * + * Currently, only NETLOGON auth with Integrity protection is implemented. + */ +int +ndr_add_sec_context(ndr_auth_ctx_t *ctx, ndr_xa_t *mxa) +{ + int rc; + + if (ctx->auth_level == NDR_C_AUTHN_NONE || + ctx->auth_type == NDR_C_AUTHN_NONE) + return (NDR_DRC_OK); + + if (ctx->auth_type != NDR_C_AUTHN_GSS_NETLOGON) + return (NDR_DRC_FAULT_SEC_TYPE_UNIMPLEMENTED); + + if (ctx->auth_level != NDR_C_AUTHN_LEVEL_PKT_INTEGRITY) + return (NDR_DRC_FAULT_SEC_LEVEL_UNIMPLEMENTED); + + if ((rc = ndr_add_auth_token(ctx, mxa)) != 0) + return (rc); + + return (ctx->auth_ops.nao_init(ctx->auth_ctx, mxa)); +} + +/* + * Does response-side gss_init_sec_context (or equivalent) and validates + * the sec_trailer and the auth token. + * + * Used during bind (and alter context) ACKs. + */ +int +ndr_recv_sec_context(ndr_auth_ctx_t *ctx, ndr_xa_t *mxa) +{ + ndr_sec_t *bind_secp = &mxa->send_auth; + ndr_sec_t *ack_secp = &mxa->recv_auth; + + if (ctx->auth_level == NDR_C_AUTHN_NONE || + ctx->auth_type == NDR_C_AUTHN_NONE) { + if (mxa->recv_hdr.common_hdr.auth_length != 0) + return (NDR_DRC_FAULT_SEC_AUTH_LENGTH_INVALID); + return (NDR_DRC_OK); + } else if (mxa->recv_hdr.common_hdr.auth_length == 0) { + return (NDR_DRC_FAULT_SEC_AUTH_LENGTH_INVALID); + } + + if (bind_secp->auth_type != ack_secp->auth_type) + return (NDR_DRC_FAULT_SEC_AUTH_TYPE_INVALID); + if (bind_secp->auth_level != ack_secp->auth_level) + return (NDR_DRC_FAULT_SEC_AUTH_LEVEL_INVALID); + + return (ctx->auth_ops.nao_recv(ctx->auth_ctx, mxa)); +} + +/* + * Does gss_MICEx (or equivalent) and creates + * the sec_trailer and the auth token. + * + * Used upon sending a request (client)/response (server) packet. + */ +int +ndr_add_auth(ndr_auth_ctx_t *ctx, ndr_xa_t *mxa) +{ + int rc; + + if (ctx->auth_level == NDR_C_AUTHN_NONE || + ctx->auth_type == NDR_C_AUTHN_NONE) + return (NDR_DRC_OK); + + if (ctx->auth_type != NDR_C_AUTHN_GSS_NETLOGON) + return (NDR_DRC_FAULT_SEC_TYPE_UNIMPLEMENTED); + + if (ctx->auth_level != NDR_C_AUTHN_LEVEL_PKT_INTEGRITY) + return (NDR_DRC_FAULT_SEC_LEVEL_UNIMPLEMENTED); + + if ((rc = ndr_add_auth_token(ctx, mxa)) != 0) + return (rc); + + return (ctx->auth_ops.nao_sign(ctx->auth_ctx, mxa)); +} + +/* + * Does gss_VerifyMICEx (or equivalent) and validates + * the sec_trailer and the auth token. + * + * Used upon receiving a request (server)/response (client) packet. + * + * If auth_verify_resp is B_FALSE, this doesn't verify responses (but + * the SSP may still have side-effects). + */ +int +ndr_check_auth(ndr_auth_ctx_t *ctx, ndr_xa_t *mxa) +{ + ndr_sec_t *secp = &mxa->recv_auth; + + if (ctx->auth_level == NDR_C_AUTHN_NONE || + ctx->auth_type == NDR_C_AUTHN_NONE) { + if (mxa->recv_hdr.common_hdr.auth_length != 0) + return (NDR_DRC_FAULT_SEC_AUTH_LENGTH_INVALID); + return (NDR_DRC_OK); + } else if (mxa->recv_hdr.common_hdr.auth_length == 0) { + return (NDR_DRC_FAULT_SEC_AUTH_LENGTH_INVALID); + } + + if (ctx->auth_type != secp->auth_type || + ctx->auth_type != NDR_C_AUTHN_GSS_NETLOGON) + return (NDR_DRC_FAULT_SEC_AUTH_TYPE_INVALID); + + if (ctx->auth_level != secp->auth_level || + ctx->auth_level != NDR_C_AUTHN_LEVEL_PKT_INTEGRITY) + return (NDR_DRC_FAULT_SEC_AUTH_LEVEL_INVALID); + + return (ctx->auth_ops.nao_verify(ctx->auth_ctx, mxa, + ctx->auth_verify_resp)); +}
--- a/usr/src/lib/libmlrpc/common/ndr_client.c Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/lib/libmlrpc/common/ndr_client.c Sat Jan 16 16:59:32 2021 +0000 @@ -22,7 +22,7 @@ * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ #include <sys/errno.h> @@ -45,6 +45,7 @@ ndr_binding_t *mbind; ndr_xa_t mxa; ndr_bind_hdr_t *bhdr; + ndr_common_header_t *hdr; ndr_p_cont_elem_t *pce; ndr_bind_ack_hdr_t *bahdr; ndr_p_result_t *pre; @@ -59,8 +60,8 @@ ndr_clnt_init_hdr(clnt, &mxa); bhdr = &mxa.send_hdr.bind_hdr; - bhdr->common_hdr.ptype = NDR_PTYPE_BIND; - bhdr->common_hdr.frag_length = sizeof (*bhdr); + hdr = &mxa.send_hdr.bind_hdr.common_hdr; + hdr->ptype = NDR_PTYPE_BIND; bhdr->max_xmit_frag = NDR_DEFAULT_FRAGSZ; bhdr->max_recv_frag = NDR_DEFAULT_FRAGSZ; bhdr->assoc_group_id = 0; @@ -89,6 +90,30 @@ if ((*clnt->xa_init)(clnt, &mxa) < 0) return (NDR_DRC_FAULT_OUT_OF_MEMORY); + /* Reserve room for hdr */ + mxa.send_nds.pdu_scan_offset = sizeof (*bhdr); + + /* GSS_Init_sec_context */ + rc = ndr_add_sec_context(&clnt->auth_ctx, &mxa); + if (NDR_DRC_IS_FAULT(rc)) + goto fault_exit; + + rc = ndr_encode_pdu_auth(&mxa); + if (NDR_DRC_IS_FAULT(rc)) + goto fault_exit; + + /* + * If we have auth data, then pdu_size has been initialized. + * Otherwise, it hasn't. + */ + if (hdr->auth_length == 0) + hdr->frag_length = sizeof (*bhdr); + else + hdr->frag_length = mxa.send_nds.pdu_size; + + /* Reset scan_offset to header */ + mxa.send_nds.pdu_scan_offset = 0; + rc = ndr_encode_pdu_hdr(&mxa); if (NDR_DRC_IS_FAULT(rc)) goto fault_exit; @@ -102,21 +127,36 @@ if (NDR_DRC_IS_FAULT(rc)) goto fault_exit; - /* done with buffers */ - (*clnt->xa_destruct)(clnt, &mxa); + rc = ndr_decode_pdu_auth(&mxa); + if (NDR_DRC_IS_FAULT(rc)) + goto fault_exit; bahdr = &mxa.recv_hdr.bind_ack_hdr; - if (mxa.ptype != NDR_PTYPE_BIND_ACK) - return (NDR_DRC_FAULT_RECEIVED_MALFORMED); + if (mxa.ptype != NDR_PTYPE_BIND_ACK) { + rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; + goto fault_exit; + } - if (bahdr->p_result_list.n_results != 1) - return (NDR_DRC_FAULT_RECEIVED_MALFORMED); + if (bahdr->p_result_list.n_results != 1) { + rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; + goto fault_exit; + } pre = &bahdr->p_result_list.p_results[0]; - if (pre->result != NDR_PCDR_ACCEPTANCE) - return (NDR_DRC_FAULT_RECEIVED_MALFORMED); + if (pre->result != NDR_PCDR_ACCEPTANCE) { + rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; + goto fault_exit; + } + + /* GSS_init_sec_context 2 */ + rc = ndr_recv_sec_context(&clnt->auth_ctx, &mxa); + if (NDR_DRC_IS_FAULT(rc)) + goto fault_exit; + + /* done with buffers */ + (*clnt->xa_destruct)(clnt, &mxa); mbind->p_cont_id = pce->p_cont_id; mbind->which_side = NDR_BIND_SIDE_CLIENT; @@ -139,7 +179,7 @@ ndr_xa_t mxa; ndr_request_hdr_t *reqhdr; ndr_common_header_t *rsphdr; - unsigned long recv_pdu_scan_offset; + unsigned long recv_pdu_scan_offset, recv_pdu_size; int rc; bzero(&mxa, sizeof (mxa)); @@ -160,20 +200,36 @@ /* Reserve room for hdr */ mxa.send_nds.pdu_scan_offset = sizeof (*reqhdr); + /* pdu_scan_offset now points to start of stub */ + mxa.send_nds.pdu_body_offset = mxa.send_nds.pdu_scan_offset; rc = ndr_encode_call(&mxa, params); if (!NDR_DRC_IS_OK(rc)) goto fault_exit; - mxa.send_nds.pdu_scan_offset = 0; + /* + * With the Stub data encoded, calculate the alloc_hint + * before we add padding or auth data. + */ + reqhdr->alloc_hint = mxa.send_nds.pdu_size - + sizeof (ndr_request_hdr_t); + + /* GSS_WrapEx/VerifyMICEx */ + rc = ndr_add_auth(&clnt->auth_ctx, &mxa); + if (NDR_DRC_IS_FAULT(rc)) + goto fault_exit; + + rc = ndr_encode_pdu_auth(&mxa); + if (NDR_DRC_IS_FAULT(rc)) + goto fault_exit; /* * Now we have the PDU size, we need to set up the - * frag_length and calculate the alloc_hint. + * frag_length. + * Also reset pdu_scan_offset to header. */ mxa.send_hdr.common_hdr.frag_length = mxa.send_nds.pdu_size; - reqhdr->alloc_hint = mxa.send_nds.pdu_size - - sizeof (ndr_request_hdr_t); + mxa.send_nds.pdu_scan_offset = 0; rc = ndr_encode_pdu_hdr(&mxa); if (NDR_DRC_IS_FAULT(rc)) @@ -192,26 +248,44 @@ goto fault_exit; } + rc = ndr_decode_pdu_auth(&mxa); + if (NDR_DRC_IS_FAULT(rc)) + goto fault_exit; + + rc = ndr_check_auth(&clnt->auth_ctx, &mxa); + if (NDR_DRC_IS_FAULT(rc)) + goto fault_exit; + rsphdr = &mxa.recv_hdr.common_hdr; if (!NDR_IS_LAST_FRAG(rsphdr->pfc_flags)) { /* * This is a multi-fragment response. - * Preserve the current scan offset while getting + * Preserve the current body offset while getting * fragments so that we can continue afterward * as if we had received the entire response as * a single PDU. + * + * GROW_PDU trashes pdu_size; reset it afterwards. */ + recv_pdu_size = mxa.recv_nds.pdu_size; (void) NDS_GROW_PDU(&mxa.recv_nds, NDR_MULTI_FRAGSZ, NULL); - recv_pdu_scan_offset = mxa.recv_nds.pdu_scan_offset; - mxa.recv_nds.pdu_scan_offset = rsphdr->frag_length; - mxa.recv_nds.pdu_size = rsphdr->frag_length; + /* + * pdu_scan_offset needs to be the first byte after the first + * fragment in pdu_base_addr (minus the sec_trailer). + * + * pdu_size needs to be all of the (usable) data we've + * received thus far. + */ + recv_pdu_scan_offset = mxa.recv_nds.pdu_body_offset; + mxa.recv_nds.pdu_scan_offset = mxa.recv_nds.pdu_body_offset + + mxa.recv_nds.pdu_body_size - mxa.recv_auth.auth_pad_len; + mxa.recv_nds.pdu_size = recv_pdu_size; - if (ndr_clnt_get_frags(clnt, &mxa) < 0) { - rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; + rc = ndr_clnt_get_frags(clnt, &mxa); + if (NDR_DRC_IS_FAULT(rc)) goto fault_exit; - } mxa.recv_nds.pdu_scan_offset = recv_pdu_scan_offset; } @@ -225,6 +299,15 @@ return (NDR_DRC_OK); fault_exit: + ndr_show_hdr(&mxa.send_hdr.common_hdr); + nds_show_state(&mxa.send_nds); + if (mxa.send_hdr.common_hdr.auth_length != 0) + ndr_show_auth(&mxa.send_auth); + + ndr_show_hdr(&mxa.recv_hdr.common_hdr); + nds_show_state(&mxa.recv_nds); + if (mxa.recv_hdr.common_hdr.auth_length != 0) + ndr_show_auth(&mxa.recv_auth); (*clnt->xa_destruct)(clnt, &mxa); return (rc); } @@ -270,23 +353,44 @@ ndr_common_header_t hdr; int frag_size; int last_frag; + int rc; do { if (ndr_clnt_get_frag(clnt, mxa, &hdr) < 0) { nds_show_state(nds); - return (-1); + return (NDR_DRC_FAULT_RECEIVED_RUNT); } last_frag = NDR_IS_LAST_FRAG(hdr.pfc_flags); frag_size = hdr.frag_length; + /* + * ndr_clnt_get_frag() doesn't change pdu_scan_offset. + */ if (frag_size > (nds->pdu_size - nds->pdu_scan_offset)) { nds_show_state(nds); - return (-1); + return (NDR_DRC_FAULT_RECEIVED_MALFORMED); } + if (hdr.auth_length != 0 && hdr.auth_length > + (hdr.frag_length - nds->pdu_hdr_size - SEC_TRAILER_SIZE)) + return (NDR_DRC_FAULT_RECEIVED_MALFORMED); + + rc = ndr_decode_pdu_auth(mxa); + if (NDR_DRC_IS_FAULT(rc)) + return (rc); + + rc = ndr_check_auth(&clnt->auth_ctx, mxa); + if (NDR_DRC_IS_FAULT(rc)) + return (rc); + + /* + * Headers, Auth Padding, and auth data shouldn't be kept + * from fragments. + */ ndr_remove_frag_hdr(nds); - nds->pdu_scan_offset += frag_size - NDR_RSP_HDR_SIZE; + nds->pdu_scan_offset += + nds->pdu_body_size - mxa->recv_auth.auth_pad_len; } while (!last_frag); return (0); @@ -307,7 +411,7 @@ available = nds->pdu_size - nds->pdu_scan_offset; while (available < NDR_RSP_HDR_SIZE) { - if ((nbytes += (*clnt->xa_read)(clnt, mxa)) <= 0) + if ((nbytes = (*clnt->xa_read)(clnt, mxa)) <= 0) return (-1); available += nbytes; }
--- a/usr/src/lib/libmlrpc/common/ndr_marshal.c Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/lib/libmlrpc/common/ndr_marshal.c Sat Jan 16 16:59:32 2021 +0000 @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ #include <assert.h> @@ -38,18 +38,40 @@ static int ndr_decode_hdr_common(ndr_stream_t *, ndr_common_header_t *); static int ndr_decode_pac_hdr(ndr_stream_t *, ndr_pac_hdr_t *); +/* + * This is the layout of an RPC PDU, as shown in + * [MS-RPCE] 2.2.2.13 "Verification Trailer". + * + * +-------------------------------+ + * | PDU Header | + * +-------------------------------+ ==== + * | Stub Data | + * +-------------------------------+ PDU + * | Stub Padding Octets | + * +-------------------------------+ Body + * | Verification Trailer | + * +-------------------------------+ Here + * | Authentication Padding | + * +-------------------------------+ ==== + * | sec_trailer | + * +-------------------------------+ + * | Authentication Token | + * +-------------------------------+ + * + * We don't use the "Verification Trailer" for anything yet. + * sec_trailer and Authentication Token are for Secure RPC, + * and are collectively the 'auth_verifier_co' in DCERPC. + * + * Each fragment of a multi-fragment response has a unique + * header and, if authentication was requested, a unique + * sec_trailer. + */ + static int -ndr_encode_decode_common(ndr_stream_t *nds, unsigned opnum, - ndr_typeinfo_t *ti, void *datum) +ndr_convert_nds_error(ndr_stream_t *nds) { int rc; - /* - * Perform the (un)marshalling - */ - if (ndo_operation(nds, ti, opnum, datum)) - return (NDR_DRC_OK); - switch (nds->error) { case NDR_ERR_MALLOC_FAILED: rc = NDR_DRC_FAULT_OUT_OF_MEMORY; @@ -78,6 +100,31 @@ return (rc); } +static int +ndr_encode_decode_common(ndr_stream_t *nds, unsigned opnum, + ndr_typeinfo_t *ti, void *datum) +{ + /* + * Perform the (un)marshalling + */ + if (ndo_operation(nds, ti, opnum, datum)) + return (NDR_DRC_OK); + + return (ndr_convert_nds_error(nds)); +} + +static int +ndr_encode_decode_type(ndr_stream_t *nds, ndr_typeinfo_t *ti, void *datum) +{ + /* + * Perform the (un)marshalling + */ + if (ndo_process(nds, ti, datum)) + return (NDR_DRC_OK); + + return (ndr_convert_nds_error(nds)); +} + ndr_buf_t * ndr_buf_init(ndr_typeinfo_t *ti) { @@ -115,7 +162,7 @@ * * pac_info_t info; * - * if ((nbuf = ndr_buf_init(&TYPEINFO(ndr_pac)) != NULL) { + * if ((nbuf = ndr_buf_init(&TYPEINFO(ndr_pac)) != NULL) { * rc = ndr_decode_buf(nbuf, opnum, data, datalen, &info); * ... * ndr_buf_fini(nbuf); @@ -145,6 +192,7 @@ return (rc); bcopy(data, nbuf->nb_nds.pdu_base_addr, datalen); + nbuf->nb_nds.pdu_size = datalen; switch (hdr_type) { case NDR_PTYPE_COMMON: @@ -252,7 +300,9 @@ ndr_common_header_t *hdr = &mxa->recv_hdr.common_hdr; ndr_stream_t *nds = &mxa->recv_nds; int rc; + ulong_t saved_offset; + saved_offset = nds->pdu_scan_offset; rc = ndr_decode_hdr_common(nds, hdr); if (NDR_DRC_IS_FAULT(rc)) return (rc); @@ -264,6 +314,16 @@ return (NDR_DRC_FAULT_RPCHDR_DECODE_FAILED); mxa->ptype = hdr->ptype; + /* pdu_scan_offset now points to (this fragment's) stub data */ + nds->pdu_body_offset = nds->pdu_scan_offset; + nds->pdu_hdr_size = nds->pdu_scan_offset - saved_offset; + nds->pdu_body_size = hdr->frag_length - hdr->auth_length - + nds->pdu_hdr_size - + ((hdr->auth_length != 0) ? SEC_TRAILER_SIZE : 0); + + if (hdr->auth_length != 0 && hdr->auth_length > + (hdr->frag_length - nds->pdu_hdr_size - SEC_TRAILER_SIZE)) + return (NDR_DRC_FAULT_RECEIVED_MALFORMED); return (NDR_DRC_OK); } @@ -274,6 +334,7 @@ int rc; int charset; int byte_order; + ulong_t saved_offset; if (nds->m_op != NDR_M_OP_UNMARSHALL) return (NDR_DRC_FAULT_RPCHDR_MODE_MISMATCH); @@ -281,8 +342,8 @@ /* * All PDU headers are at least this big */ - rc = NDS_GROW_PDU(nds, sizeof (ndr_common_header_t), 0); - if (!rc) + saved_offset = nds->pdu_scan_offset; + if ((nds->pdu_size - saved_offset) < sizeof (ndr_common_header_t)) return (NDR_DRC_FAULT_RPCHDR_RECEIVED_RUNT); /* @@ -315,6 +376,8 @@ rc = ndr_encode_decode_common(nds, ptype, &TYPEINFO(ndr_hdr), hdr); + if (hdr->frag_length > (nds->pdu_size - saved_offset)) + rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; return (NDR_DRC_PTYPE_RPCHDR(rc)); } @@ -329,8 +392,7 @@ /* * All PDU headers are at least this big */ - rc = NDS_GROW_PDU(nds, sizeof (ndr_pac_hdr_t), 0); - if (!rc) + if ((nds->pdu_size - nds->pdu_scan_offset) < sizeof (ndr_pac_hdr_t)) return (NDR_DRC_FAULT_RPCHDR_RECEIVED_RUNT); /* @@ -388,6 +450,13 @@ sizeof (WORD)); nds_bswap(&tmp->call_id, &hdr->call_id, sizeof (DWORD)); } + + /* pdu_scan_offset points to byte 0 of this fragment */ + nds->pdu_hdr_size = NDR_RSP_HDR_SIZE; + nds->pdu_body_offset = nds->pdu_scan_offset + nds->pdu_hdr_size; + nds->pdu_body_size = hdr->frag_length - hdr->auth_length - + nds->pdu_hdr_size - + ((hdr->auth_length != 0) ? SEC_TRAILER_SIZE : 0); } /* @@ -418,7 +487,10 @@ data = hdr + NDR_RSP_HDR_SIZE; nbytes = nds->pdu_size - nds->pdu_scan_offset - NDR_RSP_HDR_SIZE; - bcopy(data, hdr, nbytes); + /* + * Move all of the data after the header back to where the header began. + */ + memmove(hdr, data, nbytes); nds->pdu_size -= NDR_RSP_HDR_SIZE; } @@ -442,9 +514,24 @@ fragtype = "intermediate"; ndo_printf(NULL, NULL, - "ndr hdr: %d.%d ptype=%d, %s frag (flags=0x%08x) len=%d", + "ndr hdr: %d.%d ptype=%d, %s frag (flags=0x%08x) len=%d " + "auth_len=%d", hdr->rpc_vers, hdr->rpc_vers_minor, hdr->ptype, - fragtype, hdr->pfc_flags, hdr->frag_length); + fragtype, hdr->pfc_flags, hdr->frag_length, hdr->auth_length); +} + +void +ndr_show_auth(ndr_sec_t *auth) +{ + if (auth == NULL) { + ndo_printf(NULL, NULL, "ndr auth: <null>"); + return; + } + + ndo_printf(NULL, NULL, + "ndr auth: type=0x%x, level=0x%x, pad_len=%d, ctx_id=%d", + auth->auth_type, auth->auth_level, auth->auth_pad_len, + auth->auth_context_id); } int @@ -646,3 +733,196 @@ offset += sizeof (ndr_p_result_list_t); return (offset); } + +/* + * This is a hand-coded (un)marshalling routine for auth_verifier_co + * (aka ndr_sec_t). + * + * We need to pretend this structure isn't variably sized, until ndrgen + * has been modified to support variable-sized arrays. + * Here, we only account for the fixed-size members (8 bytes), plus + * a pointer for the C structure. + * + * We then convert between a pointer to the auth token (auth_value, + * allocated here during unmarshall) and a flat, 'fixed'-sized array. + */ + +int ndr__auth_verifier_co(ndr_ref_t *encl_ref); +ndr_typeinfo_t ndt__auth_verifier_co = { + 1, /* NDR version */ + 3, /* alignment */ + NDR_F_STRUCT, /* flags */ + ndr__auth_verifier_co, /* ndr_func */ + 8, /* pdu_size_fixed_part */ + 0, /* pdu_size_variable_part */ + 8 + sizeof (void *), /* c_size_fixed_part */ + 0, /* c_size_variable_part */ +}; + +/* + * [_no_reorder] + */ +int +ndr__auth_verifier_co(ndr_ref_t *encl_ref) +{ + ndr_stream_t *nds = encl_ref->stream; + ndr_xa_t *mxa = /*LINTED E_BAD_PTR_CAST_ALIGN*/ + (ndr_xa_t *)encl_ref->datum; + ndr_common_header_t *hdr; + ndr_ref_t myref; + ndr_sec_t *val; + + /* + * Assumes scan_offset points to the end of PDU body. + * (That's base + frag_len - auth_len - SEC_TRAILER_SIZE) + * + * At some point, NDRGEN could use struct initializers instead of + * bzero() + initialization. + */ + bzero(&myref, sizeof (myref)); + myref.enclosing = encl_ref; + myref.stream = encl_ref->stream; + + switch (nds->m_op) { + case NDR_M_OP_MARSHALL: + val = &mxa->send_auth; + hdr = &mxa->send_hdr.common_hdr; + break; + + case NDR_M_OP_UNMARSHALL: + val = &mxa->recv_auth; + hdr = &mxa->recv_hdr.common_hdr; + val->auth_value = (uchar_t *)NDS_MALLOC(nds, hdr->auth_length, + encl_ref); + break; + + default: + NDR_SET_ERROR(encl_ref, NDR_ERR_M_OP_INVALID); + return (0); + } + + /* + * ndr_topmost() can't account for auth_length (pdu_scan/end_offset). + * This would only matter if any of this struct's members + * are treated as 'outer' constructs, but they aren't. + */ + encl_ref->pdu_end_offset += hdr->auth_length; + nds->pdu_scan_offset += hdr->auth_length; + + NDR_MEMBER(_uchar, auth_type, 0UL); + NDR_MEMBER(_uchar, auth_level, 1UL); + NDR_MEMBER(_uchar, auth_pad_len, 2UL); + NDR_MEMBER(_uchar, auth_rsvd, 3UL); + NDR_MEMBER(_ulong, auth_context_id, 4UL); + + NDR_MEMBER_PTR_WITH_DIMENSION(_uchar, auth_value, 8UL, + hdr->auth_length); + + return (1); +} + +int +ndr_encode_pdu_auth(ndr_xa_t *mxa) +{ + ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr; + ndr_stream_t *nds = &mxa->send_nds; + int rc; + ulong_t want_size; + + if (nds->m_op != NDR_M_OP_MARSHALL) + return (NDR_DRC_FAULT_MODE_MISMATCH); + + if (hdr->auth_length == 0) + return (NDR_DRC_OK); + + want_size = nds->pdu_scan_offset + hdr->auth_length + SEC_TRAILER_SIZE; + + /* + * Make sure we have space for the sec trailer - the marshaller + * doesn't know how large the auth token is. + * Note: ndr_add_auth_token() has already added padding. + * + * NDS_GROW_PDU will adjust pdu_size for us. + */ + if (nds->pdu_max_size < want_size) { + if (NDS_GROW_PDU(nds, want_size, NULL) == 0) + return (NDR_DRC_FAULT_ENCODE_TOO_BIG); + } else { + nds->pdu_size = want_size; + } + rc = ndr_encode_decode_type(nds, &TYPEINFO(auth_verifier_co), + mxa); + + return (rc); +} + +int +ndr_decode_pdu_auth(ndr_xa_t *mxa) +{ + ndr_common_header_t *hdr = &mxa->recv_hdr.common_hdr; + ndr_stream_t *nds = &mxa->recv_nds; + ndr_sec_t *auth = &mxa->recv_auth; + int rc; + ulong_t saved_offset; + size_t auth_size; + + if (nds->m_op != NDR_M_OP_UNMARSHALL) + return (NDR_DRC_FAULT_MODE_MISMATCH); + + mxa->recv_auth.auth_pad_len = 0; + if (hdr->auth_length == 0) + return (NDR_DRC_OK); + + /* + * Save the current offset, and skip to the sec_trailer. + * That's located after the (fragment of) stub data and the auth + * pad bytes (collectively the 'PDU Body'). + */ + saved_offset = nds->pdu_scan_offset; + nds->pdu_scan_offset = nds->pdu_body_offset + nds->pdu_body_size; + + /* auth_length is all of the data after the sec_trailer */ + if (hdr->auth_length > + (nds->pdu_size - nds->pdu_scan_offset - SEC_TRAILER_SIZE)) { + nds->pdu_scan_offset = saved_offset; + return (NDR_DRC_FAULT_RECEIVED_MALFORMED); + } + + rc = ndr_encode_decode_type(nds, &TYPEINFO(auth_verifier_co), + mxa); + + /* + * Reset the scan_offset for call decode processing. + * If we were successful, remove the sec trailer and padding + * from size accounting. + */ + if (auth->auth_pad_len > nds->pdu_body_size) + rc = NDR_DRC_FAULT_RECEIVED_MALFORMED; + else if (rc == NDR_DRC_OK) { + auth_size = hdr->auth_length + SEC_TRAILER_SIZE + + auth->auth_pad_len; + + /* + * After the authenticator has been decoded, + * pdu_scan_offset points to just after the auth token, + * which is the end of the fragment. + * + * If there's no data after the authenticator, then we + * just remove the authenticator from size accounting. + * Otherwise, need to memmove() all of that data back to after + * the stub data. The data we move starts at the beginning of + * the next fragment. + */ + if (nds->pdu_size > nds->pdu_scan_offset) { + uchar_t *next_frag_ptr = nds->pdu_base_addr + + nds->pdu_scan_offset; + + memmove(next_frag_ptr - auth_size, next_frag_ptr, + nds->pdu_size - nds->pdu_scan_offset); + } + + nds->pdu_size -= auth_size; + } + nds->pdu_scan_offset = saved_offset; + return (rc); +}
--- a/usr/src/lib/libmlrpc/common/ndr_ops.c Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/lib/libmlrpc/common/ndr_ops.c Sat Jan 16 16:59:32 2021 +0000 @@ -21,7 +21,7 @@ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ /* @@ -186,9 +186,11 @@ return; } - ndo_printf(NULL, NULL, "nds: base=0x%x, size=%d, max=%d, scan=%d", + ndo_printf(NULL, NULL, "nds: base=0x%x, size=%d, max=%d, scan=%d, " + "hdr_size=%d, body_size=%d, body_offset=%d", nds->pdu_base_offset, nds->pdu_size, nds->pdu_max_size, - nds->pdu_scan_offset); + nds->pdu_scan_offset, nds->pdu_hdr_size, nds->pdu_body_size, + nds->pdu_body_offset); } /*
--- a/usr/src/lib/libmlrpc/common/ndr_process.c Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/lib/libmlrpc/common/ndr_process.c Sat Jan 16 16:59:32 2021 +0000 @@ -23,7 +23,7 @@ * Use is subject to license terms. * * Copyright 2012 Milan Jurik. All rights reserved. - * Copyright 2019 Nexenta by DDN, Inc. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ /* @@ -1474,7 +1474,7 @@ n_pad = NDR_ALIGN4(nds->pdu_scan_offset); } - if (n_pad == 0) + if ((outer_ref->ti->type_flags & NDR_F_FAKE) != 0 || n_pad == 0) return (1); /* already aligned, often the case */ if (!ndr_outer_grow(outer_ref, n_pad)) @@ -1544,6 +1544,57 @@ } /* + * Some 'outer' constructs incorrectly align the entire construct + * on a 4-byte boundary, when each of its members needs to be + * aligned separately. This function handles aligning 'inner' + * members on their natural alignment boundary. + * + * NOTE: This assumes it is not being used for headers. + * Headers present some unique concerns that this may not + * adequately address (e.g. reserved space, pdu_body_offset). + */ +int +ndr_inner_align(ndr_ref_t *arg_ref) +{ + ndr_stream_t *nds = arg_ref->stream; + int rc; + unsigned n_pad; + + n_pad = ((arg_ref->ti->alignment + 1) - arg_ref->pdu_offset) & + arg_ref->ti->alignment; + + if (n_pad == 0) + return (1); /* already aligned, often the case */ + + if (!ndr_outer_grow(arg_ref->enclosing, n_pad)) + return (0); /* error already set */ + + switch (nds->m_op) { + case NDR_M_OP_MARSHALL: + rc = NDS_PAD_PDU(nds, arg_ref->pdu_offset, n_pad, arg_ref); + if (!rc) { + NDR_SET_ERROR(arg_ref, NDR_ERR_PAD_FAILED); + return (0); + } + break; + + case NDR_M_OP_UNMARSHALL: + break; + + default: + NDR_SET_ERROR(arg_ref, NDR_ERR_M_OP_INVALID); + return (0); + } + + /* All current and future offsets need to advance */ + arg_ref->enclosing->pdu_offset += n_pad; + arg_ref->pdu_offset += n_pad; + /* ndr_outer_grow changed pdu_end_offset */ + nds->pdu_scan_offset += n_pad; + return (1); +} + +/* * INNER ELEMENTS * * The local datum (arg_ref->datum) already exists, there is no need to @@ -1568,6 +1619,22 @@ params = arg_ref->inner_flags & NDR_F_PARAMS_MASK; + /* + * Switched unions are meant to be converted to and from encapsulated + * structures on the wire. However, NDRGEN doesn't implement this. + * As a result, our interface definitions use 'fake' structures + * to represent switched unions. + * This causes the structure to be aligned on a struct (4 byte) + * boundary, with none of its members having separate alignment - + * but that's not correct. Each of its members has its own, + * natural alignment, and we need to honor that. + * That happens here. + */ + if (arg_ref->enclosing != NULL && + (arg_ref->enclosing->ti->type_flags & NDR_F_FAKE) != 0 && + !ndr_inner_align(arg_ref)) + return (0); /* error already set */ + switch (params) { case NDR_F_NONE: if (is_union) {
--- a/usr/src/lib/libmlrpc/common/ndrtypes.ndl Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/lib/libmlrpc/common/ndrtypes.ndl Sat Jan 16 16:59:32 2021 +0000 @@ -22,7 +22,7 @@ * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ #ifndef _NDRTYPES_NDL_ @@ -42,6 +42,7 @@ #define IN [in] #define OUT [out] #define INOUT [in out] +#define FAKE [fake] #define STRING [string] #define SIZE_IS(X) [size_is(X)] @@ -85,6 +86,7 @@ #define IN #define OUT #define INOUT +#define FAKE #define STRING #define SIZE_IS(X)
--- a/usr/src/lib/libmlrpc/common/rpcpdu.ndl Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/lib/libmlrpc/common/rpcpdu.ndl Sat Jan 16 16:59:32 2021 +0000 @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ #ifndef _RPCPDU_NDL_ @@ -132,7 +133,7 @@ * MS-RPCE 2.2.2.3 PFC_SUPPORT_HEADER_SIGN * For PDU types bind, bind_ack, alter_context and alter_context_resp, * 0x04 means PFC_SUPPORT_HEADER_SIGN. - * For other PDU types 0x04 means PFC_PENDING_CANCEL. + * For other PDU types 0x04 means PFC_PENDING_CANCEL. */ #define NDR_PFC_FIRST_FRAG 0x01 /* First fragment */ #define NDR_PFC_LAST_FRAG 0x02 /* Last framgent */ @@ -202,7 +203,7 @@ * One header per serialization stream: the header must be little endian. * The filler must be set to 0xcccccccc during marshaling and ignored * during unmarshaling. - */ + */ _NO_REORDER_ struct ndr_serialtype1_hdr { BYTE version; /* 00:01 1 */ @@ -232,7 +233,7 @@ * The header must be little endian. * The endianinfo and reserved fields must be set to 0xcccccccc during * marshaling and ignored during unmarshaling. - */ + */ _NO_REORDER_ struct ndr_serialtype2_hdr { BYTE version; /* 00:01 1 */ @@ -362,7 +363,35 @@ #define NDR_USER_DATA_NOT_READABLE 6 #define NDR_NO_PSAP_AVAILABLE 7 #define NDR_AUTH_TYPE_NOT_RECOGNIZED 8 -#define NDR_INAVLID_CHECKSUM 9 +#define NDR_INVALID_CHECKSUM 9 + +/* + * NDRGEN can't handle variable-length arrays, so we provide our own + * marshal/unmarshal routine, so that we can convert to and from + * our pointer and a fixed-size array. + * + * NOTE: MS-RPCE calls this a sec_trailer_t, so we use ndr_sec_t. + * auth_value should also be an ANY_SIZE_ARRAY, but NDRGEN can't handle + * variable-sized arrays. + */ +IMPORT_EXTERN +_NO_REORDER_ +struct auth_verifier_co { + /* restore 16 byte alignment */ + /* SIZE_IS(auth_pad_len) */ + /* BYTE auth_pad[ANY_SIZE_ARRAY] */ /* align(16) */ + BYTE auth_type; /* 00:01 */ + BYTE auth_level; /* 01:01 */ + BYTE auth_pad_len; /* 02:01 */ + BYTE auth_rsvd; /* 03:01 */ + DWORD auth_context_id; /* 04:04 */ + /* SIZE_IS(hdr->auth_length) */ + BYTE *auth_value; /* 08:* credentials */ +}; +typedef struct auth_verifier_co ndr_sec_t; +EXTERNTYPEINFO(auth_verifier_co) + +#define SEC_TRAILER_SIZE 8 /* * Alter Context PDU (0x0E) @@ -376,7 +405,7 @@ WORD max_recv_frag; /* 18:02 ignored */ DWORD assoc_group_id; /* 20:04 ignored */ - /* + /* * Presentation context list (see bind hdr comments). */ ndr_p_cont_list_t p_context_elem; /* 24: */ @@ -406,7 +435,7 @@ DWORD assoc_group_id; /* 20:04 ignored */ ndr_port_any_t sec_addr; /* 24:20 ignored */ - /* + /* * Presentation context list (see bind hdr comments). */ ndr_p_result_list_t p_result_list; /* 44:nn */
--- a/usr/src/lib/smbsrv/libmlsvc/Makefile.com Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/lib/smbsrv/libmlsvc/Makefile.com Sat Jan 16 16:59:32 2021 +0000 @@ -20,7 +20,7 @@ # # # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. -# Copyright 2014 Nexenta Systems, Inc. All rights reserved. +# Copyright 2020 Tintri by DDN, Inc. All rights reserved. # # Copyright (c) 2018, Joyent, Inc. @@ -45,6 +45,7 @@ netdfs.o \ netr_auth.o \ netr_logon.o \ + netr_ssp.o \ samlib.o \ samr_clnt.o \ samr_svc.o \ @@ -85,8 +86,9 @@ INCS += -I$(SRC)/uts/common/smbsrv/ndl LDLIBS += $(MACH_LDLIBS) -LDLIBS += -lmlrpc -lsmb -lsmbns -lshare -lsmbfs -lnsl -lpkcs11 \ - -lscf -lcmdutils -lsec -lavl -lnvpair -luutil -luuid -lgen -lzfs -lc +LDLIBS += -lmlrpc -lsmb -lsmbns -lshare -lsmbfs -lnsl -lpkcs11 -lmd5 \ + -lscf -lcmdutils -lsec -lavl -lnvpair -luutil -luuid -lgen -lzfs \ + -lresolv -lc CPPFLAGS += $(INCS) -D_REENTRANT CPPFLAGS += -Dsyslog=smb_syslog
--- a/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h Sat Jan 16 16:59:32 2021 +0000 @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2019 Nexenta Systems, Inc. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ #ifndef _LIBMLSVC_H @@ -155,6 +155,8 @@ void ndr_rpc_init(void); void ndr_rpc_fini(void); uint32_t ndr_rpc_bind(mlsvc_handle_t *, char *, char *, char *, const char *); +uint32_t ndr_rpc_bind_secure(mlsvc_handle_t *, char *, char *, char *, + const char *, ndr_auth_ctx_t *); void ndr_rpc_unbind(mlsvc_handle_t *); void ndr_rpc_status(mlsvc_handle_t *, int, uint32_t);
--- a/usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers Sat Jan 16 16:59:32 2021 +0000 @@ -20,7 +20,7 @@ # # # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. -# Copyright 2019 Nexenta Systems, Inc. All rights reserved. +# Copyright 2020 Tintri by DDN, Inc. All rights reserved. # # @@ -54,6 +54,9 @@ mlsvc_init; mlsvc_join; mlsvc_netlogon; + netlogon_init_global; + netlogon_logon; + netr_initialize; smb_autohome_add; smb_autohome_remove; smb_ddiscover_bad_dc;
--- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h Sat Jan 16 16:59:32 2021 +0000 @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2018 Nexenta Systems, Inc. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ #ifndef _SMBSRV_MLSVC_H @@ -56,9 +56,12 @@ void netdfs_finalize(void); /* netr_auth.c */ +/* No RPC-level auth */ DWORD netr_open(char *, char *, mlsvc_handle_t *); +/* Uses RPC-level auth if supported */ +DWORD netr_open_secure(char *, char *, mlsvc_handle_t *); int netr_close(mlsvc_handle_t *); -DWORD netlogon_auth(char *, mlsvc_handle_t *, DWORD); +DWORD netlogon_auth(char *, char *, DWORD); int netr_setup_authenticator(struct netr_info *, struct netr_authenticator *, struct netr_authenticator *); DWORD netr_validate_chain(struct netr_info *, struct netr_authenticator *);
--- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c Sat Jan 16 16:59:32 2021 +0000 @@ -21,7 +21,7 @@ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ /* @@ -73,9 +73,9 @@ * NT_STATUS_INTERNAL_ERROR (bad args etc) * NT_STATUS_NO_MEMORY */ -DWORD -ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain, - char *username, const char *service) +static DWORD +ndr_rpc_bind_common(mlsvc_handle_t *handle, char *server, char *domain, + char *username, const char *service, ndr_auth_ctx_t *auth_ctx) { struct smb_ctx *ctx = NULL; ndr_service_t *svc; @@ -145,6 +145,18 @@ } /* + * Setup authentication, if requested. + */ + status = mlrpc_clh_set_auth(handle, auth_ctx); + if (status != 0) { + syslog(LOG_DEBUG, "ndr_rpc_bind: " + "mlrpc_clh_set_auth, %s (0x%x)", + xlate_nt_status(status), status); + + goto errout; + } + + /* * This does the pipe open and OtW RPC bind. * Handles pipe open retries. */ @@ -161,15 +173,36 @@ default: break; } - ctx = mlrpc_clh_free(handle); - if (ctx != NULL) { - smbrdr_ctx_free(ctx); - } + + goto errout; } + return (NT_STATUS_SUCCESS); + +errout: + ctx = mlrpc_clh_free(handle); + if (ctx != NULL) { + smbrdr_ctx_free(ctx); + } return (status); } +DWORD +ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain, + char *username, const char *service) +{ + return (ndr_rpc_bind_common(handle, server, domain, username, service, + NULL)); +} + +DWORD +ndr_rpc_bind_secure(mlsvc_handle_t *handle, char *server, char *domain, + char *username, const char *service, ndr_auth_ctx_t *auth_ctx) +{ + return (ndr_rpc_bind_common(handle, server, domain, username, service, + auth_ctx)); +} + /* * Unbind and close the pipe to an RPC service * and cleanup the smb_ctx.
--- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_netr.c Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_netr.c Sat Jan 16 16:59:32 2021 +0000 @@ -21,6 +21,7 @@ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ /* @@ -77,7 +78,12 @@ void netr_initialize(void) { + uint32_t flags; + (void) ndr_svc_register(&netr_service); + + flags = smb_get_netlogon_flags(); + netlogon_init_global(flags); } /* @@ -156,18 +162,16 @@ DECL_FIXUP_STRUCT(netr_validation_u); DECL_FIXUP_STRUCT(netr_validation_info); DECL_FIXUP_STRUCT(netr_SamLogon); +DECL_FIXUP_STRUCT(netr_SamLogonEx); /* - * Patch the netr_SamLogon union. - * This function is called from mlsvc_netr_ndr.c + * Patch the netr_validation_info union. */ -void -fixup_netr_SamLogon(struct netr_SamLogon *arg) +static unsigned short +fixup_netr_validation_info(WORD level) { unsigned short size1 = 0; unsigned short size2 = 0; - unsigned short size3 = 0; - WORD level = (WORD)arg->validation_level; switch (level) { case 3: @@ -190,9 +194,43 @@ }; size2 = size1 + (2 * sizeof (DWORD)); - size3 = size2 + sizeof (ndr_request_hdr_t) + sizeof (DWORD); FIXUP_PDU_SIZE(netr_validation_u, size1); FIXUP_PDU_SIZE(netr_validation_info, size2); + + return (size2); +} + + +/* + * Patch the netr_SamLogon union. + * This function is called from mlsvc_netr_ndr.c + */ +void +fixup_netr_SamLogon(struct netr_SamLogon *arg) +{ + unsigned short size2 = 0; + unsigned short size3 = 0; + + size2 = fixup_netr_validation_info(arg->validation_level); + /* netr_valid ENC-UNION + hdr + ret_auth PTR + authoritative + status */ + size3 = size2 + sizeof (ndr_request_hdr_t) + 3 * sizeof (DWORD); FIXUP_PDU_SIZE(netr_SamLogon, size3); } + +/* + * Patch the netr_SamLogonEx union. + * This function is called from mlsvc_netr_ndr.c + */ +void +fixup_netr_SamLogonEx(struct netr_SamLogonEx *arg) +{ + unsigned short size2 = 0; + unsigned short size3 = 0; + + size2 = fixup_netr_validation_info(arg->validation_level); + /* netr_valid ENC-UNION + hdr + authoritative + flags + status */ + size3 = size2 + sizeof (ndr_request_hdr_t) + 3 * sizeof (DWORD); + + FIXUP_PDU_SIZE(netr_SamLogonEx, size3); +}
--- a/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c Sat Jan 16 16:59:32 2021 +0000 @@ -21,7 +21,7 @@ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ /* @@ -43,6 +43,7 @@ #include <smbsrv/libsmb.h> #include <smbsrv/libsmbns.h> #include <smbsrv/libmlsvc.h> +#include <mlsvc.h> #include <smbsrv/ndl/netlogon.ndl> #include <smbsrv/smbinfo.h> #include <smbsrv/netrauth.h> @@ -62,7 +63,78 @@ /* * Shared with netr_logon.c */ -netr_info_t netr_global_info; +netr_info_t netr_global_info = { + .use_secure_rpc = B_TRUE, + .use_logon_ex = B_TRUE +}; +extern ndr_auth_ctx_t netr_ssp_ctx; + +/* + * These flags control various parts of NetLogon RPC messages. + * The default is 0 - setting a bit disables some feature. + * They are set in smbd/netlogon_flags in svc:/network/smb/server. + * These are set when smbd starts. Changing them requires + * restarting smbd. + * + * These shouldn't be confused with either SamLogonEx's ExtraFlags, + * or NetrServerAuthenticate's negotiate_flags. + * + * DISABLE_SECURE_RPC causes Netlogon to use unauthenticated RPC. + * Note that the underlying transport is still authenticated and signed. + * + * DISABLE_RESP_VERIF instructs RPC authentication to ignore failures + * when verifying responses. + * + * DISABLE_SAMLOGONEX causes Netlogon to always use SamLogon, which + * makes use of Netlogon Authenticators. + */ +#define NETR_CFG_DISABLE_SECURE_RPC 0x00000001 +#define NETR_CFG_DISABLE_RESP_VERIF 0x00000002 +#define NETR_CFG_DISABLE_SAMLOGONEX 0x00000004 + +void +netlogon_init_global(uint32_t flags) +{ + netr_global_info.use_secure_rpc = + ((flags & NETR_CFG_DISABLE_SECURE_RPC) == 0); + netr_ssp_ctx.auth_verify_resp = + ((flags & NETR_CFG_DISABLE_RESP_VERIF) == 0); + netr_global_info.use_logon_ex = + ((flags & NETR_CFG_DISABLE_SAMLOGONEX) == 0); +} + +/* + * AES-CFB8 has the odd property that 1/256 keys will encrypt + * a full block of 0s to all 0s. In order to mitigate this, Windows DCs + * now reject Challenges and Credentials where "none of the first 5 bytes + * are unique" (i.e. [MS-NRPC] 3.1.4.1 "Session-Key Negotiation" Step 7). + * This detects that condition so that we can avoid having our connection + * rejected unexpectedly. + * + * I've interpreted this condition as 'amongst the first 5 bytes, + * at least one must appear exactly once'. + * + * NOTE: Win2012r2 seems to only reject challenges whose first 5 bytes are 0. + */ +boolean_t +passes_dc_mitigation(uint8_t *buf) +{ + int i, j; + + for (i = 0; i < 5; i++) { + for (j = 0; j < 5; j++) { + if (i != j && buf[i] == buf[j]) + break; + } + + /* if this byte didn't match any other byte, this passes */ + if (j == 5) + return (B_TRUE); + } + + /* None of the bytes were unique - the check fails */ + return (B_FALSE); +} /* * netlogon_auth @@ -79,32 +151,73 @@ * */ DWORD -netlogon_auth(char *server, mlsvc_handle_t *netr_handle, DWORD flags) +netlogon_auth(char *server, char *domain, DWORD flags) { + mlsvc_handle_t netr_handle; netr_info_t *netr_info; int rc; DWORD leout_rc[2]; + boolean_t retry; + DWORD status; + + /* + * [MS-NRPC] 3.1.4.1 "Session-Key Negotiation" + * Negotiation happens on an 'unprotected RPC channel' + * (no RPC-level auth). + */ + status = netr_open(server, domain, &netr_handle); + + if (status != 0) { + syslog(LOG_ERR, "netlogon_auth remote open failed (%s)", + xlate_nt_status(status)); + return (status); + } netr_info = &netr_global_info; - bzero(netr_info, sizeof (netr_info_t)); - - netr_info->flags |= flags; + bzero(&netr_info->session_key, sizeof (netr_info->session_key)); + netr_info->flags = flags; rc = smb_getnetbiosname(netr_info->hostname, NETBIOS_NAME_SZ); if (rc != 0) - return (NT_STATUS_UNSUCCESSFUL); + goto errout; /* server is our DC. Note: normally an FQDN. */ (void) snprintf(netr_info->server, sizeof (netr_info->server), "\\\\%s", server); - LE_OUT32(&leout_rc[0], random()); - LE_OUT32(&leout_rc[1], random()); - (void) memcpy(&netr_info->client_challenge, leout_rc, - sizeof (struct netr_credential)); + /* + * Domain (FQDN and NetBIOS) Name needed for Netlogon SSP-based + * Secure RPC. + */ + rc = smb_getdomainname(netr_info->nb_domain, + sizeof (netr_info->nb_domain)); + if (rc != 0) + goto errout; + + rc = smb_getfqdomainname(netr_info->fqdn_domain, + sizeof (netr_info->fqdn_domain)); + if (rc != 0) + goto errout; - if ((rc = netr_server_req_challenge(netr_handle, netr_info)) == 0) { - rc = netr_server_authenticate2(netr_handle, netr_info); + /* + * [MS-NRPC] 3.1.4.1 "Session-Key Negotiation" Step 7 + * Windows DCs will reject negotiate attempts if none of the first + * 5 bytes of the Challenge are unique. + * Keep retrying until we've generated one that satisfies this. + */ + do { + retry = B_FALSE; + LE_OUT32(&leout_rc[0], arc4random()); + LE_OUT32(&leout_rc[1], arc4random()); + (void) memcpy(&netr_info->client_challenge, leout_rc, + sizeof (struct netr_credential)); + + if (!passes_dc_mitigation(netr_info->client_challenge.data)) + retry = B_TRUE; + } while (retry); + + if ((rc = netr_server_req_challenge(&netr_handle, netr_info)) == 0) { + rc = netr_server_authenticate2(&netr_handle, netr_info); if (rc == 0) { /* * TODO: (later) When joining a domain using a @@ -118,6 +231,9 @@ } } +errout: + (void) netr_close(&netr_handle); + return ((rc) ? NT_STATUS_UNSUCCESSFUL : NT_STATUS_SUCCESS); } @@ -145,6 +261,34 @@ return (status); } +uint32_t auth_context_id = 1; + +DWORD +netr_open_secure(char *server, char *domain, mlsvc_handle_t *netr_handle) +{ + char user[SMB_USERNAME_MAXLEN]; + DWORD status; + + smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); + + /* + * If the server doesn't support SECURE_RPC_FLAG, or we've disabled + * secure rpc (netr_global_info.use_secure_rpc), then SECURE_RPC_FLAG + * won't be in the set of negotiated flags. Don't use SecureRPC if + * that's the case. + */ + if ((netr_global_info.nego_flags & NETR_NEGO_SECURE_RPC_FLAG) != 0) { + netr_ssp_ctx.auth_context_id = auth_context_id++; + status = ndr_rpc_bind_secure(netr_handle, server, domain, user, + "NETR", &netr_ssp_ctx); + } else { + status = ndr_rpc_bind(netr_handle, server, domain, user, + "NETR"); + } + + return (status); +} + /* * netr_close * @@ -192,8 +336,9 @@ } uint32_t netr_server_auth2_flags = - NETR_NEGOTIATE_BASE_FLAGS | - NETR_NEGOTIATE_STRONGKEY_FLAG; + NETR_NEGO_BASE_FLAGS | + NETR_NEGO_STRONGKEY_FLAG | + NETR_NEGO_SECURE_RPC_FLAG; /* * netr_server_authenticate2 @@ -222,7 +367,15 @@ arg.hostname = (unsigned char *)netr_info->hostname; arg.negotiate_flags = netr_server_auth2_flags; - if (arg.negotiate_flags & NETR_NEGOTIATE_STRONGKEY_FLAG) { + /* + * If we've disabled SecureRPC, remove it from our negotiate_flags + * so that the returned flags don't include it. We won't later use + * SecureRPC if the returned flags don't include the flag. + */ + if (!netr_global_info.use_secure_rpc) + arg.negotiate_flags &= ~NETR_NEGO_SECURE_RPC_FLAG; + + if (arg.negotiate_flags & NETR_NEGO_STRONGKEY_FLAG) { if (netr_gen_skey128(netr_info) != SMBAUTH_SUCCESS) return (-1); } else { @@ -230,15 +383,22 @@ return (-1); } + /* + * We can't 'fiddle' with anything here to prevent getting bitten by + * ClientStoredCredential-based mitigations. + * + * If we're using SamLogonEx, we won't use authenticators unless + * some other NetLogon command is implemented and used. + */ if (netr_gen_credentials(netr_info->session_key.key, &netr_info->client_challenge, 0, - &netr_info->client_credential) != SMBAUTH_SUCCESS) { + &netr_info->client_credential, B_FALSE) != SMBAUTH_SUCCESS) { return (-1); } if (netr_gen_credentials(netr_info->session_key.key, &netr_info->server_challenge, 0, - &netr_info->server_credential) != SMBAUTH_SUCCESS) { + &netr_info->server_credential, B_FALSE) != SMBAUTH_SUCCESS) { return (-1); } @@ -254,6 +414,9 @@ return (-1); } + /* The server returns the intersection of our flags and their flags. */ + netr_info->nego_flags = arg.negotiate_flags; + rc = memcmp(&netr_info->server_credential, &arg.server_credential, sizeof (struct netr_credential)); @@ -450,7 +613,7 @@ */ int netr_gen_credentials(BYTE *session_key, netr_cred_t *challenge, - DWORD timestamp, netr_cred_t *out_cred) + DWORD timestamp, netr_cred_t *out_cred, boolean_t retry) { unsigned char buffer[8]; DWORD data[2]; @@ -472,6 +635,18 @@ rc = smb_auth_DES(out_cred->data, 8, &session_key[NETR_DESKEY_LEN], NETR_DESKEY_LEN, buffer, 8); + /* + * [MS-NRPC] 3.1.4.6 "Calling Methods Requiring Session-Key + * Establishment" Step 6 + * + * Windows DCs will reject authenticators if none of the first + * 5 bytes of the ClientStoredCredential are unique. + * Keep retrying until we've generated one that satisfies this, + * but only if the caller can handle retries. + */ + if (retry && !passes_dc_mitigation(out_cred->data)) + return (SMBAUTH_RETRY); + return (rc); }
--- a/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c Sat Jan 16 16:59:32 2021 +0000 @@ -21,7 +21,7 @@ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2019 Nexenta by DDN, Inc. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ /* @@ -46,7 +46,7 @@ #include <smbsrv/smb_token.h> #include <mlsvc.h> -static uint32_t netlogon_logon(smb_logon_t *, smb_token_t *, smb_domainex_t *); +uint32_t netlogon_logon(smb_logon_t *, smb_token_t *, smb_domainex_t *); static uint32_t netr_server_samlogon(mlsvc_handle_t *, netr_info_t *, char *, smb_logon_t *, smb_token_t *); static void netr_invalidate_chain(void); @@ -243,6 +243,56 @@ user_info->lg_status = status; } +static uint32_t +netr_get_handle(char *server, char *domain, mlsvc_handle_t *netr_handle) +{ + uint32_t status; + boolean_t did_renego = B_FALSE; + +reauth: + if ((netr_global_info.flags & NETR_FLG_VALID) == 0 || + !smb_match_netlogon_seqnum()) { + /* + * This does netr_server_req_challenge() and + * netr_server_authenticate2(), updating the + * current netlogon sequence number. + */ + status = netlogon_auth(server, domain, NETR_FLG_NULL); + + if (status != 0) { + syslog(LOG_ERR, "%s: auth failed (%s)", + __func__, xlate_nt_status(status)); + return (status); + } + + netr_global_info.flags |= NETR_FLG_VALID; + } + + /* + * This netr_open_secure call does the work to connect to the DC, + * get the IPC share, open the named pipe, RPC bind, etc. + */ + status = netr_open_secure(server, domain, netr_handle); + if (status != 0) { + /* + * This may have failed because the DC restarted. + * Re-negotiate once. + */ + if (!did_renego) { + did_renego = B_TRUE; + netr_invalidate_chain(); + syslog(LOG_ERR, "%s: open failed (%s); " + "renegotiating...", + __func__, xlate_nt_status(status)); + goto reauth; + } + syslog(LOG_ERR, "%s: open failed (%s)", + __func__, xlate_nt_status(status)); + } + + return (status); +} + /* * Run a netr_server_samlogon call, dealing with the possible need to * re-establish the NetLogon credential chain. If that fails, return @@ -251,7 +301,7 @@ * netr_server_samlogon() call including the many possibilities listed * above that function. */ -static uint32_t +uint32_t netlogon_logon(smb_logon_t *user_info, smb_token_t *token, smb_domainex_t *di) { char server[MAXHOSTNAMELEN]; @@ -259,18 +309,6 @@ uint32_t status; boolean_t did_reauth = B_FALSE; - /* - * This netr_open call does the work to connect to the DC, - * get the IPC share, open the named pipe, RPC bind, etc. - */ - status = netr_open(di->d_dci.dc_name, di->d_primary.di_nbname, - &netr_handle); - if (status != 0) { - syslog(LOG_ERR, "netlogon remote open failed (%s)", - xlate_nt_status(status)); - return (status); - } - if (di->d_dci.dc_name[0] != '\0' && (*netr_global_info.server != '\0')) { (void) snprintf(server, sizeof (server), @@ -281,24 +319,13 @@ } reauth: - if ((netr_global_info.flags & NETR_FLG_VALID) == 0 || - !smb_match_netlogon_seqnum()) { - /* - * This does netr_server_req_challenge() and - * netr_server_authenticate2(), updating the - * current netlogon sequence number. - */ - status = netlogon_auth(di->d_dci.dc_name, &netr_handle, - NETR_FLG_NULL); + status = netr_get_handle(di->d_dci.dc_name, + di->d_primary.di_nbname, &netr_handle); - if (status != 0) { - syslog(LOG_ERR, "netlogon remote auth failed (%s)", - xlate_nt_status(status)); - (void) netr_close(&netr_handle); - return (NT_STATUS_DOMAIN_TRUST_INCONSISTENT); - } - - netr_global_info.flags |= NETR_FLG_VALID; + if (status != 0) { + syslog(LOG_ERR, "%s: failed to get handle (%s)", + __func__, xlate_nt_status(status)); + return (NT_STATUS_DOMAIN_TRUST_INCONSISTENT); } status = netr_server_samlogon(&netr_handle, @@ -307,6 +334,7 @@ if (status == NT_STATUS_INSUFFICIENT_LOGON_INFO) { if (!did_reauth) { /* Call netlogon_auth() again, just once. */ + (void) netr_close(&netr_handle); did_reauth = B_TRUE; goto reauth; } @@ -339,38 +367,16 @@ (void) mutex_unlock(&netlogon_mutex); /* - * This section like netlogon_logon(), but only does - * one pass and no netr_server_samlogon call. + * Like netlogon_logon(), but no netr_server_samlogon call. + * We're just making sure we can connect to the NETLOGON server. */ - - status = netr_open(server, domain, - &netr_handle); - if (status != 0) { - syslog(LOG_ERR, "netlogon remote open failed (%s)", - xlate_nt_status(status)); - goto unlock_out; - } + status = netr_get_handle(server, domain, &netr_handle); + if (status == 0) + (void) netr_close(&netr_handle); + else + syslog(LOG_ERR, "%s: failed to get handle (%s)", + __func__, xlate_nt_status(status)); - if ((netr_global_info.flags & NETR_FLG_VALID) == 0 || - !smb_match_netlogon_seqnum()) { - /* - * This does netr_server_req_challenge() and - * netr_server_authenticate2(), updating the - * current netlogon sequence number. - */ - status = netlogon_auth(server, &netr_handle, - NETR_FLG_NULL); - if (status != 0) { - syslog(LOG_ERR, "netlogon remote auth failed (%s)", - xlate_nt_status(status)); - } else { - netr_global_info.flags |= NETR_FLG_VALID; - } - } - - (void) netr_close(&netr_handle); - -unlock_out: (void) mutex_lock(&netlogon_mutex); netlogon_busy = B_FALSE; (void) cond_signal(&netlogon_cv); @@ -477,34 +483,36 @@ netr_server_samlogon(mlsvc_handle_t *netr_handle, netr_info_t *netr_info, char *server, smb_logon_t *user_info, smb_token_t *token) { - struct netr_SamLogon arg; + struct netr_SamLogon logon_op; + struct netr_SamLogonEx logon_ex_op; struct netr_authenticator auth; struct netr_authenticator ret_auth; struct netr_logon_info1 info1; struct netr_logon_info2 info2; struct netr_validation_info3 *info3; + union netr_validation_u *valid_info; + union netr_logon_info_u *logon_info; + LPTSTR servername, hostname; ndr_heap_t *heap; int opnum; int rc, len; - uint32_t status; - - bzero(&arg, sizeof (struct netr_SamLogon)); - opnum = NETR_OPNUM_SamLogon; + uint32_t status, *rpc_status; + void *rpc_arg; /* * Should we get the server and hostname from netr_info? */ len = strlen(server) + 4; - arg.servername = ndr_rpc_malloc(netr_handle, len); - arg.hostname = ndr_rpc_malloc(netr_handle, NETBIOS_NAME_SZ); - if (arg.servername == NULL || arg.hostname == NULL) { + servername = ndr_rpc_malloc(netr_handle, len); + hostname = ndr_rpc_malloc(netr_handle, NETBIOS_NAME_SZ); + if (servername == NULL || hostname == NULL) { ndr_rpc_release(netr_handle); return (NT_STATUS_INTERNAL_ERROR); } - (void) snprintf((char *)arg.servername, len, "\\\\%s", server); - if (smb_getnetbiosname((char *)arg.hostname, NETBIOS_NAME_SZ) != 0) { + (void) snprintf((char *)servername, len, "\\\\%s", server); + if (smb_getnetbiosname((char *)hostname, NETBIOS_NAME_SZ) != 0) { ndr_rpc_release(netr_handle); return (NT_STATUS_INTERNAL_ERROR); } @@ -515,19 +523,49 @@ return (NT_STATUS_INTERNAL_ERROR); } - arg.auth = &auth; - arg.ret_auth = &ret_auth; - arg.validation_level = NETR_VALIDATION_LEVEL3; - arg.logon_info.logon_level = user_info->lg_level; - arg.logon_info.switch_value = user_info->lg_level; - + /* + * If we use Secure RPC, we can use SamLogonEx instead of SamLogon. + * SamLogonEx doesn't use NetLogon authenticators, instead relying + * on Secure RPC to provide security. + * This allows us to avoid being bitten by mitigations in + * the authenticator verification logic on DCs. + */ + if (netr_info->use_logon_ex && + (netr_info->nego_flags & NETR_NEGO_SECURE_RPC_FLAG) != 0) { + bzero(&logon_ex_op, sizeof (struct netr_SamLogonEx)); + logon_ex_op.servername = servername; + logon_ex_op.hostname = hostname; + logon_ex_op.logon_info.logon_level = user_info->lg_level; + logon_ex_op.logon_info.switch_value = user_info->lg_level; + logon_ex_op.validation_level = NETR_VALIDATION_LEVEL3; + logon_ex_op.extra_flags = 0; + logon_info = &logon_ex_op.logon_info.ru; + valid_info = &logon_ex_op.ru; + rpc_status = &logon_ex_op.status; + rpc_arg = &logon_ex_op; + opnum = NETR_OPNUM_SamLogonEx; + } else { + bzero(&logon_op, sizeof (struct netr_SamLogon)); + logon_op.servername = servername; + logon_op.hostname = hostname; + logon_op.auth = &auth; + logon_op.ret_auth = &ret_auth; + logon_op.logon_info.logon_level = user_info->lg_level; + logon_op.logon_info.switch_value = user_info->lg_level; + logon_op.validation_level = NETR_VALIDATION_LEVEL3; + logon_info = &logon_op.logon_info.ru; + valid_info = &logon_op.ru; + rpc_status = &logon_op.status; + rpc_arg = &logon_op; + opnum = NETR_OPNUM_SamLogon; + } heap = ndr_rpc_get_heap(netr_handle); switch (user_info->lg_level) { case NETR_INTERACTIVE_LOGON: netr_setup_identity(heap, user_info, &info1.identity); netr_interactive_samlogon(netr_info, user_info, &info1); - arg.logon_info.ru.info1 = &info1; + logon_info->info1 = &info1; break; case NETR_NETWORK_LOGON: @@ -538,7 +576,7 @@ } netr_setup_identity(heap, user_info, &info2.identity); netr_network_samlogon(heap, netr_info, user_info, &info2); - arg.logon_info.ru.info2 = &info2; + logon_info->info2 = &info2; break; default: @@ -546,12 +584,12 @@ return (NT_STATUS_INVALID_PARAMETER); } - rc = ndr_rpc_call(netr_handle, opnum, &arg); + rc = ndr_rpc_call(netr_handle, opnum, rpc_arg); if (rc != 0) { bzero(netr_info, sizeof (netr_info_t)); status = NT_STATUS_INVALID_PARAMETER; - } else if (arg.status != 0) { - status = NT_SC_VALUE(arg.status); + } else if (*rpc_status != 0) { + status = NT_SC_VALUE(*rpc_status); /* * We need to validate the chain even though we have @@ -559,16 +597,23 @@ * this will trigger a new credential chain. However, * a valid credential is returned with some status * codes; for example, WRONG_PASSWORD. + * + * SamLogonEx doesn't use authenticators - nothing to validate. */ - (void) netr_validate_chain(netr_info, arg.ret_auth); + if (rpc_arg == &logon_op) + (void) netr_validate_chain(netr_info, + logon_op.ret_auth); } else { - status = netr_validate_chain(netr_info, arg.ret_auth); - if (status == NT_STATUS_INSUFFICIENT_LOGON_INFO) { - ndr_rpc_release(netr_handle); - return (status); + if (rpc_arg == &logon_op) { + status = netr_validate_chain(netr_info, + logon_op.ret_auth); + if (status == NT_STATUS_INSUFFICIENT_LOGON_INFO) { + ndr_rpc_release(netr_handle); + return (status); + } } - info3 = arg.ru.info3; + info3 = valid_info->info3; status = netr_setup_token(info3, user_info, netr_info, token); } @@ -664,16 +709,24 @@ netr_setup_authenticator(netr_info_t *netr_info, struct netr_authenticator *auth, struct netr_authenticator *ret_auth) { + int rc; bzero(auth, sizeof (struct netr_authenticator)); - netr_info->timestamp = time(0); - auth->timestamp = netr_info->timestamp; - - if (netr_gen_credentials(netr_info->session_key.key, - &netr_info->client_credential, - netr_info->timestamp, - (netr_cred_t *)&auth->credential) != SMBAUTH_SUCCESS) - return (SMBAUTH_FAILURE); + /* + * Windows DCs will reject Authenticators if none of the first + * 5 bytes of the ClientStoredCredential are unique. + * Keep retrying until we've generated one that satisfies this. + */ + netr_info->timestamp = time(0) - 1; + do { + auth->timestamp = ++netr_info->timestamp; + rc = netr_gen_credentials(netr_info->session_key.key, + &netr_info->client_credential, + netr_info->timestamp, + (netr_cred_t *)&auth->credential, B_TRUE); + if (rc != SMBAUTH_SUCCESS && rc != SMBAUTH_RETRY) + return (SMBAUTH_FAILURE); + } while (rc == SMBAUTH_RETRY); if (ret_auth) { bzero(ret_auth, sizeof (struct netr_authenticator)); @@ -713,7 +766,7 @@ if (netr_gen_credentials(netr_info->session_key.key, &netr_info->client_credential, - netr_info->timestamp, &cred) != SMBAUTH_SUCCESS) + netr_info->timestamp, &cred, B_FALSE) != SMBAUTH_SUCCESS) return (NT_STATUS_INTERNAL_ERROR); if (&auth->credential == 0) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/lib/smbsrv/libmlsvc/common/netr_ssp.c Sat Jan 16 16:59:32 2021 +0000 @@ -0,0 +1,494 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2020 Tintri by DDN, Inc. All Rights Reserved. + */ + +#include <sys/md5.h> +#include <strings.h> +#include <stdio.h> +#include <smbsrv/netrauth.h> +#include <smbsrv/string.h> +#include <smbsrv/libsmb.h> +#include <libmlsvc.h> +#include <resolv.h> + +/* + * NETLOGON SSP for "Secure RPC" works as follows: + * 1. The client binds to the DC without RPC-level authentication. + * 2. The client and server negotiate a Session Key using a client + * and server challenge, plus a shared secret (the machine password). + * This happens via NetrServerReqChallenge and NetrServerAuthenticate. + * The key is bound to a particular Computer/Server Name pair. + * 3. The client then establishes a new bind (or alters its existing one), + * this time requesting the NETLOGON provider for RPC-level authentication. + * The server uses the Computer and Domain names provided in the + * authentication token in the bind request in order to find + * the previously-negotiated Session Key (and rejects the bind if none + * exists). + * 4. The client and server then use this Session Key to provide + * integrity and/or confidentiality to future NETLOGON RPC messages. + * + * The functions in this file implement the NETLOGON SSP, as defined in + * [MS-NRPC] 3.3 "Netlogon as a Security Support Provider". + * + * Session Key negotiation is implemented in netr_auth.c. + * It is the same key that is used for generating NETLOGON credentials. + */ + +enum nl_token_type { + NL_AUTH_REQUEST = 0x00000000, + NL_AUTH_RESPONSE = 0x00000001 +}; + +/* + * DOMAIN = domain name + * COMPUTER = client computer name + * HOST = client host name + * + * NB = NetBios format + * DNS = FQDN + * + * OEM = OEM_STRING + * COMPRESSED = Compressed UTF-8 string + * + * Each of these is NULL-terminated, and delinated by such. + * They are always found in this order, when specified. + * + * We currently use everything but NL_HOST_DNS_COMPRESSED_FLAG. + */ +#define NL_DOMAIN_NB_OEM_FLAG 0x00000001 +#define NL_COMPUTER_NB_OEM_FLAG 0x00000002 +#define NL_DOMAIN_DNS_COMPRESSED_FLAG 0x00000004 +#define NL_HOST_DNS_COMPRESSED_FLAG 0x00000008 +#define NL_COMPUTER_NB_COMPRESSED_FLAG 0x00000010 + +#define NL_DOMAIN_FLAGS \ + (NL_DOMAIN_NB_OEM_FLAG|NL_DOMAIN_DNS_COMPRESSED_FLAG) +#define NL_COMPUTER_FLAGS \ + (NL_COMPUTER_NB_OEM_FLAG| \ + NL_HOST_DNS_COMPRESSED_FLAG| \ + NL_COMPUTER_NB_COMPRESSED_FLAG) + +#define MD_DIGEST_LEN 16 + +/* These structures are OPAQUE at the RPC level - not marshalled. */ +typedef struct nl_auth_message { + uint32_t nam_type; + uint32_t nam_flags; + uchar_t nam_str[1]; +} nl_auth_message_t; + +/* + * The size of this structure is used for space accounting. + * The confounder is not present on the wire unless confidentiality + * has been negotiated. If we ever support confidentiality, + * we'll need to adjust space accounting based on whether + * the confounder is needed. + */ +typedef struct nl_auth_sig { + uint16_t nas_sig_alg; + uint16_t nas_seal_alg; + uint16_t nas_pad; + uint16_t nas_flags; + uchar_t nas_seqnum[8]; + uchar_t nas_sig[8]; + /* uchar_t nas_confounder[8]; */ /* only for encryption */ +} nl_auth_sig_t; + +void +netr_show_msg(nl_auth_message_t *nam, ndr_stream_t *nds) +{ + ndo_printf(nds, NULL, "nl_auth_message: type=0x%x flags=0x%x"); +} + +void +netr_show_sig(nl_auth_sig_t *nas, ndr_stream_t *nds) +{ + ndo_printf(nds, NULL, "nl_auth_sig: SignatureAlg=0x%x SealAlg=0x%x " + "pad=0x%x flags=0x%x SequenceNumber=%llu Signature=0x%x", + nas->nas_sig_alg, nas->nas_seal_alg, nas->nas_pad, + nas->nas_flags, *(uint64_t *)nas->nas_seqnum, + *(uint64_t *)nas->nas_sig); +} + +/* + * NETLOGON SSP gss_init_sec_context equivalent + * [MS-RPCE] 3.3.4.1.1 "Generating an Initial NL_AUTH_MESSAGE" + * + * We need to encode at least one Computer name and at least one + * Domain name. The server uses this to find the Session Key + * negotiated earlier between this client and server. + * + * We attempt to provide NL_DOMAIN_NB_OEM_FLAG, NL_COMPUTER_NB_OEM_FLAG, + * NL_DOMAIN_DNS_COMPRESSED_FLAG, and NL_COMPUTER_NB_COMPRESSED_FLAG. + * + * See the above comments for how these are encoded. + */ +int +netr_ssp_init(void *arg, ndr_xa_t *mxa) +{ + netr_info_t *auth = arg; + ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr; + nl_auth_message_t *nam; + size_t domain_len, comp_len, len; + int slen; + uchar_t *dnptrs[3], **dnlastptr; + + domain_len = smb_sbequiv_strlen(auth->nb_domain); + comp_len = smb_sbequiv_strlen(auth->hostname); + + /* + * Need to allocate length for two OEM_STRINGs + NULL bytes, plus space + * sufficient for two NULL-terminated compressed UTF-8 strings. + * For the UTF-8 strings, use 2*len as a heuristic. + */ + len = domain_len + 1 + comp_len + 1 + + strlen(auth->hostname) * 2 + strlen(auth->server) * 2; + + hdr->auth_length = 0; + + nam = NDR_MALLOC(mxa, len); + if (nam == NULL) + return (NDR_DRC_FAULT_SEC_OUT_OF_MEMORY); + + nam->nam_type = NL_AUTH_REQUEST; + nam->nam_flags = 0; + + if (domain_len != -1) { + slen = smb_mbstooem(nam->nam_str, auth->nb_domain, domain_len); + if (slen >= 0) { + hdr->auth_length += slen + 1; + nam->nam_str[hdr->auth_length - 1] = '\0'; + nam->nam_flags |= NL_DOMAIN_NB_OEM_FLAG; + } + } + + if (comp_len != -1) { + slen = smb_mbstooem(nam->nam_str + hdr->auth_length, + auth->hostname, comp_len); + if (slen >= 0) { + hdr->auth_length += slen + 1; + nam->nam_str[hdr->auth_length - 1] = '\0'; + nam->nam_flags |= NL_COMPUTER_NB_OEM_FLAG; + } + } + + dnptrs[0] = NULL; + dnlastptr = &dnptrs[sizeof (dnptrs) / sizeof (dnptrs[0])]; + + slen = dn_comp(auth->fqdn_domain, nam->nam_str + hdr->auth_length, + len - hdr->auth_length, dnptrs, dnlastptr); + + if (slen >= 0) { + hdr->auth_length += slen; + nam->nam_str[hdr->auth_length] = '\0'; + nam->nam_flags |= NL_DOMAIN_DNS_COMPRESSED_FLAG; + } + + slen = dn_comp(auth->hostname, nam->nam_str + hdr->auth_length, + len - hdr->auth_length, dnptrs, dnlastptr); + if (slen >= 0) { + hdr->auth_length += slen; + nam->nam_str[hdr->auth_length] = '\0'; + nam->nam_flags |= NL_COMPUTER_NB_COMPRESSED_FLAG; + } + + /* We must provide at least one Domain Name and Computer Name */ + if ((nam->nam_flags & NL_DOMAIN_FLAGS) == 0 || + (nam->nam_flags & NL_COMPUTER_FLAGS) == 0) + return (NDR_DRC_FAULT_SEC_ENCODE_FAILED); + + mxa->send_auth.auth_value = (void *)nam; + hdr->auth_length += sizeof (nam->nam_flags) + sizeof (nam->nam_type); + + return (0); +} + +/* + * NETLOGON SSP response-side gss_init_sec_context equivalent + * [MS-RPCE] 3.3.4.1.4 "Receiving a Return NL_AUTH_MESSAGE" + */ +int +netr_ssp_recv(void *arg, ndr_xa_t *mxa) +{ + netr_info_t *auth = arg; + ndr_common_header_t *ahdr = &mxa->recv_hdr.common_hdr; + ndr_sec_t *ack_secp = &mxa->recv_auth; + nl_auth_message_t *nam; + int rc; + + nam = (nl_auth_message_t *)ack_secp->auth_value; + + /* We only need to verify the length ("at least 12") and the type */ + if (ahdr->auth_length < 12) { + rc = NDR_DRC_FAULT_SEC_AUTH_LENGTH_INVALID; + goto errout; + } + if (nam->nam_type != NL_AUTH_RESPONSE) { + rc = NDR_DRC_FAULT_SEC_META_INVALID; + goto errout; + } + auth->clh_seqnum = 0; + + return (NDR_DRC_OK); + +errout: + netr_show_msg(nam, &mxa->recv_nds); + return (rc); +} + +/* returns byte N of seqnum */ +#define CLS_BYTE(n, seqnum) ((seqnum >> (8 * (n))) & 0xff) + +/* + * NETLOGON SSP gss_MICEx equivalent + * [MS-RPCE] 3.3.4.2.1 "Generating a Client Netlogon Signature Token" + * + * Set up the metadata, encrypt and increment the SequenceNumber, + * and sign the PDU body. + */ +int +netr_ssp_sign(void *arg, ndr_xa_t *mxa) +{ + uint32_t zeroes = 0; + netr_info_t *auth = arg; + ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr; + ndr_stream_t *nds = &mxa->send_nds; + nl_auth_sig_t *nas; + MD5_CTX md5h; + BYTE local_sig[MD_DIGEST_LEN]; + BYTE enc_key[MD_DIGEST_LEN]; + + hdr->auth_length = sizeof (nl_auth_sig_t); + + nas = NDR_MALLOC(mxa, hdr->auth_length); + if (nas == NULL) + return (NDR_DRC_FAULT_SEC_OUT_OF_MEMORY); + + /* + * SignatureAlgorithm is first byte 0x77, second byte 00 for HMAC-MD5 + * or 0x13, 0x00 for AES-HMAC-SHA256. + * + * SealAlgorithm is first byte 0x7A, second byte 00 for RC4 + * or 0x1A, 0x00 for AES-CFB8, or 0xffff for No Sealing. + * + * Pad is always 0xffff, and flags is always 0x0000. + * + * SequenceNumber is a computed, encrypted, 64-bit number. + * + * Each of these is always encoded in little-endian order. + */ + nas->nas_sig_alg = 0x0077; + nas->nas_seal_alg = 0xffff; + nas->nas_pad = 0xffff; + nas->nas_flags = 0; + + /* + * Calculate the SequenceNumber. + * Note that byte 4 gets modified, as per the spec - + * It's the only byte that is not just set to some other byte. + */ + nas->nas_seqnum[0] = CLS_BYTE(3, auth->clh_seqnum); + nas->nas_seqnum[1] = CLS_BYTE(2, auth->clh_seqnum); + nas->nas_seqnum[2] = CLS_BYTE(1, auth->clh_seqnum); + nas->nas_seqnum[3] = CLS_BYTE(0, auth->clh_seqnum); + nas->nas_seqnum[4] = CLS_BYTE(7, auth->clh_seqnum) | 0x80; + nas->nas_seqnum[5] = CLS_BYTE(6, auth->clh_seqnum); + nas->nas_seqnum[6] = CLS_BYTE(5, auth->clh_seqnum); + nas->nas_seqnum[7] = CLS_BYTE(4, auth->clh_seqnum); + + auth->clh_seqnum++; + + /* + * The HMAC-MD5 signature is computed as follows: + * First 8 bytes of + * HMAC_MD5( + * MD5(0x00000000 | sig_alg | seal_alg | pad | flags | PDU body), + * session_key) + */ + MD5Init(&md5h); + MD5Update(&md5h, (uchar_t *)&zeroes, 4); + MD5Update(&md5h, (uchar_t *)nas, 8); + MD5Update(&md5h, + (uchar_t *)nds->pdu_base_addr + nds->pdu_body_offset, + nds->pdu_body_size); + + MD5Final(local_sig, &md5h); + if (smb_auth_hmac_md5(local_sig, sizeof (local_sig), + auth->session_key.key, auth->session_key.len, + local_sig) != 0) + return (NDR_DRC_FAULT_SEC_SSP_FAILED); + + bcopy(local_sig, nas->nas_sig, 8); + + /* + * Encrypt the SequenceNumber. + * For RC4 Encryption, the EncryptionKey is computed as follows: + * HMAC_MD5(signature, HMAC_MD5(0x00000000, session_key)) + */ + if (smb_auth_hmac_md5((uchar_t *)&zeroes, 4, + auth->session_key.key, auth->session_key.len, + enc_key) != 0) + return (NDR_DRC_FAULT_SEC_SSP_FAILED); + if (smb_auth_hmac_md5((uchar_t *)nas->nas_sig, sizeof (nas->nas_sig), + enc_key, sizeof (enc_key), + enc_key) != 0) + return (NDR_DRC_FAULT_SEC_SSP_FAILED); + + if (smb_auth_RC4(nas->nas_seqnum, sizeof (nas->nas_seqnum), + enc_key, sizeof (enc_key), + nas->nas_seqnum, sizeof (nas->nas_seqnum)) != 0) + return (NDR_DRC_FAULT_SEC_SSP_FAILED); + + mxa->send_auth.auth_value = (void *)nas; + + return (NDR_DRC_OK); +} + +/* + * NETLOGON SSP gss_VerifyMICEx equivalent + * [MS-RPCE] 3.3.4.2.4 "Receiving a Server Netlogon Signature Token" + * + * Verify the metadata, decrypt, verify, and increment the SequenceNumber, + * and validate the PDU body against the provided signature. + */ +int +netr_ssp_verify(void *arg, ndr_xa_t *mxa, boolean_t verify_resp) +{ + uint32_t zeroes = 0; + netr_info_t *auth = arg; + ndr_sec_t *secp = &mxa->recv_auth; + ndr_stream_t *nds = &mxa->recv_nds; + nl_auth_sig_t *nas; + MD5_CTX md5h; + BYTE local_sig[MD_DIGEST_LEN]; + BYTE dec_key[MD_DIGEST_LEN]; + BYTE local_seqnum[8]; + int rc; + boolean_t seqnum_bumped = B_FALSE; + + nas = (nl_auth_sig_t *)secp->auth_value; + + /* + * Verify SignatureAlgorithm, SealAlgorithm, and Pad are as expected. + * These follow the same values as in the Client Signature. + */ + if (nas->nas_sig_alg != 0x0077 || + nas->nas_seal_alg != 0xffff || + nas->nas_pad != 0xffff) { + rc = NDR_DRC_FAULT_SEC_META_INVALID; + goto errout; + } + + /* Decrypt the SequenceNumber. This is done the same as the Client. */ + if (smb_auth_hmac_md5((uchar_t *)&zeroes, 4, + auth->session_key.key, auth->session_key.len, + dec_key) != 0) { + rc = NDR_DRC_FAULT_SEC_SSP_FAILED; + goto errout; + } + if (smb_auth_hmac_md5((uchar_t *)nas->nas_sig, sizeof (nas->nas_sig), + dec_key, sizeof (dec_key), + dec_key) != 0) { + rc = NDR_DRC_FAULT_SEC_SSP_FAILED; + goto errout; + } + + if (smb_auth_RC4(nas->nas_seqnum, sizeof (nas->nas_seqnum), + dec_key, sizeof (dec_key), + nas->nas_seqnum, sizeof (nas->nas_seqnum)) != 0) { + rc = NDR_DRC_FAULT_SEC_SSP_FAILED; + goto errout; + } + + /* + * Calculate a local version of the SequenceNumber. + * Note that byte 4 does NOT get modified, unlike the client. + */ + local_seqnum[0] = CLS_BYTE(3, auth->clh_seqnum); + local_seqnum[1] = CLS_BYTE(2, auth->clh_seqnum); + local_seqnum[2] = CLS_BYTE(1, auth->clh_seqnum); + local_seqnum[3] = CLS_BYTE(0, auth->clh_seqnum); + local_seqnum[4] = CLS_BYTE(7, auth->clh_seqnum); + local_seqnum[5] = CLS_BYTE(6, auth->clh_seqnum); + local_seqnum[6] = CLS_BYTE(5, auth->clh_seqnum); + local_seqnum[7] = CLS_BYTE(4, auth->clh_seqnum); + + /* If the SequenceNumbers don't match, this is out of order - drop it */ + if (bcmp(local_seqnum, nas->nas_seqnum, sizeof (local_seqnum)) != 0) { + ndo_printf(nds, NULL, "CalculatedSeqnum: %llu " + "DecryptedSeqnum: %llu", + *(uint64_t *)local_seqnum, *(uint64_t *)nas->nas_seqnum); + rc = NDR_DRC_FAULT_SEC_SEQNUM_INVALID; + goto errout; + } + + auth->clh_seqnum++; + seqnum_bumped = B_TRUE; + + /* + * Calculate the signature. + * This is done the same as the Client. + */ + MD5Init(&md5h); + MD5Update(&md5h, (uchar_t *)&zeroes, 4); + MD5Update(&md5h, (uchar_t *)nas, 8); + MD5Update(&md5h, + (uchar_t *)nds->pdu_base_addr + nds->pdu_body_offset, + nds->pdu_body_size); + MD5Final(local_sig, &md5h); + if (smb_auth_hmac_md5(local_sig, sizeof (local_sig), + auth->session_key.key, auth->session_key.len, + local_sig) != 0) { + rc = NDR_DRC_FAULT_SEC_SSP_FAILED; + goto errout; + } + + /* If the first 8 bytes don't match, drop it */ + if (bcmp(local_sig, nas->nas_sig, 8) != 0) { + ndo_printf(nds, NULL, "CalculatedSig: %llu " + "PacketSig: %llu", + *(uint64_t *)local_sig, *(uint64_t *)nas->nas_sig); + rc = NDR_DRC_FAULT_SEC_SIG_INVALID; + goto errout; + } + + return (NDR_DRC_OK); + +errout: + netr_show_sig(nas, &mxa->recv_nds); + + if (!verify_resp) { + if (!seqnum_bumped) + auth->clh_seqnum++; + return (NDR_DRC_OK); + } + + return (rc); +} + +extern struct netr_info netr_global_info; + +ndr_auth_ctx_t netr_ssp_ctx = { + .auth_ops = { + .nao_init = netr_ssp_init, + .nao_recv = netr_ssp_recv, + .nao_sign = netr_ssp_sign, + .nao_verify = netr_ssp_verify + }, + .auth_ctx = &netr_global_info, + .auth_context_id = 0, + .auth_type = NDR_C_AUTHN_GSS_NETLOGON, + .auth_level = NDR_C_AUTHN_LEVEL_PKT_INTEGRITY, + .auth_verify_resp = B_TRUE +};
--- a/usr/src/lib/smbsrv/libsmb/common/libsmb.h Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/lib/smbsrv/libsmb/common/libsmb.h Sat Jan 16 16:59:32 2021 +0000 @@ -21,7 +21,7 @@ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2019 Nexenta by DDN, Inc. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. * Copyright 2020 RackTop Systems, Inc. */ @@ -162,6 +162,7 @@ SMB_CI_MIN_PROTOCOL, SMB_CI_BYPASS_TRAVERSE_CHECKING, SMB_CI_ENCRYPT_CIPHER, + SMB_CI_NETLOGON_FLAGS, SMB_CI_MAX } smb_cfg_id_t; @@ -320,6 +321,8 @@ extern int smb_getnameinfo(smb_inaddr_t *, char *, int, int); +extern uint32_t smb_get_netlogon_flags(void); + void smb_trace(const char *s); void smb_tracef(const char *fmt, ...); @@ -340,6 +343,7 @@ #define SMBAUTH_SESSION_KEY_SZ SMBAUTH_HASH_SZ #define SMBAUTH_HEXHASH_SZ (SMBAUTH_HASH_SZ * 2) +#define SMBAUTH_RETRY 2 #define SMBAUTH_FAILURE 1 #define SMBAUTH_SUCCESS 0 #define MD_DIGEST_LEN 16
--- a/usr/src/lib/smbsrv/libsmb/common/mapfile-vers Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/lib/smbsrv/libsmb/common/mapfile-vers Sat Jan 16 16:59:32 2021 +0000 @@ -19,7 +19,7 @@ # # # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. -# Copyright 2019 Nexenta Systems, Inc. All rights reserved. +# Copyright 2020 Tintri by DDN, Inc. All rights reserved. # # @@ -185,6 +185,7 @@ smb_gen_random_passwd; smb_get_dcinfo; smb_get_nameservers; + smb_get_netlogon_flags; smb_get_txid; smb_getdataset; smb_getdomainname;
--- a/usr/src/lib/smbsrv/libsmb/common/smb_cfg.c Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/lib/smbsrv/libsmb/common/smb_cfg.c Sat Jan 16 16:59:32 2021 +0000 @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2018 Nexenta Systems, Inc. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. * Copyright 2020 RackTop Systems, Inc. */ @@ -152,6 +152,7 @@ {SMB_CI_BYPASS_TRAVERSE_CHECKING, "bypass_traverse_checking", SCF_TYPE_BOOLEAN, 0}, {SMB_CI_ENCRYPT_CIPHER, "encrypt_cipher", SCF_TYPE_ASTRING, 0}, + {SMB_CI_NETLOGON_FLAGS, "netlogon_flags", SCF_TYPE_INTEGER, 0}, /* SMB_CI_MAX */ };
--- a/usr/src/lib/smbsrv/libsmb/common/smb_info.c Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/lib/smbsrv/libsmb/common/smb_info.c Sat Jan 16 16:59:32 2021 +0000 @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2017 Nexenta Systems, Inc. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. * Copyright 2020 RackTop Systems, Inc. */ @@ -723,3 +723,15 @@ return (h); } + +uint32_t +smb_get_netlogon_flags(void) +{ + int64_t val; + + if (smb_config_getnum(SMB_CI_NETLOGON_FLAGS, &val) != SMBD_SMF_OK) + return (SMB_PI_NETLOGON_FLAGS_DEFAULT); + + /* These are flags, and we only use the lowest 32 bits */ + return ((uint32_t)val); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/pkg/manifests/system-test-libmlrpctest.mf Sat Jan 16 16:59:32 2021 +0000 @@ -0,0 +1,46 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Tintri by DDN, Inc. All rights reserved. +# + +set name=pkg.fmri value=pkg:/system/test/libmlrpctest@$(PKGVERS) +set name=pkg.description value="Unit Tests for libmlrpc and its consumers" +set name=pkg.summary value="Libmlrpc Unit Test Suite" +set name=info.classification \ + value=org.opensolaris.category.2008:Development/System +set name=variant.arch value=$(ARCH) +dir path=opt/libmlrpc-tests +dir path=opt/libmlrpc-tests/bin +dir path=opt/libmlrpc-tests/cfg +dir path=opt/libmlrpc-tests/runfiles +dir path=opt/libmlrpc-tests/tests +dir path=opt/libmlrpc-tests/tests/netrlogon +dir path=opt/libmlrpc-tests/tests/netrlogon/krb5_pac_tests +dir path=opt/libmlrpc-tests/tests/netrlogon/samlogon_tests +file path=opt/libmlrpc-tests/README mode=0444 +file path=opt/libmlrpc-tests/bin/libmlrpctest mode=0555 +file path=opt/libmlrpc-tests/cfg/krb5_pac.config mode=0644 +file path=opt/libmlrpc-tests/cfg/samlogon.config mode=0644 +file path=opt/libmlrpc-tests/runfiles/default.run mode=0444 +file path=opt/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/krb5_pac.bin \ + mode=0444 +file path=opt/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/krb5_pac_decode \ + mode=0555 +file path=opt/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/run_krb5_pac_tests \ + mode=0555 +file path=opt/libmlrpc-tests/tests/netrlogon/samlogon_tests/run_samlogon_tests \ + mode=0555 +file path=opt/libmlrpc-tests/tests/netrlogon/samlogon_tests/samlogon mode=0555 +license lic_CDDL license=lic_CDDL +depend fmri=service/file-system/smb type=require +depend fmri=system/test/testrunner type=require
--- a/usr/src/test/Makefile Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/test/Makefile Sat Jan 16 16:59:32 2021 +0000 @@ -13,6 +13,7 @@ # Copyright (c) 2012 by Delphix. All rights reserved. # Copyright 2014 Garrett D'Amore <garrett@damore.org> # Copyright 2019 Joyent, Inc. +# Copyright 2020 Tintri by DDN, Inc. All rights reserved. # .PARALLEL: $(SUBDIRS) @@ -21,6 +22,7 @@ crypto-tests \ elf-tests \ libc-tests \ + libmlrpc-tests \ net-tests \ os-tests \ smbclient-tests \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/test/libmlrpc-tests/Makefile Sat Jan 16 16:59:32 2021 +0000 @@ -0,0 +1,20 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Tintri by DDN, Inc. All rights reserved. +# + +.PARALLEL: $(SUBDIRS) + +SUBDIRS = cfg cmd runfiles tests doc + +include $(SRC)/test/Makefile.com
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/test/libmlrpc-tests/cfg/Makefile Sat Jan 16 16:59:32 2021 +0000 @@ -0,0 +1,39 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2019 Joyent, Inc. +# Copyright 2020 Tintri by DDN, Inc. All rights reserved. +# + +include $(SRC)/Makefile.master + +CFGS = samlogon.config krb5_pac.config +ROOTOPTPKG = $(ROOT)/opt/libmlrpc-tests +ROOTOPTPKGCFG = $(ROOT)/opt/libmlrpc-tests/cfg +ROOTOPTPKGDIRS = $(ROOTOPTPKG) $(ROOTOPTPKGCFG) +FILES = $(CFGS:%=$(ROOTOPTPKGCFG)/%) +$(FILES) := FILEMODE = 0644 + +include $(SRC)/test/Makefile.com + +all: $(CFGS) + +install: $(ROOTOPTPKG) $(ROOTOPTPKGCFG) $(FILES) + +clobber: clean + $(RM) $(FILES) + +$(ROOTOPTPKGDIRS): + $(INS.dir) + +$(ROOTOPTPKGCFG)/%: % $(ROOTOPTPKGDIRS) + $(INS.file)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/test/libmlrpc-tests/cfg/krb5_pac.config Sat Jan 16 16:59:32 2021 +0000 @@ -0,0 +1,21 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. + +# +# Copyright 2020 Tintri by DDN, Inc. All rights reserved. +# + +# +# A binary file containing the PAC_LOGON_INFO from a decrypted Kerberos ticket. +# This can be exported from the AD-Win2k-PAC (including the MES header) +# using Wireshark. +# +export KRB5_PAC_BIN="$MLRPC_TESTS/tests/netrlogon/krb5_pac_tests/krb5_pac.bin"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/test/libmlrpc-tests/cfg/samlogon.config Sat Jan 16 16:59:32 2021 +0000 @@ -0,0 +1,31 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. + +# +# Copyright 2020 Tintri by DDN, Inc. All rights reserved. +# + +# The NETBIOS domain name to which the server is joined. +export NETBIOS_DOMAIN="DOMAIN" +# An FQDN of a dc in the domain. +export DC_FQDN="dc.domain.com" +# The authenticating user. +export USERNAME="user" +# The name of the computer from which the user is authenticating. +export CLIENT_COMPUTER="client1" + +# +# Binary files exported from Wireshark, containing parts of a +# NetrSamLogon(Ex) request. +# +export CHALLENGE_FILE=/path/to/challenge.bin +export NT_RESPONSE_FILE=/path/to/nt_file.bin +export LM_RESPONSE_FILE=/path/to/lm_file.bin \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/test/libmlrpc-tests/cmd/Makefile Sat Jan 16 16:59:32 2021 +0000 @@ -0,0 +1,40 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Tintri by DDN, Inc. All rights reserved. +# + +include $(SRC)/Makefile.master +include $(SRC)/test/Makefile.com + +ROOTOPTPKG = $(ROOT)/opt/libmlrpc-tests +ROOTBIN = $(ROOTOPTPKG)/bin + +PROGS = libmlrpctest + +CMDS = $(PROGS:%=$(ROOTBIN)/%) +$(CMDS) := FILEMODE = 0555 + +all lint clean clobber: + +install: $(CMDS) + +$(CMDS): $(ROOTBIN) + +$(ROOTBIN): + $(INS.dir) + +$(ROOTBIN)/%: % + $(INS.file) + +$(ROOTBIN)/%: %.ksh + $(INS.rename)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/test/libmlrpc-tests/cmd/libmlrpctest.ksh Sat Jan 16 16:59:32 2021 +0000 @@ -0,0 +1,53 @@ +#!/usr/bin/ksh + +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2012 by Delphix. All rights reserved. +# Copyright 2020 Tintri by DDN, Inc. All rights reserved. +# + +export MLRPC_TESTS="/opt/libmlrpc-tests" +runner="/opt/test-runner/bin/run" + +function fail +{ + echo $1 + exit ${2:-1} +} + +function find_runfile +{ + typeset distro= + if [[ -f $MLRPC_TESTS/runfiles/default.run ]]; then + distro=default + fi + + [[ -n $distro ]] && echo $MLRPC_TESTS/runfiles/$distro.run +} + +while getopts c: c; do + case $c in + 'c') + runfile=$OPTARG + [[ -f $runfile ]] || fail "Cannot read file: $runfile" + ;; + esac +done +shift $((OPTIND - 1)) + +[[ -z $runfile ]] && runfile=$(find_runfile) +[[ -z $runfile ]] && fail "Couldn't determine distro" + +$runner -c $runfile + +exit $?
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/test/libmlrpc-tests/doc/Makefile Sat Jan 16 16:59:32 2021 +0000 @@ -0,0 +1,36 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2012 by Delphix. All rights reserved. +# Copyright 2020 Tintri by DDN, Inc. All rights reserved. +# + +include $(SRC)/Makefile.master + +READMES = README + +ROOTOPTPKG = $(ROOT)/opt/libmlrpc-tests + +FILES = $(READMES:%=$(ROOTOPTPKG)/%) +$(FILES) := FILEMODE = 0444 + +all: $(READMES) + +install: $(ROOTOPTPKG) $(FILES) + +clean lint clobber: + +$(ROOTOPTPKG): + $(INS.dir) + +$(ROOTOPTPKG)/%: % + $(INS.file)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/test/libmlrpc-tests/doc/README Sat Jan 16 16:59:32 2021 +0000 @@ -0,0 +1,77 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2012 by Delphix. All rights reserved. +# Copyright 2020 Tintri by DDN, Inc. All rights reserved. +# + +libmlrpc Unit Test Suite README + +1. Building and installing this Unit Test Suite +2. Running this Unit Test Suite +3. Test results + +-------------------------------------------------------------------------------- + +1. Building and installing this Unit Test Suite + +This Test Suite runs under the testrunner framework (which can be installed +as pkg:/system/test/testrunner). To build both this Unit Test Suite and the +testrunner without running a full nightly: + + build_machine$ bldenv [-d] <your_env_file> + build_machine$ cd $SRC/test + build_machine$ dmake install + build_machine$ cd $SRC/pkg + build_machine$ dmake install + +Then set the publisher on the test machine to point to your repository and +install the Utils Unit Test Suite. + + test_machine# pkg install pkg:/system/test/libmlrpctest + +Note, the framework will be installed automatically, as this test suite +depends on it. + +2. Running this Unit Test Suite + +The pre-requisites for running the this Unit Test Suite are: + - A non-root user with the ability to sudo(1M) to root without a + password or the root user must run the test. + (The samlogon test requires a user with 'solaris.smf.read.smb' + authorization, such as root.) + - The libmlrpc library must be installed. + - The system must be joined to the domain. + - Certain information must be collected from a packet capture performing + NTLM authentication against the system using a domain user, all from + the NetrSamLogon(Ex) request: + - IDENTITY_INFO/Domain + - IDENTITY_INFO/Acct Name + - IDENTITY_INFO/Wkst Name + - three binary files, exported from Wireshark: + 1. NETWORK_INFO/Challenge (8 bytes) + 2. First NETWORK_INFO/'LM Chal resp'/Bytes array/'LM Chal resp' + (variable - the first is NT) + 3. Second NETWORK_INFO/'LM Chal resp'/Bytes array/'LM Chal resp' + (variable - the second is LM) + - Enter configuration data in /opt/libmlrpc-tests/cfg/samlogon.config. + +Once the pre-requisites are satisfied, simply run the script: + + test_machine$ /opt/libmlrpc-tests/bin/libmlrpctest + +3. Test results + +While the Unit Test Suite is running, one informational line is printed at +the end of each test, and a results summary is printed at the end of the run. +The results summary includes the location of the complete logs, which is of the +form /var/tmp/test_results/<ISO 8601 date>. \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/test/libmlrpc-tests/runfiles/Makefile Sat Jan 16 16:59:32 2021 +0000 @@ -0,0 +1,39 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2012 by Delphix. All rights reserved. +# Copyright 2020 Tintri by DDN, Inc. All rights reserved. +# + +include $(SRC)/Makefile.master + +SRCS = default.run + +ROOTOPTPKG = $(ROOT)/opt/libmlrpc-tests +RUNFILES = $(ROOTOPTPKG)/runfiles + +CMDS = $(SRCS:%=$(RUNFILES)/%) +$(CMDS) := FILEMODE = 0444 + +all: $(SRCS) + +install: $(CMDS) + +clean lint clobber: + +$(CMDS): $(RUNFILES) $(SRCS) + +$(RUNFILES): + $(INS.dir) + +$(RUNFILES)/%: % + $(INS.file)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/test/libmlrpc-tests/runfiles/default.run Sat Jan 16 16:59:32 2021 +0000 @@ -0,0 +1,29 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Tintri by DDN, Inc. All rights reserved. +# + +[DEFAULT] +pre = +verbose = False +quiet = False +timeout = 60 +post = +outputdir = /var/tmp/test_results + +[/opt/libmlrpc-tests/tests/netrlogon/krb5_pac_tests] +tests = ['run_krb5_pac_tests'] + +[/opt/libmlrpc-tests/tests/netrlogon/samlogon_tests] +user = root +tests = ['run_samlogon_tests']
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/test/libmlrpc-tests/tests/Makefile Sat Jan 16 16:59:32 2021 +0000 @@ -0,0 +1,43 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Tintri by DDN, Inc. All rights reserved. +# + +SUBDIRS = \ + netrlogon + +ROOTOPTDIR = $(ROOT)/opt/libmlrpc-tests/tests + +all := TARGET = all +install := TARGET = install +clean := TARGET = clean +clobber := TARGET = clobber +lint := TARGET = lint + +.KEEP_STATE: + +install: $(SUBDIRS) + +all: $(SUBDIRS) + +clean lint: $(SUBDIRS) + +$(ROOTOPTDIR): + $(INS.dir) + +clobber: $(SUBDIRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/test/libmlrpc-tests/tests/Makefile.com Sat Jan 16 16:59:32 2021 +0000 @@ -0,0 +1,75 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2012 by Delphix. All rights reserved. +# Copyright 2014 Garrett D'Amore <garrett@damore.org> +# Copyright 2020 Tintri by DDN, Inc. All rights reserved. +# + +include $(SRC)/Makefile.master +include $(SRC)/cmd/Makefile.cmd +include $(SRC)/test/Makefile.com + +# +# Note: NDR currently is only supported in 32-bit programs. +# +OBJS = $(PROG).o util_common.o +SRCS = $(PROG).c $(TESTCOMMONDIR)/util_common.c + +CSTD = $(CSTD_GNU99) +CPPFLAGS += -I$(TESTCOMMONDIR) + +ROOTOPTPKG = $(ROOT)/opt/libmlrpc-tests +TESTDIR = $(ROOTOPTPKG)/tests/$(TESTSUBDIR) + +CMDS = $(PROG:%=$(TESTDIR)/%) $(KSHPROG:%=$(TESTDIR)/%) +$(CMDS) := FILEMODE = 0555 + +BINS = $(BINFILES:%=$(TESTDIR)/%) +$(BINS) := FILEMODE = 0444 + +all: $(PROG) $(KSHPROG) $(SUBDIRS) + +$(PROG): $(OBJS) + $(LINK.c) $(OBJS) -o $@ $(LDLIBS) + $(POST_PROCESS) + +$(KSHPROG): $(KSHPROG).ksh + $(RM) $@ + $(CP) $(KSHPROG).ksh $(@) + $(CHMOD) +x $@ + +%.o: %.c + $(COMPILE.c) -o $@ $(CFLAGS_$(MACH)) $< + +%.o: $(TESTCOMMONDIR)/%.c + $(COMPILE.c) -o $@ $(CFLAGS_$(MACH)) $< + +install: $(SUBDIRS) $(CMDS) $(BINS) + +lint: lint_SRCS + +clobber: clean + -$(RM) $(PROG) $(KSHPROG) + +clean: + -$(RM) $(OBJS) + +$(CMDS): $(TESTDIR) $(PROG) $(KSHPROG) + +$(BINS): $(TESTDIR) + +$(TESTDIR): + $(INS.dir) + +$(TESTDIR)/%: % + $(INS.file)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/test/libmlrpc-tests/tests/common/util_common.c Sat Jan 16 16:59:32 2021 +0000 @@ -0,0 +1,93 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. + */ + +/* + * Common utilities for libmlrpc tests. + */ + +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/stat.h> + +uchar_t * +read_buf_from_file(char *file, uint32_t *size) +{ + struct stat stats; + uchar_t *buf; + FILE *fp; + size_t nread; + int rc; + + errno = 0; + rc = stat(file, &stats); + + if (rc < 0) { + fprintf(stderr, "stat failed with rc %d:\n", rc); + perror(file); + return (NULL); + } + + buf = malloc(stats.st_size); + + if (buf == NULL) { + fprintf(stderr, "couldn't allocate buffer\n"); + return (NULL); + } + errno = 0; + fp = fopen(file, "r"); + if (fp == NULL) { + fprintf(stderr, "fopen failed to open file:\n"); + perror(file); + free(buf); + return (NULL); + } + + errno = 0; + nread = fread(buf, 1, stats.st_size, fp); + if (nread == EOF && errno != 0) { + fprintf(stderr, "fread failed:\n"); + perror(file); + free(buf); + return (NULL); + } + + (void) fclose(fp); + if (nread == EOF) { + free(buf); + buf = NULL; + } + *size = nread; + return (buf); +} + +/* + * smb_token_log() outputs to syslog. The library defines syslog to be + * smb_syslog, which it defines as NODIRECT to allow fksmbd to provide + * its own version. We use that to redirect syslog to stderr, so that + * we can print the token output to a useful location. + */ +void +smb_syslog(int pri, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + (void) vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/test/libmlrpc-tests/tests/common/util_common.h Sat Jan 16 16:59:32 2021 +0000 @@ -0,0 +1,34 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. + */ + +#ifndef _UTIL_COMMON_H +#define _UTIL_COMMON_H + +/* + * Common utilities for libmlrpc tests. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +uchar_t *read_buf_from_file(char *, uint32_t *); +void smb_syslog(int, const char *, ...); + +#ifdef __cplusplus +} +#endif + +#endif /* _UTIL_COMMON_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/test/libmlrpc-tests/tests/netrlogon/Makefile Sat Jan 16 16:59:32 2021 +0000 @@ -0,0 +1,20 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Tintri by DDN, Inc. All Rights Reserved. +# + +.PARALLEL: $(SUBDIRS) + +SUBDIRS = samlogon_tests krb5_pac_tests + +include $(SRC)/test/Makefile.com
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/test/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/Makefile Sat Jan 16 16:59:32 2021 +0000 @@ -0,0 +1,29 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Tintri by DDN, Inc. All rights reserved. +# + +include $(SRC)/Makefile.master + +TESTSUBDIR = netrlogon/krb5_pac_tests +TESTCOMMONDIR = ../../common +PROG = krb5_pac_decode +KSHPROG = run_krb5_pac_tests +BINFILES = krb5_pac.bin + +include ../../Makefile.com + +LDFLAGS += -R/usr/lib/smbsrv +LDLIBS += -L$(ROOT)/usr/lib/smbsrv +LDLIBS += -lmlsvc -lsmb +CPPFLAGS += -Dsyslog=smb_syslog
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/test/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/krb5_pac_decode.c Sat Jan 16 16:59:32 2021 +0000 @@ -0,0 +1,76 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. + */ + +/* + * Test the ability to decode PAC data from AD Kerberos tickets. + */ + +#include <smbsrv/libmlsvc.h> +#include <stdio.h> +#include <stdlib.h> + +#include <util_common.h> + +enum KPAC_RC { + KPC_SUCCESS = 0, + KPC_ARGC, + KPC_PAC_FILE, + KPC_TOKEN_ALLOC, + KPC_DECODE_PAC +}; + +int +main(int argc, char *argv[]) +{ + char *pac_file; + uchar_t *pac_buf; + size_t buflen; + smb_token_t *token; + uint32_t status; + + if (argc < 2) { + fprintf(stderr, "usage: %s <Binary PAC File>\n", argv[0]); + return (-KPC_ARGC); + } + + pac_file = argv[1]; + + pac_buf = read_buf_from_file(pac_file, &buflen); + + if (pac_buf == NULL) { + fprintf(stderr, "failed to read pac data\n"); + return (-KPC_PAC_FILE); + } + + token = calloc(1, sizeof (*token)); + if (token == NULL) { + fprintf(stderr, "failed to allocate token\n"); + return (-KPC_TOKEN_ALLOC); + } + + /* Initialize only those bits on which smb_decode_krb5_pac depends */ + (void) smb_lgrp_start(); + + status = smb_decode_krb5_pac(token, (char *)pac_buf, buflen); + if (status != 0) { + fprintf(stderr, "smb_decode_krb5_pac failed with 0x%x\n", + status); + return (-KPC_DECODE_PAC); + } + + smb_token_log(token); + + return (KPC_SUCCESS); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/test/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/run_krb5_pac_tests.ksh Sat Jan 16 16:59:32 2021 +0000 @@ -0,0 +1,26 @@ +#!/usr/bin/ksh + +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Tintri by DDN, Inc. All rights reserved. +# + +if [[ -z $MLRPC_TESTS ]]; then + echo "MLRPC_TESTS not set" >&2 + exit 1 +fi + +. $MLRPC_TESTS/cfg/krb5_pac.config + +$MLRPC_TESTS/tests/netrlogon/krb5_pac_tests/krb5_pac_decode $KRB5_PAC_BIN +exit $?
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/test/libmlrpc-tests/tests/netrlogon/samlogon_tests/Makefile Sat Jan 16 16:59:32 2021 +0000 @@ -0,0 +1,28 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Tintri by DDN, Inc. All rights reserved. +# + +include $(SRC)/Makefile.master + +TESTSUBDIR = netrlogon/samlogon_tests +TESTCOMMONDIR = ../../common +PROG = samlogon +KSHPROG = run_samlogon_tests + +include ../../Makefile.com + +LDFLAGS += -R/usr/lib/smbsrv +LDLIBS += -L$(ROOT)/usr/lib/smbsrv +LDLIBS += -lmlsvc -lsmb +CPPFLAGS += -Dsyslog=smb_syslog
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/test/libmlrpc-tests/tests/netrlogon/samlogon_tests/run_samlogon_tests.ksh Sat Jan 16 16:59:32 2021 +0000 @@ -0,0 +1,28 @@ +#!/usr/bin/ksh + +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Tintri by DDN, Inc. All rights reserved. +# + +if [[ -z $MLRPC_TESTS ]]; then + echo "MLRPC_TESTS not set" >&2 + exit 1 +fi + +. $MLRPC_TESTS/cfg/samlogon.config + +$MLRPC_TESTS/tests/netrlogon/samlogon_tests/samlogon $NETBIOS_DOMAIN $DC_FQDN \ + $USERNAME $CLIENT_COMPUTER $CHALLENGE_FILE $NT_RESPONSE_FILE \ + $LM_RESPONSE_FILE +exit $?
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/test/libmlrpc-tests/tests/netrlogon/samlogon_tests/samlogon.c Sat Jan 16 16:59:32 2021 +0000 @@ -0,0 +1,341 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. + */ + +/* + * Test NetrSamLogon and NetrSamLogonEx, uses for NTLM pass-thru auth. + */ + +#include <smbsrv/libmlsvc.h> +#include <smbsrv/netrauth.h> +#include <stdio.h> +#include <stdlib.h> + +#include <util_common.h> + +extern void netr_initialize(void); +extern uint32_t netlogon_logon(smb_logon_t *, smb_token_t *, smb_domainex_t *); + +boolean_t +compare_tokens(const smb_token_t *src, const smb_token_t *dst) +{ + int i; + const smb_ids_t *src_wgrps, *dst_wgrps; + smb_id_t *src_grp, *dst_grp; + char src_sid[SMB_SID_STRSZ] = "NULL", dst_sid[SMB_SID_STRSZ] = "NULL"; + + if (strcmp(src->tkn_domain_name, dst->tkn_domain_name) != 0) { + fprintf(stderr, "src domain %s does not match dst %s\n", + src->tkn_domain_name, dst->tkn_domain_name); + return (B_FALSE); + } + + if (strcmp(src->tkn_account_name, dst->tkn_account_name) != 0) { + fprintf(stderr, "src account %s does not match dst %s\n", + src->tkn_account_name, dst->tkn_account_name); + return (B_FALSE); + } + + if (src->tkn_user.i_attrs != dst->tkn_user.i_attrs) { + fprintf(stderr, "src attrs 0x%x does not match dst 0x%x\n", + src->tkn_user.i_attrs, dst->tkn_user.i_attrs); + return (B_FALSE); + } + + if (!smb_sid_cmp(src->tkn_user.i_sid, dst->tkn_user.i_sid)) { + smb_sid_tostr(src->tkn_user.i_sid, src_sid); + smb_sid_tostr(dst->tkn_user.i_sid, dst_sid); + fprintf(stderr, "src usersid %s does not match dst %s\n", + src_sid, dst_sid); + return (B_FALSE); + } + + /* tkn_owner can be NULL if we haven't called smb_token_setup_common */ + if (src->tkn_owner.i_sid != dst->tkn_owner.i_sid && + !smb_sid_cmp(src->tkn_owner.i_sid, dst->tkn_owner.i_sid)) { + smb_sid_tostr(src->tkn_owner.i_sid, src_sid); + smb_sid_tostr(dst->tkn_owner.i_sid, dst_sid); + fprintf(stderr, "src ownersid %s does not match dst %s\n", + src_sid, dst_sid); + return (B_FALSE); + } + + if (!smb_sid_cmp(src->tkn_primary_grp.i_sid, + dst->tkn_primary_grp.i_sid)) { + smb_sid_tostr(src->tkn_primary_grp.i_sid, src_sid); + smb_sid_tostr(dst->tkn_primary_grp.i_sid, dst_sid); + fprintf(stderr, "src primarysid %s does not match dst %s\n", + src_sid, dst_sid); + return (B_FALSE); + } + + src_wgrps = &src->tkn_win_grps; + dst_wgrps = &dst->tkn_win_grps; + + if ((src_wgrps->i_ids == NULL && dst_wgrps->i_ids != NULL) || + (src_wgrps->i_ids != NULL && dst_wgrps->i_ids == NULL)) { + fprintf(stderr, + "src wingrp nullness 0x%p does not match dst 0x%p\n", + src_wgrps->i_ids, dst_wgrps->i_ids); + return (B_FALSE); + } + + if (src_wgrps->i_ids != NULL) { + src_grp = &src_wgrps->i_ids[0]; + dst_grp = &dst_wgrps->i_ids[0]; + if (src_wgrps->i_cnt != dst_wgrps->i_cnt) { + fprintf(stderr, + "src wingrp count %d does not match dst %d\n", + src_wgrps->i_cnt, dst_wgrps->i_cnt); + return (B_FALSE); + } + + for (i = 0; i < src_wgrps->i_cnt; i++, src_grp++, dst_grp++) { + if ((src_grp->i_sid == NULL && + dst_grp->i_sid != NULL) || + (src_grp->i_sid != NULL && + dst_grp->i_sid == NULL)) { + fprintf(stderr, + "src wgrp %d nullness 0x%p does not " + "match dst 0x%p\n", + i, src_grp->i_sid, dst_grp->i_sid); + return (B_FALSE); + } + + + if (src_grp->i_sid != NULL && + !smb_sid_cmp(src_grp->i_sid, dst_grp->i_sid)) { + smb_sid_tostr(src_grp->i_sid, src_sid); + smb_sid_tostr(dst_grp->i_sid, dst_sid); + fprintf(stderr, "src wingrp %d sid %s " + "does not match dst %s\n", + i, src_sid, dst_sid); + return (B_FALSE); + } + } + } + + if ((src->tkn_posix_grps == NULL && dst->tkn_posix_grps != NULL) || + (src->tkn_posix_grps != NULL && dst->tkn_posix_grps == NULL)) { + fprintf(stderr, "src pgrp nullness 0x%p does not match " + "dst 0x%p\n", + src->tkn_posix_grps, dst->tkn_posix_grps); + return (B_FALSE); + } + + if (src->tkn_posix_grps != NULL) { + if (src->tkn_posix_grps->pg_ngrps != + dst->tkn_posix_grps->pg_ngrps) { + fprintf(stderr, + "src pgrp count %d does not match dst %d\n", + src->tkn_posix_grps->pg_ngrps, + dst->tkn_posix_grps->pg_ngrps); + return (B_FALSE); + } + + for (i = 0; i < src->tkn_posix_grps->pg_ngrps; i++) { + if (src->tkn_posix_grps->pg_grps[i] != + dst->tkn_posix_grps->pg_grps[i]) { + fprintf(stderr, + "src pgrp num %d %d does not match " + "dst %d\n", i, + src->tkn_posix_grps->pg_grps[i], + dst->tkn_posix_grps->pg_grps[i]); + return (B_FALSE); + } + } + } + + return (B_TRUE); +} + +enum SAMLOGON_RC { + SL_SUCCESS = 0, + SL_ARGC, + SL_DC_FQDN, + SL_NB_DOMAIN, + SL_CHALLENGE, + SL_NT_PASS, + SL_LM_PASS, + SL_TOKEN_ALLOC, + SL_NETLOGON, + SL_TOKEN_COMP, + SL_NETLOGON_LOOP, + SL_NETLOGON_SAMLOGON, + SL_NETLOGON_NOVERIFY +}; + +int +main(int argc, char *argv[]) +{ + smb_logon_t user_info = { + .lg_secmode = SMB_SECMODE_DOMAIN, + .lg_domain_type = SMB_DOMAIN_PRIMARY, + .lg_level = NETR_NETWORK_LOGON + }; + smb_token_t *token = NULL; + smb_token_t cmp_token; + smb_domainex_t di = {0}; + char *nb_domain, *dc_name, *user_name, *workstation, *chall_file; + char *nt_file, *lm_file; + uint32_t status; + int i; + + if (argc < 8) { + fprintf(stderr, "usage: %s <NETBIOS domain> <DC FQDN> " + "<user name> " + "<client computer name> <Binary Challenge File> " + "<Binary NT response file> <Binary LM response file>\n", + argv[0]); + return (-SL_ARGC); + } + + nb_domain = argv[1]; + dc_name = argv[2]; + user_name = argv[3]; + workstation = argv[4]; + chall_file = argv[5]; + nt_file = argv[6]; + lm_file = argv[7]; + + if (strlcpy(di.d_dci.dc_name, dc_name, sizeof (di.d_dci.dc_name)) >= + sizeof (di.d_dci.dc_name)) { + fprintf(stderr, "DC FQDN %s is too long\n", dc_name); + return (-SL_DC_FQDN); + } + if (strlcpy(di.d_primary.di_nbname, nb_domain, + sizeof (di.d_primary.di_nbname)) >= + sizeof (di.d_primary.di_nbname)) { + fprintf(stderr, "Netbios Domain %s is too long\n", nb_domain); + return (-SL_NB_DOMAIN); + } + + user_info.lg_domain = nb_domain; + user_info.lg_e_domain = user_info.lg_domain; + user_info.lg_username = user_name; + user_info.lg_workstation = workstation; + + user_info.lg_challenge_key.val = + read_buf_from_file(chall_file, &user_info.lg_challenge_key.len); + if (user_info.lg_challenge_key.val == NULL) { + fprintf(stderr, "failed to get challenge\n"); + return (-SL_CHALLENGE); + } + + user_info.lg_nt_password.val = + read_buf_from_file(nt_file, &user_info.lg_nt_password.len); + if (user_info.lg_nt_password.val == NULL) { + fprintf(stderr, "failed to get NT pass\n"); + return (-SL_NT_PASS); + } + + user_info.lg_lm_password.val = + read_buf_from_file(lm_file, &user_info.lg_lm_password.len); + if (user_info.lg_lm_password.val == NULL) { + fprintf(stderr, "failed to get LM pass\n"); + return (-SL_LM_PASS); + } + + /* Initialize only those bits on which netlogon_logon depends */ + (void) smb_lgrp_start(); + smb_ipc_init(); + netr_initialize(); + + token = calloc(1, sizeof (*token)); + if (token == NULL) { + fprintf(stderr, "failed to allocate token\n"); + return (-SL_TOKEN_ALLOC); + } + status = netlogon_logon(&user_info, token, &di); + + if (status != NT_STATUS_SUCCESS) { + fprintf(stderr, "netlogon_logon failed: 0x%x\n", status); + return (-SL_NETLOGON); + } + smb_token_log(token); + + /* struct copy */ + cmp_token = *token; + + for (i = 0; i < 10; i++) { + token = calloc(1, sizeof (*token)); + if (token == NULL) { + fprintf(stderr, "iter %d: failed to allocate token\n", + i); + return (-SL_TOKEN_ALLOC); + } + status = netlogon_logon(&user_info, token, &di); + + if (status != NT_STATUS_SUCCESS) { + fprintf(stderr, + "iter %d: netlogon_logon failed: 0x%x\n", + i, status); + return (-SL_NETLOGON_LOOP); + } + if (!compare_tokens(&cmp_token, token)) { + fprintf(stderr, "iter %d: tokens didn't match\n", i); + smb_token_log(token); + return (-SL_TOKEN_COMP); + } + if (i != 9) + smb_token_destroy(token); + } + smb_token_log(token); + smb_token_destroy(token); + + token = calloc(1, sizeof (*token)); + if (token == NULL) { + fprintf(stderr, "failed to allocate token\n"); + return (-SL_TOKEN_ALLOC); + } + + /* Turn off SamLogonEx */ + netlogon_init_global(0x00000004); + status = netlogon_logon(&user_info, token, &di); + if (status != NT_STATUS_SUCCESS) { + fprintf(stderr, "NoSamLogonEx: netlogon_logon failed: 0x%x\n", + status); + return (-SL_NETLOGON_SAMLOGON); + } + smb_token_log(token); + if (!compare_tokens(&cmp_token, token)) { + fprintf(stderr, "tokens didn't match\n"); + return (-SL_TOKEN_COMP); + } + smb_token_destroy(token); + + token = calloc(1, sizeof (*token)); + if (token == NULL) { + fprintf(stderr, "failed to allocate token\n"); + return (-SL_TOKEN_ALLOC); + } + + /* Don't verify responses */ + netlogon_init_global(0x00000002); + status = netlogon_logon(&user_info, token, &di); + + if (status != NT_STATUS_SUCCESS) { + fprintf(stderr, "NoVerify: netlogon_logon failed: 0x%x\n", + status); + return (-SL_NETLOGON_NOVERIFY); + } + smb_token_log(token); + + if (!compare_tokens(&cmp_token, token)) { + fprintf(stderr, "tokens didn't match\n"); + return (-SL_TOKEN_COMP); + } + smb_token_destroy(token); + return (SL_SUCCESS); +}
--- a/usr/src/tools/ndrgen/ndr_anal.c Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/tools/ndrgen/ndr_anal.c Sat Jan 16 16:59:32 2021 +0000 @@ -24,7 +24,9 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. + */ #include <strings.h> #include <string.h> @@ -1003,6 +1005,7 @@ advice->a_reference = find_advice(advice_list, REFERENCE_KW); advice->a_align = find_advice(advice_list, ALIGN_KW); + advice->a_fake = find_advice(advice_list, FAKE_KW); } static ndr_node_t *
--- a/usr/src/tools/ndrgen/ndr_gen.c Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/tools/ndrgen/ndr_gen.c Sat Jan 16 16:59:32 2021 +0000 @@ -24,6 +24,10 @@ * Use is subject to license terms. */ +/* + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. + */ + #include <string.h> #include "ndrgen.h" #include "y.tab.h" @@ -528,6 +532,9 @@ if (ti->is_conformant) (void) strlcat(flags, "|NDR_F_CONFORMANT", NDLBUFSZ); + if (ti->advice.a_fake) + (void) strlcat(flags, "|NDR_F_FAKE", NDLBUFSZ); + if (ti->type_op == STRUCT_KW) { if (ti->advice.a_operation) (void) strlcat(flags, "|NDR_F_OPERATION", NDLBUFSZ);
--- a/usr/src/tools/ndrgen/ndr_lex.c Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/tools/ndrgen/ndr_lex.c Sat Jan 16 16:59:32 2021 +0000 @@ -24,6 +24,10 @@ * Use is subject to license terms. */ +/* + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. + */ + #include <errno.h> #include <stdarg.h> #include "ndrgen.h" @@ -117,6 +121,7 @@ { "transmit_as", TRANSMIT_AS_KW, 0 }, { "arg_is", ARG_IS_KW, 0 }, + { "fake", FAKE_KW, 0 }, { "char", BASIC_TYPE, 1 }, { "uchar", BASIC_TYPE, 1 },
--- a/usr/src/tools/ndrgen/ndr_parse.y Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/tools/ndrgen/ndr_parse.y Sat Jan 16 16:59:32 2021 +0000 @@ -25,6 +25,10 @@ * Use is subject to license terms. */ +/* + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. + */ + #include "ndrgen.h" typedef struct node *node_ptr; @@ -39,7 +43,7 @@ %token INTERFACE_KW UUID_KW _NO_REORDER_KW EXTERN_KW %token SIZE_IS_KW LENGTH_IS_KW STRING_KW REFERENCE_KW %token CASE_KW DEFAULT_KW SWITCH_IS_KW -%token TRANSMIT_AS_KW ARG_IS_KW +%token TRANSMIT_AS_KW ARG_IS_KW FAKE_KW /* composite keywords */ %token BASIC_TYPE TYPENAME @@ -112,6 +116,7 @@ | OPERATION_KW LP arg RP ={ $$ = n_cons (OPERATION_KW, $3); } | ALIGN_KW LP arg RP ={ $$ = n_cons (ALIGN_KW, $3); } | STRING_KW ={ $$ = n_cons (STRING_KW); } + | FAKE_KW ={ $$ = n_cons (FAKE_KW); } | SIZE_IS_KW LP arg RP ={ $$ = n_cons (SIZE_IS_KW, $3, $3, $3); }
--- a/usr/src/tools/ndrgen/ndr_print.c Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/tools/ndrgen/ndr_print.c Sat Jan 16 16:59:32 2021 +0000 @@ -24,6 +24,10 @@ * Use is subject to license terms. */ +/* + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. + */ + #include "ndrgen.h" #include "y.tab.h" @@ -51,6 +55,7 @@ switch (np->label) { case ALIGN_KW: nm = "align"; break; + case FAKE_KW: nm = "fake"; break; case STRUCT_KW: nm = "struct"; break; case UNION_KW: nm = "union"; break; case TYPEDEF_KW: nm = "typedef"; break; @@ -104,6 +109,7 @@ case DEFAULT_KW: case _NO_REORDER_KW: case EXTERN_KW: + case FAKE_KW: (void) printf("%s", nm); break;
--- a/usr/src/tools/ndrgen/ndrgen.h Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/tools/ndrgen/ndrgen.h Sat Jan 16 16:59:32 2021 +0000 @@ -24,6 +24,10 @@ * Use is subject to license terms. */ +/* + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. + */ + #ifndef _NDRGEN_H #define _NDRGEN_H @@ -97,7 +101,7 @@ #define NDLBUFSZ 100 /* This makes certain things much easier */ -#define N_ADVICE 18 +#define N_ADVICE 19 typedef struct advice { struct node *a_nodes[N_ADVICE]; @@ -130,6 +134,7 @@ #define a_extern a_nodes[14] #define a_reference a_nodes[15] #define a_align a_nodes[16] +#define a_fake a_nodes[17] } ndr_advice_t; typedef struct typeinfo {
--- a/usr/src/uts/common/fs/smbsrv/smb_common_open.c Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/uts/common/fs/smbsrv/smb_common_open.c Sat Jan 16 16:59:32 2021 +0000 @@ -22,6 +22,7 @@ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2020 Tintri by DDN, Inc. All rights reserved. + * Copyright 2020 Nexenta by DDN, Inc. All rights reserved. */ /* @@ -253,6 +254,7 @@ smb_node_t *fnode = NULL; smb_node_t *dnode = NULL; smb_node_t *cur_node = NULL; + smb_node_t *tmp_node = NULL; smb_arg_open_t *op = &sr->sr_open; smb_pathname_t *pn = &op->fqi.fq_path; smb_ofile_t *of = NULL; @@ -269,6 +271,7 @@ uint16_t tree_fid = 0; boolean_t created = B_FALSE; boolean_t last_comp_found = B_FALSE; + boolean_t stream_found = B_FALSE; boolean_t opening_incr = B_FALSE; boolean_t dnode_held = B_FALSE; boolean_t dnode_wlock = B_FALSE; @@ -278,6 +281,7 @@ boolean_t did_open = B_FALSE; boolean_t did_break_handle = B_FALSE; boolean_t did_cleanup_orphans = B_FALSE; + char *sname = NULL; /* Get out now if we've been cancelled. */ mutex_enter(&sr->sr_mutex); @@ -418,9 +422,13 @@ if ((op->desired_access & ~FILE_READ_ATTRIBUTES) == DELETE) lookup_flags &= ~SMB_FOLLOW_LINKS; - rc = smb_fsop_lookup_name(sr, zone_kcred(), lookup_flags, + /* + * Lookup *just* the file portion of the name. + * Returns stream name in sname, which this allocates + */ + rc = smb_fsop_lookup_file(sr, zone_kcred(), lookup_flags, sr->tid_tree->t_snode, op->fqi.fq_dnode, op->fqi.fq_last_comp, - &op->fqi.fq_fnode); + &sname, &op->fqi.fq_fnode); if (rc == 0) { last_comp_found = B_TRUE; @@ -449,9 +457,6 @@ if (last_comp_found) { - smb_node_unlock(dnode); - dnode_wlock = B_FALSE; - fnode = op->fqi.fq_fnode; dnode = op->fqi.fq_dnode; @@ -468,8 +473,9 @@ * it must NOT be (required by Lotus Notes) * - the target is NOT a directory and client requires that * it MUST be. + * Streams are never directories. */ - if (smb_node_is_dir(fnode)) { + if (smb_node_is_dir(fnode) && sname == NULL) { if (op->create_options & FILE_NON_DIRECTORY_FILE) { status = NT_STATUS_FILE_IS_A_DIRECTORY; goto errout; @@ -482,20 +488,81 @@ } } - /* - * No more open should be accepted when "Delete on close" - * flag is set. - */ - if (fnode->flags & NODE_FLAGS_DELETE_ON_CLOSE) { - status = NT_STATUS_DELETE_PENDING; - goto errout; + /* If we're given a stream name, look it up now */ + if (sname != NULL) { + tmp_node = fnode; + rc = smb_fsop_lookup_stream(sr, zone_kcred(), + lookup_flags, sr->tid_tree->t_snode, fnode, sname, + &fnode); + } else { + rc = 0; } - /* - * Specified file already exists so the operation should fail. - */ - if (op->create_disposition == FILE_CREATE) { - status = NT_STATUS_OBJECT_NAME_COLLISION; + if (rc == 0) { /* Stream Exists (including unnamed stream) */ + stream_found = B_TRUE; + smb_node_unlock(dnode); + dnode_wlock = B_FALSE; + + if (tmp_node != NULL) + smb_node_release(tmp_node); + + /* + * No more open should be accepted when + * "Delete on close" flag is set. + */ + if (fnode->flags & NODE_FLAGS_DELETE_ON_CLOSE) { + status = NT_STATUS_DELETE_PENDING; + goto errout; + } + + /* + * Specified file already exists + * so the operation should fail. + */ + if (op->create_disposition == FILE_CREATE) { + status = NT_STATUS_OBJECT_NAME_COLLISION; + goto errout; + } + + if ((op->create_disposition == FILE_SUPERSEDE) || + (op->create_disposition == FILE_OVERWRITE_IF) || + (op->create_disposition == FILE_OVERWRITE)) { + + if (sname == NULL) { + if (!smb_sattr_check( + op->fqi.fq_fattr.sa_dosattr, + op->dattr)) { + status = + NT_STATUS_ACCESS_DENIED; + goto errout; + } + op->desired_access |= + FILE_WRITE_ATTRIBUTES; + } + + if (smb_node_is_dir(fnode)) { + status = NT_STATUS_ACCESS_DENIED; + goto errout; + } + } + + /* MS-FSA 2.1.5.1.2 */ + if (op->create_disposition == FILE_SUPERSEDE) + op->desired_access |= DELETE; + if ((op->create_disposition == FILE_OVERWRITE_IF) || + (op->create_disposition == FILE_OVERWRITE)) + op->desired_access |= FILE_WRITE_DATA; + } else if (rc == ENOENT) { /* File Exists, but Stream doesn't */ + if (op->create_disposition == FILE_OPEN || + op->create_disposition == FILE_OVERWRITE) { + status = NT_STATUS_OBJECT_NAME_NOT_FOUND; + goto errout; + } + + op->desired_access |= FILE_WRITE_DATA; + } else { /* Error looking up stream */ + status = smb_errno2status(rc); + fnode = tmp_node; goto errout; } @@ -520,29 +587,6 @@ } } - if ((op->create_disposition == FILE_SUPERSEDE) || - (op->create_disposition == FILE_OVERWRITE_IF) || - (op->create_disposition == FILE_OVERWRITE)) { - - if (!smb_sattr_check(op->fqi.fq_fattr.sa_dosattr, - op->dattr)) { - status = NT_STATUS_ACCESS_DENIED; - goto errout; - } - - if (smb_node_is_dir(fnode)) { - status = NT_STATUS_ACCESS_DENIED; - goto errout; - } - } - - /* MS-FSA 2.1.5.1.2 */ - if (op->create_disposition == FILE_SUPERSEDE) - op->desired_access |= DELETE; - if ((op->create_disposition == FILE_OVERWRITE_IF) || - (op->create_disposition == FILE_OVERWRITE)) - op->desired_access |= FILE_WRITE_DATA; - /* Dataset roots can't be deleted, so don't set DOC */ if ((op->create_options & FILE_DELETE_ON_CLOSE) != 0 && (fnode->flags & NODE_FLAGS_VFSROOT) != 0) { @@ -552,6 +596,7 @@ status = smb_fsop_access(sr, sr->user_cr, fnode, op->desired_access); + if (status != NT_STATUS_SUCCESS) goto errout; @@ -575,6 +620,31 @@ if ((op->desired_access & FILE_DATA_ALL) != 0) op->desired_access |= FILE_READ_ATTRIBUTES; + /* If the stream didn't exist, create it now */ + if (!stream_found) { + smb_node_t *tmp_node = fnode; + + bzero(&new_attr, sizeof (new_attr)); + new_attr.sa_vattr.va_type = VREG; + new_attr.sa_vattr.va_mode = S_IRUSR; + new_attr.sa_mask |= SMB_AT_TYPE | SMB_AT_MODE; + + rc = smb_fsop_create_stream(sr, sr->user_cr, dnode, + fnode, sname, lookup_flags, &new_attr, &fnode); + smb_node_release(tmp_node); + + if (rc != 0) { + status = smb_errno2status(rc); + fnode_held = B_FALSE; + goto errout; + } + op->action_taken = SMB_OACT_CREATED; + created = B_TRUE; + + smb_node_unlock(dnode); + dnode_wlock = B_FALSE; + } + /* * Oplock break is done prior to sharing checks as the break * may cause other clients to close the file which would @@ -593,6 +663,24 @@ smb_node_inc_opening_count(fnode); opening_incr = B_TRUE; + if (!stream_found) { + /* + * Stake our Share Access claim. + */ + smb_node_wrlock(fnode); + fnode_wlock = B_TRUE; + + status = smb_fsop_shrlock(sr->user_cr, fnode, uniq_fid, + op->desired_access, op->share_access); + if (status != 0) + goto errout; + + fnode_shrlk = B_TRUE; + smb_node_unlock(fnode); + fnode_wlock = B_FALSE; + goto stream_created; + } + /* * XXX Supposed to do share access checks next. * [MS-FSA] describes that as part of access check: @@ -780,11 +868,20 @@ case FILE_SUPERSEDE: case FILE_OVERWRITE_IF: case FILE_OVERWRITE: - op->dattr |= FILE_ATTRIBUTE_ARCHIVE; - /* Don't apply readonly until smb_set_open_attributes */ - if (op->dattr & FILE_ATTRIBUTE_READONLY) { - op->dattr &= ~FILE_ATTRIBUTE_READONLY; - op->created_readonly = B_TRUE; + bzero(&new_attr, sizeof (new_attr)); + if (sname == NULL) { + op->dattr |= FILE_ATTRIBUTE_ARCHIVE; + /* + * Don't apply readonly until + * smb_set_open_attributes + */ + if (op->dattr & FILE_ATTRIBUTE_READONLY) { + op->dattr &= ~FILE_ATTRIBUTE_READONLY; + op->created_readonly = B_TRUE; + } + new_attr.sa_dosattr = op->dattr; + } else { + new_attr.sa_dosattr = FILE_ATTRIBUTE_ARCHIVE; } /* @@ -793,8 +890,6 @@ * after we have an ofile. See: * smb_set_open_attributes */ - bzero(&new_attr, sizeof (new_attr)); - new_attr.sa_dosattr = op->dattr; new_attr.sa_vattr.va_size = 0; new_attr.sa_mask = SMB_AT_DOSATTR | SMB_AT_SIZE; rc = smb_fsop_setattr(sr, sr->user_cr, fnode, @@ -844,6 +939,12 @@ goto errout; } + if ((op->desired_access & ACCESS_SYSTEM_SECURITY) != 0 && + !smb_user_has_security_priv(sr->uid_user, sr->user_cr)) { + status = NT_STATUS_ACCESS_DENIED; + goto errout; + } + if (pn->pn_fname && smb_is_invalid_filename(pn->pn_fname)) { status = NT_STATUS_OBJECT_NAME_INVALID; goto errout; @@ -982,6 +1083,7 @@ (void) smb_oplock_break_PARENT(dnode, of); } +stream_created: /* * We might have blocked in smb_oplock_break_OPEN long enough * so a tree disconnect might have happened. In that case, @@ -1061,6 +1163,8 @@ * how that happens is protocol-specific. */ + if (sname != NULL) + kmem_free(sname, MAXNAMELEN); if (fnode_wlock) smb_node_unlock(fnode); if (opening_incr) @@ -1091,6 +1195,8 @@ smb_delete_new_object(sr); } + if (sname != NULL) + kmem_free(sname, MAXNAMELEN); if (fnode_wlock) smb_node_unlock(fnode); if (opening_incr)
--- a/usr/src/uts/common/fs/smbsrv/smb_fsops.c Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/uts/common/fs/smbsrv/smb_fsops.c Sat Jan 16 16:59:32 2021 +0000 @@ -35,8 +35,8 @@ extern caller_context_t smb_ct; -static int smb_fsop_create_stream(smb_request_t *, cred_t *, smb_node_t *, - char *, char *, int, smb_attr_t *, smb_node_t **); +static int smb_fsop_create_file_with_stream(smb_request_t *, cred_t *, + smb_node_t *, char *, char *, int, smb_attr_t *, smb_node_t **); static int smb_fsop_create_file(smb_request_t *, cred_t *, smb_node_t *, char *, int, smb_attr_t *, smb_node_t **); @@ -136,6 +136,7 @@ boolean_t is_dir; ASSERT(fs_sd); + ASSERT(ret_snode != NULL); if (SMB_TREE_IS_CASEINSENSITIVE(sr)) flags = SMB_IGNORE_CASE; @@ -319,7 +320,7 @@ sname = kmem_alloc(MAXNAMELEN, KM_SLEEP); smb_stream_parse_name(name, fname, sname); - rc = smb_fsop_create_stream(sr, cr, dnode, + rc = smb_fsop_create_file_with_stream(sr, cr, dnode, fname, sname, flags, attr, ret_snode); kmem_free(fname, MAXNAMELEN); @@ -348,17 +349,71 @@ /* - * smb_fsop_create_stream + * smb_fsop_create_file_with_stream * - * Create NTFS named stream file (sname) on unnamed stream - * file (fname), creating the unnamed stream file if it + * Create named stream (sname) on file (fname), creating the file if it * doesn't exist. - * If we created the unnamed stream file and then creation - * of the named stream file fails, we delete the unnamed stream. + * If we created the file and then creation of the named stream fails, + * we delete the file. * Since we use the real file name for the smb_vop_remove we * clear the SMB_IGNORE_CASE flag to ensure a case sensitive * match. * + * Note that some stream "types" are "restricted" and only + * internal callers (cr == kcred) can create those. + */ +static int +smb_fsop_create_file_with_stream(smb_request_t *sr, cred_t *cr, + smb_node_t *dnode, char *fname, char *sname, int flags, + smb_attr_t *attr, smb_node_t **ret_snode) +{ + smb_node_t *fnode; + cred_t *kcr = zone_kcred(); + int rc = 0; + boolean_t fcreate = B_FALSE; + + ASSERT(ret_snode != NULL); + + if (cr != kcr && smb_strname_restricted(sname)) + return (EACCES); + + /* Look up / create the unnamed stream, fname */ + rc = smb_fsop_lookup(sr, cr, flags | SMB_FOLLOW_LINKS, + sr->tid_tree->t_snode, dnode, fname, &fnode); + if (rc == 0) { + if (smb_fsop_access(sr, sr->user_cr, fnode, + sr->sr_open.desired_access) != 0) { + smb_node_release(fnode); + rc = EACCES; + } + } else if (rc == ENOENT) { + fcreate = B_TRUE; + rc = smb_fsop_create_file(sr, cr, dnode, fname, flags, + attr, &fnode); + } + if (rc != 0) + return (rc); + + rc = smb_fsop_create_stream(sr, cr, dnode, fnode, sname, flags, attr, + ret_snode); + + if (rc != 0) { + if (fcreate) { + flags &= ~SMB_IGNORE_CASE; + (void) smb_vop_remove(dnode->vp, + fnode->od_name, flags, cr); + } + } + + smb_node_release(fnode); + return (rc); +} + +/* + * smb_fsop_create_stream + * + * Create named stream (sname) on existing file (fnode). + * * The second parameter of smb_vop_setattr() is set to * NULL, even though an unnamed stream exists. This is * because we want to set the UID and GID on the named @@ -368,54 +423,33 @@ * Note that some stream "types" are "restricted" and only * internal callers (cr == kcred) can create those. */ -static int +int smb_fsop_create_stream(smb_request_t *sr, cred_t *cr, - smb_node_t *dnode, char *fname, char *sname, int flags, + smb_node_t *dnode, smb_node_t *fnode, char *sname, int flags, smb_attr_t *attr, smb_node_t **ret_snode) { smb_attr_t fattr; - smb_node_t *fnode; vnode_t *xattrdvp; vnode_t *vp; cred_t *kcr = zone_kcred(); int rc = 0; - boolean_t fcreate = B_FALSE; + + ASSERT(ret_snode != NULL); if (cr != kcr && smb_strname_restricted(sname)) return (EACCES); - /* Look up / create the unnamed stream, fname */ - rc = smb_fsop_lookup(sr, cr, flags | SMB_FOLLOW_LINKS, - sr->tid_tree->t_snode, dnode, fname, &fnode); - if (rc == 0) { - if (smb_fsop_access(sr, sr->user_cr, fnode, - sr->sr_open.desired_access) != 0) - rc = EACCES; - } else if (rc == ENOENT) { - fcreate = B_TRUE; - rc = smb_fsop_create_file(sr, cr, dnode, fname, flags, - attr, &fnode); - } - if (rc != 0) - return (rc); - + bzero(&fattr, sizeof (fattr)); fattr.sa_mask = SMB_AT_UID | SMB_AT_GID; rc = smb_vop_getattr(fnode->vp, NULL, &fattr, 0, kcr); if (rc == 0) { /* create the named stream, sname */ - rc = smb_vop_stream_create(fnode->vp, sname, attr, - &vp, &xattrdvp, flags, cr); + rc = smb_vop_stream_create(fnode->vp, sname, + attr, &vp, &xattrdvp, flags, cr); } - if (rc != 0) { - if (fcreate) { - flags &= ~SMB_IGNORE_CASE; - (void) smb_vop_remove(dnode->vp, - fnode->od_name, flags, cr); - } - smb_node_release(fnode); + if (rc != 0) return (rc); - } attr->sa_vattr.va_uid = fattr.sa_vattr.va_uid; attr->sa_vattr.va_gid = fattr.sa_vattr.va_gid; @@ -423,14 +457,14 @@ rc = smb_vop_setattr(vp, NULL, attr, 0, kcr); if (rc != 0) { - smb_node_release(fnode); + VN_RELE(xattrdvp); + VN_RELE(vp); return (rc); } *ret_snode = smb_stream_node_lookup(sr, cr, fnode, xattrdvp, vp, sname); - smb_node_release(fnode); VN_RELE(xattrdvp); VN_RELE(vp); @@ -440,7 +474,7 @@ /* notify change to the unnamed stream */ if (rc == 0) smb_node_notify_change(dnode, - FILE_ACTION_ADDED_STREAM, fname); + FILE_ACTION_ADDED_STREAM, fnode->od_name); return (rc); } @@ -457,6 +491,8 @@ vnode_t *vp; int rc; + ASSERT(ret_snode != NULL); + #ifdef _KERNEL smb_fssd_t fs_sd; uint32_t secinfo; @@ -470,6 +506,10 @@ */ secinfo = smb_sd_get_secinfo(op->sd); + if ((secinfo & SMB_SACL_SECINFO) != 0 && + !smb_user_has_security_priv(sr->uid_user, cr)) + return (NT_STATUS_PRIVILEGE_NOT_HELD); + smb_fssd_init(&fs_sd, secinfo, 0); status = smb_sd_tofs(op->sd, &fs_sd); @@ -616,6 +656,10 @@ */ secinfo = smb_sd_get_secinfo(op->sd); + if ((secinfo & SMB_SACL_SECINFO) != 0 && + !smb_user_has_security_priv(sr->uid_user, cr)) + return (NT_STATUS_PRIVILEGE_NOT_HELD); + smb_fssd_init(&fs_sd, secinfo, SMB_FSSD_FLAGS_DIR); status = smb_sd_tofs(op->sd, &fs_sd); @@ -1699,10 +1743,7 @@ * it's not part of DACL. It's only granted via proper * privileges. */ - if ((sr->uid_user->u_privileges & - (SMB_USER_PRIV_BACKUP | - SMB_USER_PRIV_RESTORE | - SMB_USER_PRIV_SECURITY)) == 0) + if (!smb_user_has_security_priv(sr->uid_user, cr)) return (NT_STATUS_PRIVILEGE_NOT_HELD); faccess &= ~ACCESS_SYSTEM_SECURITY; @@ -1744,9 +1785,13 @@ /* * smb_fsop_lookup_name() * + * Lookup both the file and stream specified in 'name'. * If name indicates that the file is a stream file, perform * stream specific lookup, otherwise call smb_fsop_lookup. * + * On success, returns the found node in *ret_snode. This will be either a named + * or unnamed stream node, depending on the name specified. + * * Return an error if the looked-up file is in outside the tree. * (Required when invoked from open path.) * @@ -1768,18 +1813,64 @@ char *name, smb_node_t **ret_snode) { - smb_node_t *fnode; - vnode_t *xattrdirvp; - vnode_t *vp; - char *od_name; + char *sname = NULL; + int rc; + smb_node_t *tmp_node; + + ASSERT(ret_snode != NULL); + + rc = smb_fsop_lookup_file(sr, cr, flags, root_node, dnode, name, + &sname, ret_snode); + + if (rc != 0 || sname == NULL) + return (rc); + + tmp_node = *ret_snode; + rc = smb_fsop_lookup_stream(sr, cr, flags, root_node, tmp_node, sname, + ret_snode); + kmem_free(sname, MAXNAMELEN); + smb_node_release(tmp_node); + + return (rc); +} + +/* + * smb_fsop_lookup_file() + * + * Look up of the file portion of 'name'. If a Stream is specified, + * return the stream name in 'sname', which this allocates. + * The caller must free 'sname'. + * + * Return an error if the looked-up file is outside the tree. + * (Required when invoked from open path.) + * + * Case sensitivity flags (SMB_IGNORE_CASE, SMB_CASE_SENSITIVE): + * if SMB_CASE_SENSITIVE is set, the SMB_IGNORE_CASE flag will NOT be set + * based on the tree's case sensitivity. However, if the SMB_IGNORE_CASE + * flag is set in the flags value passed as a parameter, a case insensitive + * lookup WILL be done (regardless of whether SMB_CASE_SENSITIVE is set + * or not). + */ + +int +smb_fsop_lookup_file( + smb_request_t *sr, + cred_t *cr, + int flags, + smb_node_t *root_node, + smb_node_t *dnode, + char *name, + char **sname, + smb_node_t **ret_snode) +{ char *fname; - char *sname; int rc; ASSERT(cr); ASSERT(dnode); ASSERT(dnode->n_magic == SMB_NODE_MAGIC); ASSERT(dnode->n_state != SMB_NODE_STATE_DESTROYING); + ASSERT(ret_snode != NULL); /* * The following check is required for streams processing, below @@ -1790,11 +1881,11 @@ flags |= SMB_IGNORE_CASE; } - fname = kmem_alloc(MAXNAMELEN, KM_SLEEP); - sname = kmem_alloc(MAXNAMELEN, KM_SLEEP); - + *sname = NULL; if (smb_is_stream_name(name)) { - smb_stream_parse_name(name, fname, sname); + *sname = kmem_alloc(MAXNAMELEN, KM_SLEEP); + fname = kmem_alloc(MAXNAMELEN, KM_SLEEP); + smb_stream_parse_name(name, fname, *sname); /* * Look up the unnamed stream (i.e. fname). @@ -1802,49 +1893,8 @@ * as well as any link target. */ rc = smb_fsop_lookup(sr, cr, flags, root_node, dnode, - fname, &fnode); - - if (rc != 0) { - kmem_free(fname, MAXNAMELEN); - kmem_free(sname, MAXNAMELEN); - return (rc); - } - - od_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); - - /* - * od_name is the on-disk name of the stream, except - * without the prepended stream prefix (SMB_STREAM_PREFIX) - */ - - /* - * XXX - * What permissions NTFS requires for stream lookup if any? - */ - rc = smb_vop_stream_lookup(fnode->vp, sname, &vp, od_name, - &xattrdirvp, flags, root_node->vp, cr); - - if (rc != 0) { - smb_node_release(fnode); - kmem_free(fname, MAXNAMELEN); - kmem_free(sname, MAXNAMELEN); - kmem_free(od_name, MAXNAMELEN); - return (rc); - } - - *ret_snode = smb_stream_node_lookup(sr, cr, fnode, xattrdirvp, - vp, od_name); - - kmem_free(od_name, MAXNAMELEN); - smb_node_release(fnode); - VN_RELE(xattrdirvp); - VN_RELE(vp); - - if (*ret_snode == NULL) { - kmem_free(fname, MAXNAMELEN); - kmem_free(sname, MAXNAMELEN); - return (ENOMEM); - } + fname, ret_snode); + kmem_free(fname, MAXNAMELEN); } else { rc = smb_fsop_lookup(sr, cr, flags, root_node, dnode, name, ret_snode); @@ -1859,8 +1909,66 @@ } } - kmem_free(fname, MAXNAMELEN); - kmem_free(sname, MAXNAMELEN); + if (rc != 0 && *sname != NULL) { + kmem_free(*sname, MAXNAMELEN); + *sname = NULL; + } + return (rc); +} + +/* + * smb_fsop_lookup_stream + * + * The file exists, see if the stream exists. + */ +int +smb_fsop_lookup_stream( + smb_request_t *sr, + cred_t *cr, + int flags, + smb_node_t *root_node, + smb_node_t *fnode, + char *sname, + smb_node_t **ret_snode) +{ + char *od_name; + vnode_t *xattrdirvp; + vnode_t *vp; + int rc; + + /* + * The following check is required for streams processing, below + */ + + if (!(flags & SMB_CASE_SENSITIVE)) { + if (SMB_TREE_IS_CASEINSENSITIVE(sr)) + flags |= SMB_IGNORE_CASE; + } + + od_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); + + /* + * od_name is the on-disk name of the stream, except + * without the prepended stream prefix (SMB_STREAM_PREFIX) + */ + + rc = smb_vop_stream_lookup(fnode->vp, sname, &vp, od_name, + &xattrdirvp, flags, root_node->vp, cr); + + if (rc != 0) { + kmem_free(od_name, MAXNAMELEN); + return (rc); + } + + *ret_snode = smb_stream_node_lookup(sr, cr, fnode, xattrdirvp, + vp, od_name); + + kmem_free(od_name, MAXNAMELEN); + VN_RELE(xattrdirvp); + VN_RELE(vp); + + if (*ret_snode == NULL) + return (ENOMEM); return (rc); }
--- a/usr/src/uts/common/fs/smbsrv/smb_node.c Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/uts/common/fs/smbsrv/smb_node.c Sat Jan 16 16:59:32 2021 +0000 @@ -1479,6 +1479,7 @@ int rc; uint_t times_mask; smb_attr_t tmp_attr; + smb_node_t *unnamed_node; SMB_NODE_VALID(node); @@ -1607,6 +1608,13 @@ FILE_ACTION_MODIFIED, node->od_name); } + if ((unnamed_node = SMB_IS_STREAM(node)) != NULL) { + ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC); + ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING); + smb_node_notify_change(node->n_dnode, + FILE_ACTION_MODIFIED_STREAM, node->od_name); + } + return (0); }
--- a/usr/src/uts/common/fs/smbsrv/smb_user.c Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/uts/common/fs/smbsrv/smb_user.c Sat Jan 16 16:59:32 2021 +0000 @@ -205,6 +205,8 @@ #include <sys/types.h> #include <sys/sid.h> #include <sys/priv_names.h> +#include <sys/priv.h> +#include <sys/policy.h> #include <smbsrv/smb_kproto.h> #include <smbsrv/smb_door.h> @@ -831,6 +833,45 @@ #endif /* _KERNEL */ /* + * Determines whether a user can be granted ACCESS_SYSTEM_SECURITY + */ +boolean_t +smb_user_has_security_priv(smb_user_t *user, cred_t *cr) +{ + /* Need SeSecurityPrivilege to get/set SACL */ + if ((user->u_privileges & SMB_USER_PRIV_SECURITY) != 0) + return (B_TRUE); + +#ifdef _KERNEL + /* + * ACCESS_SYSTEM_SECURITY is also granted if the file is opened with + * BACKUP/RESTORE intent by a user with BACKUP/RESTORE privilege, + * which means we'll be using u_privcred. + * + * We translate BACKUP as DAC_READ and RESTORE as DAC_WRITE, + * to account for our various SMB_USER_* privileges. + */ + if (PRIV_POLICY_ONLY(cr, + priv_getbyname(PRIV_FILE_DAC_READ, 0), B_FALSE) || + PRIV_POLICY_ONLY(cr, + priv_getbyname(PRIV_FILE_DAC_WRITE, 0), B_FALSE)) + return (B_TRUE); +#else + /* + * No "real" privileges in fksmbsrv, so use the SMB privs instead. + */ + if ((user->u_privileges & + (SMB_USER_PRIV_BACKUP | + SMB_USER_PRIV_RESTORE | + SMB_USER_PRIV_READ_FILE | + SMB_USER_PRIV_WRITE_FILE)) != 0) + return (B_TRUE); +#endif + + return (B_FALSE); +} + +/* * Private function to support smb_user_enum. */ static int
--- a/usr/src/uts/common/smbsrv/ndl/netlogon.ndl Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/uts/common/smbsrv/ndl/netlogon.ndl Sat Jan 16 16:59:32 2021 +0000 @@ -22,7 +22,7 @@ * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * - * Copyright 2018 Nexenta Systems, Inc. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ #ifndef _MLSVC_NETR_NDL_ @@ -71,6 +71,8 @@ #define NETR_OPNUM_LogonGetDomainInfo 0x1D #define NETR_OPNUM_ServerPasswordSet2 0x1E +#define NETR_OPNUM_SamLogonEx 0x27 + /* * This is not a real NETR OPNUM. It's used to unpack the * struct krb5_validation_info found in the Kerberos PAC. @@ -302,6 +304,7 @@ }; +FAKE struct netr_login_info { WORD logon_level; WORD switch_value; @@ -410,6 +413,19 @@ OUT DWORD status; }; +ALIGN(2) +OPERATION(NETR_OPNUM_SamLogonEx) +struct netr_SamLogonEx { + IN LPTSTR servername; + IN LPTSTR hostname; + IN struct netr_login_info logon_info; + INOUT WORD validation_level; + SWITCH(validation_level) + OUT union netr_validation_u ru; + OUT DWORD authoritative; + INOUT DWORD extra_flags; + OUT DWORD status; +}; /* *********************************************************************** @@ -442,6 +458,8 @@ struct netr_ServerAuthenticate2 ServerAuthenticate2; CASE(NETR_OPNUM_SamLogon) struct netr_SamLogon SamLogon; + CASE(NETR_OPNUM_SamLogonEx) + struct netr_SamLogonEx SamLogonEx; CASE(NETR_OPNUM_SamLogoff) struct netr_SamLogoff SamLogoff; CASE(NETR_OPNUM_ServerPasswordSet)
--- a/usr/src/uts/common/smbsrv/netrauth.h Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/uts/common/smbsrv/netrauth.h Sat Jan 16 16:59:32 2021 +0000 @@ -22,7 +22,7 @@ * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * - * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ #ifndef _SMBSRV_NETRAUTH_H @@ -51,8 +51,61 @@ /* * Negotiation flags for challenge/response authentication. */ -#define NETR_NEGOTIATE_BASE_FLAGS 0x000001FF -#define NETR_NEGOTIATE_STRONGKEY_FLAG 0x00004000 +#define NETR_NEGO_UNUSED_A_FLAG 0x00000001 +#define NETR_NEGO_BDC_UPDATE_FLAG 0x00000002 +#define NETR_NEGO_RC4_ENCRYPT_FLAG 0x00000004 +#define NETR_NEGO_UNUSED_D_FLAG 0x00000008 + +#define NETR_NEGO_BDC_CHANGELOG_FLAG 0x00000010 +#define NETR_NEGO_DC_RESTART_SYNC_FLAG 0x00000020 +#define NETR_NEGO_VALID2_NOTREQUIRED_FLAG 0x00000040 +#define NETR_NEGO_OPNUM17_FLAG 0x00000080 + +#define NETR_NEGO_PWCHANGE_REFUSE_FLAG 0x00000100 +#define NETR_NEGO_OPNUM32_FLAG 0x00000200 +#define NETR_NEGO_GENERIC_PASSTHRU_FLAG 0x00000400 +#define NETR_NEGO_CONCURRENT_RPC_FLAG 0x00000800 + +#define NETR_NEGO_AVOID_USERDB_REPL_FLAG 0x00001000 +#define NETR_NEGO_AVOID_SECURITYDB_REPL_FLAG 0x00002000 +#define NETR_NEGO_STRONGKEY_FLAG 0x00004000 +#define NETR_NEGO_TRANSITIVE_TRUSTFLAG 0x00008000 + +#define NETR_NEGO_UNUSED_Q_FLAG 0x00010000 +#define NETR_NEGO_PASSWORDSET2_FLAG 0x00020000 +#define NETR_NEGO_GETDOMAININFO_FLAG 0x00040000 +#define NETR_NEGO_CROSS_FOREST_TRUST_FLAG 0x00080000 + +#define NETR_NEGO_IGNORE_NT4EMULATOR_FLAG 0x00100000 +#define NETR_NEGO_RODC_PASSTHRU_FLAG 0x00200000 +#define NETR_NEGO_UNDEFINED_9_FLAG 0x00400000 +#define NETR_NEGO_UNDEFINED_8_FLAG 0x00800000 + +#define NETR_NEGO_AES_SHA2_FLAG 0x01000000 +#define NETR_NEGO_UNDEFINED_6_FLAG 0x02000000 +#define NETR_NEGO_UNDEFINED_5_FLAG 0x04000000 +#define NETR_NEGO_UNDEFINED_4_FLAG 0x08000000 + +#define NETR_NEGO_UNDEFINED_3_FLAG 0x10000000 +#define NETR_NEGO_UNUSED_X_FLAG 0x20000000 +#define NETR_NEGO_SECURE_RPC_FLAG 0x40000000 +#define NETR_NEGO_UNDEFINED_0_FLAG 0x80000000 + +/* + * TODO: This needs review - some of these are inappropriate. + * I.E. BDC_UPDATE, BDC_CHANGELOG, and DC_RESTART_SYNC are + * server-to-server only, but we implement a client. + */ +#define NETR_NEGO_BASE_FLAGS ( \ + NETR_NEGO_UNUSED_A_FLAG | \ + NETR_NEGO_BDC_UPDATE_FLAG | \ + NETR_NEGO_RC4_ENCRYPT_FLAG | \ + NETR_NEGO_UNUSED_D_FLAG | \ + NETR_NEGO_BDC_CHANGELOG_FLAG | \ + NETR_NEGO_DC_RESTART_SYNC_FLAG | \ + NETR_NEGO_VALID2_NOTREQUIRED_FLAG | \ + NETR_NEGO_OPNUM17_FLAG | \ + NETR_NEGO_PWCHANGE_REFUSE_FLAG) \ #define NETR_SESSKEY64_SZ 8 #define NETR_SESSKEY128_SZ 16 @@ -123,6 +176,8 @@ DWORD flags; char server[MAXHOSTNAMELEN]; /* Current DC, FQDN */ char hostname[NETBIOS_NAME_SZ * 2]; /* local "flat" name */ + char nb_domain[NETBIOS_NAME_SZ * 2]; /* Current NetBios domain */ + char fqdn_domain[MAXHOSTNAMELEN]; /* Current Domain */ netr_cred_t client_challenge; netr_cred_t server_challenge; netr_cred_t client_credential; @@ -130,6 +185,10 @@ netr_session_key_t session_key; BYTE password[NETR_MACHINE_ACCT_PASSWD_MAX]; time_t timestamp; + uint64_t clh_seqnum; /* Client SequenceNumber for Netlogon SSP */ + DWORD nego_flags; /* Negotiated flags returned from ServerAuthenciate */ + boolean_t use_secure_rpc; /* Use "SecureRPC" (RPC-level auth) */ + boolean_t use_logon_ex; /* Use SamLogonEx (instead of SamLogon) */ } netr_info_t; /* @@ -138,8 +197,10 @@ int netr_gen_skey64(netr_info_t *); int netr_gen_skey128(netr_info_t *); -int netr_gen_credentials(BYTE *, netr_cred_t *, DWORD, netr_cred_t *); +int netr_gen_credentials(BYTE *, netr_cred_t *, DWORD, netr_cred_t *, + boolean_t); +void netlogon_init_global(uint32_t); #define NETR_A2H(c) (isdigit(c)) ? ((c) - '0') : ((c) - 'A' + 10)
--- a/usr/src/uts/common/smbsrv/smb_fsops.h Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/uts/common/smbsrv/smb_fsops.h Sat Jan 16 16:59:32 2021 +0000 @@ -54,6 +54,10 @@ int smb_fsop_create(smb_request_t *, cred_t *, smb_node_t *, char *, smb_attr_t *, smb_node_t **); +int +smb_fsop_create_stream(smb_request_t *, cred_t *, smb_node_t *, smb_node_t *, + char *, int, smb_attr_t *, smb_node_t **); + int smb_fsop_mkdir(smb_request_t *, cred_t *, smb_node_t *, char *, smb_attr_t *, smb_node_t **); @@ -93,12 +97,18 @@ void smb_fsop_eaccess(smb_request_t *, cred_t *, smb_node_t *, uint32_t *); +int smb_fsop_lookup_file(smb_request_t *, cred_t *, int, + smb_node_t *, smb_node_t *, char *, char **, smb_node_t **); + int smb_fsop_lookup_name(smb_request_t *, cred_t *, int, smb_node_t *, smb_node_t *, char *, smb_node_t **); int smb_fsop_lookup(smb_request_t *, cred_t *, int, smb_node_t *, smb_node_t *, char *, smb_node_t **); +int smb_fsop_lookup_stream(smb_request_t *, cred_t *, int, smb_node_t *, + smb_node_t *, char *, smb_node_t **); + int smb_fsop_commit(smb_request_t *, cred_t *, smb_node_t *); int smb_fsop_aclread(smb_request_t *, cred_t *, smb_node_t *, smb_fssd_t *);
--- a/usr/src/uts/common/smbsrv/smb_kproto.h Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/uts/common/smbsrv/smb_kproto.h Sat Jan 16 16:59:32 2021 +0000 @@ -739,6 +739,7 @@ cred_t *smb_kcred_create(void); void smb_user_setcred(smb_user_t *, cred_t *, uint32_t); boolean_t smb_is_same_user(cred_t *, cred_t *); +boolean_t smb_user_has_security_priv(smb_user_t *, cred_t *); /* * SMB tree functions (file smb_tree.c)
--- a/usr/src/uts/common/smbsrv/smbinfo.h Thu Jan 14 17:27:56 2021 +0100 +++ b/usr/src/uts/common/smbsrv/smbinfo.h Sat Jan 16 16:59:32 2021 +0000 @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2018 Nexenta Systems, Inc. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. * Copyright 2020 RackTop Systems, Inc. */ @@ -120,6 +120,8 @@ #define SMB_PI_MAXIMUM_CREDITS_DEF 1000 #define SMB_PI_MAXIMUM_CREDITS_MAX 1024 +#define SMB_PI_NETLOGON_FLAGS_DEFAULT 0x00000000 + /* * sv_size is used by the RPC services and should be set to * sizeof (smb_version_t).