summaryrefslogtreecommitdiff
path: root/server/providers/krb5
diff options
context:
space:
mode:
Diffstat (limited to 'server/providers/krb5')
-rw-r--r--server/providers/krb5/krb5_auth.c61
-rw-r--r--server/providers/krb5/krb5_auth.h4
-rw-r--r--server/providers/krb5/krb5_common.c224
-rw-r--r--server/providers/krb5/krb5_common.h12
-rw-r--r--server/providers/krb5/krb5_init.c52
5 files changed, 323 insertions, 30 deletions
diff --git a/server/providers/krb5/krb5_auth.c b/server/providers/krb5/krb5_auth.c
index 8068bce9..7ac440f0 100644
--- a/server/providers/krb5/krb5_auth.c
+++ b/server/providers/krb5/krb5_auth.c
@@ -528,6 +528,7 @@ static errno_t krb5_setup(struct be_req *req, struct krb5child_req **krb5_req)
kr->read_from_child_fd = -1;
kr->write_to_child_fd = -1;
kr->is_offline = false;
+ kr->active_ccache_present = true;
talloc_set_destructor((TALLOC_CTX *) kr, krb5_cleanup);
kr->pd = pd;
@@ -929,6 +930,7 @@ 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_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);
@@ -988,7 +990,6 @@ static void get_user_attr_done(void *pvt, int err, struct ldb_result *res)
int dp_err = DP_ERR_FATAL;
const char *ccache_file = NULL;
const char *dummy;
- bool active_ccache_present = false;
ret = krb5_setup(be_req, &kr);
if (ret != EOK) {
@@ -1047,7 +1048,7 @@ static void get_user_attr_done(void *pvt, int err, struct ldb_result *res)
}
if (ccache_file == NULL) {
- active_ccache_present = false;
+ kr->active_ccache_present = false;
DEBUG(4, ("No active ccache file for user [%s] found.\n",
pd->user));
ccache_file = expand_ccname_template(kr, kr,
@@ -1059,11 +1060,11 @@ static void get_user_attr_done(void *pvt, int err, struct ldb_result *res)
goto failed;
}
} else {
- active_ccache_present = true;
+ kr->active_ccache_present = true;
}
DEBUG(9, ("Ccache_file is [%s] and %s.\n", ccache_file,
- active_ccache_present ? "will be kept/renewed" :
- "will be generated"));
+ kr->active_ccache_present ? "will be kept/renewed" :
+ "will be generated"));
kr->ccname = ccache_file;
break;
@@ -1074,6 +1075,44 @@ static void get_user_attr_done(void *pvt, int err, struct ldb_result *res)
break;
}
+ 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"));
+ goto failed;
+ }
+
+ tevent_req_set_callback(req, krb5_resolve_done, kr);
+
+ return;
+
+failed:
+ talloc_free(kr);
+
+ pd->pam_status = pam_status;
+ krb_reply(be_req, dp_err, pd->pam_status);
+}
+
+static void krb5_resolve_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;
+
+ ret = be_resolve_server_recv(req, &kr->srv);
+ talloc_zfree(req);
+ if (ret) {
+ /* all servers have been tried and none
+ * was found good, setting offline,
+ * but we still have to call the child to setup
+ * the ccache file. */
+ be_mark_offline(be_req->be_ctx);
+ kr->is_offline = true;
+ }
if (be_is_offline(be_req->be_ctx)) {
DEBUG(9, ("Preparing for offline operation.\n"));
@@ -1081,14 +1120,14 @@ static void get_user_attr_done(void *pvt, int err, struct ldb_result *res)
memset(pd->authtok, 0, pd->authtok_size);
pd->authtok_size = 0;
- if (active_ccache_present) {
+ if (kr->active_ccache_present) {
req = krb5_save_ccname_send(kr, be_req->be_ctx->ev,
be_req->be_ctx->sysdb,
be_req->be_ctx->domain, pd->user,
kr->ccname);
if (req == NULL) {
DEBUG(1, ("krb5_save_ccname_send failed.\n"));
- goto failed;
+ goto done;
}
tevent_req_set_callback(req, krb5_save_ccname_done, kr);
@@ -1099,15 +1138,14 @@ static void get_user_attr_done(void *pvt, int err, struct ldb_result *res)
req = handle_child_send(kr, be_req->be_ctx->ev, kr);
if (req == NULL) {
DEBUG(1, ("handle_child_send failed.\n"));
- goto failed;
+ goto done;
}
tevent_req_set_callback(req, krb5_child_done, kr);
return;
-failed:
+done:
talloc_free(kr);
-
pd->pam_status = pam_status;
krb_reply(be_req, dp_err, pd->pam_status);
}
@@ -1189,6 +1227,9 @@ static void krb5_child_done(struct tevent_req *req)
}
if (*msg_status == PAM_AUTHINFO_UNAVAIL) {
+ if (kr->srv != NULL) {
+ fo_set_server_status(kr->srv, SERVER_NOT_WORKING);
+ }
be_mark_offline(be_req->be_ctx);
kr->is_offline = true;
}
diff --git a/server/providers/krb5/krb5_auth.h b/server/providers/krb5/krb5_auth.h
index 7851ebba..7facb003 100644
--- a/server/providers/krb5/krb5_auth.h
+++ b/server/providers/krb5/krb5_auth.h
@@ -50,6 +50,8 @@ struct krb5child_req {
const char *ccname;
const char *homedir;
bool is_offline;
+ struct fo_server *srv;
+ bool active_ccache_present;
};
struct fo_service;
@@ -80,7 +82,7 @@ struct krb5_ctx {
action_type action;
struct dp_option *opts;
- struct krb_server *server;
+ struct krb5_service *service;
int child_debug_fd;
};
diff --git a/server/providers/krb5/krb5_common.c b/server/providers/krb5/krb5_common.c
index 6c235364..6817d650 100644
--- a/server/providers/krb5/krb5_common.c
+++ b/server/providers/krb5/krb5_common.c
@@ -24,6 +24,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
+#include <netdb.h>
#include "providers/dp_backend.h"
#include "providers/krb5/krb5_common.h"
@@ -47,17 +48,8 @@ errno_t check_and_export_options(struct dp_option *opts,
const char *realm;
const char *dummy;
struct stat stat_buf;
-
- dummy = dp_opt_get_cstring(opts, KRB5_KDC);
- if (dummy == NULL) {
- DEBUG(2, ("No KDC expicitly configured, using defaults"));
- } else {
- ret = setenv(SSSD_KRB5_KDC, dummy, 1);
- if (ret != EOK) {
- DEBUG(2, ("setenv %s failed, authentication might fail.\n",
- SSSD_KRB5_KDC));
- }
- }
+ char **list;
+ int count;
realm = dp_opt_get_cstring(opts, KRB5_REALM);
if (realm == NULL) {
@@ -68,12 +60,30 @@ errno_t check_and_export_options(struct dp_option *opts,
}
realm = dom->name;
}
+
ret = setenv(SSSD_KRB5_REALM, realm, 1);
if (ret != EOK) {
DEBUG(2, ("setenv %s failed, authentication might fail.\n",
SSSD_KRB5_REALM));
}
+ dummy = dp_opt_get_cstring(opts, KRB5_KDC);
+ if (dummy == NULL) {
+ DEBUG(1, ("No KDC expicitly configured, using defaults"));
+ } else {
+ ret = sss_split_list(opts, dummy, ", ", &list, &count);
+ if (ret != EOK) {
+ DEBUG(1, ("Failed to parse server list!\n"));
+ return ret;
+ }
+ ret = write_kdcinfo_file(realm, list[0]);
+ if (ret != EOK) {
+ DEBUG(1, ("write_kdcinfo_file failed, "
+ "using kerberos defaults from /etc/krb5.conf"));
+ }
+ talloc_free(list);
+ }
+
dummy = dp_opt_get_cstring(opts, KRB5_CCACHEDIR);
ret = lstat(dummy, &stat_buf);
if (ret != EOK) {
@@ -154,3 +164,195 @@ done:
return ret;
}
+
+errno_t write_kdcinfo_file(const char *realm, const char *kdc)
+{
+ int ret;
+ int fd = -1;
+ char *tmp_name = NULL;
+ char *kdcinfo_name = NULL;
+ TALLOC_CTX *tmp_ctx = NULL;
+ int kdc_len;
+
+ if (realm == NULL || *realm == '\0' || kdc == NULL || *kdc == '\0') {
+ DEBUG(1, ("Missing or empty realm or kdc.\n"));
+ return EINVAL;
+ }
+
+ kdc_len = strlen(kdc);
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(1, ("talloc_new failed.\n"));
+ return ENOMEM;
+ }
+
+ tmp_name = talloc_asprintf(tmp_ctx, PUBCONF_PATH"/.kdcinfo_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) {
+ DEBUG(1, ("talloc_asprintf failed.\n"));
+ ret = ENOMEM;
+ goto done;
+ }
+
+ fd = mkstemp(tmp_name);
+ if (fd == -1) {
+ DEBUG(1, ("mkstemp failed [%d][%s].\n", errno, strerror(errno)));
+ ret = errno;
+ goto done;
+ }
+
+ ret = write(fd, kdc, kdc_len);
+ if (ret == -1) {
+ DEBUG(1, ("write failed [%d][%s].\n", errno, strerror(errno)));
+ goto done;
+ }
+ if (ret != kdc_len) {
+ DEBUG(1, ("Partial write occured, this should never happen.\n"));
+ ret = EINTR;
+ goto done;
+ }
+
+ ret = fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+ if (ret == -1) {
+ DEBUG(1, ("fchmod failed [%d][%s].\n", errno, strerror(errno)));
+ goto done;
+ }
+
+ ret = close(fd);
+ if (ret == -1) {
+ DEBUG(1, ("close failed [%d][%s].\n", errno, strerror(errno)));
+ goto done;
+ }
+
+ ret = rename(tmp_name, kdcinfo_name);
+ if (ret == -1) {
+ DEBUG(1, ("rename failed [%d][%s].\n", errno, strerror(errno)));
+ goto done;
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static void krb5_resolve_callback(void *private_data, struct fo_server *server)
+{
+ struct krb5_service *krb5_service;
+ struct hostent *srvaddr;
+ char *address;
+ int ret;
+
+ krb5_service = talloc_get_type(private_data, struct krb5_service);
+ if (!krb5_service) {
+ DEBUG(1, ("FATAL: Bad private_data\n"));
+ return;
+ }
+
+ srvaddr = fo_get_server_hostent(server);
+ if (!srvaddr) {
+ DEBUG(1, ("FATAL: No hostent available for server (%s)\n",
+ fo_get_server_name(server)));
+ return;
+ }
+
+ address = talloc_asprintf(krb5_service, srvaddr->h_name);
+ if (!address) {
+ DEBUG(1, ("Failed to copy address ...\n"));
+ return;
+ }
+
+ talloc_zfree(krb5_service->address);
+ krb5_service->address = address;
+
+ ret = write_kdcinfo_file(krb5_service->realm, address);
+ if (ret != EOK) {
+ DEBUG(2, ("write_kdcinfo_file failed, authentication might fail.\n"));
+ }
+
+ return;
+}
+
+
+int krb5_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx,
+ const char *service_name, const char *servers,
+ const char *realm, struct krb5_service **_service)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct krb5_service *service;
+ char **list = NULL;
+ int count = 0;
+ int ret;
+ int i;
+
+ tmp_ctx = talloc_new(memctx);
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ service = talloc_zero(tmp_ctx, struct krb5_service);
+ if (!service) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = be_fo_add_service(ctx, service_name);
+ if (ret != EOK) {
+ DEBUG(1, ("Failed to create failover service!\n"));
+ goto done;
+ }
+
+ service->name = talloc_strdup(service, service_name);
+ if (!service->name) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ service->realm = talloc_strdup(service, realm);
+ if (!service->realm) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_split_list(tmp_ctx, servers, ", ", &list, &count);
+ if (ret != EOK) {
+ DEBUG(1, ("Failed to parse server list!\n"));
+ goto done;
+ }
+
+ for (i = 0; i < count; i++) {
+
+ talloc_steal(service, list[i]);
+
+ ret = be_fo_add_server(ctx, service_name, list[i], 0, NULL);
+ if (ret && ret != EEXIST) {
+ DEBUG(0, ("Failed to add server\n"));
+ goto done;
+ }
+
+ DEBUG(6, ("Added Server %s\n", list[i]));
+ }
+
+ ret = be_fo_service_add_callback(memctx, ctx, service_name,
+ krb5_resolve_callback, service);
+ if (ret != EOK) {
+ DEBUG(1, ("Failed to add failover callback!\n"));
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret == EOK) {
+ *_service = talloc_steal(memctx, service);
+ }
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
diff --git a/server/providers/krb5/krb5_common.h b/server/providers/krb5/krb5_common.h
index 42b00373..832ffcdd 100644
--- a/server/providers/krb5/krb5_common.h
+++ b/server/providers/krb5/krb5_common.h
@@ -37,6 +37,8 @@
#define SSSD_KRB5_REALM "SSSD_KRB5_REALM"
#define SSSD_KRB5_CHANGEPW_PRINCIPLE "SSSD_KRB5_CHANGEPW_PRINCIPLE"
+#define KDCINFO_TMPL PUBCONF_PATH"/kdcinfo.%s"
+
enum krb5_opts {
KRB5_KDC = 0,
KRB5_REALM,
@@ -50,8 +52,10 @@ enum krb5_opts {
KRB5_OPTS
};
-struct krb_server {
+struct krb5_service {
+ char *name;
char *address;
+ char *realm;
};
errno_t check_and_export_options(struct dp_option *opts,
@@ -59,4 +63,10 @@ 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);
+
+int krb5_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx,
+ const char *service_name, const char *servers,
+ const char *realm, struct krb5_service **_service);
#endif /* __KRB5_COMMON_H__ */
diff --git a/server/providers/krb5/krb5_init.c b/server/providers/krb5/krb5_init.c
index 26cf5e3b..a070c8c3 100644
--- a/server/providers/krb5/krb5_init.c
+++ b/server/providers/krb5/krb5_init.c
@@ -29,12 +29,14 @@
#include "providers/krb5/krb5_auth.h"
#include "providers/krb5/krb5_common.h"
-struct bet_ops krb5_auth_ops = {
- .handler = krb5_pam_handler,
- .finalize = NULL,
+struct krb5_options {
+ struct dp_option *opts;
+ struct krb5_ctx *auth_ctx;
};
-struct bet_ops krb5_chpass_ops = {
+struct krb5_options *krb5_options = NULL;
+
+struct bet_ops krb5_auth_ops = {
.handler = krb5_pam_handler,
.finalize = NULL,
};
@@ -48,6 +50,28 @@ int sssm_krb5_auth_init(struct be_ctx *bectx,
struct tevent_signal *sige;
unsigned v;
FILE *debug_filep;
+ const char *krb5_servers;
+ const char *krb5_realm;
+
+ if (krb5_options == NULL) {
+ krb5_options = talloc_zero(bectx, struct krb5_options);
+ if (krb5_options == NULL) {
+ DEBUG(1, ("talloc_zero failed.\n"));
+ return ENOMEM;
+ }
+ ret = krb5_get_options(krb5_options, bectx->cdb, bectx->conf_path,
+ &krb5_options->opts);
+ if (ret != EOK) {
+ DEBUG(1, ("krb5_get_options failed.\n"));
+ return ret;
+ }
+ }
+
+ if (krb5_options->auth_ctx != NULL) {
+ *ops = &krb5_auth_ops;
+ *pvt_auth_data = krb5_options->auth_ctx;
+ return EOK;
+ }
ctx = talloc_zero(bectx, struct krb5_ctx);
if (!ctx) {
@@ -56,11 +80,25 @@ int sssm_krb5_auth_init(struct be_ctx *bectx,
}
ctx->action = INIT_PW;
+ ctx->opts = krb5_options->opts;
+
+ krb5_servers = dp_opt_get_string(ctx->opts, KRB5_KDC);
+ if (krb5_servers == NULL) {
+ DEBUG(0, ("Missing krb5_kdcip option!\n"));
+ return EINVAL;
+ }
+
+ krb5_realm = dp_opt_get_string(ctx->opts, KRB5_REALM);
+ if (krb5_realm == NULL) {
+ DEBUG(0, ("Missing krb5_realm option!\n"));
+ return EINVAL;
+ }
- ret = krb5_get_options(ctx, bectx->cdb, bectx->conf_path, &ctx->opts);
+ ret = krb5_service_init(ctx, bectx, "KRB5", krb5_servers, krb5_realm,
+ &ctx->service);
if (ret != EOK) {
- DEBUG(1, ("krb5_get_options failed.\n"));
- goto fail;
+ DEBUG(0, ("Failed to init IPA failover service!\n"));
+ return ret;
}
ret = check_and_export_options(ctx->opts, bectx->domain);