summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--contrib/sssd.spec.in1
-rw-r--r--src/Makefile.am2
-rw-r--r--src/config/SSSDConfig.py2
-rw-r--r--src/config/etc/sssd.api.d/sssd-ipa.conf2
-rw-r--r--src/configure.ac1
-rw-r--r--src/external/nsupdate.m48
-rw-r--r--src/man/sssd-ipa.5.xml28
-rw-r--r--src/providers/ipa/ipa_common.c2
-rw-r--r--src/providers/ipa/ipa_common.h2
-rw-r--r--src/providers/ipa/ipa_dyndns.c580
-rw-r--r--src/providers/ipa/ipa_dyndns.h31
-rw-r--r--src/providers/ipa/ipa_init.c41
-rw-r--r--src/providers/ldap/sdap_async_private.h2
-rw-r--r--src/providers/ldap/sdap_fd_events.c28
14 files changed, 716 insertions, 14 deletions
diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
index 9d5d30414..bbc172830 100644
--- a/contrib/sssd.spec.in
+++ b/contrib/sssd.spec.in
@@ -74,6 +74,7 @@ BuildRequires: check-devel
BuildRequires: doxygen
BuildRequires: libselinux-devel
BuildRequires: libsemanage-devel
+BuildRequires: bind-utils
%description
Provides a set of daemons to manage access to remote directories and
diff --git a/src/Makefile.am b/src/Makefile.am
index 5fb406234..90b382b00 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -349,6 +349,7 @@ dist_noinst_HEADERS = \
providers/ipa/ipa_access.h \
providers/ipa/ipa_timerules.h \
providers/ipa/ipa_auth.h \
+ providers/ipa/ipa_dyndns.h \
tools/tools_util.h \
tools/sss_sync_ops.h \
resolv/async_resolv.h \
@@ -789,6 +790,7 @@ libsss_ipa_la_SOURCES = \
providers/ipa/ipa_auth.c \
providers/ipa/ipa_access.c \
providers/ipa/ipa_timerules.c \
+ providers/ipa/ipa_dyndns.c \
providers/ldap/ldap_id.c \
providers/ldap/ldap_id_enum.c \
providers/ldap/ldap_id_cleanup.c \
diff --git a/src/config/SSSDConfig.py b/src/config/SSSDConfig.py
index 18df97904..ef16bb973 100644
--- a/src/config/SSSDConfig.py
+++ b/src/config/SSSDConfig.py
@@ -87,6 +87,8 @@ option_strings = {
'ipa_domain' : _('IPA domain'),
'ipa_server' : _('IPA server address'),
'ipa_hostname' : _('IPA client hostname'),
+ 'ipa_dyndns_update' : _("Whether to automatically update the client's DNS entry in FreeIPA"),
+ 'ipa_dyndns_iface' : _("The interface whose IP should be used for dynamic DNS updates"),
# [provider/krb5]
'krb5_kdcip' : _('Kerberos server address'),
diff --git a/src/config/etc/sssd.api.d/sssd-ipa.conf b/src/config/etc/sssd.api.d/sssd-ipa.conf
index f71498cc2..7fbc312d9 100644
--- a/src/config/etc/sssd.api.d/sssd-ipa.conf
+++ b/src/config/etc/sssd.api.d/sssd-ipa.conf
@@ -2,6 +2,8 @@
ipa_domain = str, None, true
ipa_server = str, None, true
ipa_hostname = str, None, false
+ipa_dyndns_update = bool, None, false
+ipa_dyndns_iface = str, None, false
ldap_uri = str, None, false
ldap_search_base = str, None, false
ldap_schema = str, None, false
diff --git a/src/configure.ac b/src/configure.ac
index 93debb60a..4cc5d853f 100644
--- a/src/configure.ac
+++ b/src/configure.ac
@@ -94,6 +94,7 @@ m4_include([external/python.m4])
m4_include([external/selinux.m4])
m4_include([external/crypto.m4])
m4_include([external/nscd.m4])
+m4_include([external/nsupdate.m4])
m4_include([util/signal.m4])
PKG_CHECK_MODULES([DBUS],[dbus-1])
diff --git a/src/external/nsupdate.m4 b/src/external/nsupdate.m4
new file mode 100644
index 000000000..6e18f017b
--- /dev/null
+++ b/src/external/nsupdate.m4
@@ -0,0 +1,8 @@
+AC_PATH_PROG(NSUPDATE, nsupdate)
+AC_MSG_CHECKING(for nsupdate)
+if test -x "$NSUPDATE"; then
+ AC_DEFINE_UNQUOTED([NSUPDATE_PATH], ["$NSUPDATE"], [The path to nsupdate])
+ AC_MSG_RESULT(yes)
+else
+ AC_MSG_ERROR([no. nsupdate is not available])
+fi
diff --git a/src/man/sssd-ipa.5.xml b/src/man/sssd-ipa.5.xml
index 103558b04..95f8613da 100644
--- a/src/man/sssd-ipa.5.xml
+++ b/src/man/sssd-ipa.5.xml
@@ -100,6 +100,34 @@
</varlistentry>
<varlistentry>
+ <term>ipa_dyndns_update (boolean)</term>
+ <listitem>
+ <para>
+ Optional. This option tells SSSD to automatically
+ update the DNS server built into FreeIPA v2 with
+ the IP address of this client.
+ </para>
+ <para>
+ Default: false
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>ipa_dyndns_iface (string)</term>
+ <listitem>
+ <para>
+ Optional. Applicable only when ipa_dyndns_update
+ is true. Choose the interface whose IP address
+ should be used for dynamic DNS updates.
+ </para>
+ <para>
+ Default: Use the IP address of the IPA LDAP connection
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term>krb5_validate (boolean)</term>
<listitem>
<para>
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
index aa84e7a94..03c023679 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -32,6 +32,8 @@ struct dp_option ipa_basic_opts[] = {
{ "ipa_domain", DP_OPT_STRING, NULL_STRING, NULL_STRING },
{ "ipa_server", DP_OPT_STRING, NULL_STRING, NULL_STRING },
{ "ipa_hostname", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+ { "ipa_dyndns_update", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
+ { "ipa_dyndns_iface", DP_OPT_STRING, NULL_STRING, NULL_STRING}
};
struct dp_option ipa_def_ldap_opts[] = {
diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h
index 9daede2db..20d557e4a 100644
--- a/src/providers/ipa/ipa_common.h
+++ b/src/providers/ipa/ipa_common.h
@@ -46,6 +46,8 @@ enum ipa_basic_opt {
IPA_DOMAIN = 0,
IPA_SERVER,
IPA_HOSTNAME,
+ IPA_DYNDNS_UPDATE,
+ IPA_DYNDNS_IFACE,
IPA_OPTS_BASIC /* opts counter */
};
diff --git a/src/providers/ipa/ipa_dyndns.c b/src/providers/ipa/ipa_dyndns.c
new file mode 100644
index 000000000..b1edc194c
--- /dev/null
+++ b/src/providers/ipa/ipa_dyndns.c
@@ -0,0 +1,580 @@
+/*
+ SSSD
+
+ ipa_dyndns.c
+
+ Authors:
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2010 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <ifaddrs.h>
+#include <ctype.h>
+#include "util/util.h"
+#include "confdb/confdb.h"
+#include "providers/ipa/ipa_common.h"
+#include "providers/ipa/ipa_dyndns.h"
+#include "providers/child_common.h"
+#include "providers/data_provider.h"
+#include "providers/ldap/ldap_common.h"
+#include "providers/ldap/sdap_async_private.h"
+#include "resolv/async_resolv.h"
+
+#define IPA_DYNDNS_TIMEOUT 15
+
+struct ipa_ipaddress {
+ struct ipa_ipaddress *next;
+ struct ipa_ipaddress *prev;
+
+ struct sockaddr *addr;
+ bool matched;
+};
+
+struct ipa_dyndns_ctx {
+ struct ipa_options *ipa_ctx;
+ char *hostname;
+ struct ipa_ipaddress *addresses;
+ int child_status;
+};
+
+
+static struct tevent_req * ipa_dyndns_update_send(struct ipa_options *ctx);
+
+static void ipa_dyndns_update_done(struct tevent_req *req);
+
+void ipa_dyndns_update(void *pvt)
+{
+ struct ipa_options *ctx = talloc_get_type(pvt, struct ipa_options);
+ struct tevent_req *req = ipa_dyndns_update_send(ctx);
+ if (req == NULL) {
+ DEBUG(1, ("Could not update DNS\n"));
+ return;
+ }
+ tevent_req_set_callback(req, ipa_dyndns_update_done, req);
+}
+
+
+static struct tevent_req *
+ipa_dyndns_gss_tsig_update_send(struct ipa_dyndns_ctx *ctx);
+
+static void ipa_dyndns_gss_tsig_update_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+ipa_dyndns_update_send(struct ipa_options *ctx)
+{
+ int ret;
+ int fd;
+ char *iface;
+ char *ipa_hostname;
+ struct ipa_dyndns_ctx *state;
+ struct sockaddr sa;
+ socklen_t sa_len = sizeof(sa);
+ struct ifaddrs *ifaces;
+ struct ifaddrs *ifa;
+ struct ipa_ipaddress *address;
+ struct tevent_req *req, *subreq;
+
+ DEBUG (9, ("Performing update\n"));
+
+ req = tevent_req_create(ctx, &state, struct ipa_dyndns_ctx);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ipa_ctx = ctx;
+
+ iface = dp_opt_get_string(ctx->basic, IPA_DYNDNS_IFACE);
+
+ if (iface) {
+ /* Get the IP addresses associated with the
+ * specified interface
+ */
+ errno = 0;
+ ret = getifaddrs(&ifaces);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(0, ("Could not read interfaces [%d][%s]\n",
+ ret, strerror(ret)));
+ goto failed;
+ }
+
+ for(ifa = ifaces; ifa != NULL; ifa=ifa->ifa_next) {
+ /* Some interfaces don't have an ifa_addr */
+ if (!ifa->ifa_addr) continue;
+
+ /* Add IP addresses to the list */
+ if((ifa->ifa_addr->sa_family == AF_INET ||
+ ifa->ifa_addr->sa_family == AF_INET6) &&
+ strcasecmp(ifa->ifa_name, iface) == 0) {
+ /* Add this address to the IP address list */
+ address = talloc_zero(state, struct ipa_ipaddress);
+ if (!address) {
+ goto failed;
+ }
+
+ address->addr = talloc_memdup(address, ifa->ifa_addr,
+ sizeof(struct sockaddr));
+ if(address->addr == NULL) {
+ goto failed;
+ }
+ DLIST_ADD(state->addresses, address);
+ }
+ }
+
+ freeifaddrs(ifaces);
+ }
+
+ else {
+ /* Get the file descriptor for the primary LDAP connection */
+ ret = get_fd_from_ldap(ctx->id_ctx->gsh->ldap, &fd);
+ if (ret != EOK) {
+ goto failed;
+ }
+
+ ret = getsockname(fd, &sa, &sa_len);
+ if (ret == -1) {
+ DEBUG(0,("Failed to get socket name\n"));
+ goto failed;
+ }
+
+ switch(sa.sa_family) {
+ case AF_INET:
+ case AF_INET6:
+ address = talloc(state, struct ipa_ipaddress);
+ if (!address) {
+ goto failed;
+ }
+ address->addr = talloc_memdup(address, &sa,
+ sizeof(struct sockaddr));
+ if(address->addr == NULL) {
+ goto failed;
+ }
+ DLIST_ADD(state->addresses, address);
+ break;
+ default:
+ DEBUG(1, ("Connection to LDAP is neither IPv4 nor IPv6\n"));
+ ret = EIO;
+ goto failed;
+ }
+ }
+
+ /* Get the IPA hostname */
+ ipa_hostname = dp_opt_get_string(state->ipa_ctx->basic,
+ IPA_HOSTNAME);
+ if (!ipa_hostname) {
+ /* This should never happen, but we'll protect
+ * against it anyway.
+ */
+ talloc_free(req);
+ return NULL;
+ }
+
+ state->hostname = talloc_strdup(state, ipa_hostname);
+ if(state->hostname == NULL) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ /* In the future, it might be best to check that an update
+ * needs to be run before running it, but this is such a
+ * rare event that it's probably fine to just run an update
+ * every time we come online.
+ */
+ subreq = ipa_dyndns_gss_tsig_update_send(state);
+ if(subreq == NULL) {
+ tevent_req_error(req, EIO);
+ }
+ tevent_req_set_callback(subreq,
+ ipa_dyndns_gss_tsig_update_done,
+ req);
+ return req;
+
+failed:
+ talloc_free(req);
+ return NULL;
+}
+
+struct ipa_nsupdate_ctx {
+ char *update_msg;
+ struct ipa_dyndns_ctx *dyndns_ctx;
+ int pipefd_to_child;
+ struct tevent_timer *timeout_handler;
+};
+
+
+static int create_nsupdate_message(struct ipa_nsupdate_ctx *ctx);
+
+static struct tevent_req *
+fork_nsupdate_send(struct ipa_nsupdate_ctx *ctx);
+
+static void fork_nsupdate_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+ipa_dyndns_gss_tsig_update_send(struct ipa_dyndns_ctx *ctx)
+{
+ int ret;
+ struct ipa_nsupdate_ctx *state;
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+
+ req = tevent_req_create(ctx, &state, struct ipa_nsupdate_ctx);
+ if(req == NULL) {
+ return NULL;
+ }
+ state->dyndns_ctx = ctx;
+
+ /* Format the message to pass to the nsupdate command */
+ ret = create_nsupdate_message(state);
+ if (ret != EOK) {
+ goto failed;
+ }
+
+ /* Fork a child process to perform the DNS update */
+ subreq = fork_nsupdate_send(state);
+ if(subreq == NULL) {
+ goto failed;
+ }
+ tevent_req_set_callback(subreq, fork_nsupdate_done, req);
+
+ return req;
+
+failed:
+ talloc_free(req);
+ return NULL;
+}
+
+struct nsupdate_send_ctx {
+ struct ipa_nsupdate_ctx *nsupdate_ctx;
+};
+
+static int create_nsupdate_message(struct ipa_nsupdate_ctx *ctx)
+{
+ int ret, i;
+ char *servername;
+ char *zone;
+ char ip_addr[INET6_ADDRSTRLEN];
+ const char *ip;
+ struct ipa_ipaddress *new_record;
+
+ servername = dp_opt_get_string(ctx->dyndns_ctx->ipa_ctx->basic,
+ IPA_SERVER);
+ if (!servername) {
+ return EIO;
+ }
+
+ zone = dp_opt_get_string(ctx->dyndns_ctx->ipa_ctx->basic,
+ IPA_DOMAIN);
+ if (!zone) {
+ return EIO;
+ }
+
+ /* The DNS zone for IPA is the lower-case
+ * version of hte IPA domain
+ */
+ for(i = 0; zone[i] != '\0'; i++) {
+ zone[i] = tolower(zone[i]);
+ }
+
+ /* Add the server and zone headers */
+ ctx->update_msg = talloc_asprintf(ctx, "server %s\nzone %s.\n",
+ servername,
+ zone);
+ if (ctx->update_msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Remove any existing entries */
+ ctx->update_msg = talloc_asprintf_append(ctx->update_msg,
+ "update delete %s. in A\nsend\n"
+ "update delete %s. in AAAA\nsend\n",
+ ctx->dyndns_ctx->hostname,
+ ctx->dyndns_ctx->hostname);
+ if (ctx->update_msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DLIST_FOR_EACH(new_record, ctx->dyndns_ctx->addresses) {
+ switch(new_record->addr->sa_family) {
+ case AF_INET:
+ ip = inet_ntop(new_record->addr->sa_family,
+ &(((struct sockaddr_in *)new_record->addr)->sin_addr),
+ ip_addr, INET6_ADDRSTRLEN);
+ if (ip == NULL) {
+ ret = EIO;
+ goto done;
+ }
+ break;
+
+ case AF_INET6:
+ ip = inet_ntop(new_record->addr->sa_family,
+ &(((struct sockaddr_in6 *)new_record->addr)->sin6_addr),
+ ip_addr, INET6_ADDRSTRLEN);
+ if (ip == NULL) {
+ ret = EIO;
+ goto done;
+ }
+ break;
+
+ default:
+ DEBUG(0, ("Unknown address family\n"));
+ ret = EIO;
+ goto done;
+ }
+
+ /* Format the record update */
+ ctx->update_msg = talloc_asprintf_append(
+ ctx->update_msg,
+ "update add %s. 86400 in %s %s\n",
+ ctx->dyndns_ctx->hostname,
+ new_record->addr->sa_family == AF_INET ? "A" : "AAAA",
+ ip_addr);
+ if (ctx->update_msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ ctx->update_msg = talloc_asprintf_append(ctx->update_msg, "send\n");
+ if (ctx->update_msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ return ret;
+}
+
+static void ipa_dyndns_stdin_done(struct tevent_req *subreq);
+
+static void ipa_dyndns_child_handler(int child_status,
+ struct tevent_signal *sige,
+ void *pvt);
+
+static void ipa_dyndns_timeout(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv, void *pvt);
+
+static struct tevent_req *
+fork_nsupdate_send(struct ipa_nsupdate_ctx *ctx)
+{
+ int pipefd_to_child[2];
+ pid_t pid;
+ int ret;
+ errno_t err;
+ struct timeval tv;
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct nsupdate_send_ctx *state;
+ char *args[3];
+
+ req = tevent_req_create(ctx, &state, struct nsupdate_send_ctx);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->nsupdate_ctx = ctx;
+
+ ret = pipe(pipefd_to_child);
+ if (ret == -1) {
+ err = errno;
+ DEBUG(1, ("pipe failed [%d][%s].\n", err, strerror(err)));
+ return NULL;
+ }
+
+ pid = fork();
+
+ if (pid == 0) { /* child */
+ args[0] = talloc_strdup(ctx, NSUPDATE_PATH);
+ args[1] = talloc_strdup(ctx, "-g");
+ args[2] = NULL;
+ if (args[0] == NULL || args[1] == NULL) {
+ return NULL;
+ }
+
+ close(pipefd_to_child[1]);
+ ret = dup2(pipefd_to_child[0], STDIN_FILENO);
+ if (ret == -1) {
+ err = errno;
+ DEBUG(1, ("dup2 failed [%d][%s].\n", err, strerror(err)));
+ return NULL;
+ }
+
+ errno = 0;
+ ret = execv(NSUPDATE_PATH, args);
+ if(ret == -1) {
+ err = errno;
+ DEBUG(1, ("execv failed [%d][%s].\n", err, strerror(err)));
+ }
+ return NULL;
+ }
+
+ else if (pid > 0) { /* parent */
+ close(pipefd_to_child[0]);
+
+ ctx->pipefd_to_child = pipefd_to_child[1];
+
+ /* Write the update message to the nsupdate child */
+ subreq = write_pipe_send(req,
+ ctx->dyndns_ctx->ipa_ctx->id_ctx->be->ev,
+ (uint8_t *)ctx->update_msg,
+ strlen(ctx->update_msg)+1,
+ ctx->pipefd_to_child);
+ if (subreq == NULL) {
+ return NULL;
+ }
+ tevent_req_set_callback(subreq, ipa_dyndns_stdin_done, req);
+
+ /* Set up SIGCHLD handler */
+ ret = child_handler_setup(ctx->dyndns_ctx->ipa_ctx->id_ctx->be->ev,
+ pid, ipa_dyndns_child_handler, req);
+ if (ret != EOK) {
+ return NULL;
+ }
+
+ /* Set up timeout handler */
+ tv = tevent_timeval_current_ofs(IPA_DYNDNS_TIMEOUT, 0);
+ ctx->timeout_handler = tevent_add_timer(
+ ctx->dyndns_ctx->ipa_ctx->id_ctx->be->ev,
+ req, tv, ipa_dyndns_timeout, req);
+ if(ctx->timeout_handler == NULL) {
+ return NULL;
+ }
+ }
+
+ else { /* error */
+ err = errno;
+ DEBUG(1, ("fork failed [%d][%s].\n", err, strerror(err)));
+ return NULL;
+ }
+
+ return req;
+}
+
+static void ipa_dyndns_timeout(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv, void *pvt)
+{
+ struct tevent_req *req =
+ talloc_get_type(pvt, struct tevent_req);
+
+ DEBUG(1, ("Timeout reached for dynamic DNS update\n"));
+
+ tevent_req_error(req, ETIMEDOUT);
+}
+
+static void ipa_dyndns_stdin_done(struct tevent_req *subreq)
+{
+ /* Verify that the buffer was sent, then return
+ * and wait for the sigchld handler to finish.
+ */
+ DEBUG(9, ("Sending nsupdate data complete\n"));
+
+ int ret;
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct nsupdate_send_ctx *state =
+ tevent_req_data(req, struct nsupdate_send_ctx);
+
+ ret = write_pipe_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(1, ("Sending nsupdate data failed\n"));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ close(state->nsupdate_ctx->pipefd_to_child);
+ state->nsupdate_ctx->pipefd_to_child = -1;
+}
+
+static void ipa_dyndns_child_handler(int child_status,
+ struct tevent_signal *sige,
+ void *pvt)
+{
+ struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
+
+ if (WEXITSTATUS(child_status) != 0) {
+ DEBUG(1, ("Dynamic DNS child failed with status [%d]\n",
+ child_status));
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static int ipa_dyndns_generic_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+static void fork_nsupdate_done(struct tevent_req *subreq)
+{
+ int ret;
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+
+ ret = ipa_dyndns_generic_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static void ipa_dyndns_gss_tsig_update_done(struct tevent_req *subreq)
+{
+ /* Check the return code from the sigchld handler
+ * and return it to the parent request.
+ */
+ int ret;
+
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+
+ ret = ipa_dyndns_generic_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static void ipa_dyndns_update_done(struct tevent_req *req)
+{
+ int ret = ipa_dyndns_generic_recv(req);
+ talloc_free(req);
+ if (ret != EOK) {
+ DEBUG(1, ("Updating DNS entry failed\n"));
+ return;
+ }
+
+ DEBUG(1,("Updated DNS entry\n"));
+}
diff --git a/src/providers/ipa/ipa_dyndns.h b/src/providers/ipa/ipa_dyndns.h
new file mode 100644
index 000000000..406e8b2f0
--- /dev/null
+++ b/src/providers/ipa/ipa_dyndns.h
@@ -0,0 +1,31 @@
+/*
+ SSSD
+
+ ipa_dyndns.h
+
+ Authors:
+ Stephen Gallagher <sgallagh@redhat.com>
+
+ Copyright (C) 2010 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef IPA_DYNDNS_H_
+#define IPA_DYNDNS_H_
+
+void ipa_dyndns_update(void *pvt);
+
+
+#endif /* IPA_DYNDNS_H_ */
diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c
index 596aecfbd..9c30d9736 100644
--- a/src/providers/ipa/ipa_init.c
+++ b/src/providers/ipa/ipa_init.c
@@ -33,6 +33,7 @@
#include "providers/ipa/ipa_auth.h"
#include "providers/ipa/ipa_access.h"
#include "providers/ipa/ipa_timerules.h"
+#include "providers/ipa/ipa_dyndns.h"
struct ipa_options *ipa_options = NULL;
@@ -96,6 +97,8 @@ int sssm_ipa_id_init(struct be_ctx *bectx,
void **pvt_data)
{
struct sdap_id_ctx *ctx;
+ struct stat stat_buf;
+ errno_t err;
int ret;
if (!ipa_options) {
@@ -127,6 +130,44 @@ int sssm_ipa_id_init(struct be_ctx *bectx,
goto done;
}
+ if(dp_opt_get_bool(ipa_options->basic, IPA_DYNDNS_UPDATE)) {
+ /* Perform automatic DNS updates when the
+ * IP address changes.
+ * Register a callback for successful LDAP
+ * reconnections. This is the easiest way to
+ * identify that we have gone online.
+ */
+
+ /* Ensure that nsupdate exists */
+ errno = 0;
+ ret = stat(NSUPDATE_PATH, &stat_buf);
+ if (ret == -1) {
+ err = errno;
+ if (err == ENOENT) {
+ DEBUG(0, ("%s does not exist. Dynamic DNS updates disabled\n",
+ NSUPDATE_PATH));
+ }
+ else {
+ DEBUG(0, ("Could not set up dynamic DNS updates: [%d][%s]\n",
+ err, strerror(err)));
+ }
+ }
+ else {
+ /* nsupdate is available. Dynamic updates
+ * are supported
+ */
+ ret = be_add_online_cb(ctx, ctx->be,
+ ipa_dyndns_update,
+ ipa_options, NULL);
+ if (ret != EOK) {
+ DEBUG(1,("Failure setting up automatic DNS update\n"));
+ /* We will continue without DNS updating */
+ }
+ }
+ }
+
+
+
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_async_private.h b/src/providers/ldap/sdap_async_private.h
index 75597c6d3..c74a7e60c 100644
--- a/src/providers/ldap/sdap_async_private.h
+++ b/src/providers/ldap/sdap_async_private.h
@@ -34,6 +34,8 @@ void sdap_ldap_result(struct tevent_context *ev, struct tevent_fd *fde,
int setup_ldap_connection_callbacks(struct sdap_handle *sh,
struct tevent_context *ev);
+int get_fd_from_ldap(LDAP *ldap, int *fd);
+
errno_t sdap_set_connected(struct sdap_handle *sh, struct tevent_context *ev);
int sdap_op_add(TALLOC_CTX *memctx, struct tevent_context *ev,
diff --git a/src/providers/ldap/sdap_fd_events.c b/src/providers/ldap/sdap_fd_events.c
index 11527789f..327829630 100644
--- a/src/providers/ldap/sdap_fd_events.c
+++ b/src/providers/ldap/sdap_fd_events.c
@@ -33,6 +33,20 @@ struct sdap_fd_events {
#endif
};
+int get_fd_from_ldap(LDAP *ldap, int *fd)
+{
+ int ret;
+
+ ret = ldap_get_option(ldap, LDAP_OPT_DESC, fd);
+ if (ret != LDAP_OPT_SUCCESS) {
+ DEBUG(1, ("Failed to get fd from ldap!!\n"));
+ *fd = -1;
+ return EIO;
+ }
+
+ return EOK;
+}
+
#ifdef HAVE_LDAP_CONNCB
static int remove_connection_callback(TALLOC_CTX *mem_ctx)
{
@@ -135,20 +149,6 @@ static void sdap_ldap_connect_callback_del(LDAP *ld, Sockbuf *sb,
#else
-static int get_fd_from_ldap(LDAP *ldap, int *fd)
-{
- int ret;
-
- ret = ldap_get_option(ldap, LDAP_OPT_DESC, fd);
- if (ret != LDAP_OPT_SUCCESS) {
- DEBUG(1, ("Failed to get fd from ldap!!\n"));
- *fd = -1;
- return EIO;
- }
-
- return EOK;
-}
-
static int sdap_install_ldap_callbacks(struct sdap_handle *sh,
struct tevent_context *ev)
{