static NTSTATUS smb2srv_negprot_secblob(struct smb2srv_request *req, DATA_BLOB *_blob) { struct gensec_security *gensec_security; DATA_BLOB null_data_blob = data_blob(NULL, 0); DATA_BLOB blob; NTSTATUS nt_status; struct cli_credentials *server_credentials; server_credentials = cli_credentials_init(req); if (!server_credentials) { smbsrv_terminate_connection(req->smb_conn, "Failed to init server credentials\n"); return NT_STATUS_NO_MEMORY; } cli_credentials_set_conf(server_credentials, req->smb_conn->lp_ctx); nt_status = cli_credentials_set_machine_account(server_credentials, req->smb_conn->lp_ctx); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(10, ("Failed to obtain server credentials, perhaps a standalone server?: %s\n", nt_errstr(nt_status))); /* * We keep the server_credentials as anonymous * this is required for the spoolss.notify test */ } req->smb_conn->negotiate.server_credentials = talloc_steal(req->smb_conn, server_credentials); nt_status = samba_server_gensec_start(req, req->smb_conn->connection->event.ctx, req->smb_conn->connection->msg_ctx, req->smb_conn->lp_ctx, server_credentials, "cifs", &gensec_security); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(0, ("Failed to start GENSEC: %s\n", nt_errstr(nt_status))); smbsrv_terminate_connection(req->smb_conn, "Failed to start GENSEC\n"); return nt_status; } gensec_set_target_service(gensec_security, "cifs"); gensec_set_credentials(gensec_security, server_credentials); nt_status = gensec_start_mech_by_oid(gensec_security, GENSEC_OID_SPNEGO); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(0, ("Failed to start SPNEGO: %s\n", nt_errstr(nt_status))); smbsrv_terminate_connection(req->smb_conn, "Failed to start SPNEGO\n"); return nt_status; } nt_status = gensec_update_ev(gensec_security, req, req->smb_conn->connection->event.ctx, null_data_blob, &blob); if (!NT_STATUS_IS_OK(nt_status) && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { DEBUG(0, ("Failed to get SPNEGO to give us the first token: %s\n", nt_errstr(nt_status))); smbsrv_terminate_connection(req->smb_conn, "Failed to start SPNEGO - no first token\n"); return nt_status; } *_blob = blob; return NT_STATUS_OK; }
/* setup a chained reply in req->out with the given word count and initial data buffer size. */ static void req_setup_chain_reply(struct smbsrv_request *req, unsigned int wct, unsigned int buflen) { uint32_t chain_base_size = req->out.size; /* we need room for the wct value, the words, the buffer length and the buffer */ req->out.size += 1 + VWV(wct) + 2 + buflen; /* over allocate by a small amount */ req->out.allocated = req->out.size + REQ_OVER_ALLOCATION; req->out.buffer = talloc_realloc(req, req->out.buffer, uint8_t, req->out.allocated); if (!req->out.buffer) { smbsrv_terminate_connection(req->smb_conn, "allocation failed"); return; } req->out.hdr = req->out.buffer + NBT_HDR_SIZE; req->out.vwv = req->out.buffer + chain_base_size + 1; req->out.wct = wct; req->out.data = req->out.vwv + VWV(wct) + 2; req->out.data_size = buflen; req->out.ptr = req->out.data; SCVAL(req->out.buffer, chain_base_size, wct); SSVAL(req->out.vwv, VWV(wct), buflen); }
void smb2srv_send_error(struct smb2srv_request *req, NTSTATUS error) { NTSTATUS status; if (req->smb_conn->connection->event.fde == NULL) { /* the socket has been destroyed - no point trying to send an error! */ talloc_free(req); return; } status = smb2srv_setup_reply(req, 8, true, 0); if (!NT_STATUS_IS_OK(status)) { smbsrv_terminate_connection(req->smb_conn, nt_errstr(status)); talloc_free(req); return; } SIVAL(req->out.hdr, SMB2_HDR_STATUS, NT_STATUS_V(error)); SSVAL(req->out.body, 0x02, 0); SIVAL(req->out.body, 0x04, 0); req->chain_status = NT_STATUS_INVALID_PARAMETER; smb2srv_send_reply(req); }
void smb2srv_negprot_recv(struct smb2srv_request *req) { struct smb2_negprot *io; if (req->in.body_size < 0x26) { smb2srv_send_error(req, NT_STATUS_FOOBAR); return; } io = talloc(req, struct smb2_negprot); if (!io) { smbsrv_terminate_connection(req->smb_conn, nt_errstr(NT_STATUS_NO_MEMORY)); talloc_free(req); return; } io->in.unknown1 = SVAL(req->in.body, 0x02); memcpy(io->in.unknown2, req->in.body + 0x04, 0x20); io->in.unknown3 = SVAL(req->in.body, 0x24); req->status = smb2srv_negprot_backend(req, io); if (req->control_flags & SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY) { talloc_free(req); return; } smb2srv_negprot_send(req, io); }
void smb2srv_negprot_recv(struct smb2srv_request *req) { struct smb2_negprot *io; int i; if (req->in.body_size < 0x26) { smbsrv_terminate_connection(req->smb_conn, "Bad body size in SMB2 negprot"); return; } io = talloc(req, struct smb2_negprot); if (!io) { smbsrv_terminate_connection(req->smb_conn, nt_errstr(NT_STATUS_NO_MEMORY)); talloc_free(req); return; } io->in.dialect_count = SVAL(req->in.body, 0x02); io->in.security_mode = SVAL(req->in.body, 0x04); io->in.reserved = SVAL(req->in.body, 0x06); io->in.capabilities = IVAL(req->in.body, 0x08); req->status = smbcli_pull_guid(req->in.body, 0xC, &io->in.client_guid); if (!NT_STATUS_IS_OK(req->status)) { smbsrv_terminate_connection(req->smb_conn, "Bad GUID in SMB2 negprot"); talloc_free(req); return; } io->in.start_time = smbcli_pull_nttime(req->in.body, 0x1C); io->in.dialects = talloc_array(req, uint16_t, io->in.dialect_count); if (io->in.dialects == NULL) { smbsrv_terminate_connection(req->smb_conn, nt_errstr(NT_STATUS_NO_MEMORY)); talloc_free(req); return; } for (i=0;i<io->in.dialect_count;i++) { io->in.dialects[i] = SVAL(req->in.body, 0x24+i*2); } req->status = smb2srv_negprot_backend(req, io); if (req->control_flags & SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY) { talloc_free(req); return; } smb2srv_negprot_send(req, io); }
static void smb2srv_negprot_send(struct smb2srv_request *req, struct smb2_negprot *io) { NTSTATUS status; if (NT_STATUS_IS_ERR(req->status)) { smb2srv_send_error(req, req->status); /* TODO: is this correct? */ return; } status = smb2srv_setup_reply(req, 0x40, true, io->out.secblob.length); if (!NT_STATUS_IS_OK(status)) { smbsrv_terminate_connection(req->smb_conn, nt_errstr(status)); talloc_free(req); return; } SSVAL(req->out.body, 0x02, io->out.security_mode); SIVAL(req->out.body, 0x04, io->out.dialect_revision); SIVAL(req->out.body, 0x06, io->out.reserved); status = smbcli_push_guid(req->out.body, 0x08, &io->out.server_guid); if (!NT_STATUS_IS_OK(status)) { smbsrv_terminate_connection(req->smb_conn, nt_errstr(status)); talloc_free(req); return; } SIVAL(req->out.body, 0x18, io->out.capabilities); SIVAL(req->out.body, 0x1C, io->out.max_transact_size); SIVAL(req->out.body, 0x20, io->out.max_read_size); SIVAL(req->out.body, 0x24, io->out.max_write_size); push_nttime(req->out.body, 0x28, io->out.system_time); push_nttime(req->out.body, 0x30, io->out.server_start_time); SIVAL(req->out.body, 0x3C, io->out.reserved2); status = smb2_push_o16s16_blob(&req->out, 0x38, io->out.secblob); if (!NT_STATUS_IS_OK(status)) { smbsrv_terminate_connection(req->smb_conn, nt_errstr(status)); talloc_free(req); return; } smb2srv_send_reply(req); }
void smb2srv_send_reply(struct smb2srv_request *req) { DATA_BLOB blob; NTSTATUS status; if (req->smb_conn->connection->event.fde == NULL) { /* the socket has been destroyed - no point trying to send a reply! */ talloc_free(req); return; } if (req->out.size > NBT_HDR_SIZE) { _smb_setlen_tcp(req->out.buffer, req->out.size - NBT_HDR_SIZE); } /* if signing is active on the session then sign the packet */ if (req->is_signed) { status = smb2_sign_message(&req->out, req->session->session_info->session_key); if (!NT_STATUS_IS_OK(status)) { smbsrv_terminate_connection(req->smb_conn, nt_errstr(status)); return; } } blob = data_blob_const(req->out.buffer, req->out.size); status = packet_send(req->smb_conn->packet, blob); if (!NT_STATUS_IS_OK(status)) { smbsrv_terminate_connection(req->smb_conn, nt_errstr(status)); return; } if (req->chain_offset) { smb2srv_chain_reply(req); return; } talloc_free(req); }
/* * reply to a SMB negprot request with dialect "SMB 2.002" */ void smb2srv_reply_smb_negprot(struct smbsrv_request *smb_req) { struct smb2srv_request *req; uint32_t body_fixed_size = 0x26; req = talloc_zero(smb_req->smb_conn, struct smb2srv_request); if (!req) goto nomem; req->smb_conn = smb_req->smb_conn; req->request_time = smb_req->request_time; talloc_steal(req, smb_req); req->in.size = NBT_HDR_SIZE+SMB2_HDR_BODY+body_fixed_size; req->in.allocated = req->in.size; req->in.buffer = talloc_array(req, uint8_t, req->in.allocated); if (!req->in.buffer) goto nomem; req->in.hdr = req->in.buffer + NBT_HDR_SIZE; req->in.body = req->in.hdr + SMB2_HDR_BODY; req->in.body_size = body_fixed_size; req->in.dynamic = NULL; smb2srv_setup_bufinfo(req); SIVAL(req->in.hdr, 0, SMB2_MAGIC); SSVAL(req->in.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY); SSVAL(req->in.hdr, SMB2_HDR_EPOCH, 0); SIVAL(req->in.hdr, SMB2_HDR_STATUS, 0); SSVAL(req->in.hdr, SMB2_HDR_OPCODE, SMB2_OP_NEGPROT); SSVAL(req->in.hdr, SMB2_HDR_CREDIT, 0); SIVAL(req->in.hdr, SMB2_HDR_FLAGS, 0); SIVAL(req->in.hdr, SMB2_HDR_NEXT_COMMAND, 0); SBVAL(req->in.hdr, SMB2_HDR_MESSAGE_ID, 0); SIVAL(req->in.hdr, SMB2_HDR_PID, 0); SIVAL(req->in.hdr, SMB2_HDR_TID, 0); SBVAL(req->in.hdr, SMB2_HDR_SESSION_ID, 0); memset(req->in.hdr+SMB2_HDR_SIGNATURE, 0, 16); /* this seems to be a bug, they use 0x24 but the length is 0x26 */ SSVAL(req->in.body, 0x00, 0x24); SSVAL(req->in.body, 0x02, 1); memset(req->in.body+0x04, 0, 32); SSVAL(req->in.body, 0x24, SMB2_DIALECT_REVISION_202); smb2srv_negprot_recv(req); return; nomem: smbsrv_terminate_connection(smb_req->smb_conn, nt_errstr(NT_STATUS_NO_MEMORY)); talloc_free(req); return; }
static void smb2srv_negprot_send(struct smb2srv_request *req, struct smb2_negprot *io) { NTSTATUS status; if (NT_STATUS_IS_ERR(req->status)) { smb2srv_send_error(req, req->status); /* TODO: is this correct? */ return; } status = smb2srv_setup_reply(req, 0x40, True, io->out.secblob.length); if (!NT_STATUS_IS_OK(status)) { smbsrv_terminate_connection(req->smb_conn, nt_errstr(status)); talloc_free(req); return; } SSVAL(req->out.body, 0x02, io->out._pad); SIVAL(req->out.body, 0x04, io->out.unknown2); memcpy(req->out.body+0x08, io->out.sessid, 16); SIVAL(req->out.body, 0x18, io->out.unknown3); SSVAL(req->out.body, 0x1C, io->out.unknown4); SIVAL(req->out.body, 0x1E, io->out.unknown5); SIVAL(req->out.body, 0x22, io->out.unknown6); SSVAL(req->out.body, 0x26, io->out.unknown7); push_nttime(req->out.body, 0x28, io->out.current_time); push_nttime(req->out.body, 0x30, io->out.boot_time); status = smb2_push_o16s16_blob(&req->out, 0x38, io->out.secblob); if (!NT_STATUS_IS_OK(status)) { smbsrv_terminate_connection(req->smb_conn, nt_errstr(status)); talloc_free(req); return; } SIVAL(req->out.body, 0x3C, io->out.unknown9); smb2srv_send_reply(req); }
/* * reply to a SMB negprot request with dialect "SMB 2.001" */ void smb2srv_reply_smb_negprot(struct smbsrv_request *smb_req) { struct smb2srv_request *req; uint32_t body_fixed_size = 0x26; /* create a fake SMB2 negprot request */ req = talloc_zero(smb_req->smb_conn, struct smb2srv_request); if (!req) goto nomem; req->smb_conn = smb_req->smb_conn; req->request_time = smb_req->request_time; talloc_steal(req, smb_req); req->in.size = NBT_HDR_SIZE+SMB2_HDR_BODY+body_fixed_size; req->in.allocated = req->in.size; req->in.buffer = talloc_size(req, req->in.allocated); if (!req->in.buffer) goto nomem; req->in.hdr = req->in.buffer + NBT_HDR_SIZE; req->in.body = req->in.hdr + SMB2_HDR_BODY; req->in.body_size = body_fixed_size; req->in.dynamic = NULL; SIVAL(req->in.hdr, 0, SMB2_MAGIC); SSVAL(req->in.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY); SSVAL(req->in.hdr, SMB2_HDR_PAD1, 0); SIVAL(req->in.hdr, SMB2_HDR_STATUS, 0); SSVAL(req->in.hdr, SMB2_HDR_OPCODE, SMB2_OP_NEGPROT); SSVAL(req->in.hdr, SMB2_HDR_UNKNOWN1, 0); SIVAL(req->in.hdr, SMB2_HDR_FLAGS, 0); SIVAL(req->in.hdr, SMB2_HDR_CHAIN_OFFSET, 0); SBVAL(req->in.hdr, SMB2_HDR_SEQNUM, 0); SIVAL(req->in.hdr, SMB2_HDR_PID, 0); SIVAL(req->in.hdr, SMB2_HDR_TID, 0); SBVAL(req->in.hdr, SMB2_HDR_UID, 0); memset(req->in.hdr+SMB2_HDR_SIG, 0, 16); /* this seems to be a bug, they use 0x24 but the length is 0x26 */ SSVAL(req->in.body, 0x00, 0x24); SSVAL(req->in.body, 0x02, 1); memset(req->in.body+0x04, 0, 32); SSVAL(req->in.body, 0x24, 0); smb2srv_negprot_recv(req); return; nomem: smbsrv_terminate_connection(smb_req->smb_conn, nt_errstr(NT_STATUS_NO_MEMORY)); talloc_free(req); return; }
static void smb2srv_keepalive_send(struct smb2srv_request *req) { NTSTATUS status; if (NT_STATUS_IS_ERR(req->status)) { smb2srv_send_error(req, req->status); return; } status = smb2srv_setup_reply(req, 0x04, false, 0); if (!NT_STATUS_IS_OK(status)) { smbsrv_terminate_connection(req->smb_conn, nt_errstr(status)); talloc_free(req); return; } SSVAL(req->out.body, 0x02, 0); smb2srv_send_reply(req); }
/* send a reply and destroy the request buffer note that this only looks at req->out.buffer and req->out.size, allowing manually constructed packets to be sent */ void smbsrv_send_reply_nosign(struct smbsrv_request *req) { DATA_BLOB blob; NTSTATUS status; if (req->smb_conn->connection->event.fde == NULL) { /* we are in the process of shutting down this connection */ talloc_free(req); return; } if (req->out.size > NBT_HDR_SIZE) { _smb_setlen_nbt(req->out.buffer, req->out.size - NBT_HDR_SIZE); } blob = data_blob_const(req->out.buffer, req->out.size); status = packet_send(req->smb_conn->packet, blob); if (!NT_STATUS_IS_OK(status)) { smbsrv_terminate_connection(req->smb_conn, nt_errstr(status)); } talloc_free(req); }
NTSTATUS smbsrv_recv_smb2_request(void *private_data, DATA_BLOB blob) { struct smbsrv_connection *smb_conn = talloc_get_type(private_data, struct smbsrv_connection); struct smb2srv_request *req; struct timeval cur_time = timeval_current(); uint32_t protocol_version; uint16_t buffer_code; uint32_t dynamic_size; uint32_t flags; smb_conn->statistics.last_request_time = cur_time; /* see if its a special NBT packet */ if (CVAL(blob.data,0) != 0) { DEBUG(2,("Special NBT packet on SMB2 connection")); smbsrv_terminate_connection(smb_conn, "Special NBT packet on SMB2 connection"); return NT_STATUS_OK; } if (blob.length < (NBT_HDR_SIZE + SMB2_MIN_SIZE_NO_BODY)) { DEBUG(2,("Invalid SMB2 packet length count %ld\n", (long)blob.length)); smbsrv_terminate_connection(smb_conn, "Invalid SMB2 packet"); return NT_STATUS_OK; } protocol_version = IVAL(blob.data, NBT_HDR_SIZE); if (protocol_version != SMB2_MAGIC) { DEBUG(2,("Invalid SMB packet: protocol prefix: 0x%08X\n", protocol_version)); smbsrv_terminate_connection(smb_conn, "NON-SMB2 packet"); return NT_STATUS_OK; } req = smb2srv_init_request(smb_conn); NT_STATUS_HAVE_NO_MEMORY(req); req->in.buffer = talloc_steal(req, blob.data); req->in.size = blob.length; req->request_time = cur_time; req->in.allocated = req->in.size; req->in.hdr = req->in.buffer+ NBT_HDR_SIZE; req->in.body = req->in.hdr + SMB2_HDR_BODY; req->in.body_size = req->in.size - (SMB2_HDR_BODY+NBT_HDR_SIZE); req->in.dynamic = NULL; req->seqnum = BVAL(req->in.hdr, SMB2_HDR_MESSAGE_ID); if (req->in.body_size < 2) { /* error handling for this is different for negprot to other packet types */ uint16_t opcode = SVAL(req->in.hdr, SMB2_HDR_OPCODE); if (opcode == SMB2_OP_NEGPROT) { smbsrv_terminate_connection(req->smb_conn, "Bad body size in SMB2 negprot"); return NT_STATUS_OK; } else { smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER); return NT_STATUS_OK; } } buffer_code = SVAL(req->in.body, 0); req->in.body_fixed = (buffer_code & ~1); dynamic_size = req->in.body_size - req->in.body_fixed; if (dynamic_size != 0 && (buffer_code & 1)) { req->in.dynamic = req->in.body + req->in.body_fixed; if (smb2_oob(&req->in, req->in.dynamic, dynamic_size)) { DEBUG(1,("SMB2 request invalid dynamic size 0x%x\n", dynamic_size)); smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER); return NT_STATUS_OK; } } smb2srv_setup_bufinfo(req); /* * TODO: - make sure the length field is 64 * - make sure it's a request */ flags = IVAL(req->in.hdr, SMB2_HDR_FLAGS); /* the first request should never have the related flag set */ if (flags & SMB2_HDR_FLAG_CHAINED) { req->chain_status = NT_STATUS_INVALID_PARAMETER; } return smb2srv_reply(req); }
static NTSTATUS smb2srv_reply(struct smb2srv_request *req) { uint16_t opcode; uint32_t tid; uint64_t uid; uint32_t flags; if (SVAL(req->in.hdr, SMB2_HDR_LENGTH) != SMB2_HDR_BODY) { smbsrv_terminate_connection(req->smb_conn, "Invalid SMB2 header length"); return NT_STATUS_INVALID_PARAMETER; } opcode = SVAL(req->in.hdr, SMB2_HDR_OPCODE); req->chain_offset = IVAL(req->in.hdr, SMB2_HDR_NEXT_COMMAND); req->seqnum = BVAL(req->in.hdr, SMB2_HDR_MESSAGE_ID); tid = IVAL(req->in.hdr, SMB2_HDR_TID); uid = BVAL(req->in.hdr, SMB2_HDR_SESSION_ID); flags = IVAL(req->in.hdr, SMB2_HDR_FLAGS); if (opcode != SMB2_OP_CANCEL && req->smb_conn->highest_smb2_seqnum != 0 && req->seqnum <= req->smb_conn->highest_smb2_seqnum) { smbsrv_terminate_connection(req->smb_conn, "Invalid SMB2 sequence number"); return NT_STATUS_INVALID_PARAMETER; } if (opcode != SMB2_OP_CANCEL) { req->smb_conn->highest_smb2_seqnum = req->seqnum; } if (flags & SMB2_HDR_FLAG_CHAINED) { uid = req->chained_session_id; tid = req->chained_tree_id; } req->session = smbsrv_session_find(req->smb_conn, uid, req->request_time); req->tcon = smbsrv_smb2_tcon_find(req->session, tid, req->request_time); req->chained_session_id = uid; req->chained_tree_id = tid; errno = 0; /* supporting signing is mandatory in SMB2, and is per-packet. So we should check the signature on any incoming packet that is signed, and should give a signed reply to any signed request */ if (flags & SMB2_HDR_FLAG_SIGNED) { NTSTATUS status; if (!req->session) goto nosession; req->is_signed = true; status = smb2_check_signature(&req->in, req->session->session_info->session_key); if (!NT_STATUS_IS_OK(status)) { smb2srv_send_error(req, status); return NT_STATUS_OK; } } else if (req->session && req->session->smb2_signing.active) { /* we require signing and this request was not signed */ smb2srv_send_error(req, NT_STATUS_ACCESS_DENIED); return NT_STATUS_OK; } if (!NT_STATUS_IS_OK(req->chain_status)) { smb2srv_send_error(req, req->chain_status); return NT_STATUS_OK; } switch (opcode) { case SMB2_OP_NEGPROT: smb2srv_negprot_recv(req); return NT_STATUS_OK; case SMB2_OP_SESSSETUP: smb2srv_sesssetup_recv(req); return NT_STATUS_OK; case SMB2_OP_LOGOFF: if (!req->session) goto nosession; smb2srv_logoff_recv(req); return NT_STATUS_OK; case SMB2_OP_TCON: if (!req->session) goto nosession; smb2srv_tcon_recv(req); return NT_STATUS_OK; case SMB2_OP_TDIS: if (!req->session) goto nosession; if (!req->tcon) goto notcon; smb2srv_tdis_recv(req); return NT_STATUS_OK; case SMB2_OP_CREATE: if (!req->session) goto nosession; if (!req->tcon) goto notcon; smb2srv_create_recv(req); return NT_STATUS_OK; case SMB2_OP_CLOSE: if (!req->session) goto nosession; if (!req->tcon) goto notcon; smb2srv_close_recv(req); return NT_STATUS_OK; case SMB2_OP_FLUSH: if (!req->session) goto nosession; if (!req->tcon) goto notcon; smb2srv_flush_recv(req); return NT_STATUS_OK; case SMB2_OP_READ: if (!req->session) goto nosession; if (!req->tcon) goto notcon; smb2srv_read_recv(req); return NT_STATUS_OK; case SMB2_OP_WRITE: if (!req->session) goto nosession; if (!req->tcon) goto notcon; smb2srv_write_recv(req); return NT_STATUS_OK; case SMB2_OP_LOCK: if (!req->session) goto nosession; if (!req->tcon) goto notcon; smb2srv_lock_recv(req); return NT_STATUS_OK; case SMB2_OP_IOCTL: if (!req->session) goto nosession; if (!req->tcon) goto notcon; smb2srv_ioctl_recv(req); return NT_STATUS_OK; case SMB2_OP_CANCEL: smb2srv_cancel_recv(req); return NT_STATUS_OK; case SMB2_OP_KEEPALIVE: smb2srv_keepalive_recv(req); return NT_STATUS_OK; case SMB2_OP_FIND: if (!req->session) goto nosession; if (!req->tcon) goto notcon; smb2srv_find_recv(req); return NT_STATUS_OK; case SMB2_OP_NOTIFY: if (!req->session) goto nosession; if (!req->tcon) goto notcon; smb2srv_notify_recv(req); return NT_STATUS_OK; case SMB2_OP_GETINFO: if (!req->session) goto nosession; if (!req->tcon) goto notcon; smb2srv_getinfo_recv(req); return NT_STATUS_OK; case SMB2_OP_SETINFO: if (!req->session) goto nosession; if (!req->tcon) goto notcon; smb2srv_setinfo_recv(req); return NT_STATUS_OK; case SMB2_OP_BREAK: if (!req->session) goto nosession; if (!req->tcon) goto notcon; smb2srv_break_recv(req); return NT_STATUS_OK; } DEBUG(1,("Invalid SMB2 opcode: 0x%04X\n", opcode)); smbsrv_terminate_connection(req->smb_conn, "Invalid SMB2 opcode"); return NT_STATUS_OK; nosession: smb2srv_send_error(req, NT_STATUS_USER_SESSION_DELETED); return NT_STATUS_OK; notcon: smb2srv_send_error(req, NT_STATUS_NETWORK_NAME_DELETED); return NT_STATUS_OK; }
static void smb2srv_chain_reply(struct smb2srv_request *p_req) { NTSTATUS status; struct smb2srv_request *req; uint32_t chain_offset; uint32_t protocol_version; uint16_t buffer_code; uint32_t dynamic_size; uint32_t flags; uint32_t last_hdr_offset; last_hdr_offset = p_req->in.hdr - p_req->in.buffer; chain_offset = p_req->chain_offset; p_req->chain_offset = 0; if (p_req->in.size < (last_hdr_offset + chain_offset + SMB2_MIN_SIZE_NO_BODY)) { DEBUG(2,("Invalid SMB2 chained packet at offset 0x%X from last hdr 0x%X\n", chain_offset, last_hdr_offset)); smbsrv_terminate_connection(p_req->smb_conn, "Invalid SMB2 chained packet"); return; } protocol_version = IVAL(p_req->in.buffer, last_hdr_offset + chain_offset); if (protocol_version != SMB2_MAGIC) { DEBUG(2,("Invalid SMB chained packet: protocol prefix: 0x%08X\n", protocol_version)); smbsrv_terminate_connection(p_req->smb_conn, "NON-SMB2 chained packet"); return; } req = smb2srv_init_request(p_req->smb_conn); if (!req) { smbsrv_terminate_connection(p_req->smb_conn, "SMB2 chained packet - no memory"); return; } req->in.buffer = talloc_steal(req, p_req->in.buffer); req->in.size = p_req->in.size; req->request_time = p_req->request_time; req->in.allocated = req->in.size; req->in.hdr = req->in.buffer+ last_hdr_offset + chain_offset; req->in.body = req->in.hdr + SMB2_HDR_BODY; req->in.body_size = req->in.size - (last_hdr_offset+ chain_offset + SMB2_HDR_BODY); req->in.dynamic = NULL; req->seqnum = BVAL(req->in.hdr, SMB2_HDR_MESSAGE_ID); if (req->in.body_size < 2) { /* error handling for this is different for negprot to other packet types */ uint16_t opcode = SVAL(req->in.hdr, SMB2_HDR_OPCODE); if (opcode == SMB2_OP_NEGPROT) { smbsrv_terminate_connection(req->smb_conn, "Bad body size in SMB2 negprot"); } else { smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER); } } buffer_code = SVAL(req->in.body, 0); req->in.body_fixed = (buffer_code & ~1); dynamic_size = req->in.body_size - req->in.body_fixed; if (dynamic_size != 0 && (buffer_code & 1)) { req->in.dynamic = req->in.body + req->in.body_fixed; if (smb2_oob(&req->in, req->in.dynamic, dynamic_size)) { DEBUG(1,("SMB2 chained request invalid dynamic size 0x%x\n", dynamic_size)); smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER); return; } } smb2srv_setup_bufinfo(req); flags = IVAL(req->in.hdr, SMB2_HDR_FLAGS); if (flags & SMB2_HDR_FLAG_CHAINED) { if (p_req->chained_file_handle) { memcpy(req->_chained_file_handle, p_req->_chained_file_handle, sizeof(req->_chained_file_handle)); req->chained_file_handle = req->_chained_file_handle; } req->chained_session_id = p_req->chained_session_id; req->chained_tree_id = p_req->chained_tree_id; req->chain_status = p_req->chain_status; } /* * TODO: - make sure the length field is 64 * - make sure it's a request */ status = smb2srv_reply(req); if (!NT_STATUS_IS_OK(status)) { smbsrv_terminate_connection(req->smb_conn, nt_errstr(status)); talloc_free(req); return; } }
/* setup a reply in req->out with the given word count and initial data buffer size. the caller will then fill in the command words and data before calling req_send_reply() to send the reply on its way */ void smbsrv_setup_reply(struct smbsrv_request *req, unsigned int wct, size_t buflen) { uint16_t flags2; if (req->chain_count != 0) { req_setup_chain_reply(req, wct, buflen); return; } req->out.size = NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct) + buflen; /* over allocate by a small amount */ req->out.allocated = req->out.size + REQ_OVER_ALLOCATION; req->out.buffer = talloc_size(req, req->out.allocated); if (!req->out.buffer) { smbsrv_terminate_connection(req->smb_conn, "allocation failed"); return; } flags2 = FLAGS2_LONG_PATH_COMPONENTS | FLAGS2_EXTENDED_ATTRIBUTES | FLAGS2_IS_LONG_NAME; #define _SMB_FLAGS2_ECHOED_FLAGS ( \ FLAGS2_UNICODE_STRINGS | \ FLAGS2_EXTENDED_SECURITY | \ FLAGS2_SMB_SECURITY_SIGNATURES \ ) flags2 |= (req->flags2 & _SMB_FLAGS2_ECHOED_FLAGS); if (req->smb_conn->negotiate.client_caps & CAP_STATUS32) { flags2 |= FLAGS2_32_BIT_ERROR_CODES; } req->out.hdr = req->out.buffer + NBT_HDR_SIZE; req->out.vwv = req->out.hdr + HDR_VWV; req->out.wct = wct; req->out.data = req->out.vwv + VWV(wct) + 2; req->out.data_size = buflen; req->out.ptr = req->out.data; SIVAL(req->out.hdr, HDR_RCLS, 0); SCVAL(req->out.hdr, HDR_WCT, wct); SSVAL(req->out.vwv, VWV(wct), buflen); memcpy(req->out.hdr, "\377SMB", 4); SCVAL(req->out.hdr,HDR_FLG, FLAG_REPLY | FLAG_CASELESS_PATHNAMES); SSVAL(req->out.hdr,HDR_FLG2, flags2); SSVAL(req->out.hdr,HDR_PIDHIGH,0); memset(req->out.hdr + HDR_SS_FIELD, 0, 10); if (req->in.hdr) { /* copy the cmd, tid, pid, uid and mid from the request */ SCVAL(req->out.hdr,HDR_COM,CVAL(req->in.hdr,HDR_COM)); SSVAL(req->out.hdr,HDR_TID,SVAL(req->in.hdr,HDR_TID)); SSVAL(req->out.hdr,HDR_PID,SVAL(req->in.hdr,HDR_PID)); SSVAL(req->out.hdr,HDR_UID,SVAL(req->in.hdr,HDR_UID)); SSVAL(req->out.hdr,HDR_MID,SVAL(req->in.hdr,HDR_MID)); } else { SCVAL(req->out.hdr,HDR_COM,0); SSVAL(req->out.hdr,HDR_TID,0); SSVAL(req->out.hdr,HDR_PID,0); SSVAL(req->out.hdr,HDR_UID,0); SSVAL(req->out.hdr,HDR_MID,0); } }