/** * @brief Append an auth footer according to what is the current mechanism * * @param auth The pipe_auth_data associated with the connection * @param pad_len The padding used in the packet * @param rpc_out Packet blob up to and including the auth header * * @return A NTSTATUS error code. */ NTSTATUS dcerpc_add_auth_footer(struct pipe_auth_data *auth, size_t pad_len, DATA_BLOB *rpc_out) { struct gensec_security *gensec_security; char pad[CLIENT_NDR_PADDING_SIZE] = { 0, }; DATA_BLOB auth_info; DATA_BLOB auth_blob; NTSTATUS status; if (auth->auth_type == DCERPC_AUTH_TYPE_NONE) { return NT_STATUS_OK; } if (pad_len) { /* Copy the sign/seal padding data. */ if (!data_blob_append(NULL, rpc_out, pad, pad_len)) { return NT_STATUS_NO_MEMORY; } } /* marshall the dcerpc_auth with an actually empty auth_blob. * This is needed because the ntmlssp signature includes the * auth header. We will append the actual blob later. */ auth_blob = data_blob_null; status = dcerpc_push_dcerpc_auth(rpc_out->data, auth->auth_type, auth->auth_level, pad_len, 1 /* context id. */, &auth_blob, &auth_info); if (!NT_STATUS_IS_OK(status)) { return status; } /* append the header */ if (!data_blob_append(NULL, rpc_out, auth_info.data, auth_info.length)) { DEBUG(0, ("Failed to add %u bytes auth blob.\n", (unsigned int)auth_info.length)); return NT_STATUS_NO_MEMORY; } data_blob_free(&auth_info); /* Generate any auth sign/seal and add the auth footer. */ switch (auth->auth_type) { case DCERPC_AUTH_TYPE_NONE: status = NT_STATUS_OK; break; default: gensec_security = auth->auth_ctx; status = add_generic_auth_footer(gensec_security, auth->auth_level, rpc_out); break; } return status; }
/* * These functions are for use in the deprecated * gensec_socket code (public because SPNEGO must * use them for recursion) */ _PUBLIC_ NTSTATUS gensec_wrap_packets(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, const DATA_BLOB *in, DATA_BLOB *out, size_t *len_processed) { if (!gensec_security->ops->wrap_packets) { NTSTATUS nt_status; size_t max_input_size; DATA_BLOB unwrapped, wrapped; max_input_size = gensec_max_input_size(gensec_security); unwrapped = data_blob_const(in->data, MIN(max_input_size, (size_t)in->length)); nt_status = gensec_wrap(gensec_security, mem_ctx, &unwrapped, &wrapped); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; } *out = data_blob_talloc(mem_ctx, NULL, 4); if (!out->data) { return NT_STATUS_NO_MEMORY; } RSIVAL(out->data, 0, wrapped.length); if (!data_blob_append(mem_ctx, out, wrapped.data, wrapped.length)) { return NT_STATUS_NO_MEMORY; } *len_processed = unwrapped.length; return NT_STATUS_OK; } return gensec_security->ops->wrap_packets(gensec_security, mem_ctx, in, out, len_processed); }
void websrv_output(struct websrv_context *web, void *data, size_t length) { data_blob_append(web, &web->output.content, data, length); EVENT_FD_NOT_READABLE(web->conn->event.fde); EVENT_FD_WRITEABLE(web->conn->event.fde); web->output.output_pending = true; }
static NTSTATUS fsctl_qar_buf_push(TALLOC_CTX *mem_ctx, struct file_alloced_range_buf *qar_buf, DATA_BLOB *qar_array_blob) { DATA_BLOB new_slot; enum ndr_err_code ndr_ret; bool ok; ndr_ret = ndr_push_struct_blob(&new_slot, mem_ctx, qar_buf, (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf); if (ndr_ret != NDR_ERR_SUCCESS) { DEBUG(0, ("failed to marshall QAR buf\n")); return NT_STATUS_INVALID_PARAMETER; } /* TODO should be able to avoid copy by pushing into prealloced buf */ ok = data_blob_append(mem_ctx, qar_array_blob, new_slot.data, new_slot.length); data_blob_free(&new_slot); if (!ok) { return NT_STATUS_NO_MEMORY; } return NT_STATUS_OK; }
static NTSTATUS add_schannel_auth_footer(struct schannel_state *sas, enum dcerpc_AuthLevel auth_level, DATA_BLOB *rpc_out) { uint8_t *data_p = rpc_out->data + DCERPC_RESPONSE_LENGTH; size_t data_and_pad_len = rpc_out->length - DCERPC_RESPONSE_LENGTH - DCERPC_AUTH_TRAILER_LENGTH; DATA_BLOB auth_blob; NTSTATUS status; if (!sas) { return NT_STATUS_INVALID_PARAMETER; } DEBUG(10,("add_schannel_auth_footer: SCHANNEL seq_num=%d\n", sas->seq_num)); switch (auth_level) { case DCERPC_AUTH_LEVEL_PRIVACY: status = netsec_outgoing_packet(sas, rpc_out->data, true, data_p, data_and_pad_len, &auth_blob); break; case DCERPC_AUTH_LEVEL_INTEGRITY: status = netsec_outgoing_packet(sas, rpc_out->data, false, data_p, data_and_pad_len, &auth_blob); break; default: status = NT_STATUS_INTERNAL_ERROR; break; } if (!NT_STATUS_IS_OK(status)) { DEBUG(1,("add_schannel_auth_footer: failed to process packet: %s\n", nt_errstr(status))); return status; } if (DEBUGLEVEL >= 10) { dump_NL_AUTH_SIGNATURE(talloc_tos(), &auth_blob); } /* Finally attach the blob. */ if (!data_blob_append(NULL, rpc_out, auth_blob.data, auth_blob.length)) { return NT_STATUS_NO_MEMORY; } data_blob_free(&auth_blob); return NT_STATUS_OK; }
static NTSTATUS add_spnego_auth_footer(struct spnego_context *spnego_ctx, enum dcerpc_AuthLevel auth_level, DATA_BLOB *rpc_out) { DATA_BLOB auth_blob; DATA_BLOB rpc_data; NTSTATUS status; if (!spnego_ctx) { return NT_STATUS_INVALID_PARAMETER; } rpc_data = data_blob_const(rpc_out->data + DCERPC_RESPONSE_LENGTH, rpc_out->length - DCERPC_RESPONSE_LENGTH - DCERPC_AUTH_TRAILER_LENGTH); switch (auth_level) { case DCERPC_AUTH_LEVEL_PRIVACY: /* Data portion is encrypted. */ status = spnego_seal(rpc_out->data, spnego_ctx, &rpc_data, rpc_out, &auth_blob); break; if (!NT_STATUS_IS_OK(status)) { return status; } break; case DCERPC_AUTH_LEVEL_INTEGRITY: /* Data is signed. */ status = spnego_sign(rpc_out->data, spnego_ctx, &rpc_data, rpc_out, &auth_blob); break; if (!NT_STATUS_IS_OK(status)) { return status; } break; default: /* Can't happen. */ smb_panic("bad auth level"); /* Notreached. */ return NT_STATUS_INVALID_PARAMETER; } /* Finally attach the blob. */ if (!data_blob_append(NULL, rpc_out, auth_blob.data, auth_blob.length)) { DEBUG(0, ("Failed to add %u bytes auth blob.\n", (unsigned int)auth_blob.length)); return NT_STATUS_NO_MEMORY; } data_blob_free(&auth_blob); return NT_STATUS_OK; }
static NTSTATUS add_gssapi_auth_footer(struct gse_context *gse_ctx, enum dcerpc_AuthLevel auth_level, DATA_BLOB *rpc_out) { DATA_BLOB data; DATA_BLOB auth_blob; NTSTATUS status; if (!gse_ctx) { return NT_STATUS_INVALID_PARAMETER; } data.data = rpc_out->data + DCERPC_RESPONSE_LENGTH; data.length = rpc_out->length - DCERPC_RESPONSE_LENGTH - DCERPC_AUTH_TRAILER_LENGTH; switch (auth_level) { case DCERPC_AUTH_LEVEL_PRIVACY: status = gse_seal(talloc_tos(), gse_ctx, &data, &auth_blob); break; case DCERPC_AUTH_LEVEL_INTEGRITY: status = gse_sign(talloc_tos(), gse_ctx, &data, &auth_blob); break; default: status = NT_STATUS_INTERNAL_ERROR; break; } if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("Failed to process packet: %s\n", nt_errstr(status))); return status; } /* Finally attach the blob. */ if (!data_blob_append(NULL, rpc_out, auth_blob.data, auth_blob.length)) { return NT_STATUS_NO_MEMORY; } data_blob_free(&auth_blob); return NT_STATUS_OK; }
/* Completed SASL packet callback. When we have a 'whole' SASL * packet, decrypt it, and add it to the read buffer * * This function (and anything under it) MUST NOT call the event system */ static NTSTATUS gensec_socket_unwrap(void *private_data, DATA_BLOB blob) { struct gensec_socket *gensec_socket = talloc_get_type(private_data, struct gensec_socket); DATA_BLOB unwrapped; NTSTATUS nt_status; TALLOC_CTX *mem_ctx; size_t packet_size; mem_ctx = talloc_new(gensec_socket); if (!mem_ctx) { return NT_STATUS_NO_MEMORY; } nt_status = gensec_unwrap_packets(gensec_socket->gensec_security, mem_ctx, &blob, &unwrapped, &packet_size); if (!NT_STATUS_IS_OK(nt_status)) { talloc_free(mem_ctx); return nt_status; } if (packet_size != blob.length) { DEBUG(0, ("gensec_socket_unwrap: Did not consume entire packet!\n")); talloc_free(mem_ctx); return NT_STATUS_INTERNAL_ERROR; } /* We could change this into a linked list, and have * gensec_socket_recv() and gensec_socket_pending() walk the * linked list */ if (!data_blob_append(gensec_socket, &gensec_socket->read_buffer, unwrapped.data, unwrapped.length)) { talloc_free(mem_ctx); return NT_STATUS_NO_MEMORY; } talloc_free(mem_ctx); return NT_STATUS_OK; }
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); }
static NTSTATUS add_generic_auth_footer(struct gensec_security *gensec_security, enum dcerpc_AuthLevel auth_level, DATA_BLOB *rpc_out) { uint16_t data_and_pad_len = rpc_out->length - DCERPC_RESPONSE_LENGTH - DCERPC_AUTH_TRAILER_LENGTH; DATA_BLOB auth_blob; NTSTATUS status; if (!gensec_security) { return NT_STATUS_INVALID_PARAMETER; } switch (auth_level) { case DCERPC_AUTH_LEVEL_PRIVACY: /* Data portion is encrypted. */ status = gensec_seal_packet(gensec_security, rpc_out->data, rpc_out->data + DCERPC_RESPONSE_LENGTH, data_and_pad_len, rpc_out->data, rpc_out->length, &auth_blob); if (!NT_STATUS_IS_OK(status)) { return status; } break; case DCERPC_AUTH_LEVEL_INTEGRITY: case DCERPC_AUTH_LEVEL_PACKET: /* Data is signed. */ status = gensec_sign_packet(gensec_security, rpc_out->data, rpc_out->data + DCERPC_RESPONSE_LENGTH, data_and_pad_len, rpc_out->data, rpc_out->length, &auth_blob); if (!NT_STATUS_IS_OK(status)) { return status; } break; default: /* Can't happen. */ smb_panic("bad auth level"); /* Notreached. */ return NT_STATUS_INVALID_PARAMETER; } /* Finally attach the blob. */ if (!data_blob_append(NULL, rpc_out, auth_blob.data, auth_blob.length)) { DEBUG(0, ("Failed to add %u bytes auth blob.\n", (unsigned int)auth_blob.length)); return NT_STATUS_NO_MEMORY; } data_blob_free(&auth_blob); return NT_STATUS_OK; }
/* push a signed or sealed dcerpc request packet into a blob */ bool dcesrv_auth_response(struct dcesrv_call_state *call, DATA_BLOB *blob, size_t sig_size, struct ncacn_packet *pkt) { struct dcesrv_connection *dce_conn = call->conn; NTSTATUS status; enum ndr_err_code ndr_err; struct ndr_push *ndr; uint32_t payload_length; DATA_BLOB creds2; /* non-signed packets are simple */ if (sig_size == 0) { status = ncacn_push_auth(blob, call, pkt, NULL); return NT_STATUS_IS_OK(status); } switch (dce_conn->auth_state.auth_info->auth_level) { case DCERPC_AUTH_LEVEL_PRIVACY: case DCERPC_AUTH_LEVEL_INTEGRITY: break; case DCERPC_AUTH_LEVEL_CONNECT: /* * TODO: let the gensec mech decide if it wants to generate a * signature that might be needed for schannel... */ status = ncacn_push_auth(blob, call, pkt, NULL); return NT_STATUS_IS_OK(status); case DCERPC_AUTH_LEVEL_NONE: status = ncacn_push_auth(blob, call, pkt, NULL); return NT_STATUS_IS_OK(status); default: return false; } ndr = ndr_push_init_ctx(call); if (!ndr) { return false; } if (!(pkt->drep[0] & DCERPC_DREP_LE)) { ndr->flags |= LIBNDR_FLAG_BIGENDIAN; } ndr_err = ndr_push_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { return false; } /* pad to 16 byte multiple in the payload portion of the packet. This matches what w2k3 does. Note that we can't use ndr_push_align() as that is relative to the start of the whole packet, whereas w2k8 wants it relative to the start of the stub */ dce_conn->auth_state.auth_info->auth_pad_length = (16 - (pkt->u.response.stub_and_verifier.length & 15)) & 15; ndr_err = ndr_push_zero(ndr, dce_conn->auth_state.auth_info->auth_pad_length); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { return false; } payload_length = pkt->u.response.stub_and_verifier.length + dce_conn->auth_state.auth_info->auth_pad_length; /* we start without signature, it will appended later */ dce_conn->auth_state.auth_info->credentials = data_blob(NULL, 0); /* add the auth verifier */ ndr_err = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, dce_conn->auth_state.auth_info); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { return false; } /* extract the whole packet as a blob */ *blob = ndr_push_blob(ndr); /* * Setup the frag and auth length in the packet buffer. * This is needed if the GENSEC mech does AEAD signing * of the packet headers. The signature itself will be * appended later. */ dcerpc_set_frag_length(blob, blob->length + sig_size); dcerpc_set_auth_length(blob, sig_size); /* sign or seal the packet */ switch (dce_conn->auth_state.auth_info->auth_level) { case DCERPC_AUTH_LEVEL_PRIVACY: status = gensec_seal_packet(dce_conn->auth_state.gensec_security, call, ndr->data + DCERPC_REQUEST_LENGTH, payload_length, blob->data, blob->length, &creds2); break; case DCERPC_AUTH_LEVEL_INTEGRITY: status = gensec_sign_packet(dce_conn->auth_state.gensec_security, call, ndr->data + DCERPC_REQUEST_LENGTH, payload_length, blob->data, blob->length, &creds2); break; default: status = NT_STATUS_INVALID_LEVEL; break; } if (!NT_STATUS_IS_OK(status)) { return false; } if (creds2.length != sig_size) { DEBUG(3,("dcesrv_auth_response: creds2.length[%u] != sig_size[%u] pad[%u] stub[%u]\n", (unsigned)creds2.length, (uint32_t)sig_size, (unsigned)dce_conn->auth_state.auth_info->auth_pad_length, (unsigned)pkt->u.response.stub_and_verifier.length)); dcerpc_set_frag_length(blob, blob->length + creds2.length); dcerpc_set_auth_length(blob, creds2.length); } if (!data_blob_append(call, blob, creds2.data, creds2.length)) { status = NT_STATUS_NO_MEMORY; return false; } data_blob_free(&creds2); return true; }
/** * @brief Append an auth footer according to what is the current mechanism * * @param auth The pipe_auth_data associated with the connection * @param pad_len The padding used in the packet * @param rpc_out Packet blob up to and including the auth header * * @return A NTSTATUS error code. */ NTSTATUS dcerpc_add_auth_footer(struct pipe_auth_data *auth, size_t pad_len, DATA_BLOB *rpc_out) { struct schannel_state *schannel_auth; struct auth_ntlmssp_state *ntlmssp_ctx; struct spnego_context *spnego_ctx; struct gse_context *gse_ctx; char pad[CLIENT_NDR_PADDING_SIZE] = { 0, }; DATA_BLOB auth_info; DATA_BLOB auth_blob; NTSTATUS status; if (auth->auth_type == DCERPC_AUTH_TYPE_NONE || auth->auth_type == DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM) { return NT_STATUS_OK; } if (pad_len) { /* Copy the sign/seal padding data. */ if (!data_blob_append(NULL, rpc_out, pad, pad_len)) { return NT_STATUS_NO_MEMORY; } } /* marshall the dcerpc_auth with an actually empty auth_blob. * This is needed because the ntmlssp signature includes the * auth header. We will append the actual blob later. */ auth_blob = data_blob_null; status = dcerpc_push_dcerpc_auth(rpc_out->data, auth->auth_type, auth->auth_level, pad_len, 1 /* context id. */, &auth_blob, &auth_info); if (!NT_STATUS_IS_OK(status)) { return status; } /* append the header */ if (!data_blob_append(NULL, rpc_out, auth_info.data, auth_info.length)) { DEBUG(0, ("Failed to add %u bytes auth blob.\n", (unsigned int)auth_info.length)); return NT_STATUS_NO_MEMORY; } data_blob_free(&auth_info); /* Generate any auth sign/seal and add the auth footer. */ switch (auth->auth_type) { case DCERPC_AUTH_TYPE_NONE: case DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM: status = NT_STATUS_OK; break; case DCERPC_AUTH_TYPE_SPNEGO: spnego_ctx = talloc_get_type_abort(auth->auth_ctx, struct spnego_context); status = add_spnego_auth_footer(spnego_ctx, auth->auth_level, rpc_out); break; case DCERPC_AUTH_TYPE_NTLMSSP: ntlmssp_ctx = talloc_get_type_abort(auth->auth_ctx, struct auth_ntlmssp_state); status = add_ntlmssp_auth_footer(ntlmssp_ctx, auth->auth_level, rpc_out); break; case DCERPC_AUTH_TYPE_SCHANNEL: schannel_auth = talloc_get_type_abort(auth->auth_ctx, struct schannel_state); status = add_schannel_auth_footer(schannel_auth, auth->auth_level, rpc_out); break; case DCERPC_AUTH_TYPE_KRB5: gse_ctx = talloc_get_type_abort(auth->auth_ctx, struct gse_context); status = add_gssapi_auth_footer(gse_ctx, auth->auth_level, rpc_out); break; default: status = NT_STATUS_INVALID_PARAMETER; break; } return status; }
/* process a decoded ldap message */ static void ldapsrv_process_message(struct ldapsrv_connection *conn, struct ldap_message *msg) { struct ldapsrv_call *call; NTSTATUS status; DATA_BLOB blob; call = talloc(conn, struct ldapsrv_call); if (!call) { ldapsrv_terminate_connection(conn, "no memory"); return; } call->request = talloc_steal(call, msg); call->conn = conn; call->replies = NULL; call->send_callback = NULL; call->send_private = NULL; /* make the call */ status = ldapsrv_do_call(call); if (!NT_STATUS_IS_OK(status)) { talloc_free(call); return; } blob = data_blob(NULL, 0); if (call->replies == NULL) { talloc_free(call); return; } /* build all the replies into a single blob */ while (call->replies) { DATA_BLOB b; bool ret; msg = call->replies->msg; if (!ldap_encode(msg, samba_ldap_control_handlers(), &b, call)) { DEBUG(0,("Failed to encode ldap reply of type %d\n", msg->type)); talloc_free(call); return; } ret = data_blob_append(call, &blob, b.data, b.length); data_blob_free(&b); talloc_set_name_const(blob.data, "Outgoing, encoded LDAP packet"); if (!ret) { talloc_free(call); return; } DLIST_REMOVE(call->replies, call->replies); } packet_send_callback(conn->packet, blob, call->send_callback, call->send_private); talloc_free(call); return; }
static bool api_pipe_alter_context(struct pipes_struct *p, struct ncacn_packet *pkt) { struct dcerpc_auth auth_info = {0}; uint16_t assoc_gid; NTSTATUS status; union dcerpc_payload u; struct dcerpc_ack_ctx bind_ack_ctx; DATA_BLOB auth_resp = data_blob_null; DATA_BLOB auth_blob = data_blob_null; struct gensec_security *gensec_security; DEBUG(5,("api_pipe_alter_context: make response. %d\n", __LINE__)); if (pkt->u.bind.assoc_group_id != 0) { assoc_gid = pkt->u.bind.assoc_group_id; } else { assoc_gid = 0x53f0; } /* * Create the bind response struct. */ /* If the requested abstract synt uuid doesn't match our client pipe, reject the bind_ack & set the transfer interface synt to all 0's, ver 0 (observed when NT5 attempts to bind to abstract interfaces unknown to NT4) Needed when adding entries to a DACL from NT5 - SK */ if (check_bind_req(p, &pkt->u.bind.ctx_list[0].abstract_syntax, &pkt->u.bind.ctx_list[0].transfer_syntaxes[0], pkt->u.bind.ctx_list[0].context_id)) { bind_ack_ctx.result = 0; bind_ack_ctx.reason.value = 0; bind_ack_ctx.syntax = pkt->u.bind.ctx_list[0].transfer_syntaxes[0]; } else { p->pipe_bound = False; /* Rejection reason: abstract syntax not supported */ bind_ack_ctx.result = DCERPC_BIND_PROVIDER_REJECT; bind_ack_ctx.reason.value = DCERPC_BIND_REASON_ASYNTAX; bind_ack_ctx.syntax = ndr_syntax_id_null; } /* * Check if this is an authenticated alter context request. */ if (pkt->auth_length) { /* Quick length check. Won't catch a bad auth footer, * prevents overrun. */ if (pkt->frag_length < RPC_HEADER_LEN + DCERPC_AUTH_TRAILER_LENGTH + pkt->auth_length) { DEBUG(0,("api_pipe_alter_context: auth_len (%u) " "too long for fragment %u.\n", (unsigned int)pkt->auth_length, (unsigned int)pkt->frag_length )); goto err_exit; } status = dcerpc_pull_dcerpc_auth(pkt, &pkt->u.bind.auth_info, &auth_info, p->endian); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("Unable to unmarshall dcerpc_auth.\n")); goto err_exit; } /* We can only finish if the pipe is unbound for now */ if (p->pipe_bound) { DEBUG(0, (__location__ ": Pipe already bound, " "Altering Context not yet supported!\n")); goto err_exit; } if (auth_info.auth_type != p->auth.auth_type) { DEBUG(0, ("Auth type mismatch! Client sent %d, " "but auth was started as type %d!\n", auth_info.auth_type, p->auth.auth_type)); goto err_exit; } gensec_security = p->auth.auth_ctx; status = auth_generic_server_step(gensec_security, pkt, &auth_info.credentials, &auth_resp); if (NT_STATUS_IS_OK(status)) { /* third leg of auth, verify auth info */ status = pipe_auth_verify_final(p); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("Auth Verify failed (%s)\n", nt_errstr(status))); goto err_exit; } } else if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { DEBUG(10, ("More auth legs required.\n")); } else { DEBUG(0, ("Auth step returned an error (%s)\n", nt_errstr(status))); goto err_exit; } } ZERO_STRUCT(u.alter_resp); u.alter_resp.max_xmit_frag = RPC_MAX_PDU_FRAG_LEN; u.alter_resp.max_recv_frag = RPC_MAX_PDU_FRAG_LEN; u.alter_resp.assoc_group_id = assoc_gid; /* secondary address CAN be NULL * as the specs say it's ignored. * It MUST be NULL to have the spoolss working. */ u.alter_resp.secondary_address = ""; u.alter_resp.secondary_address_size = 1; u.alter_resp.num_results = 1; u.alter_resp.ctx_list = &bind_ack_ctx; /* NOTE: We leave the auth_info empty so we can calculate the padding * later and then append the auth_info --simo */ /* * Marshall directly into the outgoing PDU space. We * must do this as we need to set to the bind response * header and are never sending more than one PDU here. */ status = dcerpc_push_ncacn_packet(p->mem_ctx, DCERPC_PKT_ALTER_RESP, DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST, auth_resp.length, pkt->call_id, &u, &p->out_data.frag); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("Failed to marshall bind_ack packet. (%s)\n", nt_errstr(status))); } if (auth_resp.length) { status = dcerpc_push_dcerpc_auth(pkt, auth_info.auth_type, auth_info.auth_level, 0, /* pad_len */ 1, /* auth_context_id */ &auth_resp, &auth_blob); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("Marshalling of dcerpc_auth failed.\n")); goto err_exit; } } /* Now that we have the auth len store it into the right place in * the dcerpc header */ dcerpc_set_frag_length(&p->out_data.frag, p->out_data.frag.length + auth_blob.length); if (auth_resp.length) { if (!data_blob_append(p->mem_ctx, &p->out_data.frag, auth_blob.data, auth_blob.length)) { DEBUG(0, ("Append of auth info failed.\n")); goto err_exit; } } /* * Setup the lengths for the initial reply. */ p->out_data.data_sent_length = 0; p->out_data.current_pdu_sent = 0; TALLOC_FREE(auth_blob.data); return True; err_exit: data_blob_free(&p->out_data.frag); TALLOC_FREE(auth_blob.data); return setup_bind_nak(p, pkt); }
static bool api_pipe_bind_req(struct pipes_struct *p, struct ncacn_packet *pkt) { struct dcerpc_auth auth_info = {0}; uint16_t assoc_gid; unsigned int auth_type = DCERPC_AUTH_TYPE_NONE; NTSTATUS status; struct ndr_syntax_id id; uint8_t pfc_flags = 0; union dcerpc_payload u; struct dcerpc_ack_ctx bind_ack_ctx; DATA_BLOB auth_resp = data_blob_null; DATA_BLOB auth_blob = data_blob_null; const struct ndr_interface_table *table; /* No rebinds on a bound pipe - use alter context. */ if (p->pipe_bound) { DEBUG(2,("Rejecting bind request on bound rpc connection\n")); return setup_bind_nak(p, pkt); } if (pkt->u.bind.num_contexts == 0) { DEBUG(0, ("api_pipe_bind_req: no rpc contexts around\n")); goto err_exit; } /* * Try and find the correct pipe name to ensure * that this is a pipe name we support. */ id = pkt->u.bind.ctx_list[0].abstract_syntax; table = ndr_table_by_uuid(&id.uuid); if (table == NULL) { DEBUG(0,("unknown interface\n")); return false; } if (rpc_srv_pipe_exists_by_id(&id)) { DEBUG(3, ("api_pipe_bind_req: %s -> %s rpc service\n", rpc_srv_get_pipe_cli_name(&id), rpc_srv_get_pipe_srv_name(&id))); } else { status = smb_probe_module( "rpc", dcerpc_default_transport_endpoint(pkt, NCACN_NP, table)); if (NT_STATUS_IS_ERR(status)) { DEBUG(3,("api_pipe_bind_req: Unknown rpc service name " "%s in bind request.\n", ndr_interface_name(&id.uuid, id.if_version))); return setup_bind_nak(p, pkt); } if (rpc_srv_get_pipe_interface_by_cli_name( dcerpc_default_transport_endpoint(pkt, NCACN_NP, table), &id)) { DEBUG(3, ("api_pipe_bind_req: %s -> %s rpc service\n", rpc_srv_get_pipe_cli_name(&id), rpc_srv_get_pipe_srv_name(&id))); } else { DEBUG(0, ("module %s doesn't provide functions for " "pipe %s!\n", ndr_interface_name(&id.uuid, id.if_version), ndr_interface_name(&id.uuid, id.if_version))); return setup_bind_nak(p, pkt); } } DEBUG(5,("api_pipe_bind_req: make response. %d\n", __LINE__)); if (pkt->u.bind.assoc_group_id != 0) { assoc_gid = pkt->u.bind.assoc_group_id; } else { assoc_gid = 0x53f0; } /* * Create the bind response struct. */ /* If the requested abstract synt uuid doesn't match our client pipe, reject the bind_ack & set the transfer interface synt to all 0's, ver 0 (observed when NT5 attempts to bind to abstract interfaces unknown to NT4) Needed when adding entries to a DACL from NT5 - SK */ if (check_bind_req(p, &pkt->u.bind.ctx_list[0].abstract_syntax, &pkt->u.bind.ctx_list[0].transfer_syntaxes[0], pkt->u.bind.ctx_list[0].context_id)) { bind_ack_ctx.result = 0; bind_ack_ctx.reason.value = 0; bind_ack_ctx.syntax = pkt->u.bind.ctx_list[0].transfer_syntaxes[0]; } else { p->pipe_bound = False; /* Rejection reason: abstract syntax not supported */ bind_ack_ctx.result = DCERPC_BIND_PROVIDER_REJECT; bind_ack_ctx.reason.value = DCERPC_BIND_REASON_ASYNTAX; bind_ack_ctx.syntax = ndr_syntax_id_null; } /* * Check if this is an authenticated bind request. */ if (pkt->auth_length) { /* Quick length check. Won't catch a bad auth footer, * prevents overrun. */ if (pkt->frag_length < RPC_HEADER_LEN + DCERPC_AUTH_TRAILER_LENGTH + pkt->auth_length) { DEBUG(0,("api_pipe_bind_req: auth_len (%u) " "too long for fragment %u.\n", (unsigned int)pkt->auth_length, (unsigned int)pkt->frag_length)); goto err_exit; } /* * Decode the authentication verifier. */ status = dcerpc_pull_dcerpc_auth(pkt, &pkt->u.bind.auth_info, &auth_info, p->endian); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("Unable to unmarshall dcerpc_auth.\n")); goto err_exit; } auth_type = auth_info.auth_type; /* Work out if we have to sign or seal etc. */ switch (auth_info.auth_level) { case DCERPC_AUTH_LEVEL_INTEGRITY: p->auth.auth_level = DCERPC_AUTH_LEVEL_INTEGRITY; break; case DCERPC_AUTH_LEVEL_PRIVACY: p->auth.auth_level = DCERPC_AUTH_LEVEL_PRIVACY; break; case DCERPC_AUTH_LEVEL_CONNECT: p->auth.auth_level = DCERPC_AUTH_LEVEL_CONNECT; break; default: DEBUG(0, ("Unexpected auth level (%u).\n", (unsigned int)auth_info.auth_level )); goto err_exit; } switch (auth_type) { case DCERPC_AUTH_TYPE_NONE: break; default: if (!pipe_auth_generic_bind(p, pkt, &auth_info, &auth_resp)) { goto err_exit; } break; } } if (auth_type == DCERPC_AUTH_TYPE_NONE) { /* Unauthenticated bind request. */ /* We're finished - no more packets. */ p->auth.auth_type = DCERPC_AUTH_TYPE_NONE; /* We must set the pipe auth_level here also. */ p->auth.auth_level = DCERPC_AUTH_LEVEL_NONE; p->pipe_bound = True; /* The session key was initialized from the SMB * session in make_internal_rpc_pipe_p */ } ZERO_STRUCT(u.bind_ack); u.bind_ack.max_xmit_frag = RPC_MAX_PDU_FRAG_LEN; u.bind_ack.max_recv_frag = RPC_MAX_PDU_FRAG_LEN; u.bind_ack.assoc_group_id = assoc_gid; /* name has to be \PIPE\xxxxx */ u.bind_ack.secondary_address = talloc_asprintf(pkt, "\\PIPE\\%s", rpc_srv_get_pipe_srv_name(&id)); if (!u.bind_ack.secondary_address) { DEBUG(0, ("Out of memory!\n")); goto err_exit; } u.bind_ack.secondary_address_size = strlen(u.bind_ack.secondary_address) + 1; u.bind_ack.num_results = 1; u.bind_ack.ctx_list = &bind_ack_ctx; /* NOTE: We leave the auth_info empty so we can calculate the padding * later and then append the auth_info --simo */ /* * Marshall directly into the outgoing PDU space. We * must do this as we need to set to the bind response * header and are never sending more than one PDU here. */ pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST; if (p->auth.hdr_signing) { pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN; } status = dcerpc_push_ncacn_packet(p->mem_ctx, DCERPC_PKT_BIND_ACK, pfc_flags, auth_resp.length, pkt->call_id, &u, &p->out_data.frag); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("Failed to marshall bind_ack packet. (%s)\n", nt_errstr(status))); } if (auth_resp.length) { status = dcerpc_push_dcerpc_auth(pkt, auth_type, auth_info.auth_level, 0, 1, /* auth_context_id */ &auth_resp, &auth_blob); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("Marshalling of dcerpc_auth failed.\n")); goto err_exit; } } /* Now that we have the auth len store it into the right place in * the dcerpc header */ dcerpc_set_frag_length(&p->out_data.frag, p->out_data.frag.length + auth_blob.length); if (auth_blob.length) { if (!data_blob_append(p->mem_ctx, &p->out_data.frag, auth_blob.data, auth_blob.length)) { DEBUG(0, ("Append of auth info failed.\n")); goto err_exit; } } /* * Setup the lengths for the initial reply. */ p->out_data.data_sent_length = 0; p->out_data.current_pdu_sent = 0; TALLOC_FREE(auth_blob.data); return True; err_exit: data_blob_free(&p->out_data.frag); TALLOC_FREE(auth_blob.data); return setup_bind_nak(p, pkt); }
static bool process_request_pdu(struct pipes_struct *p, struct ncacn_packet *pkt) { NTSTATUS status; DATA_BLOB data; struct dcerpc_sec_vt_header2 hdr2; if (!p->pipe_bound) { DEBUG(0,("process_request_pdu: rpc request with no bind.\n")); set_incoming_fault(p); return False; } hdr2 = dcerpc_sec_vt_header2_from_ncacn_packet(pkt); if (pkt->pfc_flags & DCERPC_PFC_FLAG_FIRST) { p->header2 = hdr2; } else { if (!dcerpc_sec_vt_header2_equal(&hdr2, &p->header2)) { set_incoming_fault(p); return false; } } /* Store the opnum */ p->opnum = pkt->u.request.opnum; status = dcesrv_auth_request(&p->auth, pkt, &p->in_data.pdu); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("Failed to check packet auth. (%s)\n", nt_errstr(status))); set_incoming_fault(p); return false; } data = pkt->u.request.stub_and_verifier; /* * Check the data length doesn't go over the 15Mb limit. * increased after observing a bug in the Windows NT 4.0 SP6a * spoolsv.exe when the response to a GETPRINTERDRIVER2 RPC * will not fit in the initial buffer of size 0x1068 --jerry 22/01/2002 */ if (p->in_data.data.length + data.length > MAX_RPC_DATA_SIZE) { DEBUG(0, ("process_request_pdu: " "rpc data buffer too large (%u) + (%u)\n", (unsigned int)p->in_data.data.length, (unsigned int)data.length)); set_incoming_fault(p); return False; } /* * Append the data portion into the buffer and return. */ if (data.length) { if (!data_blob_append(p->mem_ctx, &p->in_data.data, data.data, data.length)) { DEBUG(0, ("Unable to append data size %u " "to parse buffer of size %u.\n", (unsigned int)data.length, (unsigned int)p->in_data.data.length)); set_incoming_fault(p); return False; } } if (!(pkt->pfc_flags & DCERPC_PFC_FLAG_LAST)) { return true; } /* * Ok - we finally have a complete RPC stream. * Call the rpc command to process it. */ return api_pipe_request(p, pkt); }
static void ldapsrv_call_process_done(struct tevent_req *subreq) { struct ldapsrv_call *call = tevent_req_callback_data(subreq, struct ldapsrv_call); struct ldapsrv_connection *conn = call->conn; NTSTATUS status; DATA_BLOB blob = data_blob_null; conn->active_call = NULL; status = ldapsrv_process_call_recv(subreq); TALLOC_FREE(subreq); if (!NT_STATUS_IS_OK(status)) { ldapsrv_terminate_connection(conn, nt_errstr(status)); return; } /* build all the replies into a single blob */ while (call->replies) { DATA_BLOB b; bool ret; if (!ldap_encode(call->replies->msg, samba_ldap_control_handlers(), &b, call)) { DEBUG(0,("Failed to encode ldap reply of type %d\n", call->replies->msg->type)); ldapsrv_terminate_connection(conn, "ldap_encode failed"); return; } ret = data_blob_append(call, &blob, b.data, b.length); data_blob_free(&b); talloc_set_name_const(blob.data, "Outgoing, encoded LDAP packet"); if (!ret) { ldapsrv_terminate_connection(conn, "data_blob_append failed"); return; } DLIST_REMOVE(call->replies, call->replies); } if (blob.length == 0) { TALLOC_FREE(call); ldapsrv_call_read_next(conn); return; } call->out_iov.iov_base = blob.data; call->out_iov.iov_len = blob.length; subreq = tstream_writev_queue_send(call, conn->connection->event.ctx, conn->sockets.active, conn->sockets.send_queue, &call->out_iov, 1); if (subreq == NULL) { ldapsrv_terminate_connection(conn, "stream_writev_queue_send failed"); return; } tevent_req_set_callback(subreq, ldapsrv_call_writev_done, call); }
/* push a dcerpc request packet into a blob, possibly signing it. */ static NTSTATUS ncacn_push_request_sign(struct dcerpc_connection *c, DATA_BLOB *blob, TALLOC_CTX *mem_ctx, size_t sig_size, struct ncacn_packet *pkt) { NTSTATUS status; struct ndr_push *ndr; DATA_BLOB creds2; size_t payload_length; enum ndr_err_code ndr_err; size_t hdr_size = DCERPC_REQUEST_LENGTH; /* non-signed packets are simpler */ if (sig_size == 0) { return ncacn_push_auth(blob, mem_ctx, c->iconv_convenience, pkt, NULL); } switch (c->security_state.auth_info->auth_level) { case DCERPC_AUTH_LEVEL_PRIVACY: case DCERPC_AUTH_LEVEL_INTEGRITY: break; case DCERPC_AUTH_LEVEL_CONNECT: /* TODO: let the gensec mech decide if it wants to generate a signature */ return ncacn_push_auth(blob, mem_ctx, c->iconv_convenience, pkt, NULL); case DCERPC_AUTH_LEVEL_NONE: return ncacn_push_auth(blob, mem_ctx, c->iconv_convenience, pkt, NULL); default: return NT_STATUS_INVALID_LEVEL; } ndr = ndr_push_init_ctx(mem_ctx, c->iconv_convenience); if (!ndr) { return NT_STATUS_NO_MEMORY; } if (c->flags & DCERPC_PUSH_BIGENDIAN) { ndr->flags |= LIBNDR_FLAG_BIGENDIAN; } if (c->flags & DCERPC_NDR64) { ndr->flags |= LIBNDR_FLAG_NDR64; } if (pkt->pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) { ndr->flags |= LIBNDR_FLAG_OBJECT_PRESENT; hdr_size += 16; } ndr_err = ndr_push_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { return ndr_map_error2ntstatus(ndr_err); } status = NT_STATUS_OK; /* pad to 16 byte multiple in the payload portion of the packet. This matches what w2k3 does */ c->security_state.auth_info->auth_pad_length = (16 - (pkt->u.request.stub_and_verifier.length & 15)) & 15; ndr_err = ndr_push_zero(ndr, c->security_state.auth_info->auth_pad_length); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { return ndr_map_error2ntstatus(ndr_err); } status = NT_STATUS_OK; payload_length = pkt->u.request.stub_and_verifier.length + c->security_state.auth_info->auth_pad_length; /* we start without signature, it will appended later */ c->security_state.auth_info->credentials = data_blob(NULL,0); /* add the auth verifier */ ndr_err = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, c->security_state.auth_info); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { return ndr_map_error2ntstatus(ndr_err); } status = NT_STATUS_OK; /* extract the whole packet as a blob */ *blob = ndr_push_blob(ndr); /* * Setup the frag and auth length in the packet buffer. * This is needed if the GENSEC mech does AEAD signing * of the packet headers. The signature itself will be * appended later. */ dcerpc_set_frag_length(blob, blob->length + sig_size); dcerpc_set_auth_length(blob, sig_size); /* sign or seal the packet */ switch (c->security_state.auth_info->auth_level) { case DCERPC_AUTH_LEVEL_PRIVACY: status = gensec_seal_packet(c->security_state.generic_state, mem_ctx, blob->data + hdr_size, payload_length, blob->data, blob->length, &creds2); if (!NT_STATUS_IS_OK(status)) { return status; } break; case DCERPC_AUTH_LEVEL_INTEGRITY: status = gensec_sign_packet(c->security_state.generic_state, mem_ctx, blob->data + hdr_size, payload_length, blob->data, blob->length, &creds2); if (!NT_STATUS_IS_OK(status)) { return status; } break; default: status = NT_STATUS_INVALID_LEVEL; break; } if (creds2.length != sig_size) { DEBUG(0,("ncacn_push_request_sign: creds2.length[%u] != sig_size[%u] pad[%u] stub[%u]\n", (unsigned) creds2.length, (unsigned) sig_size, (unsigned) c->security_state.auth_info->auth_pad_length, (unsigned) pkt->u.request.stub_and_verifier.length)); return NT_STATUS_INTERNAL_ERROR; } if (!data_blob_append(mem_ctx, blob, creds2.data, creds2.length)) { return NT_STATUS_NO_MEMORY; } return NT_STATUS_OK; }
/* called when a web connection becomes readable */ static void websrv_recv(struct stream_connection *conn, uint16_t flags) { struct web_server_data *wdata; struct websrv_context *web = talloc_get_type(conn->private_data, struct websrv_context); NTSTATUS status; uint8_t buf[1024]; size_t nread; uint8_t *p; DATA_BLOB b; /* not the most efficient http parser ever, but good enough for us */ status = socket_recv(conn->socket, buf, sizeof(buf), &nread); if (NT_STATUS_IS_ERR(status)) goto failed; if (!NT_STATUS_IS_OK(status)) return; if (!data_blob_append(web, &web->input.partial, buf, nread)) goto failed; /* parse any lines that are available */ b = web->input.partial; while (!web->input.end_of_headers && (p=(uint8_t *)memchr(b.data, '\n', b.length))) { const char *line = (const char *)b.data; *p = 0; if (p != b.data && p[-1] == '\r') { p[-1] = 0; } status = http_parse_header(web, line); if (!NT_STATUS_IS_OK(status)) return; b.length -= (p - b.data) + 1; b.data = p+1; } /* keep any remaining bytes in web->input.partial */ if (b.length == 0) { b.data = NULL; } b = data_blob_talloc(web, b.data, b.length); data_blob_free(&web->input.partial); web->input.partial = b; /* we finish when we have both the full headers (terminated by a blank line) and any post data, as indicated by the content_length */ if (web->input.end_of_headers && web->input.partial.length >= web->input.content_length) { if (web->input.partial.length > web->input.content_length) { web->input.partial.data[web->input.content_length] = 0; } EVENT_FD_NOT_READABLE(web->conn->event.fde); /* the reference/unlink code here is quite subtle. It is needed because the rendering of the web-pages, and in particular the esp/ejs backend, is semi-async. So we could well end up in the connection timeout code while inside http_process_input(), but we must not destroy the stack variables being used by that rendering process when we handle the timeout. */ if (!talloc_reference(web->task, web)) goto failed; wdata = talloc_get_type(web->task->private_data, struct web_server_data); if (wdata == NULL) goto failed; wdata->http_process_input(wdata, web); talloc_unlink(web->task, web); } return; failed: stream_terminate_connection(conn, "websrv_recv: failed"); }