These two patches depend on Sumit's "IPA password migration (master only)" patches to apply cleanly.

--
Stephen Gallagher
RHCE 804006346421761

Delivering value year after year.
Red Hat ranks #1 in value among software vendors.
http://www.redhat.com/promo/vendor/
From cab60245a6187678b8afa133f18d3229dbf9dcf4 Mon Sep 17 00:00:00 2001
From: Stephen Gallagher <sgall...@redhat.com>
Date: Sun, 2 May 2010 08:07:50 -0400
Subject: [PATCH 1/2] Properly set up SIGCHLD handlers

Instead of having all-purpose SIGCHLD handlers that try to catch
every occurrence, we instead create a per-PID handler. This will
allow us to specify callbacks to occur when certain children exit.
---
 src/providers/child_common.c            |  108 +++++++++++++++++++++++++------
 src/providers/child_common.h            |   12 ++++
 src/providers/ipa/ipa_init.c            |   15 ----
 src/providers/krb5/krb5_auth.c          |    6 ++
 src/providers/krb5/krb5_init.c          |    1 -
 src/providers/ldap/sdap_child_helpers.c |   18 ++---
 6 files changed, 115 insertions(+), 45 deletions(-)

diff --git a/src/providers/child_common.c b/src/providers/child_common.c
index b9802557709227a0acad8cfdccbca7898a862a27..a8a4e0409c84a102eda7a2d472bc15708dd94a06 100644
--- a/src/providers/child_common.c
+++ b/src/providers/child_common.c
@@ -33,6 +33,42 @@
 #include "db/sysdb.h"
 #include "providers/child_common.h"
 
