diff options
author | Dan McDonald <danmcd@mnx.io> | 2022-10-17 17:45:31 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-17 17:45:31 -0400 |
commit | 0304e418633be64468218263192782a12da919a2 (patch) | |
tree | a305a88a3fb813163a34c393e4ec6742c519c038 | |
parent | a41cec58980057a54ffdf464a2b2d381d819b975 (diff) | |
parent | 4012c8b05c5f0ba3a55d5f171a8906ec60b60076 (diff) | |
download | illumos-joyent-OS-8418.tar.gz |
Merge branch 'master' into OS-8418OS-8418
-rw-r--r-- | usr/src/cmd/bhyve/rfb.c | 25 | ||||
-rw-r--r-- | usr/src/cmd/bhyve/sockstream.c | 8 | ||||
-rw-r--r-- | usr/src/lib/libsec/common/aclutils.c | 153 | ||||
-rw-r--r-- | usr/src/uts/common/fs/smbsrv/smb2_fsctl_fs.c | 25 | ||||
-rw-r--r-- | usr/src/uts/common/fs/smbsrv/smb2_fsctl_odx.c | 5 | ||||
-rw-r--r-- | usr/src/uts/common/fs/smbsrv/smb2_negotiate.c | 428 | ||||
-rw-r--r-- | usr/src/uts/common/fs/smbsrv/smb2_qinfo_quota.c | 14 | ||||
-rw-r--r-- | usr/src/uts/common/fs/smbsrv/smb2_setinfo_quota.c | 17 | ||||
-rw-r--r-- | usr/src/uts/common/smb/winioctl.h | 4 | ||||
-rw-r--r-- | usr/src/uts/common/smbsrv/smb_ktypes.h | 14 |
10 files changed, 374 insertions, 319 deletions
diff --git a/usr/src/cmd/bhyve/rfb.c b/usr/src/cmd/bhyve/rfb.c index c58fd2d6cd..576b66c0e8 100644 --- a/usr/src/cmd/bhyve/rfb.c +++ b/usr/src/cmd/bhyve/rfb.c @@ -193,8 +193,10 @@ fast_crc32(void *buf, int len, uint32_t crcval) static void rfb_send_client_status(rfb_client_t *c, uint32_t status, const char *msg) { - status = htonl(status); + rfb_printf(c, RFB_LOGDEBUG, "sending client status %u (%s)", + status, msg ? msg : "NULL"); + status = htonl(status); (void) stream_write(c->rc_fd, &status, sizeof (status)); if (msg != NULL && status != 0 && c->rc_cver == RFB_CVER_3_8) { @@ -214,7 +216,7 @@ rfb_handshake_version(rfb_client_t *c) unsigned char buf[RFB_VERSION_LEN]; ssize_t l; - rfb_printf(c, RFB_LOGDEBUG, "handshake version"); + rfb_printf(c, RFB_LOGDEBUG, "handshake version"); if (stream_write(c->rc_fd, RFB_VERSION, RFB_VERSION_LEN) != RFB_VERSION_LEN) { @@ -368,12 +370,19 @@ rfb_handshake_auth(rfb_client_t *c) /* Initialize a 16-byte random challenge. */ arc4random_buf(challenge, sizeof (challenge)); - (void) stream_write(c->rc_fd, challenge, RFBP_SECURITY_VNC_AUTH_LEN); + + /* Send the challenge to the client. */ + if (stream_write(c->rc_fd, challenge, RFBP_SECURITY_VNC_AUTH_LEN) + != RFBP_SECURITY_VNC_AUTH_LEN) { + rfb_printf(c, RFB_LOGERR, + "failed to send challenge to client"); + return (false); + } /* Receive the 16-byte challenge response. */ if (stream_read(c->rc_fd, buf, RFBP_SECURITY_VNC_AUTH_LEN) != RFBP_SECURITY_VNC_AUTH_LEN) { - rfb_send_client_status(c, 1, "response read failed"); + rfb_send_client_status(c, 1, "Challenge response read failed"); return (false); } @@ -1216,8 +1225,10 @@ rfb_client_tx_thread(void *arg) c->rc_sinfo.rsi_pixfmt = c->rc_s->rs_pixfmt; c->rc_encodings = RFB_ENCODING_RAW; - if (!rfb_handshake(c)) + if (!rfb_handshake(c)) { + rfb_printf(c, RFB_LOGWARN, "handshake failure"); goto out; + } c->rc_cells = howmany(RFB_MAX_WIDTH * RFB_MAX_HEIGHT, RFB_PIX_PER_CELL); if ((c->rc_crc = calloc(c->rc_cells, sizeof (uint32_t))) == NULL || @@ -1275,10 +1286,10 @@ rfb_client_tx_thread(void *arg) } } - rfb_printf(c, RFB_LOGWARN, "disconnected"); - out: + rfb_printf(c, RFB_LOGWARN, "disconnected"); + (void) pthread_join(c->rc_rx_tid, &status); pthread_mutex_lock(&s->rs_clientlock); s->rs_clientcount--; diff --git a/usr/src/cmd/bhyve/sockstream.c b/usr/src/cmd/bhyve/sockstream.c index b592bce9aa..d8d9966cfb 100644 --- a/usr/src/cmd/bhyve/sockstream.c +++ b/usr/src/cmd/bhyve/sockstream.c @@ -34,6 +34,10 @@ __FBSDID("$FreeBSD$"); #include <sys/types.h> #include <unistd.h> +#ifndef __FreeBSD__ +#include <sys/socket.h> +#endif + #include <errno.h> #include "sockstream.h" @@ -72,7 +76,11 @@ stream_write(int fd, const void *buf, ssize_t nbytes) p = buf; while (len < nbytes) { +#ifdef __FreeBSD__ n = write(fd, p + len, nbytes - len); +#else + n = send(fd, p + len, nbytes - len, MSG_NOSIGNAL); +#endif if (n == 0) break; if (n < 0) { diff --git a/usr/src/lib/libsec/common/aclutils.c b/usr/src/lib/libsec/common/aclutils.c index ea91ddb96e..5a67ca1397 100644 --- a/usr/src/lib/libsec/common/aclutils.c +++ b/usr/src/lib/libsec/common/aclutils.c @@ -746,64 +746,95 @@ acl_error(const char *fmt, ...) va_end(va); } -int -sid_to_id(char *sid, boolean_t user, uid_t *id) +typedef enum id_type { + UID_TYPE, + GID_TYPE, + PID_TYPE +} id_type_t; + +static int +sid_to_id_impl(char *sid, id_type_t type, int *is_user, uid_t *id) { - idmap_get_handle_t *get_hdl = NULL; - char *rid_start = NULL; + idmap_get_handle_t *get_hdl; + char *rid_start; + idmap_stat rv; + idmap_rid_t rid; + const char *errstr; idmap_stat status; - char *end; int error = 1; + + rid_start = strrchr(sid, '-'); + if (rid_start == NULL) + return (error); + + rid = strtonum(rid_start + 1, 0, UINT32_MAX, &errstr); + if (errstr != NULL) + return (error); + + if (idmap_get_create(&get_hdl) != IDMAP_SUCCESS) + return (error); + + /* + * When these functions return success, the &status output is + * indeterminate. We only care about rv==success in this caller, + * so just ignore &status. + */ + /* We need sid prefix. Insert NUL on '-', restore it later. */ + *rid_start = '\0'; + switch (type) { + case UID_TYPE: + rv = idmap_get_uidbysid(get_hdl, + sid, rid, IDMAP_REQ_FLG_USE_CACHE, + id, &status); + break; + + case GID_TYPE: + rv = idmap_get_gidbysid(get_hdl, + sid, rid, IDMAP_REQ_FLG_USE_CACHE, + id, &status); + break; + + case PID_TYPE: + rv = idmap_get_pidbysid(get_hdl, sid, rid, + IDMAP_REQ_FLG_USE_CACHE, id, is_user, + &status); + break; + } + + *rid_start = '-'; /* putback character removed earlier */ + if (rv == IDMAP_SUCCESS && + idmap_get_mappings(get_hdl) == IDMAP_SUCCESS) { + error = 0; + } + idmap_get_destroy(get_hdl); + + return (error); +} + +int +sid_to_id(char *sid, boolean_t user, uid_t *id) +{ char *domain_start; + int error = 1; if ((domain_start = strchr(sid, '@')) == NULL) { - idmap_rid_t rid; - - if ((rid_start = strrchr(sid, '-')) == NULL) - return (1); - *rid_start++ = '\0'; - errno = 0; - rid = strtoul(rid_start--, &end, 10); - if (errno == 0 && *end == '\0') { - if (idmap_get_create(&get_hdl) == - IDMAP_SUCCESS) { - if (user) - error = idmap_get_uidbysid(get_hdl, - sid, rid, IDMAP_REQ_FLG_USE_CACHE, - id, &status); - else - error = idmap_get_gidbysid(get_hdl, - sid, rid, IDMAP_REQ_FLG_USE_CACHE, - id, &status); - if (error == IDMAP_SUCCESS) { - error = idmap_get_mappings(get_hdl); - if (error == IDMAP_SUCCESS && - status != IDMAP_SUCCESS) - error = 1; - else - error = 0; - } - } else { - error = 1; - } - if (get_hdl) - idmap_get_destroy(get_hdl); - } else { - error = 1; - } - *rid_start = '-'; /* putback character removed earlier */ + error = sid_to_id_impl(sid, user ? UID_TYPE : GID_TYPE, + NULL, id); } else { char *name = sid; + idmap_stat rv; + *domain_start++ = '\0'; if (user) - error = idmap_getuidbywinname(name, domain_start, + rv = idmap_getuidbywinname(name, domain_start, IDMAP_REQ_FLG_USE_CACHE, id); else - error = idmap_getgidbywinname(name, domain_start, + rv = idmap_getgidbywinname(name, domain_start, IDMAP_REQ_FLG_USE_CACHE, id); *--domain_start = '@'; - error = (error == IDMAP_SUCCESS) ? 0 : 1; + if (rv == IDMAP_SUCCESS) + error = 0; } return (error); @@ -817,42 +848,8 @@ sid_to_id(char *sid, boolean_t user, uid_t *id) int sid_to_xid(char *sid, int *is_user, uid_t *id) { - idmap_get_handle_t *get_hdl = NULL; - char *rid_start = NULL; - char *end; - idmap_stat status; - idmap_rid_t rid; - int error = 1; - if ((strchr(sid, '@')) != NULL) return (1); - if ((rid_start = strrchr(sid, '-')) == NULL) - return (1); - *rid_start++ = '\0'; - errno = 0; - rid = strtoul(rid_start--, &end, 10); - if (errno == 0 && *end == '\0') { - if (idmap_get_create(&get_hdl) == IDMAP_SUCCESS) { - error = idmap_get_pidbysid(get_hdl, - sid, rid, IDMAP_REQ_FLG_USE_CACHE, - id, is_user, &status); - if (error == IDMAP_SUCCESS) { - error = idmap_get_mappings(get_hdl); - if (error == IDMAP_SUCCESS && - status != IDMAP_SUCCESS) - error = 1; - else - error = 0; - } - } else { - error = 1; - } - if (get_hdl) - idmap_get_destroy(get_hdl); - } - - *rid_start = '-'; /* putback character removed earlier */ - - return (error); + return (sid_to_id_impl(sid, PID_TYPE, is_user, id)); } diff --git a/usr/src/uts/common/fs/smbsrv/smb2_fsctl_fs.c b/usr/src/uts/common/fs/smbsrv/smb2_fsctl_fs.c index 381fd7663e..d6da839e05 100644 --- a/usr/src/uts/common/fs/smbsrv/smb2_fsctl_fs.c +++ b/usr/src/uts/common/fs/smbsrv/smb2_fsctl_fs.c @@ -11,6 +11,7 @@ /* * Copyright 2019 Nexenta by DDN, Inc. All rights reserved. + * Copyright 2022 RackTop Systems, Inc. */ /* @@ -23,10 +24,6 @@ #include <smbsrv/smb_fsops.h> #include <smb/winioctl.h> -/* - * XXX: Should use smb2_fsctl_invalid in place of smb2_fsctl_notsup - * but that will require some re-testing. - */ static uint32_t smb2_fsctl_invalid(smb_request_t *sr, smb_fsctl_t *fsctl) { @@ -40,7 +37,7 @@ smb2_fsctl_notsup(smb_request_t *sr, smb_fsctl_t *fsctl) } /* - * Same as smb2_fsctl_notsup, but make some noise (if DEBUG) + * Same as smb2_fsctl_invalid, but make some noise (if DEBUG) * so we'll learn about new fsctl codes clients start using. */ /* ARGSUSED */ @@ -50,7 +47,7 @@ smb2_fsctl_unknown(smb_request_t *sr, smb_fsctl_t *fsctl) #ifdef DEBUG cmn_err(CE_NOTE, "smb2_fsctl_unknown: code 0x%x", fsctl->CtlCode); #endif - return (NT_STATUS_NOT_SUPPORTED); + return (NT_STATUS_INVALID_DEVICE_REQUEST); } /* @@ -145,7 +142,7 @@ smb2_fsctl_fs(smb_request_t *sr, smb_fsctl_t *fsctl) break; case FSCTL_SET_REPARSE_POINT: /* 41 */ case FSCTL_GET_REPARSE_POINT: /* 42 */ - func = smb2_fsctl_notsup; + func = smb2_fsctl_invalid; break; case FSCTL_CREATE_OR_GET_OBJECT_ID: /* 48 */ func = smb2_fsctl_invalid; @@ -160,7 +157,7 @@ smb2_fsctl_fs(smb_request_t *sr, smb_fsctl_t *fsctl) func = smb2_fsctl_query_alloc_ranges; break; case FSCTL_FILE_LEVEL_TRIM: /* 130 */ - func = smb2_fsctl_notsup; + func = smb2_fsctl_invalid; break; case FSCTL_OFFLOAD_READ: /* 153 */ func = smb2_fsctl_odx_read; @@ -168,13 +165,19 @@ smb2_fsctl_fs(smb_request_t *sr, smb_fsctl_t *fsctl) case FSCTL_OFFLOAD_WRITE: /* 154 */ func = smb2_fsctl_odx_write; break; + case FSCTL_GET_INTEGRITY_INFORMATION: /* 159 */ case FSCTL_SET_INTEGRITY_INFORMATION: /* 160 */ - func = smb2_fsctl_notsup; + func = smb2_fsctl_invalid; break; case FSCTL_QUERY_FILE_REGIONS: /* 161 */ func = smb2_fsctl_query_file_regions; break; + case FSCTL_REFS_STREAM_SNAPSHOT_MANAGEMENT: + /* WPTS wants NOT_SUPPORTED here. */ + func = smb2_fsctl_notsup; + break; + default: func = smb2_fsctl_unknown; break; @@ -213,14 +216,14 @@ smb2_fsctl_netfs(smb_request_t *sr, smb_fsctl_t *fsctl) func = smb2_fsctl_copychunk; break; case FSCTL_SRV_READ_HASH: /* 0x6e */ - func = smb2_fsctl_notsup; + func = smb2_fsctl_invalid; break; case FSCTL_LMR_REQUEST_RESILIENCY: /* 0x75 */ func = smb2_fsctl_set_resilient; break; case FSCTL_QUERY_NETWORK_INTERFACE_INFO: /* 0x7f */ need_disk_file = B_FALSE; - func = smb2_fsctl_notsup; + func = smb2_fsctl_invalid; break; case FSCTL_VALIDATE_NEGOTIATE_INFO: /* 0x81 */ need_disk_file = B_FALSE; diff --git a/usr/src/uts/common/fs/smbsrv/smb2_fsctl_odx.c b/usr/src/uts/common/fs/smbsrv/smb2_fsctl_odx.c index ebf59f6a59..cc37664ffb 100644 --- a/usr/src/uts/common/fs/smbsrv/smb2_fsctl_odx.c +++ b/usr/src/uts/common/fs/smbsrv/smb2_fsctl_odx.c @@ -11,6 +11,7 @@ /* * Copyright 2018-2021 Tintri by DDN, Inc. All rights reserved. + * Copyright 2022 RackTop Systems, Inc. */ /* @@ -197,7 +198,7 @@ smb2_fsctl_odx_read(smb_request_t *sr, smb_fsctl_t *fsctl) int rc; if (smb2_odx_enable == 0) - return (NT_STATUS_NOT_SUPPORTED); + return (NT_STATUS_INVALID_DEVICE_REQUEST); /* * Make sure the (src) ofile granted access allows read. @@ -391,7 +392,7 @@ smb2_fsctl_odx_write(smb_request_t *sr, smb_fsctl_t *fsctl) args.out_struct_size = 16; if (smb2_odx_enable == 0) - return (NT_STATUS_NOT_SUPPORTED); + return (NT_STATUS_INVALID_DEVICE_REQUEST); /* * Make sure the (dst) ofile granted_access allows write. diff --git a/usr/src/uts/common/fs/smbsrv/smb2_negotiate.c b/usr/src/uts/common/fs/smbsrv/smb2_negotiate.c index 7d67247588..473115e179 100644 --- a/usr/src/uts/common/fs/smbsrv/smb2_negotiate.c +++ b/usr/src/uts/common/fs/smbsrv/smb2_negotiate.c @@ -11,7 +11,7 @@ /* * Copyright 2019 Nexenta Systems, Inc. All rights reserved. - * Copyright 2021 RackTop Systems, Inc. + * Copyright 2022 RackTop Systems, Inc. */ /* @@ -22,6 +22,14 @@ #include <smbsrv/smb2.h> #include <sys/random.h> +/* + * Note from [MS-SMB2] Sec. 2.2.3: Windows servers return + * invalid parameter if the dialect count is greater than 64 + * This is here (and not in smb2.h) because this is technically + * an implementation detail, not protocol specification. + */ +#define SMB2_NEGOTIATE_MAX_DIALECTS 64 + static int smb2_negotiate_common(smb_request_t *, uint16_t); /* List of supported capabilities. Can be patched for testing. */ @@ -83,127 +91,6 @@ static uint16_t smb2_versions[] = { static uint16_t smb2_nversions = sizeof (smb2_versions) / sizeof (smb2_versions[0]); -static boolean_t -smb2_supported_version(smb_session_t *s, uint16_t version) -{ - int i; - - if (version > s->s_cfg.skc_max_protocol || - version < s->s_cfg.skc_min_protocol) - return (B_FALSE); - for (i = 0; i < smb2_nversions; i++) - if (version == smb2_versions[i]) - return (B_TRUE); - return (B_FALSE); -} - -/* - * Helper for the (SMB1) smb_com_negotiate(). This is the - * very unusual protocol interaction where an SMB1 negotiate - * gets an SMB2 negotiate response. This is the normal way - * clients first find out if the server supports SMB2. - * - * Note: This sends an SMB2 reply _itself_ and then returns - * SDRC_NO_REPLY so the caller will not send an SMB1 reply. - * Also, this is called directly from the reader thread, so - * we know this is the only thread using this session. - * - * The caller frees this request. - */ -smb_sdrc_t -smb1_negotiate_smb2(smb_request_t *sr) -{ - smb_session_t *s = sr->session; - smb_arg_negotiate_t *negprot = sr->sr_negprot; - uint16_t smb2_version; - - /* - * Note: In the SMB1 negotiate command handler, we - * agreed with one of the SMB2 dialects. If that - * dialect was "SMB 2.002", we'll respond here with - * version 0x202 and negotiation is done. If that - * dialect was "SMB 2.???", we'll respond here with - * the "wildcard" version 0x2FF, and the client will - * come back with an SMB2 negotiate. - */ - switch (negprot->ni_dialect) { - case DIALECT_SMB2002: /* SMB 2.002 (a.k.a. SMB2.0) */ - smb2_version = SMB_VERS_2_002; - s->dialect = smb2_version; - s->s_state = SMB_SESSION_STATE_NEGOTIATED; - /* Allow normal SMB2 requests now. */ - s->newrq_func = smb2sr_newrq; - break; - case DIALECT_SMB2XXX: /* SMB 2.??? (wildcard vers) */ - /* - * Expecting an SMB2 negotiate next, so keep the - * initial s->newrq_func. - */ - smb2_version = 0x2FF; - break; - default: - return (SDRC_DROP_VC); - } - - /* - * We did not decode an SMB2 header, so make sure - * the SMB2 header fields are initialized. - * (Most are zero from smb_request_alloc.) - * Also, the SMB1 common dispatch code reserved space - * for an SMB1 header, which we need to undo here. - */ - sr->smb2_reply_hdr = sr->reply.chain_offset = 0; - sr->smb2_cmd_code = SMB2_NEGOTIATE; - sr->smb2_hdr_flags = SMB2_FLAGS_SERVER_TO_REDIR; - - (void) smb2_encode_header(sr, B_FALSE); - if (smb2_negotiate_common(sr, smb2_version) != 0) - sr->smb2_status = NT_STATUS_INTERNAL_ERROR; - if (sr->smb2_status != 0) - smb2sr_put_error(sr, sr->smb2_status); - (void) smb2_encode_header(sr, B_TRUE); - - smb2_send_reply(sr); - - /* - * We sent the reply, so tell the SMB1 dispatch - * it should NOT (also) send a reply. - */ - return (SDRC_NO_REPLY); -} - -static uint16_t -smb2_find_best_dialect(smb_session_t *s, uint16_t cl_versions[], - uint16_t version_cnt) -{ - uint16_t best_version = 0; - int i; - - for (i = 0; i < version_cnt; i++) - if (smb2_supported_version(s, cl_versions[i]) && - best_version < cl_versions[i]) - best_version = cl_versions[i]; - - return (best_version); -} - -/* - * SMB2 Negotiate gets special handling. This is called directly by - * the reader thread (see smbsr_newrq_initial) with what _should_ be - * an SMB2 Negotiate. Only the "\feSMB" header has been checked - * when this is called, so this needs to check the SMB command, - * if it's Negotiate execute it, then send the reply, etc. - * - * Since this is called directly from the reader thread, we - * know this is the only thread currently using this session. - * This has to duplicate some of what smb2sr_work does as a - * result of bypassing the normal dispatch mechanism. - * - * The caller always frees this request. - * - * Return value is 0 for success, and anything else will - * terminate the reader thread (drop the connection). - */ enum smb2_neg_ctx_type { SMB2_PREAUTH_INTEGRITY_CAPS = 1, SMB2_ENCRYPTION_CAPS = 2, @@ -272,6 +159,44 @@ typedef struct smb2_neg_ctxs { #define SMB3_CIPHER_ENABLED(c, f) ((c) <= SMB3_CIPHER_MAX && \ SMB3_CIPHER_BIT(c) & (f)) +typedef struct smb2_arg_negotiate { + struct smb2_neg_ctxs neg_in_ctxs; + struct smb2_neg_ctxs neg_out_ctxs; + uint16_t neg_dialect_cnt; + uint16_t neg_dialects[SMB2_NEGOTIATE_MAX_DIALECTS]; + uint16_t neg_highest_dialect; +} smb2_arg_negotiate_t; + + +static boolean_t +smb2_supported_version(smb_session_t *s, uint16_t version) +{ + int i; + + if (version > s->s_cfg.skc_max_protocol || + version < s->s_cfg.skc_min_protocol) + return (B_FALSE); + for (i = 0; i < smb2_nversions; i++) + if (version == smb2_versions[i]) + return (B_TRUE); + return (B_FALSE); +} + +static uint16_t +smb2_find_best_dialect(smb_session_t *s, uint16_t cl_versions[], + uint16_t version_cnt) +{ + uint16_t best_version = 0; + int i; + + for (i = 0; i < version_cnt; i++) + if (smb2_supported_version(s, cl_versions[i]) && + best_version < cl_versions[i]) + best_version = cl_versions[i]; + + return (best_version); +} + /* * This function should be called only for dialect >= 0x311 * Negotiate context list should contain exactly one @@ -281,9 +206,11 @@ typedef struct smb2_neg_ctxs { * Otehrwise STATUS_SMB_NO_PREAUTH_INEGRITY_HASH_OVERLAP. */ static uint32_t -smb31_decode_neg_ctxs(smb_request_t *sr, smb2_neg_ctxs_t *neg_ctxs) +smb31_decode_neg_ctxs(smb_request_t *sr) { smb_session_t *s = sr->session; + smb2_arg_negotiate_t *nego = sr->arg.other; + smb2_neg_ctxs_t *neg_ctxs = &nego->neg_in_ctxs; smb2_preauth_caps_t *picap = &neg_ctxs->preauth_ctx.preauth_caps; smb2_encrypt_caps_t *encap = &neg_ctxs->encrypt_ctx.encrypt_caps; boolean_t found_sha512 = B_FALSE; @@ -296,15 +223,6 @@ smb31_decode_neg_ctxs(smb_request_t *sr, smb2_neg_ctxs_t *neg_ctxs) int cnt, i; int rc; - sr->command.chain_offset = NEG_CTX_INFO_OFFSET; - - rc = smb_mbc_decodef(&sr->command, "lw2.", - &neg_ctxs->offset, /* l */ - &neg_ctxs->count); /* w */ - if (rc != 0) { - status = NT_STATUS_INVALID_PARAMETER; - goto errout; - } /* * There should be exactly 1 SMB2_PREAUTH_INTEGRITY_CAPS negotiate ctx. * SMB2_ENCRYPTION_CAPS is optional one. @@ -497,9 +415,11 @@ errout: } static int -smb31_encode_neg_ctxs(smb_request_t *sr, smb2_neg_ctxs_t *neg_ctxs) +smb31_encode_neg_ctxs(smb_request_t *sr) { smb_session_t *s = sr->session; + smb2_arg_negotiate_t *nego = sr->arg.other; + smb2_neg_ctxs_t *neg_ctxs = &nego->neg_out_ctxs; smb2_preauth_caps_t *picap = &neg_ctxs->preauth_ctx.preauth_caps; smb2_encrypt_caps_t *encap = &neg_ctxs->encrypt_ctx.encrypt_caps; uint16_t salt_len = sizeof (picap->picap_salt); @@ -561,20 +481,121 @@ smb31_encode_neg_ctxs(smb_request_t *sr, smb2_neg_ctxs_t *neg_ctxs) return (rc); } +/* + * Helper for the (SMB1) smb_com_negotiate(). This is the + * very unusual protocol interaction where an SMB1 negotiate + * gets an SMB2 negotiate response. This is the normal way + * clients first find out if the server supports SMB2. + * + * Note: This sends an SMB2 reply _itself_ and then returns + * SDRC_NO_REPLY so the caller will not send an SMB1 reply. + * Also, this is called directly from the reader thread, so + * we know this is the only thread using this session. + * Otherwise, this is similar to smb2_newrq_negotiate(). + * + * The caller frees this request. + */ +smb_sdrc_t +smb1_negotiate_smb2(smb_request_t *sr) +{ + smb_session_t *s = sr->session; + smb_arg_negotiate_t *negprot = sr->sr_negprot; + uint16_t smb2_version; + + /* + * Note: In the SMB1 negotiate command handler, we + * agreed with one of the SMB2 dialects. If that + * dialect was "SMB 2.002", we'll respond here with + * version 0x202 and negotiation is done. If that + * dialect was "SMB 2.???", we'll respond here with + * the "wildcard" version 0x2FF, and the client will + * come back with an SMB2 negotiate. + */ + switch (negprot->ni_dialect) { + case DIALECT_SMB2002: /* SMB 2.002 (a.k.a. SMB2.0) */ + smb2_version = SMB_VERS_2_002; + s->dialect = smb2_version; + s->s_state = SMB_SESSION_STATE_NEGOTIATED; + /* Allow normal SMB2 requests now. */ + s->newrq_func = smb2sr_newrq; + break; + case DIALECT_SMB2XXX: /* SMB 2.??? (wildcard vers) */ + /* + * Expecting an SMB2 negotiate next, so keep the + * initial s->newrq_func. + */ + smb2_version = 0x2FF; + break; + default: + return (SDRC_DROP_VC); + } + + /* + * We did not decode an SMB2 header, so make sure + * the SMB2 header fields are initialized. + * (Most are zero from smb_request_alloc.) + * Also, the SMB1 common dispatch code reserved space + * for an SMB1 header, which we need to undo here. + */ + sr->smb2_reply_hdr = sr->reply.chain_offset = 0; + sr->smb2_cmd_code = SMB2_NEGOTIATE; + sr->smb2_hdr_flags = SMB2_FLAGS_SERVER_TO_REDIR; + + /* + * Also setup SMB2 negotiate args (empty here). + * SMB1 args free'd by smb_srm_fini(sr) + */ + sr->arg.other = smb_srm_zalloc(sr, sizeof (smb2_arg_negotiate_t)); + + (void) smb2_encode_header(sr, B_FALSE); + if (smb2_negotiate_common(sr, smb2_version) != 0) + sr->smb2_status = NT_STATUS_INTERNAL_ERROR; + if (sr->smb2_status != 0) + smb2sr_put_error(sr, sr->smb2_status); + (void) smb2_encode_header(sr, B_TRUE); + + smb2_send_reply(sr); + + /* + * We sent the reply, so tell the SMB1 dispatch + * it should NOT (also) send a reply. + */ + return (SDRC_NO_REPLY); +} + +/* + * SMB2 Negotiate gets special handling. This is called directly by + * the reader thread (see smbsr_newrq_initial) with what _should_ be + * an SMB2 Negotiate. Only the "\feSMB" header has been checked + * when this is called, so this needs to check the SMB command, + * if it's Negotiate execute it, then send the reply, etc. + * + * Since this is called directly from the reader thread, we + * know this is the only thread currently using this session. + * This has to duplicate some of what smb2sr_work does as a + * result of bypassing the normal dispatch mechanism. + * + * The caller always frees this request. + * + * Return value is 0 for success, and anything else will + * terminate the reader thread (drop the connection). + */ int smb2_newrq_negotiate(smb_request_t *sr) { smb_session_t *s = sr->session; - smb2_neg_ctxs_t neg_in_ctxs; - smb2_neg_ctxs_t neg_out_ctxs; - smb2_arg_negotiate_t *nego2 = &sr->sr_nego2; + smb2_arg_negotiate_t *nego; int rc; + uint32_t nctx_status = 0; uint32_t status = 0; + uint32_t neg_ctx_off; + uint16_t neg_ctx_cnt; uint16_t struct_size; + uint16_t dialect_cnt; uint16_t best_version; - bzero(&neg_in_ctxs, sizeof (neg_in_ctxs)); - bzero(&neg_out_ctxs, sizeof (neg_out_ctxs)); + nego = smb_srm_zalloc(sr, sizeof (smb2_arg_negotiate_t)); + sr->arg.other = nego; // for dtrace sr->smb2_cmd_hdr = sr->command.chain_offset; rc = smb2_decode_header(sr); @@ -592,14 +613,16 @@ smb2_newrq_negotiate(smb_request_t *sr) * Decode SMB2 Negotiate (fixed-size part) */ rc = smb_mbc_decodef( - &sr->command, "www..l16c8.", + &sr->command, "www..l16clw..", &struct_size, /* w */ - &s->cli_dialect_cnt, /* w */ + &dialect_cnt, /* w */ &s->cli_secmode, /* w */ /* reserved (..) */ &s->capabilities, /* l */ - s->clnt_uuid); /* 16c */ - /* start_time 8. */ + s->clnt_uuid, /* 16c */ + &neg_ctx_off, /* l */ + &neg_ctx_cnt); /* w */ + /* reserverd (..) */ if (rc != 0) return (rc); if (struct_size != 36) @@ -611,56 +634,40 @@ smb2_newrq_negotiate(smb_request_t *sr) * Be somewhat tolerant while decoding the variable part * so we can return errors instead of dropping the client. * Will limit decoding to the size of cli_dialects here, - * and do the error checks on s->cli_dialect_cnt after the + * and do error checks on the decoded dialect_cnt after the * dtrace start probe. */ - if (s->cli_dialect_cnt > 0 && - s->cli_dialect_cnt <= SMB2_NEGOTIATE_MAX_DIALECTS && - smb_mbc_decodef(&sr->command, "#w", s->cli_dialect_cnt, - s->cli_dialects) != 0) { - /* decode error; force an error below */ - s->cli_dialect_cnt = 0; - } - - /* - * [MS-SMB2] 3.3.5.4 Receiving an SMB2 NEGOTIATE Request - * "If the DialectCount of the SMB2 NEGOTIATE Request is 0, the - * server MUST fail the request with STATUS_INVALID_PARAMETER." - */ - if (s->cli_dialect_cnt == 0 || - s->cli_dialect_cnt > SMB2_NEGOTIATE_MAX_DIALECTS) { - status = NT_STATUS_INVALID_PARAMETER; + if (dialect_cnt > SMB2_NEGOTIATE_MAX_DIALECTS) + nego->neg_dialect_cnt = SMB2_NEGOTIATE_MAX_DIALECTS; + else + nego->neg_dialect_cnt = dialect_cnt; + if (nego->neg_dialect_cnt > 0) { + rc = smb_mbc_decodef(&sr->command, "#w", + nego->neg_dialect_cnt, + nego->neg_dialects); + if (rc != 0) + return (rc); // short msg } - /* - * The client offers an array of protocol versions it - * supports, which we have decoded into s->cli_dialects[]. - * We walk the array and pick the highest supported. - * - * [MS-SMB2] 3.3.5.4 Receiving an SMB2 NEGOTIATE Request - * "If a common dialect is not found, the server MUST fail - * the request with STATUS_NOT_SUPPORTED." - */ + best_version = smb2_find_best_dialect(s, nego->neg_dialects, + nego->neg_dialect_cnt); - if (status == 0) { - best_version = smb2_find_best_dialect(s, s->cli_dialects, - s->cli_dialect_cnt); - if (best_version >= SMB_VERS_3_11) { - status = smb31_decode_neg_ctxs(sr, &neg_in_ctxs); - nego2->neg_in_ctxs = &neg_in_ctxs; - } else if (best_version == 0) { - status = NT_STATUS_NOT_SUPPORTED; - } + if (best_version >= SMB_VERS_3_11) { + nego->neg_in_ctxs.offset = neg_ctx_off; + nego->neg_in_ctxs.count = neg_ctx_cnt; + nctx_status = smb31_decode_neg_ctxs(sr); + /* check nctx_status below */ } DTRACE_SMB2_START(op__Negotiate, smb_request_t *, sr); - nego2->neg_in_ctxs = NULL; + sr->smb2_credit_response = 1; sr->smb2_hdr_flags |= SMB2_FLAGS_SERVER_TO_REDIR; (void) smb2_encode_header(sr, B_FALSE); - if (status != 0) - goto errout; + /* + * NOW start validating things (NOT before here) + */ /* * [MS-SMB2] 3.3.5.2.4 Verifying the Signature @@ -674,9 +681,41 @@ smb2_newrq_negotiate(smb_request_t *sr) goto errout; } - s->dialect = best_version; + /* + * [MS-SMB2] 3.3.5.4 Receiving an SMB2 NEGOTIATE Request + * "If the DialectCount of the SMB2 NEGOTIATE Request is 0, the + * server MUST fail the request with STATUS_INVALID_PARAMETER." + * Checking the decoded value here, not the constrained one. + */ + if (dialect_cnt == 0 || + dialect_cnt > SMB2_NEGOTIATE_MAX_DIALECTS) { + status = NT_STATUS_INVALID_PARAMETER; + goto errout; + } + + /* + * We decoded the offered dialects above, and + * determined which was the highest we support. + * + * [MS-SMB2] 3.3.5.4 Receiving an SMB2 NEGOTIATE Request + * "If a common dialect is not found, the server MUST fail + * the request with STATUS_NOT_SUPPORTED." + */ + if (best_version == 0) { + status = NT_STATUS_NOT_SUPPORTED; + goto errout; + } + + /* + * Check for problems with the negotiate contexts. + */ + if (nctx_status != 0) { + status = nctx_status; + goto errout; + } /* Allow normal SMB2 requests now. */ + s->dialect = best_version; s->s_state = SMB_SESSION_STATE_NEGOTIATED; s->newrq_func = smb2sr_newrq; @@ -684,15 +723,13 @@ smb2_newrq_negotiate(smb_request_t *sr) status = NT_STATUS_INTERNAL_ERROR; if (s->dialect >= SMB_VERS_3_11 && status == 0) { - if (smb31_encode_neg_ctxs(sr, &neg_out_ctxs) != 0) + if (smb31_encode_neg_ctxs(sr) != 0) status = NT_STATUS_INTERNAL_ERROR; - nego2->neg_out_ctxs = &neg_out_ctxs; } errout: sr->smb2_status = status; DTRACE_SMB2_DONE(op__Negotiate, smb_request_t *, sr); - nego2->neg_out_ctxs = NULL; if (sr->smb2_status != 0) smb2sr_put_error(sr, sr->smb2_status); @@ -869,7 +906,6 @@ uint32_t smb2_nego_validate(smb_request_t *sr, smb_fsctl_t *fsctl) { smb_session_t *s = sr->session; - boolean_t smb311 = s->s_cfg.skc_max_protocol >= SMB_VERS_3_11; int rc; /* @@ -911,8 +947,6 @@ smb2_nego_validate(smb_request_t *sr, smb_fsctl_t *fsctl) if (num_dialects == 0 || num_dialects > SMB2_NEGOTIATE_MAX_DIALECTS) goto drop; - if (smb311 && num_dialects != s->cli_dialect_cnt) - goto drop; if (secmode != s->cli_secmode) goto drop; if (capabilities != s->capabilities) @@ -920,23 +954,17 @@ smb2_nego_validate(smb_request_t *sr, smb_fsctl_t *fsctl) if (memcmp(clnt_guid, s->clnt_uuid, sizeof (clnt_guid)) != 0) goto drop; - if (fsctl->InputCount < (24 + num_dialects * sizeof (*dialects))) - goto drop; - rc = smb_mbc_decodef(fsctl->in_mbc, "#w", num_dialects, dialects); if (rc != 0) goto drop; - if (smb311) { - for (int i = 0; i < num_dialects; i++) { - if (dialects[i] != s->cli_dialects[i]) - goto drop; - } - } else { - if (smb2_find_best_dialect(s, dialects, num_dialects) != - s->dialect) - goto drop; - } + /* + * MS-SMB2 says we should compare the dialects array with the + * one sent previously, but that appears to be unnecessary + * as long as we end up with the same dialect. + */ + if (smb2_find_best_dialect(s, dialects, num_dialects) != s->dialect) + goto drop; rc = smb_mbc_encodef( fsctl->out_mbc, "l#cww", diff --git a/usr/src/uts/common/fs/smbsrv/smb2_qinfo_quota.c b/usr/src/uts/common/fs/smbsrv/smb2_qinfo_quota.c index da2b0176b8..496c6fe414 100644 --- a/usr/src/uts/common/fs/smbsrv/smb2_qinfo_quota.c +++ b/usr/src/uts/common/fs/smbsrv/smb2_qinfo_quota.c @@ -22,6 +22,7 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2022 RackTop Systems, Inc. */ /* @@ -32,6 +33,15 @@ #include <smbsrv/smb_fsops.h> #include <smbsrv/ntifs.h> +/* + * MS-FSA 2.1.5.20 Server Requests Querying Quota Information + * + * Support for this operation is optional. If the object store does not + * implement this functionality, the operation MUST be failed with + * STATUS_INVALID_DEVICE_REQUEST + * + * Similar to smb_nt_transact_query_quota() + */ uint32_t smb2_qinfo_quota(smb_request_t *sr, smb_queryinfo_t *qi) { @@ -49,11 +59,11 @@ smb2_qinfo_quota(smb_request_t *sr, smb_queryinfo_t *qi) bzero(&reply, sizeof (smb_quota_response_t)); if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_QUOTA)) - return (NT_STATUS_NOT_SUPPORTED); + return (NT_STATUS_INVALID_DEVICE_REQUEST); if ((ofile->f_node == NULL) || (ofile->f_ftype != SMB_FTYPE_DISK)) - return (NT_STATUS_NOT_SUPPORTED); + return (NT_STATUS_INVALID_DEVICE_REQUEST); rc = smb_mbc_decodef( &sr->smb_data, "bb..lll", diff --git a/usr/src/uts/common/fs/smbsrv/smb2_setinfo_quota.c b/usr/src/uts/common/fs/smbsrv/smb2_setinfo_quota.c index bdeda05ba7..6dcf0112b3 100644 --- a/usr/src/uts/common/fs/smbsrv/smb2_setinfo_quota.c +++ b/usr/src/uts/common/fs/smbsrv/smb2_setinfo_quota.c @@ -22,6 +22,7 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2022 RackTop Systems, Inc. */ /* @@ -33,6 +34,12 @@ #include <smbsrv/ntifs.h> /* + * MS-FSA 2.1.5.21 Server Requests Setting Quota Information + * + * Support for this operation is optional. If the object store does not + * implement this functionality, the operation MUST be failed with + * STATUS_INVALID_DEVICE_REQUEST + * * Similar to smb_nt_transact_set_quota() */ uint32_t @@ -44,18 +51,18 @@ smb2_setinfo_quota(smb_request_t *sr, smb_setinfo_t *si) smb_node_t *tnode; smb_quota_set_t request; uint32_t reply; - list_t *quota_list; + list_t *quota_list; bzero(&request, sizeof (smb_quota_set_t)); if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_QUOTA)) - return (NT_STATUS_NOT_SUPPORTED); - - if (!smb_user_is_admin(sr->uid_user)) - return (NT_STATUS_ACCESS_DENIED); + return (NT_STATUS_INVALID_DEVICE_REQUEST); if ((ofile->f_node == NULL) || (ofile->f_ftype != SMB_FTYPE_DISK)) + return (NT_STATUS_INVALID_DEVICE_REQUEST); + + if (!smb_user_is_admin(sr->uid_user)) return (NT_STATUS_ACCESS_DENIED); tnode = sr->tid_tree->t_snode; diff --git a/usr/src/uts/common/smb/winioctl.h b/usr/src/uts/common/smb/winioctl.h index 0f322cc4ef..a991841b4e 100644 --- a/usr/src/uts/common/smb/winioctl.h +++ b/usr/src/uts/common/smb/winioctl.h @@ -23,6 +23,7 @@ * Use is subject to license terms. * * Copyright 2018 Nexenta Systems, Inc. All rights reserved. + * Copyright 2022 RackTop Systems, Inc. */ #ifndef _SMB_WINIOCTL_H #define _SMB_WINIOCTL_H @@ -483,6 +484,9 @@ extern "C" { #define FSCTL_QUERY_FILE_REGIONS \ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 161, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define FSCTL_REFS_STREAM_SNAPSHOT_MANAGEMENT \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 272, METHOD_BUFFERED, FILE_ANY_ACCESS) + /* FILE_DEVICE_NETWORK_FILE_SYSTEM */ /* Read the snapshot info for Volume Shadow Copy Services */ #define FSCTL_SRV_ENUMERATE_SNAPSHOTS \ diff --git a/usr/src/uts/common/smbsrv/smb_ktypes.h b/usr/src/uts/common/smbsrv/smb_ktypes.h index 89b057ff91..a220faef6b 100644 --- a/usr/src/uts/common/smbsrv/smb_ktypes.h +++ b/usr/src/uts/common/smbsrv/smb_ktypes.h @@ -730,11 +730,6 @@ typedef struct smb_arg_negotiate { timestruc_t ni_servertime; } smb_arg_negotiate_t; -typedef struct smb2_arg_negotiate { - struct smb2_neg_ctxs *neg_in_ctxs; - struct smb2_neg_ctxs *neg_out_ctxs; -} smb2_arg_negotiate_t; - typedef enum { SMB_SSNSETUP_PRE_NTLM012 = 1, SMB_SSNSETUP_NTLM012_NOEXT, @@ -911,8 +906,6 @@ typedef enum { #define SMB_SSN_AAPL_CCEXT 1 /* Saw "AAPL" create ctx. ext. */ #define SMB_SSN_AAPL_READDIR 2 /* Wants MacOS ext. readdir */ -#define SMB2_NEGOTIATE_MAX_DIALECTS 64 - typedef struct smb_session { list_node_t s_lnd; uint32_t s_magic; @@ -977,11 +970,6 @@ typedef struct smb_session { timeout_id_t s_auth_tmo; /* - * Client dialects - */ - uint16_t cli_dialect_cnt; - uint16_t cli_dialects[SMB2_NEGOTIATE_MAX_DIALECTS]; - /* * Maximum negotiated buffer sizes between SMB client and server * in SMB_SESSION_SETUP_ANDX */ @@ -1925,7 +1913,6 @@ typedef struct smb_request { uint32_t sr_seqnum; union { - smb2_arg_negotiate_t nego2; smb_arg_negotiate_t *negprot; smb_arg_sessionsetup_t *ssetup; smb_arg_tcon_t tcon; @@ -1941,7 +1928,6 @@ typedef struct smb_request { #define sr_ssetup arg.ssetup #define sr_negprot arg.negprot -#define sr_nego2 arg.nego2 #define sr_tcon arg.tcon #define sr_dirop arg.dirop #define sr_open arg.open |