From 93109c5f1d85c028ce5cf6e31e2249ca90a7f746 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Mon, 9 Aug 2010 16:09:37 -0400 Subject: Initialize kerberos service for GSSAPI --- Makefile.am | 1 + src/providers/ipa/ipa_common.c | 1 + src/providers/krb5/krb5_common.c | 2 +- src/providers/krb5/krb5_common.h | 6 + src/providers/ldap/ldap_common.c | 171 +++++++++++++++++++++++++++++ src/providers/ldap/ldap_common.h | 20 ++++ src/providers/ldap/ldap_init.c | 11 ++ src/providers/ldap/sdap.h | 1 + src/providers/ldap/sdap_async.h | 3 + src/providers/ldap/sdap_async_connection.c | 92 +++++++++++++++- 10 files changed, 303 insertions(+), 5 deletions(-) diff --git a/Makefile.am b/Makefile.am index d1fcd862..605b97ad 100644 --- a/Makefile.am +++ b/Makefile.am @@ -769,6 +769,7 @@ libsss_ldap_la_SOURCES = \ src/providers/ldap/sdap_fd_events.c \ src/providers/ldap/sdap_id_op.c \ src/providers/ldap/sdap.c \ + src/providers/krb5/krb5_common.c \ src/util/user_info_msg.c \ src/util/sss_ldap.c \ src/util/sss_krb5.c diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c index 3d999f51..dea1a73f 100644 --- a/src/providers/ipa/ipa_common.c +++ b/src/providers/ipa/ipa_common.c @@ -542,6 +542,7 @@ int ipa_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx, ret = ENOMEM; goto done; } + service->sdap->kinit_service_name = service->krb5_service->name; realm = talloc_strdup(service, domain); if (!realm) { diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c index d41fdb9d..3863acd9 100644 --- a/src/providers/krb5/krb5_common.c +++ b/src/providers/krb5/krb5_common.c @@ -426,7 +426,7 @@ done: } -static errno_t remove_krb5_info_files(TALLOC_CTX *mem_ctx, const char *realm) +errno_t remove_krb5_info_files(TALLOC_CTX *mem_ctx, const char *realm) { int ret; errno_t err; diff --git a/src/providers/krb5/krb5_common.h b/src/providers/krb5/krb5_common.h index f3bbbb84..6398ea22 100644 --- a/src/providers/krb5/krb5_common.h +++ b/src/providers/krb5/krb5_common.h @@ -136,4 +136,10 @@ errno_t krb5_install_offline_callback(struct be_ctx *be_ctx, errno_t krb5_install_sigterm_handler(struct tevent_context *ev, struct krb5_ctx *krb5_ctx); + +errno_t write_krb5info_file(const char *realm, const char *kdc, + const char *service); + +errno_t remove_krb5_info_files(TALLOC_CTX *mem_ctx, const char *realm); + #endif /* __KRB5_COMMON_H__ */ diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c index cf2cd699..87fd43a1 100644 --- a/src/providers/ldap/ldap_common.c +++ b/src/providers/ldap/ldap_common.c @@ -25,6 +25,7 @@ #include "providers/ldap/ldap_common.h" #include "providers/fail_over.h" #include "providers/ldap/sdap_async_private.h" +#include "providers/krb5/krb5_common.h" #include "util/sss_krb5.h" @@ -420,6 +421,176 @@ static void sdap_uri_callback(void *private_data, struct fo_server *server) service->uri = new_uri; } +static void sdap_finalize(struct tevent_context *ev, + struct tevent_signal *se, + int signum, + int count, + void *siginfo, + void *private_data) +{ + char *realm = (char *) private_data; + int ret; + + ret = remove_krb5_info_files(se, realm); + if (ret != EOK) { + DEBUG(1, ("remove_krb5_info_files failed.\n")); + } + + sig_term(signum); +} + +errno_t sdap_install_sigterm_handler(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const char *realm) +{ + char *sig_realm; + struct tevent_signal *sige; + + BlockSignals(false, SIGTERM); + + sig_realm = talloc_strdup(mem_ctx, realm); + if (sig_realm == NULL) { + DEBUG(1, ("talloc_strdup failed!\n")); + return ENOMEM; + } + + sige = tevent_add_signal(ev, mem_ctx, SIGTERM, SA_SIGINFO, sdap_finalize, + sig_realm); + if (sige == NULL) { + DEBUG(1, ("tevent_add_signal failed.\n")); + talloc_free(sig_realm); + return ENOMEM; + } + talloc_steal(sige, sig_realm); + + return EOK; +} + +void sdap_remove_kdcinfo_files_callback(void *pvt) +{ + int ret; + TALLOC_CTX *tmp_ctx = NULL; + struct remove_info_files_ctx *ctx = talloc_get_type(pvt, + struct remove_info_files_ctx); + + ret = be_fo_run_callbacks_at_next_request(ctx->be_ctx, + ctx->kdc_service_name); + if (ret != EOK) { + DEBUG(1, ("be_fo_run_callbacks_at_next_request failed, " + "krb5 info files will not be removed, because " + "it is unclear if they will be recreated properly.\n")); + return; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(1, ("talloc_new failed, cannot remove krb5 info files.\n")); + return; + } + + ret = remove_krb5_info_files(tmp_ctx, ctx->realm); + if (ret != EOK) { + DEBUG(1, ("remove_krb5_info_files failed.\n")); + } + + talloc_zfree(tmp_ctx); +} + + +errno_t sdap_install_offline_callback(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + const char *realm, + const char *service_name) +{ + int ret; + struct remove_info_files_ctx *ctx; + + ctx = talloc_zero(mem_ctx, struct remove_info_files_ctx); + if (ctx == NULL) { + DEBUG(1, ("talloc_zfree failed.\n")); + return ENOMEM; + } + + ctx->be_ctx = be_ctx; + ctx->realm = talloc_strdup(ctx, realm); + ctx->kdc_service_name = talloc_strdup(ctx, service_name); + if (ctx->realm == NULL || ctx->kdc_service_name == NULL) { + DEBUG(1, ("talloc_strdup failed!\n")); + ret = ENOMEM; + goto done; + } + + ret = be_add_offline_cb(ctx, be_ctx, + sdap_remove_kdcinfo_files_callback, + ctx, NULL); + if (ret != EOK) { + DEBUG(1, ("be_add_offline_cb failed.\n")); + goto done; + } + + ret = EOK; +done: + if (ret != EOK) { + talloc_zfree(ctx); + } + return ret; +} + +int sdap_gssapi_init(TALLOC_CTX *mem_ctx, + struct dp_option *opts, + struct be_ctx *bectx, + struct sdap_service *sdap_service, + struct krb5_service **krb5_service) +{ + int ret; + const char *krb5_servers; + const char *krb5_realm; + struct krb5_service *service = NULL; + + krb5_servers = dp_opt_get_string(opts, SDAP_KRB5_KDCIP); + if (krb5_servers == NULL) { + DEBUG(1, ("Missing krb5_kdcip option, using service discovery!\n")); + } + + krb5_realm = dp_opt_get_string(opts, SDAP_KRB5_REALM); + if (krb5_realm == NULL) { + DEBUG(0, ("Missing krb5_realm option, will use libkrb default\n")); + } + + ret = krb5_service_init(mem_ctx, bectx, SSS_KRB5KDC_FO_SRV, krb5_servers, + krb5_realm, &service); + if (ret != EOK) { + DEBUG(0, ("Failed to init KRB5 failover service!\n")); + goto done; + } + + ret = sdap_install_sigterm_handler(mem_ctx, bectx->ev, krb5_realm); + if (ret != EOK) { + DEBUG(0, ("Failed to install sigterm handler\n")); + goto done; + } + + ret = sdap_install_offline_callback(mem_ctx, bectx, + krb5_realm, SSS_KRB5KDC_FO_SRV); + if (ret != EOK) { + DEBUG(0, ("Failed to install sigterm handler\n")); + goto done; + } + + sdap_service->kinit_service_name = talloc_strdup(sdap_service, + service->name); + if (sdap_service->kinit_service_name == NULL) { + ret = ENOMEM; + goto done; + } + + ret = EOK; + *krb5_service = service; +done: + if (ret != EOK) talloc_free(service); + return ret; +} + int sdap_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx, const char *service_name, const char *dns_service_name, const char *urls, struct sdap_service **_service) diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h index 2a0e9767..e2d74e41 100644 --- a/src/providers/ldap/ldap_common.h +++ b/src/providers/ldap/ldap_common.h @@ -26,6 +26,7 @@ #include "providers/ldap/sdap.h" #include "providers/ldap/sdap_id_op.h" #include "providers/fail_over.h" +#include "providers/krb5/krb5_common.h" #define PWD_POL_OPT_NONE "none" #define PWD_POL_OPT_SHADOW "shadow" @@ -44,6 +45,8 @@ struct sdap_id_ctx { /* what rootDSE returns */ struct sysdb_attrs *rootDSE; + /* If using GSSAPI */ + struct krb5_service *krb5_service; /* LDAP connection cache */ struct sdap_id_conn_cache *conn_cache; @@ -86,6 +89,23 @@ int sdap_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx, const char *service_name, const char *dns_service_name, const char *urls, struct sdap_service **_service); +int sdap_gssapi_init(TALLOC_CTX *mem_ctx, + struct dp_option *opts, + struct be_ctx *bectx, + struct sdap_service *sdap_service, + struct krb5_service **krb5_service); + +errno_t sdap_install_offline_callback(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + const char *realm, + const char *service_name); + +errno_t sdap_install_sigterm_handler(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const char *realm); + +void sdap_remove_kdcinfo_files_callback(void *pvt); + /* options parser */ int ldap_get_options(TALLOC_CTX *memctx, struct confdb_ctx *cdb, diff --git a/src/providers/ldap/ldap_init.c b/src/providers/ldap/ldap_init.c index b0b967d0..86598cac 100644 --- a/src/providers/ldap/ldap_init.c +++ b/src/providers/ldap/ldap_init.c @@ -98,6 +98,17 @@ int sssm_ldap_id_init(struct be_ctx *bectx, goto done; } + if (dp_opt_get_bool(ctx->opts->basic, SDAP_KRB5_KINIT)) { + ret = sdap_gssapi_init(ctx, ctx->opts->basic, + ctx->be, ctx->service, + &ctx->krb5_service); + if (ret != EOK) { + DEBUG(1, ("sdap_gssapi_init failed [%d][%s].\n", + ret, strerror(ret))); + goto done; + } + } + ret = setup_tls_config(ctx->opts->basic); if (ret != EOK) { DEBUG(1, ("setup_tls_config failed [%d][%s].\n", diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h index a5fab7a9..2b4318e6 100644 --- a/src/providers/ldap/sdap.h +++ b/src/providers/ldap/sdap.h @@ -106,6 +106,7 @@ struct sdap_handle { struct sdap_service { char *name; char *uri; + char *kinit_service_name; }; struct sdap_ppolicy_data { diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h index e5a60daa..20888011 100644 --- a/src/providers/ldap/sdap_async.h +++ b/src/providers/ldap/sdap_async.h @@ -61,12 +61,15 @@ int sdap_get_groups_recv(struct tevent_req *req, struct tevent_req *sdap_kinit_send(TALLOC_CTX *memctx, struct tevent_context *ev, + struct be_ctx *be, struct sdap_handle *sh, + const char *service_name, int timeout, const char *keytab, const char *principal, const char *realm, int lifetime); + int sdap_kinit_recv(struct tevent_req *req, enum sdap_result *result, time_t *expire_time); diff --git a/src/providers/ldap/sdap_async_connection.c b/src/providers/ldap/sdap_async_connection.c index 3c536a47..b6205052 100644 --- a/src/providers/ldap/sdap_async_connection.c +++ b/src/providers/ldap/sdap_async_connection.c @@ -627,15 +627,30 @@ static int sasl_bind_recv(struct tevent_req *req, int *ldaperr) /* ==Perform-Kinit-given-keytab-and-principal============================= */ struct sdap_kinit_state { + const char *keytab; + const char *principal; + const char *realm; + int timeout; + int lifetime; + + const char *krb_service_name; + struct tevent_context *ev; + struct be_ctx *be; + + struct fo_server *kdc_srv; int result; time_t expire_time; }; static void sdap_kinit_done(struct tevent_req *subreq); +static struct tevent_req *sdap_kinit_next_kdc(struct tevent_req *req); +static void sdap_kinit_kdc_resolved(struct tevent_req *subreq); struct tevent_req *sdap_kinit_send(TALLOC_CTX *memctx, struct tevent_context *ev, + struct be_ctx *be, struct sdap_handle *sh, + const char *krb_service_name, int timeout, const char *keytab, const char *principal, @@ -659,26 +674,83 @@ struct tevent_req *sdap_kinit_send(TALLOC_CTX *memctx, if (!req) return NULL; state->result = SDAP_AUTH_FAILED; + state->keytab = keytab; + state->principal = principal; + state->realm = realm; + state->ev = ev; + state->be = be; + state->timeout = timeout; + state->lifetime = lifetime; + state->krb_service_name = krb_service_name; if (keytab) { ret = setenv("KRB5_KTNAME", keytab, 1); if (ret == -1) { DEBUG(2, ("Failed to set KRB5_KTNAME to %s\n", keytab)); + talloc_free(req); return NULL; } } - subreq = sdap_get_tgt_send(state, ev, realm, principal, keytab, lifetime, - timeout); + subreq = sdap_kinit_next_kdc(req); if (!subreq) { - talloc_zfree(req); + talloc_free(req); return NULL; } - tevent_req_set_callback(subreq, sdap_kinit_done, req); return req; } +static struct tevent_req *sdap_kinit_next_kdc(struct tevent_req *req) +{ + struct tevent_req *next_req; + struct sdap_kinit_state *state = tevent_req_data(req, + struct sdap_kinit_state); + + DEBUG(7, ("Resolving next KDC for service %s\n", state->krb_service_name)); + + next_req = be_resolve_server_send(state, state->ev, + state->be, + state->krb_service_name); + if (next_req == NULL) { + DEBUG(1, ("be_resolve_server_send failed.\n")); + return NULL; + } + tevent_req_set_callback(next_req, sdap_kinit_kdc_resolved, req); + + return next_req; +} + +static void sdap_kinit_kdc_resolved(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sdap_kinit_state *state = tevent_req_data(req, + struct sdap_kinit_state); + struct tevent_req *tgtreq; + int ret; + + ret = be_resolve_server_recv(subreq, &state->kdc_srv); + talloc_zfree(subreq); + if (ret != EOK) { + /* all servers have been tried and none + * was found good, go offline */ + tevent_req_error(req, EIO); + return; + } + + DEBUG(7, ("KDC resolved, attempting to get TGT...\n")); + + tgtreq = sdap_get_tgt_send(state, state->ev, state->realm, + state->principal, state->keytab, + state->lifetime, state->timeout); + if (!tgtreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(tgtreq, sdap_kinit_done, req); +} + static void sdap_kinit_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, @@ -691,6 +763,7 @@ static void sdap_kinit_done(struct tevent_req *subreq) char *ccname = NULL; time_t expire_time; krb5_error_code kerr; + struct tevent_req *nextreq; ret = sdap_get_tgt_recv(subreq, state, &result, &kerr, &ccname, &expire_time); @@ -714,6 +787,15 @@ static void sdap_kinit_done(struct tevent_req *subreq) state->result = SDAP_AUTH_SUCCESS; tevent_req_done(req); return; + } else { + if (kerr == KRB5_KDC_UNREACH) { + nextreq = sdap_kinit_next_kdc(req); + if (!nextreq) { + tevent_req_error(req, ENOMEM); + } + return; + } + } DEBUG(4, ("Could not get TGT: %d [%s]\n", result, strerror(result))); @@ -1152,7 +1234,9 @@ static void sdap_cli_kinit_step(struct tevent_req *req) struct tevent_req *subreq; subreq = sdap_kinit_send(state, state->ev, + state->be, state->sh, + state->service->kinit_service_name, dp_opt_get_int(state->opts->basic, SDAP_OPT_TIMEOUT), dp_opt_get_string(state->opts->basic, -- cgit