+struct sss_child_ctx {
+    struct tevent_signal *sige;
+    pid_t pid;
+    int child_status;
+    sss_child_callback_t cb;
+    void *pvt;
+};
+
+int child_handler_setup(struct tevent_context *ev, int pid,
+                        sss_child_callback_t cb, void *pvt)
+{
+    struct sss_child_ctx *child_ctx;
+
+    DEBUG(8, ("Setting up signal handler up for pid [%d]\n", pid));
+
+    child_ctx = talloc_zero(ev, struct sss_child_ctx);
+    if (child_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    child_ctx->sige = tevent_add_signal(ev, child_ctx, SIGCHLD, SA_SIGINFO,
+                                        child_sig_handler, child_ctx);
+    if(!child_ctx->sige) {
+        /* Error setting up signal handler */
+        talloc_free(child_ctx);
+        return ENOMEM;
+    }
+
+    child_ctx->pid = pid;
+    child_ctx->cb = cb;
+    child_ctx->pvt = pvt;
+
+    DEBUG(8, ("Signal handler set up for pid [%d]\n", pid));
+    return EOK;
+}
+
 /* Async communication with the child process via a pipe */
 
 struct write_pipe_state {
@@ -256,37 +292,71 @@ void fd_nonblocking(int fd)
     return;
 }
 
+static void child_invoke_callback(struct tevent_context *ev,
+                                  struct tevent_immediate *imm,
+                                  void *pvt);
 void child_sig_handler(struct tevent_context *ev,
                        struct tevent_signal *sige, int signum,
                        int count, void *__siginfo, void *pvt)
 {
-    int ret;
-    int child_status;
+    int ret, err;
+    struct sss_child_ctx *child_ctx;
+    struct tevent_immediate *imm;
 
-    DEBUG(7, ("Waiting for [%d] childeren.\n", count));
-    do {
-        errno = 0;
-        ret = waitpid(-1, &child_status, WNOHANG);
+    if (count <= 0) {
+        DEBUG(0, ("SIGCHLD handler called with invalid child count\n"));
+        return;
+    }
 
-        if (ret == -1) {
-            DEBUG(1, ("waitpid failed [%d][%s].\n", errno, strerror(errno)));
-        } else if (ret == 0) {
-            DEBUG(1, ("waitpid did not found a child with changed status.\n"));
-        } else  {
-            if (WEXITSTATUS(child_status) != 0) {
-                DEBUG(1, ("child [%d] failed with status [%d].\n", ret,
-                          child_status));
-            } else {
-                DEBUG(4, ("child [%d] finished successful.\n", ret));
-            }
+    child_ctx = talloc_get_type(pvt, struct sss_child_ctx);
+    DEBUG(7, ("Waiting for child [%d].\n", child_ctx->pid));
+
+    errno = 0;
+    ret = waitpid(child_ctx->pid, &child_ctx->child_status, WNOHANG);
+
+    if (ret == -1) {
+        err = errno;
+        DEBUG(1, ("waitpid failed [%d][%s].\n", err, strerror(err)));
+    } else if (ret == 0) {
+        DEBUG(1, ("waitpid did not found a child with changed status.\n"));
+    } else if WIFEXITED(child_ctx->child_status) {
+        if (WEXITSTATUS(child_ctx->child_status) != 0) {
+            DEBUG(1, ("child [%d] failed with status [%d].\n", ret,
+                      child_ctx->child_status));
+        } else {
+            DEBUG(4, ("child [%d] finished successfully.\n", ret));
+        }
+
+        /* Invoke the callback in a tevent_immediate handler
+         * so that it is safe to free the tevent_signal *
+         */
+        imm = tevent_create_immediate(ev);
+        if (imm == NULL) {
+            DEBUG(0, ("Out of memory invoking sig handler callback\n"));
+            return;
         }
 
-        --count;
-    } while (count < 0);
+        tevent_schedule_immediate(imm, ev,child_invoke_callback,
+                                  child_ctx);
+    }
 
     return;
 }
 
+static void child_invoke_callback(struct tevent_context *ev,
+                                  struct tevent_immediate *imm,
+                                  void *pvt)
+{
+    struct sss_child_ctx *child_ctx =
+            talloc_get_type(pvt, struct sss_child_ctx);
+    if (child_ctx->cb) {
+        child_ctx->cb(child_ctx->child_status, child_ctx->sige, child_ctx->pvt);
+    }
+
+    /* Stop monitoring for this child */
+    talloc_free(child_ctx);
+}
+
 static errno_t prepare_child_argv(TALLOC_CTX *mem_ctx,
                                   int child_debug_fd,
                                   const char *binary,
diff --git a/src/providers/child_common.h b/src/providers/child_common.h
index 0b2081d2d2b47284fd1032a17f715fb4c5d51ecc..22a77dbbe28c385c7c0e49ce8a339a68028b3658 100644
--- a/src/providers/child_common.h
+++ b/src/providers/child_common.h
@@ -45,6 +45,18 @@ struct io_buffer {
     size_t size;
 };
 
+/* Callback to be invoked when a sigchld handler is called.
+ * The tevent_signal * associated with the handler will be
+ * freed automatically when this function returns.
+ */
+typedef void (*sss_child_callback_t)(int child_status,
+                                     struct tevent_signal *sige,
+                                     void *pvt);
+
+/* Set up child termination signal handler */
+int child_handler_setup(struct tevent_context *ev, int pid,
+                        sss_child_callback_t cb, void *pvt);
+
 /* Async communication with the child process via a pipe */
 struct tevent_req *write_pipe_send(TALLOC_CTX *mem_ctx,
                                    struct tevent_context *ev,
diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c
index d2f9b3dcbb263161971dd757b34f1bea1d22d61b..cb178c8777d6eebd0fa78375062d0700e825de4f 100644
--- a/src/providers/ipa/ipa_init.c
+++ b/src/providers/ipa/ipa_init.c
@@ -164,7 +164,6 @@ int sssm_ipa_auth_init(struct be_ctx *bectx,
     struct ipa_auth_ctx *ipa_auth_ctx;
     struct krb5_ctx *krb5_auth_ctx;
     struct sdap_auth_ctx *sdap_auth_ctx;
-    struct tevent_signal *sige;
     FILE *debug_filep;
     unsigned v;
     int ret;
@@ -238,20 +237,6 @@ int sssm_ipa_auth_init(struct be_ctx *bectx,
         goto done;
     }
 
-    if (ipa_options->id_ctx == NULL) {
-        DEBUG(9, ("Adding SIGCHLD handler for Kerberos child.\n"));
-        sige = tevent_add_signal(bectx->ev, krb5_auth_ctx, SIGCHLD, SA_SIGINFO,
-                                 child_sig_handler, NULL);
-        if (sige == NULL) {
-            DEBUG(1, ("tevent_add_signal failed.\n"));
-            ret = ENOMEM;
-            goto done;
-        }
-    } else {
-        DEBUG(9, ("IPA id provider already initialized, "
-                  "assuming that a SIGCHLD handler is already in place.\n"));
-    }
-
     if (debug_to_file != 0) {
         ret = open_debug_file_ex("krb5_child", &debug_filep);
         if (ret != EOK) {
diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index a3ae3942841cefb76ab5752bfb7ea7aabc27d5c2..e16bc3933d4e3547c8dc3a04f892269434822393 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -467,6 +467,12 @@ static errno_t fork_child(struct tevent_req *req, struct tevent_context *ev,
         fd_nonblocking(kr->read_from_child_fd);
         fd_nonblocking(kr->write_to_child_fd);
 
+        ret = child_handler_setup(ev, pid, NULL, NULL);
+        if (ret != EOK) {
+            DEBUG(1, ("Could not set up child signal handler\n"));
+            return ret;
+        }
+
         err = activate_child_timeout_handler(req, ev, kr);
         if (err != EOK) {
             DEBUG(1, ("activate_child_timeout_handler failed.\n"));
diff --git a/src/providers/krb5/krb5_init.c b/src/providers/krb5/krb5_init.c
index 03d95260777e67d94bae8955e637c6cb2050e240..0ad589268989e3ab8edfdf44284627b116373d4e 100644
--- a/src/providers/krb5/krb5_init.c
+++ b/src/providers/krb5/krb5_init.c
@@ -141,7 +141,6 @@ int sssm_krb5_auth_init(struct be_ctx *bectx,
         goto fail;
     }
     talloc_steal(sige, sig_realm);
-
     if (debug_to_file != 0) {
         ret = open_debug_file_ex("krb5_child", &debug_filep);
         if (ret != EOK) {
diff --git a/src/providers/ldap/sdap_child_helpers.c b/src/providers/ldap/sdap_child_helpers.c
index 273fc67873ed6029a4913b96df602dff4a45dd7b..c61f3ccebd1ddf2c42443b60c84022c179d868b7 100644
--- a/src/providers/ldap/sdap_child_helpers.c
+++ b/src/providers/ldap/sdap_child_helpers.c
@@ -77,7 +77,8 @@ static int sdap_child_destructor(void *ptr)
     return 0;
 }
 
-static errno_t sdap_fork_child(struct sdap_child *child)
+static errno_t sdap_fork_child(struct tevent_context *ev,
+                               struct sdap_child *child)
 {
     int pipefd_to_child[2];
     int pipefd_from_child[2];
@@ -118,6 +119,11 @@ static errno_t sdap_fork_child(struct sdap_child *child)
         fd_nonblocking(child->read_from_child_fd);
         fd_nonblocking(child->write_to_child_fd);
 
+        ret = child_handler_setup(ev, pid, NULL, NULL);
+        if (ret != EOK) {
+            return ret;
+        }
+
     } else { /* error */
         err = errno;
         DEBUG(1, ("fork failed [%d][%s].\n", err, strerror(err)));
@@ -275,7 +281,7 @@ struct tevent_req *sdap_get_tgt_send(TALLOC_CTX *mem_ctx,
         goto fail;
     }
 
-    ret = sdap_fork_child(state->child);
+    ret = sdap_fork_child(state->ev, state->child);
     if (ret != EOK) {
         DEBUG(1, ("sdap_fork_child failed.\n"));
         goto fail;
@@ -422,7 +428,6 @@ int setup_child(struct sdap_id_ctx *ctx)
 {
     int ret;
     const char *mech;
-    struct tevent_signal *sige;
     unsigned v;
     FILE *debug_filep;
 
@@ -432,13 +437,6 @@ int setup_child(struct sdap_id_ctx *ctx)
         return EOK;
     }
 
-    sige = tevent_add_signal(ctx->be->ev, ctx, SIGCHLD, SA_SIGINFO,
-                             child_sig_handler, NULL);
-    if (sige == NULL) {
-        DEBUG(1, ("tevent_add_signal failed.\n"));
-        return ENOMEM;
-    }
-
     if (debug_to_file != 0 && ldap_child_debug_fd == -1) {
         ret = open_debug_file_ex("ldap_child", &debug_filep);
         if (ret != EOK) {
-- 
1.7.0.1

From 5144df6d843ff5e0df7db4d8b8daa53631dff0a2 Mon Sep 17 00:00:00 2001
From: Stephen Gallagher <sgall...@redhat.com>
Date: Sun, 2 May 2010 07:48:26 -0400
Subject: [PATCH 2/2] Add dynamic DNS updates to FreeIPA

This adds two new options:

ipa_dyndns_update: Boolean value to select whether this client
should automatically update its IP address in FreeIPA DNS.

ipa_dyndns_iface: Choose an interface manually to use for
updating dynamic DNS. Default is to use the interface associated
with the LDAP connection to FreeIPA.

This patch supports A and AAAA records. It relies on the presence
of the nsupdate tool from the bind-utils package to perform the
actual update step. The location of this utility is set at build
time, but its availability is determined at runtime (so clients
that do not require dynamic update capability do not need to meet
this dependency).
---
 contrib/sssd.spec.in                           |    1 +
 src/Makefile.am                                |    2 +
 src/config/SSSDConfig.py                       |    2 +
 src/config/etc/sssd.api.d/sssd-ipa.conf        |    2 +
 src/configure.ac                               |    1 +
 src/external/nsupdate.m4                       |    8 +
 src/man/sssd-ipa.5.xml                         |   28 ++
 src/providers/ipa/ipa_common.c                 |    2 +
 src/providers/ipa/ipa_common.h                 |    2 +
 src/providers/ipa/ipa_dyndns.c                 |  580 ++++++++++++++++++++++++
 src/providers/ipa/{ipa_auth.h => ipa_dyndns.h} |   15 +-
 src/providers/ipa/ipa_init.c                   |   41 ++
 src/providers/ldap/sdap_async_private.h        |    2 +
 src/providers/ldap/sdap_fd_events.c            |   28 +-
 14 files changed, 692 insertions(+), 22 deletions(-)
 create mode 100644 src/external/nsupdate.m4
 create mode 100644 src/providers/ipa/ipa_dyndns.c
 copy src/providers/ipa/{ipa_auth.h => ipa_dyndns.h} (73%)

diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
index 7836cca60f45e88cda105f6e0b575cf1cecdbfd0..2c187a01ad0f2cd0e34c64432b0accbb2d324148 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 5fb406234abe778413f37044bd0c34aed1cbb176..90b382b009cf4e68442908485520976a999aee08 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 18df97904de5128d34e4de7c36db1a4743f916d6..ef16bb973382b55757911dbe6bce51233409b6c0 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 f71498cc2847cd09b8a78be3972b3ae171adeea6..7fbc312d9e3a603ec4b226aaea1622dab3855659 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 93debb60abe69456551b8d3d77068875bfae4815..4cc5d853f40e67d6c7e4d44e90a569ea7dc9667c 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 0000000000000000000000000000000000000000..6e18f017baf603d48c87951f1c445af9413e9506
--- /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 103558b0478140f09f7f60f3c408a2faf95f2b6c..95f8613da0d4fea97e004ef1704ff7a9883755d2 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 aa84e7a94936778cb38311e785508afa127ddb19..03c0236798bba23dea960d7b701649d001352575 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 f53c022e51b07cf8d0bb279b6bb956009e780a54..14bd971e9993cd64f96200b35915524e647b6cf9 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 0000000000000000000000000000000000000000..b1edc194c71a3d6c1bb5ae048df50a624d2448bf
--- /dev/null
+++ b/src/providers/ipa/ipa_dyndns.c
@@ -0,0 +1,580 @@
+/*
+    SSSD
+
+    ipa_dyndns.c
+
+    Authors:
+        Stephen Gallagher <sgall...@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_auth.h b/src/providers/ipa/ipa_dyndns.h
similarity index 73%
copy from src/providers/ipa/ipa_auth.h
copy to src/providers/ipa/ipa_dyndns.h
index 3079bbd1b8ffc21cc6c6b5b945717bc4e93f5f25..406e8b2f0cce0931f3babd1120655718622c871b 100644
--- a/src/providers/ipa/ipa_auth.h
+++ b/src/providers/ipa/ipa_dyndns.h
@@ -1,12 +1,12 @@
 /*
     SSSD
 
-    IPA Backend Module -- Authentication
+    ipa_dyndns.h
 
     Authors:
-        Sumit Bose <sb...@redhat.com>
+        Stephen Gallagher <sgall...@redhat.com>
 
-    Copyright (C) 2009 Red Hat
+    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
@@ -22,11 +22,10 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-#ifndef _IPA_AUTH_H_
-#define _IPA_AUTH_H_
+#ifndef IPA_DYNDNS_H_
+#define IPA_DYNDNS_H_
 
-#include "providers/dp_backend.h"
+void ipa_dyndns_update(void *pvt);
 
-void ipa_auth(struct be_req *be_req);
 
-#endif /* _IPA_AUTH_H_ */
+#endif /* IPA_DYNDNS_H_ */
diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c
index cb178c8777d6eebd0fa78375062d0700e825de4f..2f0ccf0fafbf455bc173e777eec429c5ee645293 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 75597c6d3ed1380971029e2a3b4174fcb7155c27..c74a7e60c38f752af108b7cb00ff1e019f9b0d9d 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 11527789f6e2834883b85efc983b57b7eb1c0a37..3278296308fcb1a745dbd4cb89a4cc0c05aa065b 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)
 {
-- 
1.7.0.1

_______________________________________________
sssd-devel mailing list
sssd-devel@lists.fedorahosted.org
https://fedorahosted.org/mailman/listinfo/sssd-devel

Reply via email to