X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source4%2Fkdc%2Fkdc.c;h=4a9341fcb35fb0eee36bae6a1789d7e1d4715f13;hb=773cfba9af34e64b96e843b1b60afa5a0b0dec32;hp=249004323cfabe0332f3f47458a1df6a0704886b;hpb=ad9ac99afd04b1f69ca188eb0005a1226520bc7d;p=obnox%2Fsamba%2Fsamba-obnox.git diff --git a/source4/kdc/kdc.c b/source4/kdc/kdc.c index 249004323cf..4a9341fcb35 100644 --- a/source4/kdc/kdc.c +++ b/source4/kdc/kdc.c @@ -32,11 +32,14 @@ #include "lib/socket/netif.h" #include "param/param.h" #include "kdc/kdc-glue.h" +#include "kdc/pac-glue.h" #include "dsdb/samdb/samdb.h" #include "auth/session.h" +#include "libds/common/roles.h" + +NTSTATUS server_service_kdc_init(void); extern struct krb5plugin_windc_ftable windc_plugin_table; -extern struct hdb_method hdb_samba4; static NTSTATUS kdc_proxy_unavailable_error(struct kdc_server *kdc, TALLOC_CTX *mem_ctx, @@ -62,6 +65,45 @@ static NTSTATUS kdc_proxy_unavailable_error(struct kdc_server *kdc, return NT_STATUS_OK; } +typedef enum kdc_process_ret (*kdc_process_fn_t)(struct kdc_server *kdc, + TALLOC_CTX *mem_ctx, + DATA_BLOB *input, + DATA_BLOB *reply, + struct tsocket_address *peer_addr, + struct tsocket_address *my_addr, + int datagram); + +/* hold information about one kdc socket */ +struct kdc_socket { + struct kdc_server *kdc; + struct tsocket_address *local_address; + kdc_process_fn_t process; +}; + +struct kdc_tcp_call { + struct kdc_tcp_connection *kdc_conn; + DATA_BLOB in; + DATA_BLOB out; + uint8_t out_hdr[4]; + struct iovec out_iov[2]; +}; + +/* + state of an open tcp connection +*/ +struct kdc_tcp_connection { + /* stream connection we belong to */ + struct stream_connection *conn; + + /* the kdc_server the connection belongs to */ + struct kdc_socket *kdc_socket; + + struct tstream_context *tstream; + + struct tevent_queue *send_queue; +}; + + static void kdc_tcp_terminate_connection(struct kdc_tcp_connection *kdcconn, const char *reason) { stream_terminate_connection(kdcconn->conn, reason); @@ -143,6 +185,7 @@ static enum kdc_process_ret kdc_process(struct kdc_server *kdc, return KDC_PROCESS_OK; } +static void kdc_tcp_call_proxy_done(struct tevent_req *subreq); static void kdc_tcp_call_writev_done(struct tevent_req *subreq); static void kdc_tcp_call_loop(struct tevent_req *subreq) @@ -202,14 +245,94 @@ static void kdc_tcp_call_loop(struct tevent_req *subreq) } if (ret == KDC_PROCESS_PROXY) { + uint16_t port; + if (!kdc_conn->kdc_socket->kdc->am_rodc) { kdc_tcp_terminate_connection(kdc_conn, "kdc_tcp_call_loop: proxying requested when not RODC"); return; } - kdc_tcp_proxy(kdc_conn->kdc_socket->kdc, kdc_conn, call, - tsocket_address_inet_port(kdc_conn->conn->local_address)); - goto done; + port = tsocket_address_inet_port(kdc_conn->conn->local_address); + + subreq = kdc_tcp_proxy_send(call, + kdc_conn->conn->event.ctx, + kdc_conn->kdc_socket->kdc, + port, + call->in); + if (subreq == NULL) { + kdc_tcp_terminate_connection(kdc_conn, + "kdc_tcp_call_loop: kdc_tcp_proxy_send failed"); + return; + } + tevent_req_set_callback(subreq, kdc_tcp_call_proxy_done, call); + return; + } + + /* First add the length of the out buffer */ + RSIVAL(call->out_hdr, 0, call->out.length); + call->out_iov[0].iov_base = (char *) call->out_hdr; + call->out_iov[0].iov_len = 4; + + call->out_iov[1].iov_base = (char *) call->out.data; + call->out_iov[1].iov_len = call->out.length; + + subreq = tstream_writev_queue_send(call, + kdc_conn->conn->event.ctx, + kdc_conn->tstream, + kdc_conn->send_queue, + call->out_iov, 2); + if (subreq == NULL) { + kdc_tcp_terminate_connection(kdc_conn, "kdc_tcp_call_loop: " + "no memory for tstream_writev_queue_send"); + return; + } + tevent_req_set_callback(subreq, kdc_tcp_call_writev_done, call); + + /* + * The krb5 tcp pdu's has the length as 4 byte (initial_read_size), + * packet_full_request_u32 provides the pdu length then. + */ + subreq = tstream_read_pdu_blob_send(kdc_conn, + kdc_conn->conn->event.ctx, + kdc_conn->tstream, + 4, /* initial_read_size */ + packet_full_request_u32, + kdc_conn); + if (subreq == NULL) { + kdc_tcp_terminate_connection(kdc_conn, "kdc_tcp_call_loop: " + "no memory for tstream_read_pdu_blob_send"); + return; + } + tevent_req_set_callback(subreq, kdc_tcp_call_loop, kdc_conn); +} + +static void kdc_tcp_call_proxy_done(struct tevent_req *subreq) +{ + struct kdc_tcp_call *call = tevent_req_callback_data(subreq, + struct kdc_tcp_call); + struct kdc_tcp_connection *kdc_conn = call->kdc_conn; + NTSTATUS status; + + status = kdc_tcp_proxy_recv(subreq, call, &call->out); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + /* generate an error packet */ + status = kdc_proxy_unavailable_error(kdc_conn->kdc_socket->kdc, + call, &call->out); + } + + if (!NT_STATUS_IS_OK(status)) { + const char *reason; + + reason = talloc_asprintf(call, "kdc_tcp_call_proxy_done: " + "kdc_proxy_unavailable_error - %s", + nt_errstr(status)); + if (!reason) { + reason = "kdc_tcp_call_proxy_done: kdc_proxy_unavailable_error() failed"; + } + + kdc_tcp_terminate_connection(call->kdc_conn, reason); + return; } /* First add the length of the out buffer */ @@ -232,7 +355,6 @@ static void kdc_tcp_call_loop(struct tevent_req *subreq) } tevent_req_set_callback(subreq, kdc_tcp_call_writev_done, call); -done: /* * The krb5 tcp pdu's has the length as 4 byte (initial_read_size), * packet_full_request_u32 provides the pdu length then. @@ -345,6 +467,21 @@ static const struct stream_server_ops kdc_tcp_stream_ops = { .send_handler = kdc_tcp_send }; +/* hold information about one kdc/kpasswd udp socket */ +struct kdc_udp_socket { + struct kdc_socket *kdc_socket; + struct tdgram_context *dgram; + struct tevent_queue *send_queue; +}; + +struct kdc_udp_call { + struct kdc_udp_socket *sock; + struct tsocket_address *src; + DATA_BLOB in; + DATA_BLOB out; +}; + +static void kdc_udp_call_proxy_done(struct tevent_req *subreq); static void kdc_udp_call_sendto_done(struct tevent_req *subreq); static void kdc_udp_call_loop(struct tevent_req *subreq) @@ -362,6 +499,7 @@ static void kdc_udp_call_loop(struct tevent_req *subreq) talloc_free(call); goto done; } + call->sock = sock; len = tdgram_recvfrom_recv(subreq, &sys_errno, call, &buf, &call->src); @@ -392,13 +530,26 @@ static void kdc_udp_call_loop(struct tevent_req *subreq) } if (ret == KDC_PROCESS_PROXY) { + uint16_t port; + if (!sock->kdc_socket->kdc->am_rodc) { DEBUG(0,("kdc_udp_call_loop: proxying requested when not RODC")); talloc_free(call); goto done; } - kdc_udp_proxy(sock->kdc_socket->kdc, sock, call, - tsocket_address_inet_port(sock->kdc_socket->local_address)); + + port = tsocket_address_inet_port(sock->kdc_socket->local_address); + + subreq = kdc_udp_proxy_send(call, + sock->kdc_socket->kdc->task->event_ctx, + sock->kdc_socket->kdc, + port, + call->in); + if (subreq == NULL) { + talloc_free(call); + goto done; + } + tevent_req_set_callback(subreq, kdc_udp_call_proxy_done, call); goto done; } @@ -428,14 +579,48 @@ done: tevent_req_set_callback(subreq, kdc_udp_call_loop, sock); } +static void kdc_udp_call_proxy_done(struct tevent_req *subreq) +{ + struct kdc_udp_call *call = + tevent_req_callback_data(subreq, + struct kdc_udp_call); + NTSTATUS status; + + status = kdc_udp_proxy_recv(subreq, call, &call->out); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + /* generate an error packet */ + status = kdc_proxy_unavailable_error(call->sock->kdc_socket->kdc, + call, &call->out); + } + + if (!NT_STATUS_IS_OK(status)) { + talloc_free(call); + return; + } + + subreq = tdgram_sendto_queue_send(call, + call->sock->kdc_socket->kdc->task->event_ctx, + call->sock->dgram, + call->sock->send_queue, + call->out.data, + call->out.length, + call->src); + if (subreq == NULL) { + talloc_free(call); + return; + } + + tevent_req_set_callback(subreq, kdc_udp_call_sendto_done, call); +} + static void kdc_udp_call_sendto_done(struct tevent_req *subreq) { struct kdc_udp_call *call = tevent_req_callback_data(subreq, struct kdc_udp_call); - ssize_t ret; int sys_errno; - ret = tdgram_sendto_queue_recv(subreq, &sys_errno); + tdgram_sendto_queue_recv(subreq, &sys_errno); /* We don't care about errors */ @@ -469,7 +654,7 @@ static NTSTATUS kdc_add_socket(struct kdc_server *kdc, address, port, &kdc_socket->local_address); if (ret != 0) { - status = map_nt_error_from_unix(errno); + status = map_nt_error_from_unix_common(errno); return status; } @@ -500,7 +685,7 @@ static NTSTATUS kdc_add_socket(struct kdc_server *kdc, kdc_udp_socket, &kdc_udp_socket->dgram); if (ret != 0) { - status = map_nt_error_from_unix(errno); + status = map_nt_error_from_unix_common(errno); DEBUG(0,("Failed to bind to %s:%u UDP - %s\n", address, port, nt_errstr(status))); return status; @@ -544,29 +729,42 @@ static NTSTATUS kdc_startup_interfaces(struct kdc_server *kdc, struct loadparm_c return NT_STATUS_INTERNAL_ERROR; } - num_interfaces = iface_count(ifaces); + num_interfaces = iface_list_count(ifaces); /* if we are allowing incoming packets from any address, then we need to bind to the wildcard address */ if (!lpcfg_bind_interfaces_only(lp_ctx)) { - if (kdc_port) { - status = kdc_add_socket(kdc, model_ops, - "kdc", "0.0.0.0", kdc_port, - kdc_process, false); - NT_STATUS_NOT_OK_RETURN(status); + int num_binds = 0; + char **wcard = iface_list_wildcard(kdc); + NT_STATUS_HAVE_NO_MEMORY(wcard); + for (i=0; wcard[i]; i++) { + if (kdc_port) { + status = kdc_add_socket(kdc, model_ops, + "kdc", wcard[i], kdc_port, + kdc_process, false); + if (NT_STATUS_IS_OK(status)) { + num_binds++; + } + } + + if (kpasswd_port) { + status = kdc_add_socket(kdc, model_ops, + "kpasswd", wcard[i], kpasswd_port, + kpasswdd_process, false); + if (NT_STATUS_IS_OK(status)) { + num_binds++; + } + } } - - if (kpasswd_port) { - status = kdc_add_socket(kdc, model_ops, - "kpasswd", "0.0.0.0", kpasswd_port, - kpasswdd_process, false); - NT_STATUS_NOT_OK_RETURN(status); + talloc_free(wcard); + if (num_binds == 0) { + return NT_STATUS_INVALID_PARAMETER_MIX; } done_wildcard = true; } for (i=0; iprivate_data, struct kdc_server); enum ndr_err_code ndr_err; - krb5_enctype etype; int ret; hdb_entry_ex ent; krb5_principal principal; - krb5_keyblock keyblock; - Key *key; /* There is no reply to this request */ r->out.generic_reply = data_blob(NULL, 0); @@ -613,7 +807,7 @@ static NTSTATUS kdc_check_generic_kerberos(struct irpc_message *msg, return NT_STATUS_INVALID_PARAMETER; } - if (pac_validate.MessageType != 3) { + if (pac_validate.MessageType != NETLOGON_GENERIC_KRB5_PAC_VALIDATE) { /* We don't implement any other message types - such as certificate validation - yet */ return NT_STATUS_INVALID_PARAMETER; } @@ -627,16 +821,6 @@ static NTSTATUS kdc_check_generic_kerberos(struct irpc_message *msg, srv_sig = data_blob_const(pac_validate.ChecksumAndSignature.data, pac_validate.ChecksumLength); - if (pac_validate.SignatureType == CKSUMTYPE_HMAC_MD5) { - etype = ETYPE_ARCFOUR_HMAC_MD5; - } else { - ret = krb5_cksumtype_to_enctype(kdc->smb_krb5_context->krb5_context, pac_validate.SignatureType, - &etype); - if (ret != 0) { - return NT_STATUS_LOGON_FAILURE; - } - } - ret = krb5_make_principal(kdc->smb_krb5_context->krb5_context, &principal, lpcfg_realm(kdc->task->lp_ctx), "krbtgt", lpcfg_realm(kdc->task->lp_ctx), @@ -660,21 +844,11 @@ static NTSTATUS kdc_check_generic_kerberos(struct irpc_message *msg, return NT_STATUS_LOGON_FAILURE; } - ret = hdb_enctype2key(kdc->smb_krb5_context->krb5_context, &ent.entry, etype, &key); - - if (ret != 0) { - hdb_free_entry(kdc->smb_krb5_context->krb5_context, &ent); - krb5_free_principal(kdc->smb_krb5_context->krb5_context, principal); - return NT_STATUS_LOGON_FAILURE; - } - - keyblock = key->key; - kdc_sig.type = pac_validate.SignatureType; kdc_sig.signature = data_blob_const(&pac_validate.ChecksumAndSignature.data[pac_validate.ChecksumLength], pac_validate.SignatureLength); - ret = check_pac_checksum(msg, srv_sig, &kdc_sig, - kdc->smb_krb5_context->krb5_context, &keyblock); + + ret = kdc_check_pac(kdc->smb_krb5_context->krb5_context, srv_sig, &kdc_sig, &ent); hdb_free_entry(kdc->smb_krb5_context->krb5_context, &ent); krb5_free_principal(kdc->smb_krb5_context->krb5_context, principal); @@ -705,14 +879,18 @@ static void kdc_task_init(struct task_server *task) case ROLE_DOMAIN_MEMBER: task_server_terminate(task, "kdc: no KDC required in member server configuration", false); return; - case ROLE_DOMAIN_CONTROLLER: + case ROLE_DOMAIN_PDC: + case ROLE_DOMAIN_BDC: + task_server_terminate(task, "Cannot start KDC as a 'classic Samba' DC", true); + return; + case ROLE_ACTIVE_DIRECTORY_DC: /* Yes, we want a KDC */ break; } - load_interfaces(task, lpcfg_interfaces(task->lp_ctx), &ifaces); + load_interface_list(task, task->lp_ctx, &ifaces); - if (iface_count(ifaces) == 0) { + if (iface_list_count(ifaces) == 0) { task_server_terminate(task, "kdc: no network interfaces configured", false); return; } @@ -749,7 +927,7 @@ static void kdc_task_init(struct task_server *task) initialize_krb5_error_table(); - ret = smb_krb5_init_context(kdc, task->event_ctx, task->lp_ctx, &kdc->smb_krb5_context); + ret = smb_krb5_init_context(kdc, task->lp_ctx, &kdc->smb_krb5_context); if (ret) { DEBUG(1,("kdc_task_init: krb5_init_context failed (%s)\n", error_message(ret))); @@ -766,7 +944,7 @@ static void kdc_task_init(struct task_server *task) return; } - kdc->config->logf = kdc->smb_krb5_context->logf; + kdc->config->logf = (krb5_log_facility *)kdc->smb_krb5_context->pvt_log_data; kdc->config->db = talloc(kdc, struct HDB *); if (!kdc->config->db) { task_server_terminate(task, "kdc: out of memory", true); @@ -774,6 +952,27 @@ static void kdc_task_init(struct task_server *task) } kdc->config->num_db = 1; + /* + * This restores the behavior before + * commit 255e3e18e00f717d99f3bc57c8a8895ff624f3c3 + * s4:heimdal: import lorikeet-heimdal-201107150856 + * (commit 48936803fae4a2fb362c79365d31f420c917b85b) + * + * as_use_strongest_session_key,preauth_use_strongest_session_key + * and tgs_use_strongest_session_key are input to the + * _kdc_find_etype() function. The old bahavior is in + * the use_strongest_session_key=FALSE code path. + * (The only remaining difference in _kdc_find_etype() + * is the is_preauth parameter.) + * + * The old behavior in the _kdc_get_preferred_key() + * function is use_strongest_server_key=TRUE. + */ + kdc->config->as_use_strongest_session_key = false; + kdc->config->preauth_use_strongest_session_key = false; + kdc->config->tgs_use_strongest_session_key = false; + kdc->config->use_strongest_server_key = true; + /* Register hdb-samba4 hooks for use as a keytab */ kdc->base_ctx = talloc_zero(kdc, struct samba_kdc_base_context); @@ -795,7 +994,7 @@ static void kdc_task_init(struct task_server *task) ret = krb5_plugin_register(kdc->smb_krb5_context->krb5_context, PLUGIN_TYPE_DATA, "hdb", - &hdb_samba4); + &hdb_samba4_interface); if(ret) { task_server_terminate(task, "kdc: failed to register hdb plugin", true); return;