_PUBLIC_ size_t gensec_max_input_size(struct gensec_security *gensec_security) { if (!gensec_security->ops->max_input_size) { return (1 << 17) - gensec_sig_size(gensec_security, 1 << 17); } return gensec_security->ops->max_input_size(gensec_security); }
static void dcerpc_ship_next_request(struct dcerpc_connection *c) { struct rpc_request *req; struct dcerpc_pipe *p; DATA_BLOB *stub_data; struct ncacn_packet pkt; DATA_BLOB blob; uint32_t remaining, chunk_size; bool first_packet = true; size_t sig_size = 0; req = c->request_queue; if (req == NULL) { return; } p = req->p; stub_data = &req->request_data; if (!req->async_call && (c->pending != NULL)) { return; } DLIST_REMOVE(c->request_queue, req); DLIST_ADD(c->pending, req); req->state = RPC_REQUEST_PENDING; init_ncacn_hdr(p->conn, &pkt); remaining = stub_data->length; /* we can write a full max_recv_frag size, minus the dcerpc request header size */ chunk_size = p->conn->srv_max_recv_frag; chunk_size -= DCERPC_REQUEST_LENGTH; if (c->security_state.auth_info && c->security_state.generic_state) { sig_size = gensec_sig_size(c->security_state.generic_state, p->conn->srv_max_recv_frag); if (sig_size) { chunk_size -= DCERPC_AUTH_TRAILER_LENGTH; chunk_size -= sig_size; } } chunk_size -= (chunk_size % 16); pkt.ptype = DCERPC_PKT_REQUEST; pkt.call_id = req->call_id; pkt.auth_length = 0; pkt.pfc_flags = 0; pkt.u.request.alloc_hint = remaining; pkt.u.request.context_id = p->context_id; pkt.u.request.opnum = req->opnum; if (req->object) { pkt.u.request.object.object = *req->object; pkt.pfc_flags |= DCERPC_PFC_FLAG_OBJECT_UUID; chunk_size -= ndr_size_GUID(req->object,NULL,0); } /* we send a series of pdus without waiting for a reply */ while (remaining > 0 || first_packet) { uint32_t chunk = MIN(chunk_size, remaining); bool last_frag = false; bool do_trans = false; first_packet = false; pkt.pfc_flags &= ~(DCERPC_PFC_FLAG_FIRST |DCERPC_PFC_FLAG_LAST); if (remaining == stub_data->length) { pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST; } if (chunk == remaining) { pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST; last_frag = true; } pkt.u.request.stub_and_verifier.data = stub_data->data + (stub_data->length - remaining); pkt.u.request.stub_and_verifier.length = chunk; req->status = ncacn_push_request_sign(p->conn, &blob, req, sig_size, &pkt); if (!NT_STATUS_IS_OK(req->status)) { req->state = RPC_REQUEST_DONE; DLIST_REMOVE(p->conn->pending, req); return; } if (last_frag && !req->async_call) { do_trans = true; } req->status = p->conn->transport.send_request(p->conn, &blob, do_trans); if (!NT_STATUS_IS_OK(req->status)) { req->state = RPC_REQUEST_DONE; DLIST_REMOVE(p->conn->pending, req); return; } if (last_frag && !do_trans) { req->status = p->conn->transport.send_read(p->conn); if (!NT_STATUS_IS_OK(req->status)) { req->state = RPC_REQUEST_DONE; DLIST_REMOVE(p->conn->pending, req); return; } } remaining -= chunk; } }
/** * @brief Calculate how much data we can in a packet, including calculating * auth token and pad lengths. * * @param auth The pipe_auth_data structure for this pipe. * @param header_len The length of the packet header * @param data_left The data left in the send buffer * @param max_xmit_frag The max fragment size. * @param data_to_send [out] The max data we will send in the pdu * @param frag_len [out] The total length of the fragment * @param auth_len [out] The length of the auth trailer * @param pad_len [out] The padding to be applied * * @return A NT Error status code. */ NTSTATUS dcerpc_guess_sizes(struct pipe_auth_data *auth, size_t header_len, size_t data_left, size_t max_xmit_frag, size_t *data_to_send, size_t *frag_len, size_t *auth_len, size_t *pad_len) { size_t max_len; size_t mod_len; struct gensec_security *gensec_security; /* no auth token cases first */ switch (auth->auth_level) { case DCERPC_AUTH_LEVEL_NONE: case DCERPC_AUTH_LEVEL_CONNECT: max_len = max_xmit_frag - header_len; *data_to_send = MIN(max_len, data_left); *pad_len = 0; *auth_len = 0; *frag_len = header_len + *data_to_send; return NT_STATUS_OK; case DCERPC_AUTH_LEVEL_PRIVACY: break; case DCERPC_AUTH_LEVEL_INTEGRITY: break; case DCERPC_AUTH_LEVEL_PACKET: break; default: return NT_STATUS_INVALID_PARAMETER; } /* Sign/seal case, calculate auth and pad lengths */ max_len = max_xmit_frag - header_len - DCERPC_AUTH_TRAILER_LENGTH; /* Treat the same for all authenticated rpc requests. */ switch (auth->auth_type) { case DCERPC_AUTH_TYPE_SPNEGO: case DCERPC_AUTH_TYPE_NTLMSSP: case DCERPC_AUTH_TYPE_KRB5: case DCERPC_AUTH_TYPE_SCHANNEL: gensec_security = auth->auth_ctx; mod_len = (max_len % DCERPC_AUTH_PAD_ALIGNMENT); *auth_len = gensec_sig_size(gensec_security, max_len - mod_len); if (*auth_len == 0) { return NT_STATUS_INTERNAL_ERROR; } break; default: return NT_STATUS_INVALID_PARAMETER; } max_len -= *auth_len; mod_len = (max_len % DCERPC_AUTH_PAD_ALIGNMENT); max_len -= mod_len; *data_to_send = MIN(max_len, data_left); *pad_len = DCERPC_AUTH_PAD_LENGTH(*data_to_send); *frag_len = header_len + *data_to_send + *pad_len + DCERPC_AUTH_TRAILER_LENGTH + *auth_len; return NT_STATUS_OK; }
/** * @brief Calculate how much data we can in a packet, including calculating * auth token and pad lengths. * * @param auth The pipe_auth_data structure for this pipe. * @param header_len The length of the packet header * @param data_left The data left in the send buffer * @param max_xmit_frag The max fragment size. * @param pad_alignment The NDR padding size. * @param data_to_send [out] The max data we will send in the pdu * @param frag_len [out] The total length of the fragment * @param auth_len [out] The length of the auth trailer * @param pad_len [out] The padding to be applied * * @return A NT Error status code. */ NTSTATUS dcerpc_guess_sizes(struct pipe_auth_data *auth, size_t header_len, size_t data_left, size_t max_xmit_frag, size_t pad_alignment, size_t *data_to_send, size_t *frag_len, size_t *auth_len, size_t *pad_len) { size_t max_len; size_t mod_len; struct gensec_security *gensec_security; struct schannel_state *schannel_auth; /* no auth token cases first */ switch (auth->auth_level) { case DCERPC_AUTH_LEVEL_NONE: case DCERPC_AUTH_LEVEL_CONNECT: case DCERPC_AUTH_LEVEL_PACKET: max_len = max_xmit_frag - header_len; *data_to_send = MIN(max_len, data_left); *pad_len = 0; *auth_len = 0; *frag_len = header_len + *data_to_send; return NT_STATUS_OK; case DCERPC_AUTH_LEVEL_PRIVACY: break; case DCERPC_AUTH_LEVEL_INTEGRITY: break; default: return NT_STATUS_INVALID_PARAMETER; } /* Sign/seal case, calculate auth and pad lengths */ max_len = max_xmit_frag - header_len - DCERPC_AUTH_TRAILER_LENGTH; /* Treat the same for all authenticated rpc requests. */ switch (auth->auth_type) { case DCERPC_AUTH_TYPE_SPNEGO: case DCERPC_AUTH_TYPE_NTLMSSP: case DCERPC_AUTH_TYPE_KRB5: gensec_security = talloc_get_type_abort(auth->auth_ctx, struct gensec_security); *auth_len = gensec_sig_size(gensec_security, max_len); break; case DCERPC_AUTH_TYPE_SCHANNEL: schannel_auth = talloc_get_type_abort(auth->auth_ctx, struct schannel_state); *auth_len = netsec_outgoing_sig_size(schannel_auth); break; default: return NT_STATUS_INVALID_PARAMETER; } max_len -= *auth_len; *data_to_send = MIN(max_len, data_left); mod_len = (header_len + *data_to_send) % pad_alignment; if (mod_len) { *pad_len = pad_alignment - mod_len; } else { *pad_len = 0; } if (*data_to_send + *pad_len > max_len) { *data_to_send -= pad_alignment; } *frag_len = header_len + *data_to_send + *pad_len + DCERPC_AUTH_TRAILER_LENGTH + *auth_len; return NT_STATUS_OK; }
/* perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can we fit on one socket??) */ static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads) { DATA_BLOB msg1 = data_blob_null; DATA_BLOB blob = data_blob_null; DATA_BLOB blob_in = data_blob_null; DATA_BLOB blob_out = data_blob_null; struct berval cred, *scred = NULL; int rc; NTSTATUS nt_status; ADS_STATUS status; int turn = 1; struct auth_generic_state *auth_generic_state; nt_status = auth_generic_client_prepare(NULL, &auth_generic_state); if (!NT_STATUS_IS_OK(nt_status)) { return ADS_ERROR_NT(nt_status); } if (!NT_STATUS_IS_OK(nt_status = auth_generic_set_username(auth_generic_state, ads->auth.user_name))) { return ADS_ERROR_NT(nt_status); } if (!NT_STATUS_IS_OK(nt_status = auth_generic_set_domain(auth_generic_state, ads->auth.realm))) { return ADS_ERROR_NT(nt_status); } if (!NT_STATUS_IS_OK(nt_status = auth_generic_set_password(auth_generic_state, ads->auth.password))) { return ADS_ERROR_NT(nt_status); } switch (ads->ldap.wrap_type) { case ADS_SASLWRAP_TYPE_SEAL: gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN); gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SEAL); break; case ADS_SASLWRAP_TYPE_SIGN: if (ads->auth.flags & ADS_AUTH_SASL_FORCE) { gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN); } else { /* * windows servers are broken with sign only, * so we need to use seal here too */ gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN); gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SEAL); ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL; } break; case ADS_SASLWRAP_TYPE_PLAIN: break; } nt_status = auth_generic_client_start(auth_generic_state, GENSEC_OID_NTLMSSP); if (!NT_STATUS_IS_OK(nt_status)) { return ADS_ERROR_NT(nt_status); } blob_in = data_blob_null; do { nt_status = gensec_update(auth_generic_state->gensec_security, talloc_tos(), NULL, blob_in, &blob_out); data_blob_free(&blob_in); if ((NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) || NT_STATUS_IS_OK(nt_status)) && blob_out.length) { if (turn == 1) { const char *OIDs_ntlm[] = {OID_NTLMSSP, NULL}; /* and wrap it in a SPNEGO wrapper */ msg1 = spnego_gen_negTokenInit(talloc_tos(), OIDs_ntlm, &blob_out, NULL); } else { /* wrap it in SPNEGO */ msg1 = spnego_gen_auth(talloc_tos(), blob_out); } data_blob_free(&blob_out); cred.bv_val = (char *)msg1.data; cred.bv_len = msg1.length; scred = NULL; rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred); data_blob_free(&msg1); if ((rc != LDAP_SASL_BIND_IN_PROGRESS) && (rc != 0)) { if (scred) { ber_bvfree(scred); } TALLOC_FREE(auth_generic_state); return ADS_ERROR(rc); } if (scred) { blob = data_blob(scred->bv_val, scred->bv_len); ber_bvfree(scred); } else { blob = data_blob_null; } } else { TALLOC_FREE(auth_generic_state); data_blob_free(&blob_out); return ADS_ERROR_NT(nt_status); } if ((turn == 1) && (rc == LDAP_SASL_BIND_IN_PROGRESS)) { DATA_BLOB tmp_blob = data_blob_null; /* the server might give us back two challenges */ if (!spnego_parse_challenge(talloc_tos(), blob, &blob_in, &tmp_blob)) { TALLOC_FREE(auth_generic_state); data_blob_free(&blob); DEBUG(3,("Failed to parse challenges\n")); return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER); } data_blob_free(&tmp_blob); } else if (rc == LDAP_SASL_BIND_IN_PROGRESS) { if (!spnego_parse_auth_response(talloc_tos(), blob, nt_status, OID_NTLMSSP, &blob_in)) { TALLOC_FREE(auth_generic_state); data_blob_free(&blob); DEBUG(3,("Failed to parse auth response\n")); return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER); } } data_blob_free(&blob); data_blob_free(&blob_out); turn++; } while (rc == LDAP_SASL_BIND_IN_PROGRESS && !NT_STATUS_IS_OK(nt_status)); if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) { uint32_t sig_size = gensec_sig_size(auth_generic_state->gensec_security, 0); ads->ldap.out.max_unwrapped = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED - sig_size; ads->ldap.out.sig_size = sig_size; ads->ldap.in.min_wrapped = ads->ldap.out.sig_size; ads->ldap.in.max_wrapped = ADS_SASL_WRAPPING_IN_MAX_WRAPPED; status = ads_setup_sasl_wrapping(ads, &ads_sasl_ntlmssp_ops, auth_generic_state->gensec_security); if (!ADS_ERR_OK(status)) { DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n", ads_errstr(status))); TALLOC_FREE(auth_generic_state); return status; } /* Only keep the gensec_security element around long-term */ talloc_steal(NULL, auth_generic_state->gensec_security); } TALLOC_FREE(auth_generic_state); return ADS_ERROR(rc); }
_PUBLIC_ NTSTATUS dcesrv_reply(struct dcesrv_call_state *call) { struct ndr_push *push; NTSTATUS status; DATA_BLOB stub; uint32_t total_length, chunk_size; struct dcesrv_connection_context *context = call->context; size_t sig_size = 0; /* call the reply function */ status = context->iface->reply(call, call, call->r); if (!NT_STATUS_IS_OK(status)) { return dcesrv_fault(call, call->fault_code); } /* form the reply NDR */ push = ndr_push_init_ctx(call); NT_STATUS_HAVE_NO_MEMORY(push); /* carry over the pointer count to the reply in case we are using full pointer. See NDR specification for full pointers */ push->ptr_count = call->ndr_pull->ptr_count; if (lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx)) { push->flags |= LIBNDR_FLAG_BIGENDIAN; } status = context->iface->ndr_push(call, call, push, call->r); if (!NT_STATUS_IS_OK(status)) { return dcesrv_fault(call, call->fault_code); } stub = ndr_push_blob(push); total_length = stub.length; /* we can write a full max_recv_frag size, minus the dcerpc request header size */ chunk_size = call->conn->cli_max_recv_frag; chunk_size -= DCERPC_REQUEST_LENGTH; if (call->conn->auth_state.auth_info && call->conn->auth_state.gensec_security) { size_t max_payload = chunk_size; max_payload -= DCERPC_AUTH_TRAILER_LENGTH; max_payload -= (max_payload % DCERPC_AUTH_PAD_ALIGNMENT); sig_size = gensec_sig_size(call->conn->auth_state.gensec_security, max_payload); if (sig_size) { chunk_size -= DCERPC_AUTH_TRAILER_LENGTH; chunk_size -= sig_size; } } chunk_size -= (chunk_size % DCERPC_AUTH_PAD_ALIGNMENT); do { uint32_t length; struct data_blob_list_item *rep; struct ncacn_packet pkt; rep = talloc(call, struct data_blob_list_item); NT_STATUS_HAVE_NO_MEMORY(rep); length = MIN(chunk_size, stub.length); /* form the dcerpc response packet */ dcesrv_init_hdr(&pkt, lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx)); pkt.auth_length = 0; pkt.call_id = call->pkt.call_id; pkt.ptype = DCERPC_PKT_RESPONSE; pkt.pfc_flags = 0; if (stub.length == total_length) { pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST; } if (length == stub.length) { pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST; } pkt.u.response.alloc_hint = stub.length; pkt.u.response.context_id = call->pkt.u.request.context_id; pkt.u.response.cancel_count = 0; pkt.u.response._pad.data = call->pkt.u.request._pad.data; pkt.u.response._pad.length = call->pkt.u.request._pad.length; pkt.u.response.stub_and_verifier.data = stub.data; pkt.u.response.stub_and_verifier.length = length; if (!dcesrv_auth_response(call, &rep->blob, sig_size, &pkt)) { return dcesrv_fault(call, DCERPC_FAULT_OTHER); } dcerpc_set_frag_length(&rep->blob, rep->blob.length); DLIST_ADD_END(call->replies, rep); stub.data += length; stub.length -= length; } while (stub.length != 0); /* move the call from the pending to the finished calls list */ dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST); if (call->conn->call_list && call->conn->call_list->replies) { if (call->conn->transport.report_output_data) { call->conn->transport.report_output_data(call->conn); } } return NT_STATUS_OK; }