From 1a4425f519abdc355ba4de480d667c2d3eb1d1c1 Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Wed, 10 Mar 2010 17:03:23 +0100 Subject: Add krb5_kpasswd option --- src/config/SSSDConfig.py | 1 + src/config/SSSDConfigTest.py | 3 +- src/config/etc/sssd.api.d/sssd-krb5.conf | 1 + src/krb5_plugin/sssd_krb5_locator_plugin.c | 176 +++++++++++++++++++++++------ src/man/sssd-krb5.5.xml | 23 +++- src/providers/ipa/ipa_common.c | 5 +- src/providers/krb5/krb5_auth.c | 85 ++++++++++++-- src/providers/krb5/krb5_auth.h | 2 + src/providers/krb5/krb5_child.c | 7 ++ src/providers/krb5/krb5_common.c | 112 +++++++++++++++--- src/providers/krb5/krb5_common.h | 8 +- src/providers/krb5/krb5_init.c | 21 +++- 12 files changed, 373 insertions(+), 71 deletions(-) diff --git a/src/config/SSSDConfig.py b/src/config/SSSDConfig.py index c9e08caf0..7dd90e787 100644 --- a/src/config/SSSDConfig.py +++ b/src/config/SSSDConfig.py @@ -100,6 +100,7 @@ option_strings = { # [provider/krb5/chpass] 'krb5_changepw_principal' : _('The principal of the change password service'), + 'krb5_kpasswd' : _('Server where the change password service is running if not on the KDC'), # [provider/ldap] 'ldap_uri' : _('ldap_uri, The URI of the LDAP server'), diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py index 4e9d09144..7e882e74d 100755 --- a/src/config/SSSDConfigTest.py +++ b/src/config/SSSDConfigTest.py @@ -677,7 +677,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase): #Test looking up all provider values options = domain.list_provider_options('krb5') - control_list.extend(['krb5_changepw_principal']) + control_list.extend(['krb5_changepw_principal', + 'krb5_kpasswd']) self.assertTrue(type(options) == dict, "Options should be a dictionary") diff --git a/src/config/etc/sssd.api.d/sssd-krb5.conf b/src/config/etc/sssd.api.d/sssd-krb5.conf index 7ba0ab323..7b12e08f0 100644 --- a/src/config/etc/sssd.api.d/sssd-krb5.conf +++ b/src/config/etc/sssd.api.d/sssd-krb5.conf @@ -11,3 +11,4 @@ krb5_validate = bool, None, false [provider/krb5/chpass] krb5_changepw_principal = str, None, false +krb5_kpasswd = str, None, false diff --git a/src/krb5_plugin/sssd_krb5_locator_plugin.c b/src/krb5_plugin/sssd_krb5_locator_plugin.c index 8f32a31bf..eb8666e46 100644 --- a/src/krb5_plugin/sssd_krb5_locator_plugin.c +++ b/src/krb5_plugin/sssd_krb5_locator_plugin.c @@ -29,12 +29,18 @@ #include #include #include +#include #include #include "providers/krb5/krb5_common.h" +#define DEFAULT_KERBEROS_PORT 88 +#define DEFAULT_KADMIN_PORT 749 +#define DEFAULT_KPASSWD_PORT 464 + #define BUFSIZE 512 +#define PORT_STR_SIZE 7 #define SSSD_KRB5_LOCATOR_DEBUG "SSSD_KRB5_LOCATOR_DEBUG" #define DEBUG_KEY "[sssd_krb5_locator] " #define PLUGIN_DEBUG(body) do { \ @@ -46,6 +52,9 @@ struct sssd_ctx { char *sssd_realm; char *kdc_addr; + uint16_t kdc_port; + char *kpasswd_addr; + uint16_t kpasswd_port; bool debug; }; @@ -69,31 +78,50 @@ void debug_fn(const char *format, ...) free(s); } -static int get_kdcinfo(const char *realm, struct sssd_ctx *ctx) +static int get_krb5info(const char *realm, struct sssd_ctx *ctx, + enum locate_service_type svc) { int ret; - char *kdcinfo_name = NULL; + char *krb5info_name = NULL; size_t len; uint8_t buf[BUFSIZE + 1]; uint8_t *p; int fd = -1; + const char *name_tmpl = NULL; + char *port_str; + long port; + char *endptr; + + switch (svc) { + case locate_service_kdc: + name_tmpl = KDCINFO_TMPL; + break; + case locate_service_kpasswd: + name_tmpl = KPASSWDINFO_TMPL; + break; + default: + PLUGIN_DEBUG(("Unsupported service [%d].\n", svc)); + return EINVAL; + } + - len = strlen(realm) + strlen(KDCINFO_TMPL); + len = strlen(realm) + strlen(name_tmpl); - kdcinfo_name = calloc(1, len + 1); - if (kdcinfo_name == NULL) { + krb5info_name = calloc(1, len + 1); + if (krb5info_name == NULL) { PLUGIN_DEBUG(("malloc failed.\n")); return ENOMEM; } - ret = snprintf(kdcinfo_name, len, KDCINFO_TMPL, realm); + ret = snprintf(krb5info_name, len, name_tmpl, realm); if (ret < 0) { - PLUGIN_DEBUG(("snprintf failed")); + PLUGIN_DEBUG(("snprintf failed.\n")); ret = EINVAL; + goto done; } - kdcinfo_name[len] = '\0'; + krb5info_name[len] = '\0'; - fd = open(kdcinfo_name, O_RDONLY); + fd = open(krb5info_name, O_RDONLY); if (fd == -1) { PLUGIN_DEBUG(("open failed [%d][%s].\n", errno, strerror(errno))); ret = errno; @@ -117,27 +145,73 @@ static int get_kdcinfo(const char *realm, struct sssd_ctx *ctx) close(fd); if (len == 0) { - PLUGIN_DEBUG(("Content of kdcinfo file [%s] is [%d] or larger.\n", - kdcinfo_name, BUFSIZE)); + PLUGIN_DEBUG(("Content of krb5info file [%s] is [%d] or larger.\n", + krb5info_name, BUFSIZE)); } - PLUGIN_DEBUG(("Found kdcinfo [%s].\n", buf)); + PLUGIN_DEBUG(("Found [%s] in [%s].\n", buf, krb5info_name)); - ctx->kdc_addr = strdup((char *) buf); - if (ctx->kdc_addr == NULL) { - PLUGIN_DEBUG(("strdup failed.\n")); - ret = ENOMEM; - goto done; + port_str = strrchr((char *) buf, ':'); + if (port_str == NULL) { + port = 0; } + *port_str = '\0'; + ++port_str; + + if (isdigit(*port_str)) { + errno = 0; + port = strtol(port_str, &endptr, 10); + if (errno != 0) { + ret = errno; + PLUGIN_DEBUG(("strtol failed on [%s]: [%d][%s], " + "assuming default.\n", port_str, ret, strerror(ret))); + port = 0; + } + if (*endptr != '\0') { + PLUGIN_DEBUG(("Found additional characters [%s] in port number " + "[%s], assuming default.\n", endptr, port_str)); + port = 0; + } - ctx->sssd_realm = strdup(realm); - if (ctx->sssd_realm == NULL) { - PLUGIN_DEBUG(("strdup failed.\n")); - ret = ENOMEM; - goto done; + if (port < 0 || port > 65535) { + PLUGIN_DEBUG(("Illegal port number [%d], assuming default.\n", + port)); + port = 0; + } + } else { + PLUGIN_DEBUG(("Illegal port number [%s], assuming default.\n", + port_str)); + port = 0; + } + + switch (svc) { + case locate_service_kdc: + free(ctx->kdc_addr); + ctx->kdc_addr = strdup((char *) buf); + if (ctx->kdc_addr == NULL) { + PLUGIN_DEBUG(("strdup failed.\n")); + ret = ENOMEM; + goto done; + } + ctx->kdc_port = (uint16_t) port; + break; + case locate_service_kpasswd: + free(ctx->kpasswd_addr); + ctx->kpasswd_addr = strdup((char *) buf); + if (ctx->kpasswd_addr == NULL) { + PLUGIN_DEBUG(("strdup failed.\n")); + ret = ENOMEM; + goto done; + } + ctx->kpasswd_port = (uint16_t) port; + break; + default: + PLUGIN_DEBUG(("Unsupported service [%d].\n", svc)); + ret = EINVAL; + goto done; } done: - free(kdcinfo_name); + free(krb5info_name); return ret; } @@ -173,6 +247,7 @@ void sssd_krb5_locator_close(void *private_data) PLUGIN_DEBUG(("sssd_krb5_locator_close called\n")); free(ctx->kdc_addr); + free(ctx->kpasswd_addr); free(ctx->sssd_realm); free(ctx); private_data = NULL; @@ -192,21 +267,37 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data, struct addrinfo *ai; struct sssd_ctx *ctx; struct addrinfo ai_hints; - const char *service = NULL; + uint16_t port = 0; + const char *addr = NULL; + char port_str[PORT_STR_SIZE]; if (private_data == NULL) return KRB5_PLUGIN_NO_HANDLE; ctx = (struct sssd_ctx *) private_data; if (ctx->sssd_realm == NULL || strcmp(ctx->sssd_realm, realm) != 0) { - free(ctx->kdc_addr); - ctx->kdc_addr = NULL; free(ctx->sssd_realm); - ctx->sssd_realm = NULL; - ret = get_kdcinfo(realm, ctx); + ctx->sssd_realm = strdup(realm); + if (ctx->sssd_realm == NULL) { + PLUGIN_DEBUG(("strdup failed.\n")); + return ENOMEM; + } + + ret = get_krb5info(realm, ctx, locate_service_kdc); if (ret != EOK) { - PLUGIN_DEBUG(("get_kdcinfo failed.\n")); + PLUGIN_DEBUG(("get_krb5info failed.\n")); return KRB5_PLUGIN_NO_HANDLE; } + + if (svc == locate_service_kadmin || svc == locate_service_kpasswd) { + ret = get_krb5info(realm, ctx, locate_service_kpasswd); + if (ret != EOK) { + PLUGIN_DEBUG(("reading kpasswd address failed, " + "using kdc address.\n")); + free(ctx->kpasswd_addr); + ctx->kpasswd_addr = strdup(ctx->kdc_addr); + ctx->kpasswd_port = 0; + } + } } PLUGIN_DEBUG(("sssd_realm[%s] requested realm[%s] family[%d] socktype[%d] " @@ -216,13 +307,16 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data, switch (svc) { case locate_service_kdc: case locate_service_master_kdc: - service = "kerberos"; + addr = ctx->kdc_addr; + port = ctx->kdc_port ? ctx->kdc_port : DEFAULT_KERBEROS_PORT; break; case locate_service_kadmin: - service = "kerberos-adm"; + addr = ctx->kpasswd_addr; + port = DEFAULT_KADMIN_PORT; break; case locate_service_kpasswd: - service = "kpasswd"; + addr = ctx->kpasswd_addr; + port = ctx->kpasswd_port ? ctx->kpasswd_port : DEFAULT_KPASSWD_PORT; break; case locate_service_krb524: return KRB5_PLUGIN_NO_HANDLE; @@ -250,10 +344,18 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data, if (strcmp(realm, ctx->sssd_realm) != 0) return KRB5_PLUGIN_NO_HANDLE; + memset(port_str, 0, PORT_STR_SIZE); + ret = snprintf(port_str, PORT_STR_SIZE-1, "%u", port); + if (ret < 0 || ret >= (PORT_STR_SIZE-1)) { + PLUGIN_DEBUG(("snprintf failed.\n")); + return EFAULT; + } + memset(&ai_hints, 0, sizeof(struct addrinfo)); - ai_hints.ai_flags = AI_NUMERICHOST; + ai_hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV; ai_hints.ai_socktype = socktype; - ret = getaddrinfo(ctx->kdc_addr, service, &ai_hints, &ai); + + ret = getaddrinfo(addr, port_str, &ai_hints, &ai); if (ret != 0) { PLUGIN_DEBUG(("getaddrinfo failed [%d][%s].\n", ret, gai_strerror(ret))); @@ -264,7 +366,7 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data, return EFAULT; } - PLUGIN_DEBUG(("addr[%s] family[%d] socktype[%d]\n", ctx->kdc_addr, + PLUGIN_DEBUG(("addr[%s:%s] family[%d] socktype[%d]\n", addr, port_str, ai->ai_family, ai->ai_socktype)); if ((family == AF_UNSPEC || ai->ai_family == family) && @@ -275,10 +377,10 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data, PLUGIN_DEBUG(("cbfunc failed\n")); return ret; } else { - PLUGIN_DEBUG(("[%s] used\n", ctx->kdc_addr)); + PLUGIN_DEBUG(("[%s] used\n", addr)); } } else { - PLUGIN_DEBUG(("[%s] NOT used\n", ctx->kdc_addr)); + PLUGIN_DEBUG(("[%s] NOT used\n", addr)); } return 0; diff --git a/src/man/sssd-krb5.5.xml b/src/man/sssd-krb5.5.xml index ca4dae256..c291eca79 100644 --- a/src/man/sssd-krb5.5.xml +++ b/src/man/sssd-krb5.5.xml @@ -69,7 +69,9 @@ of the Kerberos servers to which SSSD should connect in the order of preference. For more information on failover and server redundancy, - see the FAILOVER section. + see the FAILOVER section. An optional + port number (preceded by a colon) may be appended to + the addresses or hostnames. @@ -98,6 +100,25 @@ + + krb5_kpasswd (string) + + + If the change password service is not running on the + KDC alternative servers can be defined here. An + optional port number (preceded by a colon) may be + appended to the addresses or hostnames. + + + For more information on failover and server + redundancy, see the FAILOVER section. + Please note that even if there are no more kpasswd + servers to try the back end is not switch to offline + if authentication against the KDC is still possible. + + + + krb5_ccachedir (string) diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c index a50b63b9e..1fc881f41 100644 --- a/src/providers/ipa/ipa_common.c +++ b/src/providers/ipa/ipa_common.c @@ -499,9 +499,10 @@ static void ipa_resolve_callback(void *private_data, struct fo_server *server) talloc_zfree(service->krb5_service->address); service->krb5_service->address = address; - ret = write_kdcinfo_file(service->krb5_service->realm, address); + ret = write_krb5info_file(service->krb5_service->realm, address, + SSS_KRB5KDC_FO_SRV); if (ret != EOK) { - DEBUG(2, ("write_kdcinfo_file failed, authentication might fail.\n")); + DEBUG(2, ("write_krb5info_file failed, authentication might fail.\n")); } } diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c index b8b498a05..ce3aacd82 100644 --- a/src/providers/krb5/krb5_auth.c +++ b/src/providers/krb5/krb5_auth.c @@ -676,7 +676,9 @@ static int handle_child_recv(struct tevent_req *req, } static void get_user_attr_done(void *pvt, int err, struct ldb_result *res); -static void krb5_resolve_done(struct tevent_req *req); +static void krb5_resolve_kdc_done(struct tevent_req *req); +static void krb5_resolve_kpasswd_done(struct tevent_req *req); +static void krb5_find_ccache_step(struct krb5child_req *kr); static void krb5_save_ccname_done(struct tevent_req *req); static void krb5_child_done(struct tevent_req *req); static void krb5_pam_handler_cache_done(struct tevent_req *treq); @@ -852,14 +854,16 @@ static void get_user_attr_done(void *pvt, int err, struct ldb_result *res) break; } + kr->srv = NULL; + kr->kpasswd_srv = NULL; req = be_resolve_server_send(kr, be_req->be_ctx->ev, be_req->be_ctx, krb5_ctx->service->name); if (req == NULL) { - DEBUG(1, ("handle_child_send failed.\n")); + DEBUG(1, ("be_resolve_server_send failed.\n")); goto failed; } - tevent_req_set_callback(req, krb5_resolve_done, kr); + tevent_req_set_callback(req, krb5_resolve_kdc_done, kr); return; @@ -870,18 +874,13 @@ failed: krb_reply(be_req, dp_err, pd->pam_status); } -static void krb5_resolve_done(struct tevent_req *req) +static void krb5_resolve_kdc_done(struct tevent_req *req) { struct krb5child_req *kr = tevent_req_callback_data(req, struct krb5child_req); int ret; - int pam_status = PAM_SYSTEM_ERR; - int dp_err = DP_ERR_FATAL; struct pam_data *pd = kr->pd; struct be_req *be_req = kr->req; - char *msg; - size_t offset = 0; - bool private_path = false; ret = be_resolve_server_recv(req, &kr->srv); talloc_zfree(req); @@ -892,8 +891,68 @@ static void krb5_resolve_done(struct tevent_req *req) * the ccache file. */ be_mark_offline(be_req->be_ctx); kr->is_offline = true; + } else { + if (pd->cmd == SSS_PAM_CHAUTHTOK && + kr->krb5_ctx->kpasswd_service != NULL) { + req = be_resolve_server_send(kr, be_req->be_ctx->ev, be_req->be_ctx, + kr->krb5_ctx->kpasswd_service->name); + if (req == NULL) { + DEBUG(1, ("be_resolve_server_send failed.\n")); + goto failed; + } + + tevent_req_set_callback(req, krb5_resolve_kpasswd_done, kr); + + return; + } } + krb5_find_ccache_step(kr); + return; + +failed: + talloc_free(kr); + + pd->pam_status = PAM_SYSTEM_ERR; + krb_reply(be_req, DP_ERR_FATAL, pd->pam_status); +} + +static void krb5_resolve_kpasswd_done(struct tevent_req *req) +{ + struct krb5child_req *kr = tevent_req_callback_data(req, + struct krb5child_req); + int ret; + struct pam_data *pd = kr->pd; + struct be_req *be_req = kr->req; + + ret = be_resolve_server_recv(req, &kr->kpasswd_srv); + talloc_zfree(req); + if (ret) { + /* all kpasswd servers have been tried and none was found good, but the + * kdc seems ok. Password changes are not possible but + * authentication. We return an PAM error here, but do not mark the + * backend offline. */ + + talloc_free(kr); + pd->pam_status = PAM_AUTHTOK_LOCK_BUSY; + krb_reply(be_req, DP_ERR_OK, pd->pam_status); + } + + krb5_find_ccache_step(kr); +} + +static void krb5_find_ccache_step(struct krb5child_req *kr) +{ + int ret; + int pam_status = PAM_SYSTEM_ERR; + int dp_err = DP_ERR_FATAL; + struct pam_data *pd = kr->pd; + struct be_req *be_req = kr->req; + char *msg; + size_t offset = 0; + bool private_path = false; + struct tevent_req *req = NULL; + if (kr->ccname == NULL || (be_is_offline(be_req->be_ctx) && !kr->active_ccache_present && !kr->valid_tgt_present) || @@ -1081,6 +1140,14 @@ static void krb5_child_done(struct tevent_req *req) fo_set_port_status(kr->srv, PORT_WORKING); } + if (kr->kpasswd_srv != NULL) { + if (*msg_status == PAM_AUTHTOK_LOCK_BUSY) { + fo_set_port_status(kr->kpasswd_srv, PORT_NOT_WORKING); + } else { + fo_set_port_status(kr->kpasswd_srv, PORT_WORKING); + } + } + struct sysdb_attrs *attrs; attrs = sysdb_new_attrs(kr); ret = sysdb_attrs_add_string(attrs, SYSDB_CCACHE_FILE, kr->ccname); diff --git a/src/providers/krb5/krb5_auth.h b/src/providers/krb5/krb5_auth.h index 825f3d648..9f8c41444 100644 --- a/src/providers/krb5/krb5_auth.h +++ b/src/providers/krb5/krb5_auth.h @@ -57,6 +57,7 @@ struct krb5child_req { gid_t gid; bool is_offline; struct fo_server *srv; + struct fo_server *kpasswd_srv; bool active_ccache_present; bool valid_tgt_present; }; @@ -90,6 +91,7 @@ struct krb5_ctx { struct dp_option *opts; struct krb5_service *service; + struct krb5_service *kpasswd_service; int child_debug_fd; pcre *illegal_path_re; diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c index 234b83898..86242ef30 100644 --- a/src/providers/krb5/krb5_child.c +++ b/src/providers/krb5/krb5_child.c @@ -587,9 +587,16 @@ static errno_t changepw_child(int fd, struct krb5_req *kr) goto sendresponse; } + memset(&result_code_string, 0, sizeof(krb5_data)); + memset(&result_string, 0, sizeof(krb5_data)); kerr = krb5_change_password(kr->ctx, kr->creds, newpass_str, &result_code, &result_code_string, &result_string); + if (kerr == KRB5_KDC_UNREACH) { + pam_status = PAM_AUTHTOK_LOCK_BUSY; + goto sendresponse; + } + if (kerr != 0 || result_code != 0) { if (kerr != 0) { KRB5_DEBUG(1, kerr); diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c index 8c1c7facd..2b3331ed3 100644 --- a/src/providers/krb5/krb5_common.c +++ b/src/providers/krb5/krb5_common.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "providers/dp_backend.h" #include "providers/krb5/krb5_common.h" @@ -38,7 +39,8 @@ struct dp_option default_krb5_opts[] = { { "krb5_changepw_principal", DP_OPT_STRING, { "kadmin/changepw" }, NULL_STRING }, { "krb5_auth_timeout", DP_OPT_NUMBER, { .number = 15 }, NULL_NUMBER }, { "krb5_keytab", DP_OPT_STRING, { "/etc/krb5.keytab" }, NULL_STRING }, - { "krb5_validate", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE } + { "krb5_validate", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE }, + { "krb5_kpasswd", DP_OPT_STRING, NULL_STRING, NULL_STRING } }; errno_t check_and_export_options(struct dp_option *opts, @@ -67,7 +69,13 @@ errno_t check_and_export_options(struct dp_option *opts, dummy = dp_opt_get_cstring(opts, KRB5_KDC); if (dummy == NULL) { - DEBUG(1, ("No KDC expicitly configured, using defaults")); + DEBUG(1, ("No KDC explicitly configured, using defaults")); + } + + dummy = dp_opt_get_cstring(opts, KRB5_KPASSWD); + if (dummy == NULL) { + DEBUG(1, ("No kpasswd server explicitly configured, " + "using the KDC or defaults")); } dummy = dp_opt_get_cstring(opts, KRB5_CCNAME_TMPL); @@ -139,21 +147,33 @@ done: return ret; } -errno_t write_kdcinfo_file(const char *realm, const char *kdc) +errno_t write_krb5info_file(const char *realm, const char *server, + const char *service) { int ret; int fd = -1; char *tmp_name = NULL; - char *kdcinfo_name = NULL; + char *krb5info_name = NULL; TALLOC_CTX *tmp_ctx = NULL; - int kdc_len; + const char *name_tmpl = NULL; + int server_len; + + if (realm == NULL || *realm == '\0' || server == NULL || *server == '\0' || + service == NULL || service == '\0') { + DEBUG(1, ("Missing or empty realm, server or service.\n")); + return EINVAL; + } - if (realm == NULL || *realm == '\0' || kdc == NULL || *kdc == '\0') { - DEBUG(1, ("Missing or empty realm or kdc.\n")); + if (strcmp(service, SSS_KRB5KDC_FO_SRV) == 0) { + name_tmpl = KDCINFO_TMPL; + } else if (strcmp(service, SSS_KRB5KPASSWD_FO_SRV) == 0) { + name_tmpl = KPASSWDINFO_TMPL; + } else { + DEBUG(1, ("Unsupported service [%s]\n.", service)); return EINVAL; } - kdc_len = strlen(kdc); + server_len = strlen(server); tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) { @@ -161,15 +181,15 @@ errno_t write_kdcinfo_file(const char *realm, const char *kdc) return ENOMEM; } - tmp_name = talloc_asprintf(tmp_ctx, PUBCONF_PATH"/.kdcinfo_dummy_XXXXXX"); + tmp_name = talloc_asprintf(tmp_ctx, PUBCONF_PATH"/.krb5info_dummy_XXXXXX"); if (tmp_name == NULL) { DEBUG(1, ("talloc_asprintf failed.\n")); ret = ENOMEM; goto done; } - kdcinfo_name = talloc_asprintf(tmp_ctx, KDCINFO_TMPL, realm); - if (kdcinfo_name == NULL) { + krb5info_name = talloc_asprintf(tmp_ctx, name_tmpl, realm); + if (krb5info_name == NULL) { DEBUG(1, ("talloc_asprintf failed.\n")); ret = ENOMEM; goto done; @@ -182,12 +202,12 @@ errno_t write_kdcinfo_file(const char *realm, const char *kdc) goto done; } - ret = write(fd, kdc, kdc_len); + ret = write(fd, server, server_len); if (ret == -1) { DEBUG(1, ("write failed [%d][%s].\n", errno, strerror(errno))); goto done; } - if (ret != kdc_len) { + if (ret != server_len) { DEBUG(1, ("Partial write occured, this should never happen.\n")); ret = EINTR; goto done; @@ -205,7 +225,7 @@ errno_t write_kdcinfo_file(const char *realm, const char *kdc) goto done; } - ret = rename(tmp_name, kdcinfo_name); + ret = rename(tmp_name, krb5info_name); if (ret == -1) { DEBUG(1, ("rename failed [%d][%s].\n", errno, strerror(errno))); goto done; @@ -248,12 +268,20 @@ static void krb5_resolve_callback(void *private_data, struct fo_server *server) return; } + address = talloc_asprintf_append(address, ":%d", + fo_get_server_port(server)); + if (address == NULL) { + DEBUG(1, ("talloc_asprintf_append failed.\n")); + return; + } + talloc_zfree(krb5_service->address); krb5_service->address = address; - ret = write_kdcinfo_file(krb5_service->realm, address); + ret = write_krb5info_file(krb5_service->realm, address, + krb5_service->name); if (ret != EOK) { - DEBUG(2, ("write_kdcinfo_file failed, authentication might fail.\n")); + DEBUG(2, ("write_krb5info_file failed, authentication might fail.\n")); } return; @@ -269,6 +297,11 @@ int krb5_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx, char **list = NULL; int ret; int i; + char *port_str; + long port; + char *server_spec; + char *endptr; + struct servent *servent; tmp_ctx = talloc_new(memctx); if (!tmp_ctx) { @@ -308,8 +341,53 @@ int krb5_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx, for (i = 0; list[i]; i++) { talloc_steal(service, list[i]); + server_spec = talloc_strdup(service, list[i]); + port_str = strrchr(server_spec, ':'); + if (port_str == NULL) { + port = 0; + } else { + *port_str = '\0'; + ++port_str; + if (isdigit(*port_str)) { + errno = 0; + port = strtol(port_str, &endptr, 10); + if (errno != 0) { + ret = errno; + DEBUG(1, ("strtol failed on [%s]: [%d][%s].\n", port_str, + ret, strerror(ret))); + goto done; + } + if (*endptr != '\0') { + DEBUG(1, ("Found additional characters [%s] in port number " + "[%s].\n", endptr, port_str)); + ret = EINVAL; + goto done; + } + + if (port < 1 || port > 65535) { + DEBUG(1, ("Illegal port number [%d].\n", port)); + ret = EINVAL; + goto done; + } + } else if (isalpha(*port_str)) { + servent = getservbyname(port_str, NULL); + if (servent == NULL) { + DEBUG(1, ("getservbyname cannot find service [%s].\n", + port_str)); + ret = EINVAL; + goto done; + } + + port = servent->s_port; + } else { + DEBUG(1, ("Unsupported port specifier in [%s].\n", list[i])); + ret = EINVAL; + goto done; + } + } - ret = be_fo_add_server(ctx, service_name, list[i], 0, NULL); + ret = be_fo_add_server(ctx, service_name, server_spec, (int) port, + list[i]); if (ret && ret != EEXIST) { DEBUG(0, ("Failed to add server\n")); goto done; diff --git a/src/providers/krb5/krb5_common.h b/src/providers/krb5/krb5_common.h index 832ffcdd5..0b0da31f2 100644 --- a/src/providers/krb5/krb5_common.h +++ b/src/providers/krb5/krb5_common.h @@ -38,6 +38,10 @@ #define SSSD_KRB5_CHANGEPW_PRINCIPLE "SSSD_KRB5_CHANGEPW_PRINCIPLE" #define KDCINFO_TMPL PUBCONF_PATH"/kdcinfo.%s" +#define KPASSWDINFO_TMPL PUBCONF_PATH"/kpasswdinfo.%s" + +#define SSS_KRB5KDC_FO_SRV "KRB5KDC" +#define SSS_KRB5KPASSWD_FO_SRV "KRB5KPASSWD" enum krb5_opts { KRB5_KDC = 0, @@ -48,6 +52,7 @@ enum krb5_opts { KRB5_AUTH_TIMEOUT, KRB5_KEYTAB, KRB5_VALIDATE, + KRB5_KPASSWD, KRB5_OPTS }; @@ -64,7 +69,8 @@ errno_t check_and_export_options(struct dp_option *opts, errno_t krb5_get_options(TALLOC_CTX *memctx, struct confdb_ctx *cdb, const char *conf_path, struct dp_option **_opts); -errno_t write_kdcinfo_file(const char *realm, const char *kdc); +errno_t write_krb5info_file(const char *realm, const char *kdc, + const char *service); int krb5_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx, const char *service_name, const char *servers, diff --git a/src/providers/krb5/krb5_init.c b/src/providers/krb5/krb5_init.c index 4d2123815..83129d9c5 100644 --- a/src/providers/krb5/krb5_init.c +++ b/src/providers/krb5/krb5_init.c @@ -52,6 +52,7 @@ int sssm_krb5_auth_init(struct be_ctx *bectx, unsigned v; FILE *debug_filep; const char *krb5_servers; + const char *krb5_kpasswd_servers; const char *krb5_realm; const char *errstr; int errval; @@ -98,13 +99,27 @@ int sssm_krb5_auth_init(struct be_ctx *bectx, return EINVAL; } - ret = krb5_service_init(ctx, bectx, "KRB5", krb5_servers, krb5_realm, - &ctx->service); + ret = krb5_service_init(ctx, bectx, SSS_KRB5KDC_FO_SRV, krb5_servers, + krb5_realm, &ctx->service); if (ret != EOK) { - DEBUG(0, ("Failed to init IPA failover service!\n")); + DEBUG(0, ("Failed to init KRB5 failover service!\n")); return ret; } + krb5_kpasswd_servers = dp_opt_get_string(ctx->opts, KRB5_KPASSWD); + if (krb5_kpasswd_servers == NULL) { + DEBUG(0, ("Missing krb5_kpasswd option, using KDC!\n")); + ctx->kpasswd_service = NULL; + } else { + ret = krb5_service_init(ctx, bectx, SSS_KRB5KPASSWD_FO_SRV, + krb5_kpasswd_servers, krb5_realm, + &ctx->kpasswd_service); + if (ret != EOK) { + DEBUG(0, ("Failed to init KRB5KPASSWD failover service!\n")); + return ret; + } + } + ret = check_and_export_options(ctx->opts, bectx->domain); if (ret != EOK) { DEBUG(1, ("check_and_export_options failed.\n")); -- cgit