From: David Mulder Date: Wed, 23 Mar 2022 12:43:40 +0000 (-0600) Subject: smbd: Enable multi-protocol negotiate w/out SMB1 X-Git-Tag: tevent-0.12.0~48 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=aa61db2d0be624649eaa60aeb40750158a7b535d;p=thirdparty%2Fsamba.git smbd: Enable multi-protocol negotiate w/out SMB1 This enables the multi-protocol negotiate when the SMB1 build is disabled. It requires enabling parts of the SMB1 negotiation. Signed-off-by: David Mulder Signed-off-by: Jeremy Allison --- diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h index 223a512182b..eb2fcdfb154 100644 --- a/source3/smbd/globals.h +++ b/source3/smbd/globals.h @@ -249,6 +249,7 @@ NTSTATUS reply_smb20ff(struct smb_request *req, uint16_t choice); NTSTATUS smbd_smb2_process_negprot(struct smbXsrv_connection *xconn, uint64_t expected_seq_low, const uint8_t *inpdu, size_t size); +void smb2_multi_protocol_reply_negprot(struct smb_request *req); DATA_BLOB smbd_smb2_generate_outbody(struct smbd_smb2_request *req, size_t size); diff --git a/source3/smbd/smb2_negprot.c b/source3/smbd/smb2_negprot.c index 1b465b90113..01ffc4ad84a 100644 --- a/source3/smbd/smb2_negprot.c +++ b/source3/smbd/smb2_negprot.c @@ -1016,3 +1016,180 @@ DATA_BLOB negprot_spnego(TALLOC_CTX *ctx, struct smbXsrv_connection *xconn) return blob_out; } + +/* + * MS-CIFS, 2.2.4.52.2 SMB_COM_NEGOTIATE Response: + * If the server does not support any of the listed dialects, it MUST return a + * DialectIndex of 0XFFFF + */ +#define NO_PROTOCOL_CHOSEN 0xffff + +#define PROT_SMB_2_002 0x1000 +#define PROT_SMB_2_FF 0x2000 + +/* List of supported SMB1 protocols, most desired first. + * This is for enabling multi-protocol negotiation in SMB2 when SMB1 + * is disabled. + */ +static const struct { + const char *proto_name; + const char *short_name; + NTSTATUS (*proto_reply_fn)(struct smb_request *req, uint16_t choice); + int protocol_level; +} supported_protocols[] = { + {"SMB 2.???", "SMB2_FF", reply_smb20ff, PROTOCOL_SMB2_10}, + {"SMB 2.002", "SMB2_02", reply_smb2002, PROTOCOL_SMB2_02}, + {NULL,NULL,NULL,0}, +}; + +/**************************************************************************** + Reply to a negprot. + conn POINTER CAN BE NULL HERE ! +****************************************************************************/ + +void smb2_multi_protocol_reply_negprot(struct smb_request *req) +{ + size_t choice = 0; + bool choice_set = false; + int protocol; + const char *p; + int protocols = 0; + int num_cliprotos; + char **cliprotos; + size_t i; + size_t converted_size; + struct smbXsrv_connection *xconn = req->xconn; + struct smbd_server_connection *sconn = req->sconn; + int max_proto; + int min_proto; + NTSTATUS status; + + START_PROFILE(SMBnegprot); + + if (req->buflen == 0) { + DEBUG(0, ("negprot got no protocols\n")); + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + END_PROFILE(SMBnegprot); + return; + } + + if (req->buf[req->buflen-1] != '\0') { + DEBUG(0, ("negprot protocols not 0-terminated\n")); + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + END_PROFILE(SMBnegprot); + return; + } + + p = (const char *)req->buf + 1; + + num_cliprotos = 0; + cliprotos = NULL; + + while (smbreq_bufrem(req, p) > 0) { + + char **tmp; + + tmp = talloc_realloc(talloc_tos(), cliprotos, char *, + num_cliprotos+1); + if (tmp == NULL) { + DEBUG(0, ("talloc failed\n")); + TALLOC_FREE(cliprotos); + reply_nterror(req, NT_STATUS_NO_MEMORY); + END_PROFILE(SMBnegprot); + return; + } + + cliprotos = tmp; + + if (!pull_ascii_talloc(cliprotos, &cliprotos[num_cliprotos], p, + &converted_size)) { + DEBUG(0, ("pull_ascii_talloc failed\n")); + TALLOC_FREE(cliprotos); + reply_nterror(req, NT_STATUS_NO_MEMORY); + END_PROFILE(SMBnegprot); + return; + } + + DEBUG(3, ("Requested protocol [%s]\n", + cliprotos[num_cliprotos])); + + num_cliprotos += 1; + p += strlen(p) + 2; + } + + for (i=0; i PROTOCOL_SMB2_10) { + max_proto = PROTOCOL_SMB2_10; + } + min_proto = lp_server_min_protocol(); + if (min_proto > PROTOCOL_SMB2_10) { + min_proto = PROTOCOL_SMB2_10; + } + + /* Check for protocols, most desirable first */ + for (protocol = 0; supported_protocols[protocol].proto_name; protocol++) { + i = 0; + if ((supported_protocols[protocol].protocol_level <= max_proto) && + (supported_protocols[protocol].protocol_level >= min_proto)) + while (i < num_cliprotos) { + if (strequal(cliprotos[i],supported_protocols[protocol].proto_name)) { + choice = i; + choice_set = true; + } + i++; + } + if (choice_set) { + break; + } + } + + if (!choice_set) { + bool ok; + + DBG_NOTICE("No protocol supported !\n"); + reply_outbuf(req, 1, 0); + SSVAL(req->outbuf, smb_vwv0, NO_PROTOCOL_CHOSEN); + + ok = srv_send_smb(xconn, (char *)req->outbuf, + false, 0, false, NULL); + if (!ok) { + DBG_NOTICE("srv_send_smb failed\n"); + } + exit_server_cleanly("no protocol supported\n"); + } + + fstrcpy(remote_proto,supported_protocols[protocol].short_name); + reload_services(sconn, conn_snum_used, true); + status = supported_protocols[protocol].proto_reply_fn(req, choice); + if (!NT_STATUS_IS_OK(status)) { + exit_server_cleanly("negprot function failed\n"); + } + + DEBUG(3,("Selected protocol %s\n",supported_protocols[protocol].proto_name)); + + DBG_INFO("negprot index=%zu\n", choice); + + TALLOC_FREE(cliprotos); + + END_PROFILE(SMBnegprot); + return; +} diff --git a/source3/smbd/smb2_process.c b/source3/smbd/smb2_process.c index 35d18b9aaa8..64b3bcaf83e 100644 --- a/source3/smbd/smb2_process.c +++ b/source3/smbd/smb2_process.c @@ -818,6 +818,40 @@ bool init_smb_request(struct smb_request *req, return true; } +/**************************************************************************** + Construct a reply to the incoming packet. +****************************************************************************/ + +static void construct_reply_smb1negprot(struct smbXsrv_connection *xconn, + char *inbuf, int size, + size_t unread_bytes) +{ + struct smbd_server_connection *sconn = xconn->client->sconn; + struct smb_request *req; + + if (!(req = talloc(talloc_tos(), struct smb_request))) { + smb_panic("could not allocate smb_request"); + } + + if (!init_smb_request(req, sconn, xconn, (uint8_t *)inbuf, unread_bytes, + false, 0)) { + exit_server_cleanly("Invalid SMB request"); + } + + req->inbuf = (uint8_t *)talloc_move(req, &inbuf); + + smb2_multi_protocol_reply_negprot(req); + if (req->outbuf == NULL) { + /* + * req->outbuf == NULL means we bootstrapped into SMB2. + */ + return; + } + /* This code path should only *ever* bootstrap into SMB2. */ + exit_server_cleanly("Internal error SMB1negprot didn't reply " + "with an SMB2 packet"); +} + static void smbd_server_connection_write_handler( struct smbXsrv_connection *xconn) { @@ -895,7 +929,6 @@ static void smbd_smb2_server_connection_read_handler( exit_server_cleanly("Invalid initial SMB1 or SMB2 packet"); return; } -#if defined(WITH_SMB1SERVER) if (valid_smb_header(buffer)) { /* Can *only* allow an SMB1 negprot here. */ uint8_t cmd = PULL_LE_U8(buffer, smb_com); @@ -907,20 +940,13 @@ static void smbd_smb2_server_connection_read_handler( } /* Minimal process_smb(). */ show_msg((char *)buffer); - construct_reply(xconn, - (char *)buffer, - bufferlen, - 0, - 0, - false, - NULL); + construct_reply_smb1negprot(xconn, (char *)buffer, + bufferlen, 0); xconn->client->sconn->trans_num++; xconn->client->sconn->num_requests++; return; - } else -#endif - if (!smbd_is_smb2_header(buffer, bufferlen)) { + } else if (!smbd_is_smb2_header(buffer, bufferlen)) { exit_server_cleanly("Invalid initial SMB2 packet"); return; }