static int reply_nt1(char *inbuf, char *outbuf) { /* dual names + lock_and_read + nt SMBs + remote API calls */ int capabilities = CAP_NT_FIND|CAP_LOCK_AND_READ| CAP_LEVEL_II_OPLOCKS; int secword=0; char *p, *q; BOOL negotiate_spnego = False; time_t t = time(NULL); global_encrypted_passwords_negotiated = lp_encrypted_passwords(); /* Check the flags field to see if this is Vista. WinXP sets it and Vista does not. But we have to distinguish from NT which doesn't set it either. */ if ( (SVAL(inbuf, smb_flg2) & FLAGS2_EXTENDED_SECURITY) && ((SVAL(inbuf, smb_flg2) & FLAGS2_UNKNOWN_BIT4) == 0) ) { /* Don't override the SAMBA or CIFSFS arch */ if ((get_remote_arch() != RA_SAMBA) && (get_remote_arch() != RA_CIFSFS)) { set_remote_arch( RA_VISTA ); } } /* do spnego in user level security if the client supports it and we can do encrypted passwords */ if (global_encrypted_passwords_negotiated && (lp_security() != SEC_SHARE) && lp_use_spnego() && (SVAL(inbuf, smb_flg2) & FLAGS2_EXTENDED_SECURITY)) { negotiate_spnego = True; capabilities |= CAP_EXTENDED_SECURITY; add_to_common_flags2(FLAGS2_EXTENDED_SECURITY); /* Ensure FLAGS2_EXTENDED_SECURITY gets set in this reply (already partially constructed. */ SSVAL(outbuf,smb_flg2, SVAL(outbuf,smb_flg2) | FLAGS2_EXTENDED_SECURITY); } capabilities |= CAP_NT_SMBS|CAP_RPC_REMOTE_APIS|CAP_UNICODE; if (lp_unix_extensions()) { capabilities |= CAP_UNIX; } if (lp_large_readwrite() && (SMB_OFF_T_BITS == 64)) capabilities |= CAP_LARGE_READX|CAP_LARGE_WRITEX|CAP_W2K_SMBS; if (SMB_OFF_T_BITS == 64) capabilities |= CAP_LARGE_FILES; if (lp_readraw() && lp_writeraw()) capabilities |= CAP_RAW_MODE; if (lp_nt_status_support()) capabilities |= CAP_STATUS32; if (lp_host_msdfs()) capabilities |= CAP_DFS; if (lp_security() >= SEC_USER) secword |= NEGOTIATE_SECURITY_USER_LEVEL; if (global_encrypted_passwords_negotiated) secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE; if (lp_server_signing()) { if (lp_security() >= SEC_USER) { secword |= NEGOTIATE_SECURITY_SIGNATURES_ENABLED; /* No raw mode with smb signing. */ capabilities &= ~CAP_RAW_MODE; if (lp_server_signing() == Required) secword |=NEGOTIATE_SECURITY_SIGNATURES_REQUIRED; srv_set_signing_negotiated(); } else { DEBUG(0,("reply_nt1: smb signing is incompatible with share level security !\n")); if (lp_server_signing() == Required) { exit_server_cleanly("reply_nt1: smb signing required and share level security selected."); } } } set_message(outbuf,17,0,True); SCVAL(outbuf,smb_vwv1,secword); Protocol = PROTOCOL_NT1; SSVAL(outbuf,smb_vwv1+1,lp_maxmux()); /* maxmpx */ SSVAL(outbuf,smb_vwv2+1,1); /* num vcs */ SIVAL(outbuf,smb_vwv3+1,max_recv); /* max buffer. LOTS! */ SIVAL(outbuf,smb_vwv5+1,0x10000); /* raw size. full 64k */ SIVAL(outbuf,smb_vwv7+1,sys_getpid()); /* session key */ SIVAL(outbuf,smb_vwv9+1,capabilities); /* capabilities */ put_long_date(outbuf+smb_vwv11+1,t); SSVALS(outbuf,smb_vwv15+1,set_server_zone_offset(t)/60); p = q = smb_buf(outbuf); if (!negotiate_spnego) { /* Create a token value and add it to the outgoing packet. */ if (global_encrypted_passwords_negotiated) { /* note that we do not send a challenge at all if we are using plaintext */ get_challenge(p); SCVAL(outbuf,smb_vwv16+1,8); p += 8; } p += srvstr_push(outbuf, p, lp_workgroup(), BUFFER_SIZE - (p-outbuf), STR_UNICODE|STR_TERMINATE|STR_NOALIGN); DEBUG(3,("not using SPNEGO\n")); } else { DATA_BLOB spnego_blob = negprot_spnego(); if (spnego_blob.data == NULL) { return ERROR_NT(NT_STATUS_NO_MEMORY); } memcpy(p, spnego_blob.data, spnego_blob.length); p += spnego_blob.length; data_blob_free(&spnego_blob); SCVAL(outbuf,smb_vwv16+1, 0); DEBUG(3,("using SPNEGO\n")); } SSVAL(outbuf,smb_vwv17, p - q); /* length of challenge+domain strings */ set_message_end(outbuf, p); return (smb_len(outbuf)+4); }
int reply_negprot(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { int outsize = set_message(outbuf,1,0,True); int Index=0; int choice= -1; int protocol; char *p; int bcc = SVAL(smb_buf(inbuf),-2); int arch = ARCH_ALL; static BOOL done_negprot = False; START_PROFILE(SMBnegprot); if (done_negprot) { END_PROFILE(SMBnegprot); exit_server_cleanly("multiple negprot's are not permitted"); } done_negprot = True; p = smb_buf(inbuf)+1; while (p < (smb_buf(inbuf) + bcc)) { Index++; DEBUG(3,("Requested protocol [%s]\n",p)); if (strcsequal(p,"Windows for Workgroups 3.1a")) arch &= ( ARCH_WFWG | ARCH_WIN95 | ARCH_WINNT | ARCH_WIN2K ); else if (strcsequal(p,"DOS LM1.2X002")) arch &= ( ARCH_WFWG | ARCH_WIN95 ); else if (strcsequal(p,"DOS LANMAN2.1")) arch &= ( ARCH_WFWG | ARCH_WIN95 ); else if (strcsequal(p,"NT LM 0.12")) arch &= ( ARCH_WIN95 | ARCH_WINNT | ARCH_WIN2K | ARCH_CIFSFS); else if (strcsequal(p,"SMB 2.001")) arch = ARCH_VISTA; else if (strcsequal(p,"LANMAN2.1")) arch &= ( ARCH_WINNT | ARCH_WIN2K | ARCH_OS2 ); else if (strcsequal(p,"LM1.2X002")) arch &= ( ARCH_WINNT | ARCH_WIN2K | ARCH_OS2 ); else if (strcsequal(p,"MICROSOFT NETWORKS 1.03")) arch &= ARCH_WINNT; else if (strcsequal(p,"XENIX CORE")) arch &= ( ARCH_WINNT | ARCH_OS2 ); else if (strcsequal(p,"Samba")) { arch = ARCH_SAMBA; break; } else if (strcsequal(p,"POSIX 2")) { arch = ARCH_CIFSFS; break; } p += strlen(p) + 2; } /* CIFSFS can send one arch only, NT LM 0.12. */ if (Index == 1 && (arch & ARCH_CIFSFS)) { arch = ARCH_CIFSFS; } switch ( arch ) { case ARCH_CIFSFS: set_remote_arch(RA_CIFSFS); break; case ARCH_SAMBA: set_remote_arch(RA_SAMBA); break; case ARCH_WFWG: set_remote_arch(RA_WFWG); break; case ARCH_WIN95: set_remote_arch(RA_WIN95); break; case ARCH_WINNT: if(SVAL(inbuf,smb_flg2)==FLAGS2_WIN2K_SIGNATURE) set_remote_arch(RA_WIN2K); else set_remote_arch(RA_WINNT); break; case ARCH_WIN2K: /* Vista may have been set in the negprot so don't override it here */ if ( get_remote_arch() != RA_VISTA ) set_remote_arch(RA_WIN2K); break; case ARCH_VISTA: set_remote_arch(RA_VISTA); break; case ARCH_OS2: set_remote_arch(RA_OS2); break; default: set_remote_arch(RA_UNKNOWN); break; } /* possibly reload - change of architecture */ reload_services(True); /* moved from the netbios session setup code since we don't have that when the client connects to port 445. Of course there is a small window where we are listening to messages -- jerry */ claim_connection(NULL,"",0,True,FLAG_MSG_GENERAL|FLAG_MSG_SMBD|FLAG_MSG_PRINT_GENERAL); /* Check for protocols, most desirable first */ for (protocol = 0; supported_protocols[protocol].proto_name; protocol++) { p = smb_buf(inbuf)+1; Index = 0; if ((supported_protocols[protocol].protocol_level <= lp_maxprotocol()) && (supported_protocols[protocol].protocol_level >= lp_minprotocol())) while (p < (smb_buf(inbuf) + bcc)) { if (strequal(p,supported_protocols[protocol].proto_name)) choice = Index; Index++; p += strlen(p) + 2; } if(choice != -1) break; } SSVAL(outbuf,smb_vwv0,choice); if(choice != -1) { fstrcpy(remote_proto,supported_protocols[protocol].short_name); reload_services(True); outsize = supported_protocols[protocol].proto_reply_fn(inbuf, outbuf); DEBUG(3,("Selected protocol %s\n",supported_protocols[protocol].proto_name)); } else { DEBUG(0,("No protocol supported !\n")); } SSVAL(outbuf,smb_vwv0,choice); DEBUG( 5, ( "negprot index=%d\n", choice ) ); if ((lp_server_signing() == Required) && (Protocol < PROTOCOL_NT1)) { exit_server_cleanly("SMB signing is required and " "client negotiated a downlevel protocol"); } END_PROFILE(SMBnegprot); return(outsize); }
static void reply_sesssetup_and_X_spnego(struct smb_request *req) { const uint8_t *p; DATA_BLOB in_blob; DATA_BLOB out_blob = data_blob_null; size_t bufrem; char *tmp; const char *native_os; const char *native_lanman; const char *primary_domain; uint16_t data_blob_len = SVAL(req->vwv+7, 0); enum remote_arch_types ra_type = get_remote_arch(); uint64_t vuid = req->vuid; NTSTATUS status = NT_STATUS_OK; struct smbXsrv_connection *xconn = req->xconn; struct smbd_server_connection *sconn = req->sconn; uint16_t action = 0; bool is_authenticated = false; NTTIME now = timeval_to_nttime(&req->request_time); struct smbXsrv_session *session = NULL; uint16_t smb_bufsize = SVAL(req->vwv+2, 0); uint32_t client_caps = IVAL(req->vwv+10, 0); struct smbXsrv_session_auth0 *auth; DEBUG(3,("Doing spnego session setup\n")); if (!xconn->smb1.sessions.done_sesssetup) { global_client_caps = client_caps; if (!(global_client_caps & CAP_STATUS32)) { remove_from_common_flags2(FLAGS2_32_BIT_ERROR_CODES); } } p = req->buf; if (data_blob_len == 0) { /* an invalid request */ reply_nterror(req, nt_status_squash(NT_STATUS_LOGON_FAILURE)); return; } bufrem = smbreq_bufrem(req, p); /* pull the spnego blob */ in_blob = data_blob_const(p, MIN(bufrem, data_blob_len)); #if 0 file_save("negotiate.dat", in_blob.data, in_blob.length); #endif p = req->buf + in_blob.length; p += srvstr_pull_req_talloc(talloc_tos(), req, &tmp, p, STR_TERMINATE); native_os = tmp ? tmp : ""; p += srvstr_pull_req_talloc(talloc_tos(), req, &tmp, p, STR_TERMINATE); native_lanman = tmp ? tmp : ""; p += srvstr_pull_req_talloc(talloc_tos(), req, &tmp, p, STR_TERMINATE); primary_domain = tmp ? tmp : ""; DEBUG(3,("NativeOS=[%s] NativeLanMan=[%s] PrimaryDomain=[%s]\n", native_os, native_lanman, primary_domain)); if ( ra_type == RA_WIN2K ) { /* Vista sets neither the OS or lanman strings */ if ( !strlen(native_os) && !strlen(native_lanman) ) set_remote_arch(RA_VISTA); /* Windows 2003 doesn't set the native lanman string, but does set primary domain which is a bug I think */ if ( !strlen(native_lanman) ) { ra_lanman_string( primary_domain ); } else { ra_lanman_string( native_lanman ); } } else if ( ra_type == RA_VISTA ) { if ( strncmp(native_os, "Mac OS X", 8) == 0 ) { set_remote_arch(RA_OSX); } } if (vuid != 0) { status = smb1srv_session_lookup(xconn, vuid, now, &session); if (NT_STATUS_EQUAL(status, NT_STATUS_USER_SESSION_DELETED)) { reply_force_doserror(req, ERRSRV, ERRbaduid); return; } if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED)) { status = NT_STATUS_OK; } if (NT_STATUS_IS_OK(status)) { session->status = NT_STATUS_MORE_PROCESSING_REQUIRED; status = NT_STATUS_MORE_PROCESSING_REQUIRED; TALLOC_FREE(session->pending_auth); } if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { reply_nterror(req, nt_status_squash(status)); return; } } if (session == NULL) { /* create a new session */ status = smbXsrv_session_create(xconn, now, &session); if (!NT_STATUS_IS_OK(status)) { reply_nterror(req, nt_status_squash(status)); return; } } status = smbXsrv_session_find_auth(session, xconn, now, &auth); if (!NT_STATUS_IS_OK(status)) { status = smbXsrv_session_create_auth(session, xconn, now, 0, /* flags */ 0, /* security */ &auth); if (!NT_STATUS_IS_OK(status)) { reply_nterror(req, nt_status_squash(status)); return; } } if (auth->gensec == NULL) { status = auth_generic_prepare(session, xconn->remote_address, xconn->local_address, "SMB", &auth->gensec); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(session); reply_nterror(req, nt_status_squash(status)); return; } gensec_want_feature(auth->gensec, GENSEC_FEATURE_SESSION_KEY); gensec_want_feature(auth->gensec, GENSEC_FEATURE_UNIX_TOKEN); gensec_want_feature(auth->gensec, GENSEC_FEATURE_SMB_TRANSPORT); status = gensec_start_mech_by_oid(auth->gensec, GENSEC_OID_SPNEGO); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("Failed to start SPNEGO handler!\n")); TALLOC_FREE(session);; reply_nterror(req, nt_status_squash(status)); return; } } become_root(); status = gensec_update(auth->gensec, talloc_tos(), in_blob, &out_blob); unbecome_root(); if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { TALLOC_FREE(session); reply_nterror(req, nt_status_squash(status)); return; } if (NT_STATUS_IS_OK(status) && session->global->auth_session_info == NULL) { struct auth_session_info *session_info = NULL; status = gensec_session_info(auth->gensec, session, &session_info); if (!NT_STATUS_IS_OK(status)) { DEBUG(1,("Failed to generate session_info " "(user and group token) for session setup: %s\n", nt_errstr(status))); data_blob_free(&out_blob); TALLOC_FREE(session); reply_nterror(req, nt_status_squash(status)); return; } if (security_session_user_level(session_info, NULL) == SECURITY_GUEST) { action |= SMB_SETUP_GUEST; } if (session_info->session_key.length > 0) { struct smbXsrv_session *x = session; /* * Note: the SMB1 signing key is not truncated to 16 byte! */ x->global->signing_key = data_blob_dup_talloc(x->global, session_info->session_key); if (x->global->signing_key.data == NULL) { data_blob_free(&out_blob); TALLOC_FREE(session); reply_nterror(req, NT_STATUS_NO_MEMORY); return; } /* * clear the session key * the first tcon will add setup the application key */ data_blob_clear_free(&session_info->session_key); } session->compat = talloc_zero(session, struct user_struct); if (session->compat == NULL) { data_blob_free(&out_blob); TALLOC_FREE(session); reply_nterror(req, NT_STATUS_NO_MEMORY); return; } session->compat->session = session; session->compat->homes_snum = -1; session->compat->session_info = session_info; session->compat->session_keystr = NULL; session->compat->vuid = session->global->session_wire_id; DLIST_ADD(sconn->users, session->compat); sconn->num_users++; if (security_session_user_level(session_info, NULL) >= SECURITY_USER) { is_authenticated = true; session->compat->homes_snum = register_homes_share(session_info->unix_info->unix_name); } if (srv_is_signing_negotiated(xconn) && is_authenticated && session->global->signing_key.length > 0) { /* * Try and turn on server signing on the first non-guest * sessionsetup. */ srv_set_signing(xconn, session->global->signing_key, data_blob_null); } set_current_user_info(session_info->unix_info->sanitized_username, session_info->unix_info->unix_name, session_info->info->domain_name); session->status = NT_STATUS_OK; session->global->auth_session_info = talloc_move(session->global, &session_info); session->global->auth_session_info_seqnum += 1; session->global->channels[0].auth_session_info_seqnum = session->global->auth_session_info_seqnum; session->global->auth_time = now; if (client_caps & CAP_DYNAMIC_REAUTH) { session->global->expiration_time = gensec_expire_time(auth->gensec); } else { session->global->expiration_time = GENSEC_EXPIRE_TIME_INFINITY; } if (!session_claim(session)) { DEBUG(1, ("smb1: Failed to claim session for vuid=%llu\n", (unsigned long long)session->compat->vuid)); data_blob_free(&out_blob); TALLOC_FREE(session); reply_nterror(req, NT_STATUS_LOGON_FAILURE); return; } status = smbXsrv_session_update(session); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("smb1: Failed to update session for vuid=%llu - %s\n", (unsigned long long)session->compat->vuid, nt_errstr(status))); data_blob_free(&out_blob); TALLOC_FREE(session); reply_nterror(req, NT_STATUS_LOGON_FAILURE); return; } if (!xconn->smb1.sessions.done_sesssetup) { if (smb_bufsize < SMB_BUFFER_SIZE_MIN) { reply_force_doserror(req, ERRSRV, ERRerror); return; } xconn->smb1.sessions.max_send = smb_bufsize; xconn->smb1.sessions.done_sesssetup = true; } /* current_user_info is changed on new vuid */ reload_services(sconn, conn_snum_used, true); } else if (NT_STATUS_IS_OK(status)) {
NTSTATUS smbd_smb2_request_process_negprot(struct smbd_smb2_request *req) { const uint8_t *inbody; const uint8_t *indyn = NULL; int i = req->current_idx; DATA_BLOB outbody; DATA_BLOB outdyn; DATA_BLOB negprot_spnego_blob; uint16_t security_offset; DATA_BLOB security_buffer; size_t expected_body_size = 0x24; size_t body_size; size_t expected_dyn_size = 0; size_t c; uint16_t security_mode; uint16_t dialect_count; uint16_t dialect = 0; uint32_t capabilities; /* TODO: drop the connection with INVALI_PARAMETER */ if (req->in.vector[i+1].iov_len != (expected_body_size & 0xFFFFFFFE)) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } inbody = (const uint8_t *)req->in.vector[i+1].iov_base; body_size = SVAL(inbody, 0x00); if (body_size != expected_body_size) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } dialect_count = SVAL(inbody, 0x02); if (dialect_count == 0) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } expected_dyn_size = dialect_count * 2; if (req->in.vector[i+2].iov_len < expected_dyn_size) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } indyn = (const uint8_t *)req->in.vector[i+2].iov_base; for (c=0; c < dialect_count; c++) { dialect = SVAL(indyn, c*2); if (dialect == SMB2_DIALECT_REVISION_202) { break; } } if (dialect != SMB2_DIALECT_REVISION_202) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } set_Protocol(PROTOCOL_SMB2); if (get_remote_arch() != RA_SAMBA) { set_remote_arch(RA_VISTA); } /* negprot_spnego() returns a the server guid in the first 16 bytes */ negprot_spnego_blob = negprot_spnego(); if (negprot_spnego_blob.data == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } talloc_steal(req, negprot_spnego_blob.data); if (negprot_spnego_blob.length < 16) { return smbd_smb2_request_error(req, NT_STATUS_INTERNAL_ERROR); } security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED; if (lp_server_signing() == Required) { security_mode |= SMB2_NEGOTIATE_SIGNING_REQUIRED; } capabilities = 0; if (lp_host_msdfs()) { capabilities |= SMB2_CAP_DFS; } security_offset = SMB2_HDR_BODY + 0x40; #if 1 /* Try SPNEGO auth... */ security_buffer = data_blob_const(negprot_spnego_blob.data + 16, negprot_spnego_blob.length - 16); #else /* for now we want raw NTLMSSP */ security_buffer = data_blob_const(NULL, 0); #endif outbody = data_blob_talloc(req->out.vector, NULL, 0x40); if (outbody.data == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } SSVAL(outbody.data, 0x00, 0x40 + 1); /* struct size */ SSVAL(outbody.data, 0x02, security_mode); /* security mode */ SSVAL(outbody.data, 0x04, dialect); /* dialect revision */ SSVAL(outbody.data, 0x06, 0); /* reserved */ memcpy(outbody.data + 0x08, negprot_spnego_blob.data, 16); /* server guid */ SIVAL(outbody.data, 0x18, capabilities); /* capabilities */ SIVAL(outbody.data, 0x1C, 0x00010000); /* max transact size */ SIVAL(outbody.data, 0x20, 0x00010000); /* max read size */ SIVAL(outbody.data, 0x24, 0x00010000); /* max write size */ SBVAL(outbody.data, 0x28, 0); /* system time */ SBVAL(outbody.data, 0x30, 0); /* server start time */ SSVAL(outbody.data, 0x38, security_offset); /* security buffer offset */ SSVAL(outbody.data, 0x3A, security_buffer.length); /* security buffer length */ SIVAL(outbody.data, 0x3C, 0); /* reserved */ outdyn = security_buffer; return smbd_smb2_request_done(req, outbody, &outdyn); }
static void reply_nt1(struct smb_request *req, uint16 choice) { /* dual names + lock_and_read + nt SMBs + remote API calls */ int capabilities = CAP_NT_FIND|CAP_LOCK_AND_READ| CAP_LEVEL_II_OPLOCKS; int secword=0; bool negotiate_spnego = False; struct timespec ts; ssize_t ret; struct smbd_server_connection *sconn = req->sconn; bool signing_enabled = false; bool signing_required = false; sconn->smb1.negprot.encrypted_passwords = lp_encrypt_passwords(); /* Check the flags field to see if this is Vista. WinXP sets it and Vista does not. But we have to distinguish from NT which doesn't set it either. */ if ( (req->flags2 & FLAGS2_EXTENDED_SECURITY) && ((req->flags2 & FLAGS2_SMB_SECURITY_SIGNATURES_REQUIRED) == 0) ) { if (get_remote_arch() != RA_SAMBA) { set_remote_arch( RA_VISTA ); } } reply_outbuf(req,17,0); /* do spnego in user level security if the client supports it and we can do encrypted passwords */ if (sconn->smb1.negprot.encrypted_passwords && lp_use_spnego() && (req->flags2 & FLAGS2_EXTENDED_SECURITY)) { negotiate_spnego = True; capabilities |= CAP_EXTENDED_SECURITY; add_to_common_flags2(FLAGS2_EXTENDED_SECURITY); /* Ensure FLAGS2_EXTENDED_SECURITY gets set in this reply (already partially constructed. */ SSVAL(req->outbuf, smb_flg2, req->flags2 | FLAGS2_EXTENDED_SECURITY); } capabilities |= CAP_NT_SMBS|CAP_RPC_REMOTE_APIS; if (lp_unicode()) { capabilities |= CAP_UNICODE; } if (lp_unix_extensions()) { capabilities |= CAP_UNIX; } if (lp_large_readwrite()) capabilities |= CAP_LARGE_READX|CAP_LARGE_WRITEX|CAP_W2K_SMBS; capabilities |= CAP_LARGE_FILES; if (!lp_async_smb_echo_handler() && lp_read_raw() && lp_write_raw()) capabilities |= CAP_RAW_MODE; if (lp_nt_status_support()) capabilities |= CAP_STATUS32; if (lp_host_msdfs()) capabilities |= CAP_DFS; secword |= NEGOTIATE_SECURITY_USER_LEVEL; if (sconn->smb1.negprot.encrypted_passwords) { secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE; } signing_enabled = smb_signing_is_allowed(req->sconn->smb1.signing_state); signing_required = smb_signing_is_mandatory(req->sconn->smb1.signing_state); if (signing_enabled) { secword |= NEGOTIATE_SECURITY_SIGNATURES_ENABLED; /* No raw mode with smb signing. */ capabilities &= ~CAP_RAW_MODE; if (signing_required) { secword |=NEGOTIATE_SECURITY_SIGNATURES_REQUIRED; } } SSVAL(req->outbuf,smb_vwv0,choice); SCVAL(req->outbuf,smb_vwv1,secword); smbXsrv_connection_init_tables(req->sconn->conn, PROTOCOL_NT1); SSVAL(req->outbuf,smb_vwv1+1, lp_max_mux()); /* maxmpx */ SSVAL(req->outbuf,smb_vwv2+1, 1); /* num vcs */ SIVAL(req->outbuf,smb_vwv3+1, sconn->smb1.negprot.max_recv); /* max buffer. LOTS! */ SIVAL(req->outbuf,smb_vwv5+1, 0x10000); /* raw size. full 64k */ SIVAL(req->outbuf,smb_vwv7+1, getpid()); /* session key */ SIVAL(req->outbuf,smb_vwv9+1, capabilities); /* capabilities */ clock_gettime(CLOCK_REALTIME,&ts); put_long_date_timespec(TIMESTAMP_SET_NT_OR_BETTER,(char *)req->outbuf+smb_vwv11+1,ts); SSVALS(req->outbuf,smb_vwv15+1,set_server_zone_offset(ts.tv_sec)/60); if (!negotiate_spnego) { /* Create a token value and add it to the outgoing packet. */ if (sconn->smb1.negprot.encrypted_passwords) { uint8 chal[8]; /* note that we do not send a challenge at all if we are using plaintext */ get_challenge(sconn, chal); ret = message_push_blob( &req->outbuf, data_blob_const(chal, sizeof(chal))); if (ret == -1) { DEBUG(0, ("Could not push challenge\n")); reply_nterror(req, NT_STATUS_NO_MEMORY); return; } SCVAL(req->outbuf, smb_vwv16+1, ret); } ret = message_push_string(&req->outbuf, lp_workgroup(), STR_UNICODE|STR_TERMINATE |STR_NOALIGN); if (ret == -1) { DEBUG(0, ("Could not push workgroup string\n")); reply_nterror(req, NT_STATUS_NO_MEMORY); return; } ret = message_push_string(&req->outbuf, lp_netbios_name(), STR_UNICODE|STR_TERMINATE |STR_NOALIGN); if (ret == -1) { DEBUG(0, ("Could not push netbios name string\n")); reply_nterror(req, NT_STATUS_NO_MEMORY); return; } DEBUG(3,("not using SPNEGO\n")); } else { DATA_BLOB spnego_blob = negprot_spnego(req, req->sconn); if (spnego_blob.data == NULL) { reply_nterror(req, NT_STATUS_NO_MEMORY); return; } ret = message_push_blob(&req->outbuf, spnego_blob); if (ret == -1) { DEBUG(0, ("Could not push spnego blob\n")); reply_nterror(req, NT_STATUS_NO_MEMORY); return; } data_blob_free(&spnego_blob); SCVAL(req->outbuf,smb_vwv16+1, 0); DEBUG(3,("using SPNEGO\n")); } return; }
void reply_negprot(struct smb_request *req) { int choice= -1; int chosen_level = -1; int protocol; const char *p; int arch = ARCH_ALL; int num_cliprotos; char **cliprotos; int i; size_t converted_size; struct smbd_server_connection *sconn = req->sconn; START_PROFILE(SMBnegprot); if (sconn->smb1.negprot.done) { END_PROFILE(SMBnegprot); exit_server_cleanly("multiple negprot's are not permitted"); } sconn->smb1.negprot.done = true; 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<num_cliprotos; i++) { if (strcsequal(cliprotos[i], "Windows for Workgroups 3.1a")) arch &= ( ARCH_WFWG | ARCH_WIN95 | ARCH_WINNT | ARCH_WIN2K ); else if (strcsequal(cliprotos[i], "DOS LM1.2X002")) arch &= ( ARCH_WFWG | ARCH_WIN95 ); else if (strcsequal(cliprotos[i], "DOS LANMAN2.1")) arch &= ( ARCH_WFWG | ARCH_WIN95 ); else if (strcsequal(cliprotos[i], "NT LM 0.12")) arch &= ( ARCH_WIN95 | ARCH_WINNT | ARCH_WIN2K | ARCH_CIFSFS); else if (strcsequal(cliprotos[i], "SMB 2.001")) arch = ARCH_VISTA; else if (strcsequal(cliprotos[i], "LANMAN2.1")) arch &= ( ARCH_WINNT | ARCH_WIN2K | ARCH_OS2 ); else if (strcsequal(cliprotos[i], "LM1.2X002")) arch &= ( ARCH_WINNT | ARCH_WIN2K | ARCH_OS2 ); else if (strcsequal(cliprotos[i], "MICROSOFT NETWORKS 1.03")) arch &= ARCH_WINNT; else if (strcsequal(cliprotos[i], "XENIX CORE")) arch &= ( ARCH_WINNT | ARCH_OS2 ); else if (strcsequal(cliprotos[i], "Samba")) { arch = ARCH_SAMBA; break; } else if (strcsequal(cliprotos[i], "POSIX 2")) { arch = ARCH_CIFSFS; break; } } /* CIFSFS can send one arch only, NT LM 0.12. */ if (i == 1 && (arch & ARCH_CIFSFS)) { arch = ARCH_CIFSFS; } switch ( arch ) { case ARCH_CIFSFS: set_remote_arch(RA_CIFSFS); break; case ARCH_SAMBA: set_remote_arch(RA_SAMBA); break; case ARCH_WFWG: set_remote_arch(RA_WFWG); break; case ARCH_WIN95: set_remote_arch(RA_WIN95); break; case ARCH_WINNT: if(req->flags2 == FLAGS2_WIN2K_SIGNATURE) set_remote_arch(RA_WIN2K); else set_remote_arch(RA_WINNT); break; case ARCH_WIN2K: /* Vista may have been set in the negprot so don't override it here */ if ( get_remote_arch() != RA_VISTA ) set_remote_arch(RA_WIN2K); break; case ARCH_VISTA: set_remote_arch(RA_VISTA); break; case ARCH_OS2: set_remote_arch(RA_OS2); break; default: set_remote_arch(RA_UNKNOWN); break; } /* possibly reload - change of architecture */ reload_services(sconn, conn_snum_used, true); /* moved from the netbios session setup code since we don't have that when the client connects to port 445. Of course there is a small window where we are listening to messages -- jerry */ serverid_register(messaging_server_id(sconn->msg_ctx), FLAG_MSG_GENERAL|FLAG_MSG_SMBD |FLAG_MSG_PRINT_GENERAL); /* Check for protocols, most desirable first */ for (protocol = 0; supported_protocols[protocol].proto_name; protocol++) { i = 0; if ((supported_protocols[protocol].protocol_level <= lp_server_max_protocol()) && (supported_protocols[protocol].protocol_level >= lp_server_min_protocol())) while (i < num_cliprotos) { if (strequal(cliprotos[i],supported_protocols[protocol].proto_name)) { choice = i; chosen_level = supported_protocols[protocol].protocol_level; } i++; } if(choice != -1) break; } if(choice != -1) { fstrcpy(remote_proto,supported_protocols[protocol].short_name); reload_services(sconn, conn_snum_used, true); supported_protocols[protocol].proto_reply_fn(req, choice); DEBUG(3,("Selected protocol %s\n",supported_protocols[protocol].proto_name)); } else { DEBUG(0,("No protocol supported !\n")); reply_outbuf(req, 1, 0); SSVAL(req->outbuf, smb_vwv0, choice); } DEBUG( 5, ( "negprot index=%d\n", choice ) ); if ((lp_server_signing() == SMB_SIGNING_REQUIRED) && (chosen_level < PROTOCOL_NT1)) { exit_server_cleanly("SMB signing is required and " "client negotiated a downlevel protocol"); } TALLOC_FREE(cliprotos); if (lp_async_smb_echo_handler() && (chosen_level < PROTOCOL_SMB2_02) && !fork_echo_handler(sconn)) { exit_server("Failed to fork echo handler"); } END_PROFILE(SMBnegprot); return; }
NTSTATUS smbd_smb2_request_process_negprot(struct smbd_smb2_request *req) { struct smbXsrv_connection *xconn = req->xconn; NTSTATUS status; const uint8_t *inbody; const uint8_t *indyn = NULL; DATA_BLOB outbody; DATA_BLOB outdyn; DATA_BLOB negprot_spnego_blob; uint16_t security_offset; DATA_BLOB security_buffer; size_t expected_dyn_size = 0; size_t c; uint16_t security_mode; uint16_t dialect_count; uint16_t in_security_mode; uint32_t in_capabilities; DATA_BLOB in_guid_blob; struct GUID in_guid; struct smb2_negotiate_contexts in_c = { .num_contexts = 0, }; struct smb2_negotiate_context *in_preauth = NULL; struct smb2_negotiate_context *in_cipher = NULL; struct smb2_negotiate_contexts out_c = { .num_contexts = 0, }; DATA_BLOB out_negotiate_context_blob = data_blob_null; uint32_t out_negotiate_context_offset = 0; uint16_t out_negotiate_context_count = 0; uint16_t dialect = 0; uint32_t capabilities; DATA_BLOB out_guid_blob; struct GUID out_guid; enum protocol_types protocol = PROTOCOL_NONE; uint32_t max_limit; uint32_t max_trans = lp_smb2_max_trans(); uint32_t max_read = lp_smb2_max_read(); uint32_t max_write = lp_smb2_max_write(); NTTIME now = timeval_to_nttime(&req->request_time); bool signing_required = true; bool ok; status = smbd_smb2_request_verify_sizes(req, 0x24); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } inbody = SMBD_SMB2_IN_BODY_PTR(req); dialect_count = SVAL(inbody, 0x02); in_security_mode = SVAL(inbody, 0x04); in_capabilities = IVAL(inbody, 0x08); in_guid_blob = data_blob_const(inbody + 0x0C, 16); if (dialect_count == 0) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } status = GUID_from_ndr_blob(&in_guid_blob, &in_guid); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } expected_dyn_size = dialect_count * 2; if (SMBD_SMB2_IN_DYN_LEN(req) < expected_dyn_size) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } indyn = SMBD_SMB2_IN_DYN_PTR(req); protocol = smbd_smb2_protocol_dialect_match(indyn, dialect_count, &dialect); for (c=0; protocol == PROTOCOL_NONE && c < dialect_count; c++) { if (lp_server_max_protocol() < PROTOCOL_SMB2_10) { break; } dialect = SVAL(indyn, c*2); if (dialect == SMB2_DIALECT_REVISION_2FF) { if (xconn->smb2.allow_2ff) { xconn->smb2.allow_2ff = false; protocol = PROTOCOL_SMB2_10; break; } } } if (protocol == PROTOCOL_NONE) { return smbd_smb2_request_error(req, NT_STATUS_NOT_SUPPORTED); } if (protocol >= PROTOCOL_SMB3_10) { uint32_t in_negotiate_context_offset = 0; uint16_t in_negotiate_context_count = 0; DATA_BLOB in_negotiate_context_blob = data_blob_null; size_t ofs; in_negotiate_context_offset = IVAL(inbody, 0x1C); in_negotiate_context_count = SVAL(inbody, 0x20); ofs = SMB2_HDR_BODY; ofs += SMBD_SMB2_IN_BODY_LEN(req); ofs += expected_dyn_size; if ((ofs % 8) != 0) { ofs += 8 - (ofs % 8); } if (in_negotiate_context_offset != ofs) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } ofs -= SMB2_HDR_BODY; ofs -= SMBD_SMB2_IN_BODY_LEN(req); if (SMBD_SMB2_IN_DYN_LEN(req) < ofs) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } in_negotiate_context_blob = data_blob_const(indyn, SMBD_SMB2_IN_DYN_LEN(req)); in_negotiate_context_blob.data += ofs; in_negotiate_context_blob.length -= ofs; status = smb2_negotiate_context_parse(req, in_negotiate_context_blob, &in_c); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } if (in_negotiate_context_count != in_c.num_contexts) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } } if ((dialect != SMB2_DIALECT_REVISION_2FF) && (protocol >= PROTOCOL_SMB2_10) && !GUID_all_zero(&in_guid)) { ok = remote_arch_cache_update(&in_guid); if (!ok) { return smbd_smb2_request_error( req, NT_STATUS_UNSUCCESSFUL); } } switch (get_remote_arch()) { case RA_VISTA: case RA_SAMBA: case RA_CIFSFS: case RA_OSX: break; default: set_remote_arch(RA_VISTA); break; } fstr_sprintf(remote_proto, "SMB%X_%02X", (dialect >> 8) & 0xFF, dialect & 0xFF); reload_services(req->sconn, conn_snum_used, true); DEBUG(3,("Selected protocol %s\n", remote_proto)); in_preauth = smb2_negotiate_context_find(&in_c, SMB2_PREAUTH_INTEGRITY_CAPABILITIES); if (protocol >= PROTOCOL_SMB3_10 && in_preauth == NULL) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } in_cipher = smb2_negotiate_context_find(&in_c, SMB2_ENCRYPTION_CAPABILITIES); /* negprot_spnego() returns a the server guid in the first 16 bytes */ negprot_spnego_blob = negprot_spnego(req, xconn); if (negprot_spnego_blob.data == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } if (negprot_spnego_blob.length < 16) { return smbd_smb2_request_error(req, NT_STATUS_INTERNAL_ERROR); } security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED; /* * We use xconn->smb1.signing_state as that's already present * and used lpcfg_server_signing_allowed() to get the correct * defaults, e.g. signing_required for an ad_dc. */ signing_required = smb_signing_is_mandatory(xconn->smb1.signing_state); if (signing_required) { security_mode |= SMB2_NEGOTIATE_SIGNING_REQUIRED; } capabilities = 0; if (lp_host_msdfs()) { capabilities |= SMB2_CAP_DFS; } if (protocol >= PROTOCOL_SMB2_10 && lp_smb2_leases() && lp_oplocks(GLOBAL_SECTION_SNUM) && !lp_kernel_oplocks(GLOBAL_SECTION_SNUM)) { capabilities |= SMB2_CAP_LEASING; } if ((protocol >= PROTOCOL_SMB2_24) && (lp_smb_encrypt(-1) != SMB_SIGNING_OFF) && (in_capabilities & SMB2_CAP_ENCRYPTION)) { capabilities |= SMB2_CAP_ENCRYPTION; } /* * 0x10000 (65536) is the maximum allowed message size * for SMB 2.0 */ max_limit = 0x10000; if (protocol >= PROTOCOL_SMB2_10) { int p = 0; if (tsocket_address_is_inet(req->sconn->local_address, "ip")) { p = tsocket_address_inet_port(req->sconn->local_address); } /* largeMTU is not supported over NBT (tcp port 139) */ if (p != NBT_SMB_PORT) { capabilities |= SMB2_CAP_LARGE_MTU; xconn->smb2.credits.multicredit = true; /* * We allow up to almost 16MB. * * The maximum PDU size is 0xFFFFFF (16776960) * and we need some space for the header. */ max_limit = 0xFFFF00; } } /* * the defaults are 8MB, but we'll limit this to max_limit based on * the dialect (64kb for SMB 2.0, 8MB for SMB >= 2.1 with LargeMTU) * * user configured values exceeding the limits will be overwritten, * only smaller values will be accepted */ max_trans = MIN(max_limit, lp_smb2_max_trans()); max_read = MIN(max_limit, lp_smb2_max_read()); max_write = MIN(max_limit, lp_smb2_max_write()); if (in_preauth != NULL) { size_t needed = 4; uint16_t hash_count; uint16_t salt_length; uint16_t selected_preauth = 0; const uint8_t *p; uint8_t buf[38]; DATA_BLOB b; size_t i; if (in_preauth->data.length < needed) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } hash_count = SVAL(in_preauth->data.data, 0); salt_length = SVAL(in_preauth->data.data, 2); if (hash_count == 0) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } p = in_preauth->data.data + needed; needed += hash_count * 2; needed += salt_length; if (in_preauth->data.length < needed) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } for (i=0; i < hash_count; i++) { uint16_t v; v = SVAL(p, 0); p += 2; if (v == SMB2_PREAUTH_INTEGRITY_SHA512) { selected_preauth = v; break; } } if (selected_preauth == 0) { return smbd_smb2_request_error(req, NT_STATUS_SMB_NO_PREAUTH_INTEGRITY_HASH_OVERLAP); } SSVAL(buf, 0, 1); /* HashAlgorithmCount */ SSVAL(buf, 2, 32); /* SaltLength */ SSVAL(buf, 4, selected_preauth); generate_random_buffer(buf + 6, 32); b = data_blob_const(buf, sizeof(buf)); status = smb2_negotiate_context_add(req, &out_c, SMB2_PREAUTH_INTEGRITY_CAPABILITIES, b); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } req->preauth = &req->xconn->smb2.preauth; } if (in_cipher != NULL) { size_t needed = 2; uint16_t cipher_count; const uint8_t *p; uint8_t buf[4]; DATA_BLOB b; size_t i; bool aes_128_ccm_supported = false; bool aes_128_gcm_supported = false; capabilities &= ~SMB2_CAP_ENCRYPTION; if (in_cipher->data.length < needed) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } cipher_count = SVAL(in_cipher->data.data, 0); if (cipher_count == 0) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } p = in_cipher->data.data + needed; needed += cipher_count * 2; if (in_cipher->data.length < needed) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } for (i=0; i < cipher_count; i++) { uint16_t v; v = SVAL(p, 0); p += 2; if (v == SMB2_ENCRYPTION_AES128_GCM) { aes_128_gcm_supported = true; } if (v == SMB2_ENCRYPTION_AES128_CCM) { aes_128_ccm_supported = true; } } /* * For now we preferr CCM because our implementation * is faster than GCM, see bug #11451. */ if (aes_128_ccm_supported) { xconn->smb2.server.cipher = SMB2_ENCRYPTION_AES128_CCM; } else if (aes_128_gcm_supported) { xconn->smb2.server.cipher = SMB2_ENCRYPTION_AES128_GCM; } SSVAL(buf, 0, 1); /* ChiperCount */ SSVAL(buf, 2, xconn->smb2.server.cipher); b = data_blob_const(buf, sizeof(buf)); status = smb2_negotiate_context_add(req, &out_c, SMB2_ENCRYPTION_CAPABILITIES, b); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } } if (capabilities & SMB2_CAP_ENCRYPTION) { xconn->smb2.server.cipher = SMB2_ENCRYPTION_AES128_CCM; } if (protocol >= PROTOCOL_SMB2_22 && xconn->client->server_multi_channel_enabled) { if (in_capabilities & SMB2_CAP_MULTI_CHANNEL) { capabilities |= SMB2_CAP_MULTI_CHANNEL; } } security_offset = SMB2_HDR_BODY + 0x40; #if 1 /* Try SPNEGO auth... */ security_buffer = data_blob_const(negprot_spnego_blob.data + 16, negprot_spnego_blob.length - 16); #else /* for now we want raw NTLMSSP */ security_buffer = data_blob_const(NULL, 0); #endif if (out_c.num_contexts != 0) { status = smb2_negotiate_context_push(req, &out_negotiate_context_blob, out_c); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } } if (out_negotiate_context_blob.length != 0) { static const uint8_t zeros[8]; size_t pad = 0; size_t ofs; outdyn = data_blob_dup_talloc(req, security_buffer); if (outdyn.length != security_buffer.length) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } ofs = security_offset + security_buffer.length; if ((ofs % 8) != 0) { pad = 8 - (ofs % 8); } ofs += pad; ok = data_blob_append(req, &outdyn, zeros, pad); if (!ok) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } ok = data_blob_append(req, &outdyn, out_negotiate_context_blob.data, out_negotiate_context_blob.length); if (!ok) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } out_negotiate_context_offset = ofs; out_negotiate_context_count = out_c.num_contexts; } else { outdyn = security_buffer; } out_guid_blob = data_blob_const(negprot_spnego_blob.data, 16); status = GUID_from_ndr_blob(&out_guid_blob, &out_guid); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } outbody = smbd_smb2_generate_outbody(req, 0x40); if (outbody.data == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } SSVAL(outbody.data, 0x00, 0x40 + 1); /* struct size */ SSVAL(outbody.data, 0x02, security_mode); /* security mode */ SSVAL(outbody.data, 0x04, dialect); /* dialect revision */ SSVAL(outbody.data, 0x06, out_negotiate_context_count); /* reserved/NegotiateContextCount */ memcpy(outbody.data + 0x08, out_guid_blob.data, 16); /* server guid */ SIVAL(outbody.data, 0x18, capabilities); /* capabilities */ SIVAL(outbody.data, 0x1C, max_trans); /* max transact size */ SIVAL(outbody.data, 0x20, max_read); /* max read size */ SIVAL(outbody.data, 0x24, max_write); /* max write size */ SBVAL(outbody.data, 0x28, now); /* system time */ SBVAL(outbody.data, 0x30, 0); /* server start time */ SSVAL(outbody.data, 0x38, security_offset); /* security buffer offset */ SSVAL(outbody.data, 0x3A, security_buffer.length); /* security buffer length */ SIVAL(outbody.data, 0x3C, out_negotiate_context_offset); /* reserved/NegotiateContextOffset */ req->sconn->using_smb2 = true; if (dialect != SMB2_DIALECT_REVISION_2FF) { struct smbXsrv_client_global0 *global0 = NULL; status = smbXsrv_connection_init_tables(xconn, protocol); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } xconn->smb2.client.capabilities = in_capabilities; xconn->smb2.client.security_mode = in_security_mode; xconn->smb2.client.guid = in_guid; xconn->smb2.client.num_dialects = dialect_count; xconn->smb2.client.dialects = talloc_array(xconn, uint16_t, dialect_count); if (xconn->smb2.client.dialects == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } for (c=0; c < dialect_count; c++) { xconn->smb2.client.dialects[c] = SVAL(indyn, c*2); } xconn->smb2.server.capabilities = capabilities; xconn->smb2.server.security_mode = security_mode; xconn->smb2.server.guid = out_guid; xconn->smb2.server.dialect = dialect; xconn->smb2.server.max_trans = max_trans; xconn->smb2.server.max_read = max_read; xconn->smb2.server.max_write = max_write; if (xconn->protocol < PROTOCOL_SMB2_10) { /* * SMB2_02 doesn't support client guids */ return smbd_smb2_request_done(req, outbody, &outdyn); } if (!xconn->client->server_multi_channel_enabled) { /* * Only deal with the client guid database * if multi-channel is enabled. */ return smbd_smb2_request_done(req, outbody, &outdyn); } if (xconn->smb2.client.guid_verified) { /* * The connection was passed from another * smbd process. */ return smbd_smb2_request_done(req, outbody, &outdyn); } status = smb2srv_client_lookup_global(xconn->client, xconn->smb2.client.guid, req, &global0); /* * TODO: check for races... */ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECTID_NOT_FOUND)) { /* * This stores the new client information in * smbXsrv_client_global.tdb */ xconn->client->global->client_guid = xconn->smb2.client.guid; status = smbXsrv_client_update(xconn->client); if (!NT_STATUS_IS_OK(status)) { return status; } xconn->smb2.client.guid_verified = true; } else if (NT_STATUS_IS_OK(status)) { status = smb2srv_client_connection_pass(req, global0); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } smbd_server_connection_terminate(xconn, "passed connection"); return NT_STATUS_OBJECTID_EXISTS; } else { return smbd_smb2_request_error(req, status); } } return smbd_smb2_request_done(req, outbody, &outdyn); }
NTSTATUS smbd_smb2_request_process_negprot(struct smbd_smb2_request *req) { NTSTATUS status; const uint8_t *inbody; const uint8_t *indyn = NULL; DATA_BLOB outbody; DATA_BLOB outdyn; DATA_BLOB negprot_spnego_blob; uint16_t security_offset; DATA_BLOB security_buffer; size_t expected_dyn_size = 0; size_t c; uint16_t security_mode; uint16_t dialect_count; uint16_t in_security_mode; uint32_t in_capabilities; DATA_BLOB in_guid_blob; struct GUID in_guid; uint16_t dialect = 0; uint32_t capabilities; DATA_BLOB out_guid_blob; struct GUID out_guid; enum protocol_types protocol = PROTOCOL_NONE; uint32_t max_limit; uint32_t max_trans = lp_smb2_max_trans(); uint32_t max_read = lp_smb2_max_read(); uint32_t max_write = lp_smb2_max_write(); NTTIME now = timeval_to_nttime(&req->request_time); status = smbd_smb2_request_verify_sizes(req, 0x24); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } inbody = SMBD_SMB2_IN_BODY_PTR(req); dialect_count = SVAL(inbody, 0x02); in_security_mode = SVAL(inbody, 0x04); in_capabilities = IVAL(inbody, 0x08); in_guid_blob = data_blob_const(inbody + 0x0C, 16); if (dialect_count == 0) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } status = GUID_from_ndr_blob(&in_guid_blob, &in_guid); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } expected_dyn_size = dialect_count * 2; if (SMBD_SMB2_IN_DYN_LEN(req) < expected_dyn_size) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } indyn = SMBD_SMB2_IN_DYN_PTR(req); for (c=0; protocol == PROTOCOL_NONE && c < dialect_count; c++) { if (lp_srv_maxprotocol() < PROTOCOL_SMB3_00) { break; } if (lp_srv_minprotocol() > PROTOCOL_SMB3_00) { break; } dialect = SVAL(indyn, c*2); if (dialect == SMB3_DIALECT_REVISION_300) { protocol = PROTOCOL_SMB3_00; break; } } for (c=0; protocol == PROTOCOL_NONE && c < dialect_count; c++) { if (lp_srv_maxprotocol() < PROTOCOL_SMB2_24) { break; } if (lp_srv_minprotocol() > PROTOCOL_SMB2_24) { break; } dialect = SVAL(indyn, c*2); if (dialect == SMB2_DIALECT_REVISION_224) { protocol = PROTOCOL_SMB2_24; break; } } for (c=0; protocol == PROTOCOL_NONE && c < dialect_count; c++) { if (lp_srv_maxprotocol() < PROTOCOL_SMB2_22) { break; } if (lp_srv_minprotocol() > PROTOCOL_SMB2_22) { break; } dialect = SVAL(indyn, c*2); if (dialect == SMB2_DIALECT_REVISION_222) { protocol = PROTOCOL_SMB2_22; break; } } for (c=0; protocol == PROTOCOL_NONE && c < dialect_count; c++) { if (lp_srv_maxprotocol() < PROTOCOL_SMB2_10) { break; } if (lp_srv_minprotocol() > PROTOCOL_SMB2_10) { break; } dialect = SVAL(indyn, c*2); if (dialect == SMB2_DIALECT_REVISION_210) { protocol = PROTOCOL_SMB2_10; break; } } for (c=0; protocol == PROTOCOL_NONE && c < dialect_count; c++) { if (lp_srv_maxprotocol() < PROTOCOL_SMB2_02) { break; } if (lp_srv_minprotocol() > PROTOCOL_SMB2_02) { break; } dialect = SVAL(indyn, c*2); if (dialect == SMB2_DIALECT_REVISION_202) { protocol = PROTOCOL_SMB2_02; break; } } for (c=0; protocol == PROTOCOL_NONE && c < dialect_count; c++) { if (lp_srv_maxprotocol() < PROTOCOL_SMB2_10) { break; } dialect = SVAL(indyn, c*2); if (dialect == SMB2_DIALECT_REVISION_2FF) { if (req->sconn->smb2.negprot_2ff) { req->sconn->smb2.negprot_2ff = false; protocol = PROTOCOL_SMB2_10; break; } } } if (protocol == PROTOCOL_NONE) { return smbd_smb2_request_error(req, NT_STATUS_NOT_SUPPORTED); } if (get_remote_arch() != RA_SAMBA) { set_remote_arch(RA_VISTA); } fstr_sprintf(remote_proto, "SMB%X_%02X", (dialect >> 8) & 0xFF, dialect & 0xFF); reload_services(req->sconn, conn_snum_used, true); DEBUG(3,("Selected protocol %s\n", remote_proto)); /* negprot_spnego() returns a the server guid in the first 16 bytes */ negprot_spnego_blob = negprot_spnego(req, req->sconn); if (negprot_spnego_blob.data == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } if (negprot_spnego_blob.length < 16) { return smbd_smb2_request_error(req, NT_STATUS_INTERNAL_ERROR); } security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED; if (lp_server_signing() == SMB_SIGNING_REQUIRED) { security_mode |= SMB2_NEGOTIATE_SIGNING_REQUIRED; } capabilities = 0; if (lp_host_msdfs()) { capabilities |= SMB2_CAP_DFS; } if ((protocol >= PROTOCOL_SMB2_24) && (lp_smb_encrypt(-1) != SMB_SIGNING_OFF)) { if (in_capabilities & SMB2_CAP_ENCRYPTION) { capabilities |= SMB2_CAP_ENCRYPTION; } } /* * 0x10000 (65536) is the maximum allowed message size * for SMB 2.0 */ max_limit = 0x10000; if (protocol >= PROTOCOL_SMB2_10) { int p = 0; if (tsocket_address_is_inet(req->sconn->local_address, "ip")) { p = tsocket_address_inet_port(req->sconn->local_address); } /* largeMTU is not supported over NBT (tcp port 139) */ if (p != NBT_SMB_PORT) { capabilities |= SMB2_CAP_LARGE_MTU; req->sconn->smb2.supports_multicredit = true; /* SMB >= 2.1 has 1 MB of allowed size */ max_limit = 0x100000; /* 1MB */ } } /* * the defaults are 1MB, but we'll limit this to max_limit based on * the dialect (64kb for SMB2.0, 1MB for SMB2.1 with LargeMTU) * * user configured values exceeding the limits will be overwritten, * only smaller values will be accepted */ max_trans = MIN(max_limit, lp_smb2_max_trans()); max_read = MIN(max_limit, lp_smb2_max_read()); max_write = MIN(max_limit, lp_smb2_max_write()); security_offset = SMB2_HDR_BODY + 0x40; #if 1 /* Try SPNEGO auth... */ security_buffer = data_blob_const(negprot_spnego_blob.data + 16, negprot_spnego_blob.length - 16); #else /* for now we want raw NTLMSSP */ security_buffer = data_blob_const(NULL, 0); #endif out_guid_blob = data_blob_const(negprot_spnego_blob.data, 16); status = GUID_from_ndr_blob(&out_guid_blob, &out_guid); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } outbody = data_blob_talloc(req->out.vector, NULL, 0x40); if (outbody.data == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } SSVAL(outbody.data, 0x00, 0x40 + 1); /* struct size */ SSVAL(outbody.data, 0x02, security_mode); /* security mode */ SSVAL(outbody.data, 0x04, dialect); /* dialect revision */ SSVAL(outbody.data, 0x06, 0); /* reserved */ memcpy(outbody.data + 0x08, out_guid_blob.data, 16); /* server guid */ SIVAL(outbody.data, 0x18, capabilities); /* capabilities */ SIVAL(outbody.data, 0x1C, max_trans); /* max transact size */ SIVAL(outbody.data, 0x20, max_read); /* max read size */ SIVAL(outbody.data, 0x24, max_write); /* max write size */ SBVAL(outbody.data, 0x28, now); /* system time */ SBVAL(outbody.data, 0x30, 0); /* server start time */ SSVAL(outbody.data, 0x38, security_offset); /* security buffer offset */ SSVAL(outbody.data, 0x3A, security_buffer.length); /* security buffer length */ SIVAL(outbody.data, 0x3C, 0); /* reserved */ outdyn = security_buffer; req->sconn->using_smb2 = true; if (dialect != SMB2_DIALECT_REVISION_2FF) { struct smbXsrv_connection *conn = req->sconn->conn; status = smbXsrv_connection_init_tables(conn, protocol); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } conn->smb2.client.capabilities = in_capabilities; conn->smb2.client.security_mode = in_security_mode; conn->smb2.client.guid = in_guid; conn->smb2.client.num_dialects = dialect_count; conn->smb2.client.dialects = talloc_array(conn, uint16_t, dialect_count); if (conn->smb2.client.dialects == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } for (c=0; c < dialect_count; c++) { conn->smb2.client.dialects[c] = SVAL(indyn, c*2); } conn->smb2.server.capabilities = capabilities; conn->smb2.server.security_mode = security_mode; conn->smb2.server.guid = out_guid; conn->smb2.server.dialect = dialect; conn->smb2.server.max_trans = max_trans; conn->smb2.server.max_read = max_read; conn->smb2.server.max_write = max_write; req->sconn->smb2.max_trans = max_trans; req->sconn->smb2.max_read = max_read; req->sconn->smb2.max_write = max_write; } return smbd_smb2_request_done(req, outbody, &outdyn); }
NTSTATUS smbd_smb2_request_process_negprot(struct smbd_smb2_request *req) { NTSTATUS status; const uint8_t *inbody; const uint8_t *indyn = NULL; int i = req->current_idx; DATA_BLOB outbody; DATA_BLOB outdyn; DATA_BLOB negprot_spnego_blob; uint16_t security_offset; DATA_BLOB security_buffer; size_t expected_dyn_size = 0; size_t c; uint16_t security_mode; uint16_t dialect_count; uint16_t dialect = 0; uint32_t capabilities; enum protocol_types protocol = PROTOCOL_NONE; uint32_t max_limit; uint32_t max_trans = lp_smb2_max_trans(); uint32_t max_read = lp_smb2_max_read(); uint32_t max_write = lp_smb2_max_write(); status = smbd_smb2_request_verify_sizes(req, 0x24); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } inbody = (const uint8_t *)req->in.vector[i+1].iov_base; dialect_count = SVAL(inbody, 0x02); if (dialect_count == 0) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } expected_dyn_size = dialect_count * 2; if (req->in.vector[i+2].iov_len < expected_dyn_size) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } indyn = (const uint8_t *)req->in.vector[i+2].iov_base; for (c=0; protocol == PROTOCOL_NONE && c < dialect_count; c++) { if (lp_srv_maxprotocol() < PROTOCOL_SMB2_24) { break; } if (lp_srv_minprotocol() > PROTOCOL_SMB2_24) { break; } dialect = SVAL(indyn, c*2); if (dialect == SMB2_DIALECT_REVISION_224) { protocol = PROTOCOL_SMB2_24; break; } } for (c=0; protocol == PROTOCOL_NONE && c < dialect_count; c++) { if (lp_srv_maxprotocol() < PROTOCOL_SMB2_22) { break; } if (lp_srv_minprotocol() > PROTOCOL_SMB2_22) { break; } dialect = SVAL(indyn, c*2); if (dialect == SMB2_DIALECT_REVISION_222) { protocol = PROTOCOL_SMB2_22; break; } } for (c=0; protocol == PROTOCOL_NONE && c < dialect_count; c++) { if (lp_srv_maxprotocol() < PROTOCOL_SMB2_10) { break; } if (lp_srv_minprotocol() > PROTOCOL_SMB2_10) { break; } dialect = SVAL(indyn, c*2); if (dialect == SMB2_DIALECT_REVISION_210) { protocol = PROTOCOL_SMB2_10; break; } } for (c=0; protocol == PROTOCOL_NONE && c < dialect_count; c++) { if (lp_srv_maxprotocol() < PROTOCOL_SMB2_02) { break; } if (lp_srv_minprotocol() > PROTOCOL_SMB2_02) { break; } dialect = SVAL(indyn, c*2); if (dialect == SMB2_DIALECT_REVISION_202) { protocol = PROTOCOL_SMB2_02; break; } } for (c=0; protocol == PROTOCOL_NONE && c < dialect_count; c++) { if (lp_srv_maxprotocol() < PROTOCOL_SMB2_10) { break; } dialect = SVAL(indyn, c*2); if (dialect == SMB2_DIALECT_REVISION_2FF) { if (req->sconn->smb2.negprot_2ff) { req->sconn->smb2.negprot_2ff = false; protocol = PROTOCOL_SMB2_10; break; } } } if (protocol == PROTOCOL_NONE) { return smbd_smb2_request_error(req, NT_STATUS_NOT_SUPPORTED); } if (dialect != SMB2_DIALECT_REVISION_2FF) { set_Protocol(protocol); } if (get_remote_arch() != RA_SAMBA) { set_remote_arch(RA_VISTA); } /* negprot_spnego() returns a the server guid in the first 16 bytes */ negprot_spnego_blob = negprot_spnego(req, req->sconn); if (negprot_spnego_blob.data == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } if (negprot_spnego_blob.length < 16) { return smbd_smb2_request_error(req, NT_STATUS_INTERNAL_ERROR); } security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED; if (lp_server_signing() == SMB_SIGNING_REQUIRED) { security_mode |= SMB2_NEGOTIATE_SIGNING_REQUIRED; } capabilities = 0; if (lp_host_msdfs()) { capabilities |= SMB2_CAP_DFS; } /* * 0x10000 (65536) is the maximum allowed message size * for SMB 2.0 */ max_limit = 0x10000; if (protocol >= PROTOCOL_SMB2_10) { /* largeMTU is only available on port 445 */ if (TCP_SMB_PORT == tsocket_address_inet_port(req->sconn->local_address)) { capabilities |= SMB2_CAP_LARGE_MTU; req->sconn->smb2.supports_multicredit = true; /* SMB2.1 has 1 MB of allowed size */ max_limit = 0x100000; /* 1MB */ } } /* * the defaults are 1MB, but we'll limit this to max_limit based on * the dialect (64kb for SMB2.0, 1MB for SMB2.1 with LargeMTU) * * user configured values exceeding the limits will be overwritten, * only smaller values will be accepted */ max_trans = MIN(max_limit, lp_smb2_max_trans()); max_read = MIN(max_limit, lp_smb2_max_read()); max_write = MIN(max_limit, lp_smb2_max_write()); security_offset = SMB2_HDR_BODY + 0x40; #if 1 /* Try SPNEGO auth... */ security_buffer = data_blob_const(negprot_spnego_blob.data + 16, negprot_spnego_blob.length - 16); #else /* for now we want raw NTLMSSP */ security_buffer = data_blob_const(NULL, 0); #endif outbody = data_blob_talloc(req->out.vector, NULL, 0x40); if (outbody.data == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } SSVAL(outbody.data, 0x00, 0x40 + 1); /* struct size */ SSVAL(outbody.data, 0x02, security_mode); /* security mode */ SSVAL(outbody.data, 0x04, dialect); /* dialect revision */ SSVAL(outbody.data, 0x06, 0); /* reserved */ memcpy(outbody.data + 0x08, negprot_spnego_blob.data, 16); /* server guid */ SIVAL(outbody.data, 0x18, capabilities); /* capabilities */ SIVAL(outbody.data, 0x1C, max_trans); /* max transact size */ SIVAL(outbody.data, 0x20, max_read); /* max read size */ SIVAL(outbody.data, 0x24, max_write); /* max write size */ SBVAL(outbody.data, 0x28, 0); /* system time */ SBVAL(outbody.data, 0x30, 0); /* server start time */ SSVAL(outbody.data, 0x38, security_offset); /* security buffer offset */ SSVAL(outbody.data, 0x3A, security_buffer.length); /* security buffer length */ SIVAL(outbody.data, 0x3C, 0); /* reserved */ outdyn = security_buffer; req->sconn->using_smb2 = true; req->sconn->smb2.max_trans = max_trans; req->sconn->smb2.max_read = max_read; req->sconn->smb2.max_write = max_write; return smbd_smb2_request_done(req, outbody, &outdyn); }
int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf, int length,int bufsize) { int sess_vuid; int smb_bufsize; DATA_BLOB lm_resp; DATA_BLOB nt_resp; DATA_BLOB plaintext_password; fstring user; fstring sub_user; /* Sainitised username for substituion */ fstring domain; fstring native_os; fstring native_lanman; fstring primary_domain; static BOOL done_sesssetup = False; auth_usersupplied_info *user_info = NULL; auth_serversupplied_info *server_info = NULL; NTSTATUS nt_status; BOOL doencrypt = global_encrypted_passwords_negotiated; DATA_BLOB session_key; START_PROFILE(SMBsesssetupX); ZERO_STRUCT(lm_resp); ZERO_STRUCT(nt_resp); ZERO_STRUCT(plaintext_password); DEBUG(3,("wct=%d flg2=0x%x\n", CVAL(inbuf, smb_wct), SVAL(inbuf, smb_flg2))); /* a SPNEGO session setup has 12 command words, whereas a normal NT1 session setup has 13. See the cifs spec. */ if (CVAL(inbuf, smb_wct) == 12 && (SVAL(inbuf, smb_flg2) & FLAGS2_EXTENDED_SECURITY)) { if (!global_spnego_negotiated) { DEBUG(0,("reply_sesssetup_and_X: Rejecting attempt at SPNEGO session setup when it was not negoitiated.\n")); return ERROR_NT(nt_status_squash(NT_STATUS_LOGON_FAILURE)); } if (SVAL(inbuf,smb_vwv4) == 0) { setup_new_vc_session(); } return reply_sesssetup_and_X_spnego(conn, inbuf, outbuf, length, bufsize); } smb_bufsize = SVAL(inbuf,smb_vwv2); if (Protocol < PROTOCOL_NT1) { uint16 passlen1 = SVAL(inbuf,smb_vwv7); /* Never do NT status codes with protocols before NT1 as we don't get client caps. */ remove_from_common_flags2(FLAGS2_32_BIT_ERROR_CODES); if ((passlen1 > MAX_PASS_LEN) || (passlen1 > smb_bufrem(inbuf, smb_buf(inbuf)))) { return ERROR_NT(nt_status_squash(NT_STATUS_INVALID_PARAMETER)); } if (doencrypt) { lm_resp = data_blob(smb_buf(inbuf), passlen1); } else { plaintext_password = data_blob(smb_buf(inbuf), passlen1+1); /* Ensure null termination */ plaintext_password.data[passlen1] = 0; } srvstr_pull_buf(inbuf, user, smb_buf(inbuf)+passlen1, sizeof(user), STR_TERMINATE); *domain = 0; } else { uint16 passlen1 = SVAL(inbuf,smb_vwv7); uint16 passlen2 = SVAL(inbuf,smb_vwv8); enum remote_arch_types ra_type = get_remote_arch(); char *p = smb_buf(inbuf); char *save_p = smb_buf(inbuf); uint16 byte_count; if(global_client_caps == 0) { global_client_caps = IVAL(inbuf,smb_vwv11); if (!(global_client_caps & CAP_STATUS32)) { remove_from_common_flags2(FLAGS2_32_BIT_ERROR_CODES); } /* client_caps is used as final determination if client is NT or Win95. This is needed to return the correct error codes in some circumstances. */ if(ra_type == RA_WINNT || ra_type == RA_WIN2K || ra_type == RA_WIN95) { if(!(global_client_caps & (CAP_NT_SMBS | CAP_STATUS32))) { set_remote_arch( RA_WIN95); } } } if (!doencrypt) { /* both Win95 and WinNT stuff up the password lengths for non-encrypting systems. Uggh. if passlen1==24 its a win95 system, and its setting the password length incorrectly. Luckily it still works with the default code because Win95 will null terminate the password anyway if passlen1>0 and passlen2>0 then maybe its a NT box and its setting passlen2 to some random value which really stuffs things up. we need to fix that one. */ if (passlen1 > 0 && passlen2 > 0 && passlen2 != 24 && passlen2 != 1) passlen2 = 0; } /* check for nasty tricks */ if (passlen1 > MAX_PASS_LEN || passlen1 > smb_bufrem(inbuf, p)) { return ERROR_NT(nt_status_squash(NT_STATUS_INVALID_PARAMETER)); } if (passlen2 > MAX_PASS_LEN || passlen2 > smb_bufrem(inbuf, p+passlen1)) { return ERROR_NT(nt_status_squash(NT_STATUS_INVALID_PARAMETER)); } /* Save the lanman2 password and the NT md4 password. */ if ((doencrypt) && (passlen1 != 0) && (passlen1 != 24)) { doencrypt = False; } if (doencrypt) { lm_resp = data_blob(p, passlen1); nt_resp = data_blob(p+passlen1, passlen2); } else { pstring pass; BOOL unic=SVAL(inbuf, smb_flg2) & FLAGS2_UNICODE_STRINGS; #if 0 /* This was the previous fix. Not sure if it's still valid. JRA. */ if ((ra_type == RA_WINNT) && (passlen2 == 0) && unic && passlen1) { /* NT4.0 stuffs up plaintext unicode password lengths... */ srvstr_pull(inbuf, pass, smb_buf(inbuf) + 1, sizeof(pass), passlen1, STR_TERMINATE); #endif if (unic && (passlen2 == 0) && passlen1) { /* Only a ascii plaintext password was sent. */ srvstr_pull(inbuf, pass, smb_buf(inbuf), sizeof(pass), passlen1, STR_TERMINATE|STR_ASCII); } else { srvstr_pull(inbuf, pass, smb_buf(inbuf), sizeof(pass), unic ? passlen2 : passlen1, STR_TERMINATE); } plaintext_password = data_blob(pass, strlen(pass)+1); } p += passlen1 + passlen2; p += srvstr_pull_buf(inbuf, user, p, sizeof(user), STR_TERMINATE); p += srvstr_pull_buf(inbuf, domain, p, sizeof(domain), STR_TERMINATE); p += srvstr_pull_buf(inbuf, native_os, p, sizeof(native_os), STR_TERMINATE); p += srvstr_pull_buf(inbuf, native_lanman, p, sizeof(native_lanman), STR_TERMINATE); /* not documented or decoded by Ethereal but there is one more string in the extra bytes which is the same as the PrimaryDomain when using extended security. Windows NT 4 and 2003 use this string to store the native lanman string. Windows 9x does not include a string here at all so we have to check if we have any extra bytes left */ byte_count = SVAL(inbuf, smb_vwv13); if ( PTR_DIFF(p, save_p) < byte_count) p += srvstr_pull_buf(inbuf, primary_domain, p, sizeof(primary_domain), STR_TERMINATE); else fstrcpy( primary_domain, "null" ); DEBUG(3,("Domain=[%s] NativeOS=[%s] NativeLanMan=[%s] PrimaryDomain=[%s]\n", domain, native_os, native_lanman, primary_domain)); if ( ra_type == RA_WIN2K ) { if ( strlen(native_lanman) == 0 ) ra_lanman_string( primary_domain ); else ra_lanman_string( native_lanman ); } } if (SVAL(inbuf,smb_vwv4) == 0) { setup_new_vc_session(); } DEBUG(3,("sesssetupX:name=[%s]\\[%s]@[%s]\n", domain, user, get_remote_machine_name())); if (*user) { if (global_spnego_negotiated) { /* This has to be here, because this is a perfectly valid behaviour for guest logons :-( */ DEBUG(0,("reply_sesssetup_and_X: Rejecting attempt at 'normal' session setup after negotiating spnego.\n")); return ERROR_NT(nt_status_squash(NT_STATUS_LOGON_FAILURE)); } fstrcpy(sub_user, user); } else { fstrcpy(sub_user, lp_guestaccount()); } sub_set_smb_name(sub_user); reload_services(True); if (lp_security() == SEC_SHARE) { /* in share level we should ignore any passwords */ data_blob_free(&lm_resp); data_blob_free(&nt_resp); data_blob_clear_free(&plaintext_password); map_username(sub_user); add_session_user(sub_user); add_session_workgroup(domain); /* Then force it to null for the benfit of the code below */ *user = 0; } if (!*user) { nt_status = check_guest_password(&server_info); } else if (doencrypt) { if (!negprot_global_auth_context) { DEBUG(0, ("reply_sesssetup_and_X: Attempted encrypted session setup without negprot denied!\n")); return ERROR_NT(nt_status_squash(NT_STATUS_LOGON_FAILURE)); } nt_status = make_user_info_for_reply_enc(&user_info, user, domain, lm_resp, nt_resp); if (NT_STATUS_IS_OK(nt_status)) { nt_status = negprot_global_auth_context->check_ntlm_password(negprot_global_auth_context, user_info, &server_info); } } else { struct auth_context *plaintext_auth_context = NULL; const uint8 *chal; nt_status = make_auth_context_subsystem(&plaintext_auth_context); if (NT_STATUS_IS_OK(nt_status)) { chal = plaintext_auth_context->get_ntlm_challenge(plaintext_auth_context); if (!make_user_info_for_reply(&user_info, user, domain, chal, plaintext_password)) { nt_status = NT_STATUS_NO_MEMORY; } if (NT_STATUS_IS_OK(nt_status)) { nt_status = plaintext_auth_context->check_ntlm_password(plaintext_auth_context, user_info, &server_info); (plaintext_auth_context->free)(&plaintext_auth_context); } } } free_user_info(&user_info); if (!NT_STATUS_IS_OK(nt_status)) { nt_status = do_map_to_guest(nt_status, &server_info, user, domain); } if (!NT_STATUS_IS_OK(nt_status)) { data_blob_free(&nt_resp); data_blob_free(&lm_resp); data_blob_clear_free(&plaintext_password); return ERROR_NT(nt_status_squash(nt_status)); } /* Ensure we can't possible take a code path leading to a null defref. */ if (!server_info) { return ERROR_NT(nt_status_squash(NT_STATUS_LOGON_FAILURE)); } nt_status = create_local_token(server_info); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(10, ("create_local_token failed: %s\n", nt_errstr(nt_status))); data_blob_free(&nt_resp); data_blob_free(&lm_resp); data_blob_clear_free(&plaintext_password); return ERROR_NT(nt_status_squash(nt_status)); } if (server_info->user_session_key.data) { session_key = data_blob(server_info->user_session_key.data, server_info->user_session_key.length); } else { session_key = data_blob(NULL, 0); } data_blob_clear_free(&plaintext_password); /* it's ok - setup a reply */ set_message(outbuf,3,0,True); if (Protocol >= PROTOCOL_NT1) { char *p = smb_buf( outbuf ); p += add_signature( outbuf, p ); set_message_end( outbuf, p ); /* perhaps grab OS version here?? */ } if (server_info->guest) { SSVAL(outbuf,smb_vwv2,1); } /* register the name and uid as being validated, so further connections to a uid can get through without a password, on the same VC */ if (lp_security() == SEC_SHARE) { sess_vuid = UID_FIELD_INVALID; data_blob_free(&session_key); TALLOC_FREE(server_info); } else { /* register_vuid keeps the server info */ sess_vuid = register_vuid(server_info, session_key, nt_resp.data ? nt_resp : lm_resp, sub_user); if (sess_vuid == UID_FIELD_INVALID) { data_blob_free(&nt_resp); data_blob_free(&lm_resp); return ERROR_NT(nt_status_squash(NT_STATUS_LOGON_FAILURE)); } /* current_user_info is changed on new vuid */ reload_services( True ); sessionsetup_start_signing_engine(server_info, inbuf); } data_blob_free(&nt_resp); data_blob_free(&lm_resp); SSVAL(outbuf,smb_uid,sess_vuid); SSVAL(inbuf,smb_uid,sess_vuid); if (!done_sesssetup) max_send = MIN(max_send,smb_bufsize); done_sesssetup = True; END_PROFILE(SMBsesssetupX); return chain_reply(inbuf,outbuf,length,bufsize); }
static int reply_sesssetup_and_X_spnego(connection_struct *conn, char *inbuf, char *outbuf, int length,int bufsize) { uint8 *p; DATA_BLOB blob1; int ret; size_t bufrem; fstring native_os, native_lanman, primary_domain; char *p2; uint16 data_blob_len = SVAL(inbuf, smb_vwv7); enum remote_arch_types ra_type = get_remote_arch(); int vuid = SVAL(inbuf,smb_uid); user_struct *vuser = NULL; NTSTATUS status = NT_STATUS_OK; uint16 smbpid = SVAL(inbuf,smb_pid); DEBUG(3,("Doing spnego session setup\n")); if (global_client_caps == 0) { global_client_caps = IVAL(inbuf,smb_vwv10); if (!(global_client_caps & CAP_STATUS32)) { remove_from_common_flags2(FLAGS2_32_BIT_ERROR_CODES); } } p = (uint8 *)smb_buf(inbuf); if (data_blob_len == 0) { /* an invalid request */ return ERROR_NT(nt_status_squash(NT_STATUS_LOGON_FAILURE)); } bufrem = smb_bufrem(inbuf, p); /* pull the spnego blob */ blob1 = data_blob(p, MIN(bufrem, data_blob_len)); #if 0 file_save("negotiate.dat", blob1.data, blob1.length); #endif p2 = inbuf + smb_vwv13 + data_blob_len; p2 += srvstr_pull_buf(inbuf, native_os, p2, sizeof(native_os), STR_TERMINATE); p2 += srvstr_pull_buf(inbuf, native_lanman, p2, sizeof(native_lanman), STR_TERMINATE); p2 += srvstr_pull_buf(inbuf, primary_domain, p2, sizeof(primary_domain), STR_TERMINATE); DEBUG(3,("NativeOS=[%s] NativeLanMan=[%s] PrimaryDomain=[%s]\n", native_os, native_lanman, primary_domain)); if ( ra_type == RA_WIN2K ) { /* Vista sets neither the OS or lanman strings */ if ( !strlen(native_os) && !strlen(native_lanman) ) set_remote_arch(RA_VISTA); /* Windows 2003 doesn't set the native lanman string, but does set primary domain which is a bug I think */ if ( !strlen(native_lanman) ) { ra_lanman_string( primary_domain ); } else { ra_lanman_string( native_lanman ); } } vuser = get_partial_auth_user_struct(vuid); if (!vuser) { struct pending_auth_data *pad = get_pending_auth_data(smbpid); if (pad) { DEBUG(10,("reply_sesssetup_and_X_spnego: found pending vuid %u\n", (unsigned int)pad->vuid )); vuid = pad->vuid; vuser = get_partial_auth_user_struct(vuid); } } if (!vuser) { vuid = register_vuid(NULL, data_blob(NULL, 0), data_blob(NULL, 0), NULL); if (vuid == UID_FIELD_INVALID ) { data_blob_free(&blob1); return ERROR_NT(nt_status_squash(NT_STATUS_INVALID_PARAMETER)); } vuser = get_partial_auth_user_struct(vuid); } if (!vuser) { data_blob_free(&blob1); return ERROR_NT(nt_status_squash(NT_STATUS_INVALID_PARAMETER)); } SSVAL(outbuf,smb_uid,vuid); /* Large (greater than 4k) SPNEGO blobs are split into multiple * sessionsetup requests as the Windows limit on the security blob * field is 4k. Bug #4400. JRA. */ status = check_spnego_blob_complete(smbpid, vuid, &blob1); if (!NT_STATUS_IS_OK(status)) { if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { /* Real error - kill the intermediate vuid */ invalidate_vuid(vuid); } data_blob_free(&blob1); return ERROR_NT(nt_status_squash(status)); } if (blob1.data[0] == ASN1_APPLICATION(0)) { /* its a negTokenTarg packet */ ret = reply_spnego_negotiate(conn, inbuf, outbuf, vuid, length, bufsize, blob1, &vuser->auth_ntlmssp_state); data_blob_free(&blob1); return ret; } if (blob1.data[0] == ASN1_CONTEXT(1)) { /* its a auth packet */ ret = reply_spnego_auth(conn, inbuf, outbuf, vuid, length, bufsize, blob1, &vuser->auth_ntlmssp_state); data_blob_free(&blob1); return ret; } if (strncmp((char *)(blob1.data), "NTLMSSP", 7) == 0) { DATA_BLOB chal; if (!vuser->auth_ntlmssp_state) { status = auth_ntlmssp_start(&vuser->auth_ntlmssp_state); if (!NT_STATUS_IS_OK(status)) { /* Kill the intermediate vuid */ invalidate_vuid(vuid); data_blob_free(&blob1); return ERROR_NT(nt_status_squash(status)); } } status = auth_ntlmssp_update(vuser->auth_ntlmssp_state, blob1, &chal); data_blob_free(&blob1); reply_spnego_ntlmssp(conn, inbuf, outbuf, vuid, &vuser->auth_ntlmssp_state, &chal, status, False); data_blob_free(&chal); return -1; } /* what sort of packet is this? */ DEBUG(1,("Unknown packet in reply_sesssetup_and_X_spnego\n")); data_blob_free(&blob1); return ERROR_NT(nt_status_squash(NT_STATUS_LOGON_FAILURE)); }