summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSumit Bose <sbose@redhat.com>2010-03-10 17:03:23 +0100
committerStephen Gallagher <sgallagh@redhat.com>2010-03-12 09:00:28 -0500
commit5096bb4c2242b426aa6f5ea2cb82223e0b81a345 (patch)
tree7db071f1395488b0e419f93c4328330cd9b899fa /src
parent70a54fe1c527efabf0c3258a2daa669f5e2bb788 (diff)
downloadsssd-5096bb4c2242b426aa6f5ea2cb82223e0b81a345.tar.gz
sssd-5096bb4c2242b426aa6f5ea2cb82223e0b81a345.tar.bz2
sssd-5096bb4c2242b426aa6f5ea2cb82223e0b81a345.zip
Add krb5_kpasswd option
Diffstat (limited to 'src')
-rw-r--r--src/config/SSSDConfig.py1
-rwxr-xr-xsrc/config/SSSDConfigTest.py3
-rw-r--r--src/config/etc/sssd.api.d/sssd-krb5.conf1
-rw-r--r--src/krb5_plugin/sssd_krb5_locator_plugin.c176
-rw-r--r--src/man/sssd-krb5.5.xml23
-rw-r--r--src/providers/ipa/ipa_common.c5
-rw-r--r--src/providers/krb5/krb5_auth.c85
-rw-r--r--src/providers/krb5/krb5_auth.h2
-rw-r--r--src/providers/krb5/krb5_child.c7
-rw-r--r--src/providers/krb5/krb5_common.c112
-rw-r--r--src/providers/krb5/krb5_common.h8
-rw-r--r--src/providers/krb5/krb5_init.c21
12 files changed, 373 insertions, 71 deletions
diff --git a/src/config/SSSDConfig.py b/src/config/SSSDConfig.py
index c9e08caf..7dd90e78 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 4e9d0914..7e882e74 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 7ba0ab32..7b12e08f 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 8f32a31b..eb8666e4 100644
--- a/src/krb5_plugin/sssd_krb5_locator_plugin.c
+++ b/src/krb5_plugin/sssd_krb5_locator_plugin.c
@@ -29,12 +29,18 @@
#include <netdb.h>
#include <sys/stat.h>
#include <fcntl.h>
+#include <ctype.h>
#include <krb5/locate_plugin.h>
#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 ca4dae25..c291eca7 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 <quote>FAILOVER</quote> section.
+ see the <quote>FAILOVER</quote> section. An optional
+ port number (preceded by a colon) may be appended to
+ the addresses or hostnames.
</para>
</listitem>
</varlistentry>
@@ -99,6 +101,25 @@
</varlistentry>
<varlistentry>
+ <term>krb5_kpasswd (string)</term>
+ <listitem>
+ <para>
+ 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.
+ </para>
+ <para>
+ For more information on failover and server
+ redundancy, see the <quote>FAILOVER</quote> 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.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term>krb5_ccachedir (string)</term>
<listitem>
<para>
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
index a50b63b9..1fc881f4 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 b8b498a0..ce3aacd8 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 825f3d64..9f8c4144 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 234b8389..86242ef3 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 8c1c7fac..2b3331ed 100644
--- a/src/providers/krb5/krb5_common.c
+++ b/src/providers/krb5/krb5_common.c
@@ -26,6 +26,7 @@
#include <unistd.h>
#include <netdb.h>
#include <arpa/inet.h>
+#include <ctype.h>
#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 832ffcdd..0b0da31f 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 4d212381..83129d9c 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"));