]> granicus.if.org Git - sudo/commitdiff
Reorder things to avoid the need to declare static functions.
authorTodd C. Miller <Todd.Miller@sudo.ws>
Mon, 28 May 2018 13:35:51 +0000 (07:35 -0600)
committerTodd C. Miller <Todd.Miller@sudo.ws>
Mon, 28 May 2018 13:35:51 +0000 (07:35 -0600)
plugins/sudoers/file.c
plugins/sudoers/ldap.c
plugins/sudoers/sssd.c

index c604f47b29840f5b3ba73f5edce23fe06bca4cd9..71e498392d058a9288aac0c7dab28d3b7c7abb4d 100644 (file)
 #include "sudo_lbuf.h"
 #include <gram.h>
 
-/*
- * Local prototypes.
- */
-static int sudo_file_close(struct sudo_nss *);
-static int sudo_file_open(struct sudo_nss *);
-static int sudo_file_parse(struct sudo_nss *);
-static int sudo_file_query(struct sudo_nss *, struct passwd *pw);
-static int sudo_file_getdefs(struct sudo_nss *);
-
-/* sudo_nss implementation */
-struct sudo_nss sudo_nss_file = {
-    { NULL, NULL },
-    sudo_file_open,
-    sudo_file_close,
-    sudo_file_parse,
-    sudo_file_query,
-    sudo_file_getdefs
-};
-
 static int
 sudo_file_open(struct sudo_nss *nss)
 {
@@ -134,3 +115,13 @@ sudo_file_getdefs(struct sudo_nss *nss)
     debug_decl(sudo_file_getdefs, SUDOERS_DEBUG_NSS)
     debug_return_int(0);
 }
+
+/* sudo_nss implementation */
+struct sudo_nss sudo_nss_file = {
+    { NULL, NULL },
+    sudo_file_open,
+    sudo_file_close,
+    sudo_file_parse,
+    sudo_file_query,
+    sudo_file_getdefs
+};
index f0b0b36d98133d86ec3591952c2f4091b6f5b675..c3f34f401b57a43d43a59ce6024c787622531f57 100644 (file)
@@ -144,16 +144,6 @@ struct ldap_netgroup {
 };
 STAILQ_HEAD(ldap_netgroup_list, ldap_netgroup);
 
-/* sudo_nss implementation */
-static int sudo_ldap_open(struct sudo_nss *nss);
-static int sudo_ldap_close(struct sudo_nss *nss);
-static int sudo_ldap_parse(struct sudo_nss *nss);
-static int sudo_ldap_query(struct sudo_nss *nss, struct passwd *pw);
-static int sudo_ldap_getdefs(struct sudo_nss *nss);
-static struct ldap_result *sudo_ldap_result_get(struct sudo_nss *nss,
-    struct passwd *pw);
-static char *sudo_ldap_get_first_rdn(LDAP *ld, LDAPMessage *entry);
-
 /*
  * LDAP sudo_nss handle.
  * We store the connection to the LDAP server and the passwd struct of the
@@ -164,15 +154,6 @@ struct sudo_ldap_handle {
     struct passwd *pw;
 };
 
-struct sudo_nss sudo_nss_ldap = {
-    { NULL, NULL },
-    sudo_ldap_open,
-    sudo_ldap_close,
-    sudo_ldap_parse,
-    sudo_ldap_query,
-    sudo_ldap_getdefs
-};
-
 #ifdef HAVE_LDAP_INITIALIZE
 static char *
 sudo_ldap_join_uri(struct ldap_config_str_list *uri_list)
@@ -369,6 +350,37 @@ sudo_ldap_check_non_unix_group(LDAP *ld, LDAPMessage *entry, struct passwd *pw)
     debug_return_bool(ret);
 }
 
+/*
+ * Extract the dn from an entry and return the first rdn from it.
+ */
+static char *
+sudo_ldap_get_first_rdn(LDAP *ld, LDAPMessage *entry)
+{
+#ifdef HAVE_LDAP_STR2DN
+    char *dn, *rdn = NULL;
+    LDAPDN tmpDN;
+    debug_decl(sudo_ldap_get_first_rdn, SUDOERS_DEBUG_LDAP)
+
+    if ((dn = ldap_get_dn(ld, entry)) == NULL)
+       debug_return_str(NULL);
+    if (ldap_str2dn(dn, &tmpDN, LDAP_DN_FORMAT_LDAP) == LDAP_SUCCESS) {
+       ldap_rdn2str(tmpDN[0], &rdn, LDAP_DN_FORMAT_UFN);
+       ldap_dnfree(tmpDN);
+    }
+    ldap_memfree(dn);
+    debug_return_str(rdn);
+#else
+    char *dn, **edn;
+    debug_decl(sudo_ldap_get_first_rdn, SUDOERS_DEBUG_LDAP)
+
+    if ((dn = ldap_get_dn(ld, entry)) == NULL)
+       debug_return_str(NULL);
+    edn = ldap_explode_dn(dn, 1);
+    ldap_memfree(dn);
+    debug_return_str(edn ? edn[0] : NULL);
+#endif
+}
+
 /*
  * Read sudoOption and fill in the defaults list.
  * This is used to parse the cn=defaults entry.
@@ -1106,37 +1118,6 @@ sudo_ldap_build_pass2(void)
     debug_return_str(filt);
 }
 
-/*
- * Extract the dn from an entry and return the first rdn from it.
- */
-static char *
-sudo_ldap_get_first_rdn(LDAP *ld, LDAPMessage *entry)
-{
-#ifdef HAVE_LDAP_STR2DN
-    char *dn, *rdn = NULL;
-    LDAPDN tmpDN;
-    debug_decl(sudo_ldap_get_first_rdn, SUDOERS_DEBUG_LDAP)
-
-    if ((dn = ldap_get_dn(ld, entry)) == NULL)
-       debug_return_str(NULL);
-    if (ldap_str2dn(dn, &tmpDN, LDAP_DN_FORMAT_LDAP) == LDAP_SUCCESS) {
-       ldap_rdn2str(tmpDN[0], &rdn, LDAP_DN_FORMAT_UFN);
-       ldap_dnfree(tmpDN);
-    }
-    ldap_memfree(dn);
-    debug_return_str(rdn);
-#else
-    char *dn, **edn;
-    debug_decl(sudo_ldap_get_first_rdn, SUDOERS_DEBUG_LDAP)
-
-    if ((dn = ldap_get_dn(ld, entry)) == NULL)
-       debug_return_str(NULL);
-    edn = ldap_explode_dn(dn, 1);
-    ldap_memfree(dn);
-    debug_return_str(edn ? edn[0] : NULL);
-#endif
-}
-
 static char *
 berval_iter(void **vp)
 {
@@ -2055,3 +2036,13 @@ sudo_ldap_result_from_search(LDAP *ldap, LDAPMessage *searchresult)
     return result;
 }
 #endif
+
+/* sudo_nss implementation */
+struct sudo_nss sudo_nss_ldap = {
+    { NULL, NULL },
+    sudo_ldap_open,
+    sudo_ldap_close,
+    sudo_ldap_parse,
+    sudo_ldap_query,
+    sudo_ldap_getdefs
+};
index 9ecdd41095ee62f5c9d845a1adcd9f812a99c6f1..da69f32ec99db63c912ef34eb413e5cb6ec030c9 100644 (file)
@@ -76,8 +76,7 @@ typedef int  (*sss_sudo_get_values_t)(struct sss_sudo_rule*, const char*,
 
 typedef void (*sss_sudo_free_values_t)(char**);
 
-/* sudo_nss implementation */
-
+/* sudo_nss handle */
 struct sudo_sss_handle {
     char *domainname;
     char *ipa_host;
@@ -91,24 +90,6 @@ struct sudo_sss_handle {
     sss_sudo_free_values_t fn_free_values;
 };
 
-static int sudo_sss_open(struct sudo_nss *nss);
-static int sudo_sss_close(struct sudo_nss *nss);
-static int sudo_sss_parse(struct sudo_nss *nss);
-static int sudo_sss_getdefs(struct sudo_nss *nss);
-static int sudo_sss_query(struct sudo_nss *nss, struct passwd *pw);
-
-static bool sudo_sss_parse_options(struct sudo_sss_handle *handle,
-                                  struct sss_sudo_rule *rule,
-                                  struct defaults_list *defs);
-
-
-static struct sss_sudo_result *sudo_sss_result_get(struct sudo_nss *nss,
-                                                  struct passwd *pw);
-
-static bool sss_to_sudoers(struct sudo_sss_handle *handle,
-                          struct sss_sudo_result *sss_result,
-                          struct userspec_list *sss_userspecs);
-
 static int
 get_ipa_hostname(char **shostp, char **lhostp)
 {
@@ -176,253 +157,6 @@ get_ipa_hostname(char **shostp, char **lhostp)
     debug_return_int(ret);
 }
 
-struct sudo_nss sudo_nss_sss = {
-    { NULL, NULL },
-    sudo_sss_open,
-    sudo_sss_close,
-    sudo_sss_parse,
-    sudo_sss_query,
-    sudo_sss_getdefs
-};
-
-/* sudo_nss implementation */
-// ok
-static int
-sudo_sss_open(struct sudo_nss *nss)
-{
-    struct sudo_sss_handle *handle;
-    static const char path[] = _PATH_SSSD_LIB"/libsss_sudo.so";
-    debug_decl(sudo_sss_open, SUDOERS_DEBUG_SSSD);
-
-    /* Create a handle container. */
-    handle = calloc(1, sizeof(struct sudo_sss_handle));
-    if (handle == NULL) {
-       sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
-       debug_return_int(ENOMEM);
-    }
-
-    /* Load symbols */
-    handle->ssslib = sudo_dso_load(path, SUDO_DSO_LAZY);
-    if (handle->ssslib == NULL) {
-       const char *errstr = sudo_dso_strerror();
-       sudo_warnx(U_("unable to load %s: %s"), path,
-           errstr ? errstr : "unknown error");
-       sudo_warnx(U_("unable to initialize SSS source. Is SSSD installed on your machine?"));
-       free(handle);
-       debug_return_int(EFAULT);
-    }
-
-    handle->fn_send_recv =
-       sudo_dso_findsym(handle->ssslib, "sss_sudo_send_recv");
-    if (handle->fn_send_recv == NULL) {
-       sudo_warnx(U_("unable to find symbol \"%s\" in %s"), path,
-          "sss_sudo_send_recv");
-       free(handle);
-       debug_return_int(EFAULT);
-    }
-
-    handle->fn_send_recv_defaults =
-       sudo_dso_findsym(handle->ssslib, "sss_sudo_send_recv_defaults");
-    if (handle->fn_send_recv_defaults == NULL) {
-       sudo_warnx(U_("unable to find symbol \"%s\" in %s"), path,
-          "sss_sudo_send_recv_defaults");
-       free(handle);
-       debug_return_int(EFAULT);
-    }
-
-    handle->fn_free_result =
-       sudo_dso_findsym(handle->ssslib, "sss_sudo_free_result");
-    if (handle->fn_free_result == NULL) {
-       sudo_warnx(U_("unable to find symbol \"%s\" in %s"), path,
-          "sss_sudo_free_result");
-       free(handle);
-       debug_return_int(EFAULT);
-    }
-
-    handle->fn_get_values =
-       sudo_dso_findsym(handle->ssslib, "sss_sudo_get_values");
-    if (handle->fn_get_values == NULL) {
-       sudo_warnx(U_("unable to find symbol \"%s\" in %s"), path,
-          "sss_sudo_get_values");
-       free(handle);
-       debug_return_int(EFAULT);
-    }
-
-    handle->fn_free_values =
-       sudo_dso_findsym(handle->ssslib, "sss_sudo_free_values");
-    if (handle->fn_free_values == NULL) {
-       sudo_warnx(U_("unable to find symbol \"%s\" in %s"), path,
-          "sss_sudo_free_values");
-       free(handle);
-       debug_return_int(EFAULT);
-    }
-
-    nss->handle = handle;
-
-    /*
-     * If runhost is the same as the local host, check for ipa_hostname
-     * in sssd.conf and use it in preference to user_runhost.
-     */
-    if (strcasecmp(user_runhost, user_host) == 0) {
-       if (get_ipa_hostname(&handle->ipa_shost, &handle->ipa_host) == -1) {
-           free(handle);
-           debug_return_int(ENOMEM);
-       }
-    }
-
-    sudo_debug_printf(SUDO_DEBUG_DEBUG, "handle=%p", handle);
-
-    debug_return_int(0);
-}
-
-// ok
-static int
-sudo_sss_close(struct sudo_nss *nss)
-{
-    struct sudo_sss_handle *handle;
-    debug_decl(sudo_sss_close, SUDOERS_DEBUG_SSSD);
-
-    if (nss && nss->handle) {
-       handle = nss->handle;
-       sudo_dso_unload(handle->ssslib);
-       if (handle->pw != NULL)
-           sudo_pw_delref(handle->pw);
-       free(handle->ipa_host);
-       if (handle->ipa_host != handle->ipa_shost)
-           free(handle->ipa_shost);
-       free(handle);
-       nss->handle = NULL;
-
-       /* XXX - do in main module? */
-       free_userspecs(&nss->userspecs);
-       free_defaults(&nss->defaults);
-    }
-    debug_return_int(0);
-}
-
-/*
- * Perform query for user and host and convert to sudoers parse tree.
- */
-static int
-sudo_sss_query(struct sudo_nss *nss, struct passwd *pw)
-{
-    struct sudo_sss_handle *handle = nss->handle;
-    struct sss_sudo_result *sss_result = NULL;
-    int ret = 0;
-    debug_decl(sudo_sss_query, SUDOERS_DEBUG_SSSD);
-
-    /* Use cached result if it matches pw. */
-    if (handle->pw != NULL) {
-       if (pw == handle->pw)
-           goto done;
-       sudo_pw_delref(handle->pw);
-       handle->pw = NULL;
-    }
-
-    /* Free old userspecs, if any. */
-    free_userspecs(&nss->userspecs);
-
-    /* Fetch list of sudoRole entries that match user and host. */
-    sss_result = sudo_sss_result_get(nss, pw);
-
-    sudo_debug_printf(SUDO_DEBUG_DIAG,
-       "searching SSSD/LDAP for sudoers entries for user %s, host %s",
-        pw->pw_name, user_runhost);
-
-    if (sss_result == NULL)
-       goto done;
-
-    /* Stash a ref to the passwd struct in the handle. */
-    sudo_pw_addref(pw);
-    handle->pw = pw;
-
-    /* Convert to sudoers parse tree. */
-    if (!sss_to_sudoers(handle, sss_result, &nss->userspecs)) {
-       ret = -1;
-       goto done;
-    }
-
-done:
-    /* Cleanup */
-    handle->fn_free_result(sss_result);
-    if (ret == -1) {
-       free_userspecs(&nss->userspecs);
-       sudo_pw_delref(handle->pw);
-       handle->pw = NULL;
-    }
-
-    sudo_debug_printf(SUDO_DEBUG_DIAG, "Done with LDAP searches");
-
-    debug_return_int(ret);
-}
-
-// ok
-static int
-sudo_sss_parse(struct sudo_nss *nss)
-{
-    debug_decl(sudo_sss_parse, SUDOERS_DEBUG_SSSD);
-    debug_return_int(0);
-}
-
-static int
-sudo_sss_getdefs(struct sudo_nss *nss)
-{
-    struct sudo_sss_handle *handle = nss->handle;
-    struct sss_sudo_result *sss_result = NULL;
-    struct sss_sudo_rule   *sss_rule;
-    uint32_t sss_error;
-    unsigned int i;
-    int rc;
-    debug_decl(sudo_sss_getdefs, SUDOERS_DEBUG_SSSD);
-
-    if (handle == NULL)
-       debug_return_int(-1);
-
-    /* Free old defaults, if any. */
-    free_defaults(&nss->defaults);
-
-    sudo_debug_printf(SUDO_DEBUG_DIAG, "Looking for cn=defaults");
-
-    /* NOTE: these are global defaults, user ID and name are not used. */
-    rc = handle->fn_send_recv_defaults(sudo_user.pw->pw_uid,
-       sudo_user.pw->pw_name, &sss_error, &handle->domainname, &sss_result);
-    switch (rc) {
-    case 0:
-       break;
-    case ENOMEM:
-       sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
-       /* FALLTHROUGH */
-    default:
-       sudo_debug_printf(SUDO_DEBUG_INFO,
-           "handle->fn_send_recv_defaults: rc=%d, sss_error=%u", rc, sss_error);
-       debug_return_int(-1);
-    }
-    if (sss_error != 0) {
-       if (sss_error == ENOENT) {
-           sudo_debug_printf(SUDO_DEBUG_INFO,
-               "The user was not found in SSSD.");
-           goto done;
-       }
-       sudo_debug_printf(SUDO_DEBUG_INFO, "sss_error=%u\n", sss_error);
-       goto bad;
-    }
-
-    for (i = 0; i < sss_result->num_rules; ++i) {
-        sudo_debug_printf(SUDO_DEBUG_DIAG,
-           "Parsing cn=defaults, %d/%d", i, sss_result->num_rules);
-        sss_rule = sss_result->rules + i;
-        if (!sudo_sss_parse_options(handle, sss_rule, &nss->defaults))
-           goto bad;
-    }
-
-done:
-    handle->fn_free_result(sss_result);
-    debug_return_int(0);
-bad:
-    handle->fn_free_result(sss_result);
-    debug_return_int(-1);
-}
-
 /*
  * SSSD doesn't handle netgroups, we have to ensure they are correctly filtered
  * in sudo. The rules may contain mixed sudoUser specification so we have to
@@ -488,125 +222,6 @@ sudo_sss_check_user(struct sudo_sss_handle *handle, struct sss_sudo_rule *rule)
     debug_return_bool(ret);
 }
 
-static struct sss_sudo_result *
-sudo_sss_result_get(struct sudo_nss *nss, struct passwd *pw)
-{
-    struct sudo_sss_handle *handle = nss->handle;
-    struct sss_sudo_result *sss_result = NULL;
-    uint32_t sss_error = 0, rc;
-    debug_decl(sudo_sss_result_get, SUDOERS_DEBUG_SSSD);
-
-    sudo_debug_printf(SUDO_DEBUG_DIAG, "  username=%s", pw->pw_name);
-    sudo_debug_printf(SUDO_DEBUG_DIAG, "domainname=%s",
-       handle->domainname ? handle->domainname : "NULL");
-
-    rc = handle->fn_send_recv(pw->pw_uid, pw->pw_name,
-       handle->domainname, &sss_error, &sss_result);
-    switch (rc) {
-    case 0:
-       switch (sss_error) {
-       case 0:
-           if (sss_result != NULL) {
-               sudo_debug_printf(SUDO_DEBUG_INFO, "Received %u rule(s)",
-                   sss_result->num_rules);
-           } else {
-               sudo_debug_printf(SUDO_DEBUG_INFO,
-                   "Internal error: sss_result == NULL && sss_error == 0");
-               debug_return_ptr(NULL);
-           }
-           break;
-       case ENOENT:
-           sudo_debug_printf(SUDO_DEBUG_INFO, "The user was not found in SSSD.");
-           debug_return_ptr(NULL);
-       default:
-           sudo_debug_printf(SUDO_DEBUG_INFO, "sss_error=%u\n", sss_error);
-           debug_return_ptr(NULL);
-       }
-       break;
-    case ENOMEM:
-       sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
-       /* FALLTHROUGH */
-    default:
-       sudo_debug_printf(SUDO_DEBUG_INFO,
-           "handle->fn_send_recv: rc=%d", rc);
-       debug_return_ptr(NULL);
-    }
-
-    debug_return_ptr(sss_result);
-}
-
-static bool
-sudo_sss_parse_options(struct sudo_sss_handle *handle, struct sss_sudo_rule *rule, struct defaults_list *defs)
-{
-    int i;
-    char *source = NULL;
-    bool ret = false;
-    char **val_array = NULL;
-    char **cn_array = NULL;
-    debug_decl(sudo_sss_parse_options, SUDOERS_DEBUG_SSSD);
-
-    if (rule == NULL)
-       debug_return_bool(true);
-
-    switch (handle->fn_get_values(rule, "sudoOption", &val_array)) {
-    case 0:
-       break;
-    case ENOENT:
-       sudo_debug_printf(SUDO_DEBUG_INFO, "No result.");
-       debug_return_bool(true);
-    case ENOMEM:
-       goto oom;
-    default:
-       sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_get_values(sudoOption): != 0");
-       debug_return_bool(false);
-    }
-
-    /* Use sudoRole in place of file name in defaults. */
-    if (handle->fn_get_values(rule, "cn", &cn_array) == 0) {
-       if (cn_array[0] != NULL) {
-           char *cp;
-           if (asprintf(&cp, "sudoRole %s", cn_array[0]) == -1)
-               goto oom;
-           source = rcstr_dup(cp);
-           free(cp);
-           if (source == NULL)
-               goto oom;
-       }
-       handle->fn_free_values(cn_array);
-       cn_array = NULL;
-    }
-    if (source == NULL) {
-       if ((source = rcstr_dup("sudoRole UNKNOWN")) == NULL)
-           goto oom;
-    }
-
-    /* Walk through options, appending to defs. */
-    for (i = 0; val_array[i] != NULL; i++) {
-       char *copy, *var, *val;
-       int op;
-
-       /* XXX - should not need to copy */
-       if ((copy = strdup(val_array[i])) == NULL)
-           goto oom;
-       op = sudo_ldap_parse_option(copy, &var, &val);
-       if (!sudo_ldap_add_default(var, val, op, source, defs)) {
-           free(copy);
-           goto oom;
-       }
-       free(copy);
-    }
-    ret = true;
-    goto done;
-
-oom:
-    sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
-
-done:
-    rcstr_delref(source);
-    handle->fn_free_values(val_array);
-    debug_return_bool(ret);
-}
-
 static char *
 val_array_iter(void **vp)
 {
@@ -618,7 +233,8 @@ val_array_iter(void **vp)
 }
 
 static bool
-sss_to_sudoers(struct sudo_sss_handle *handle, struct sss_sudo_result *sss_result, struct userspec_list *sss_userspecs)
+sss_to_sudoers(struct sudo_sss_handle *handle,
+    struct sss_sudo_result *sss_result, struct userspec_list *sss_userspecs)
 {
     struct userspec *us;
     struct member *m;
@@ -771,4 +387,372 @@ oom:
     free_userspecs(sss_userspecs);
     debug_return_bool(false);
 }
+
+static bool
+sudo_sss_parse_options(struct sudo_sss_handle *handle, struct sss_sudo_rule *rule, struct defaults_list *defs)
+{
+    int i;
+    char *source = NULL;
+    bool ret = false;
+    char **val_array = NULL;
+    char **cn_array = NULL;
+    debug_decl(sudo_sss_parse_options, SUDOERS_DEBUG_SSSD);
+
+    if (rule == NULL)
+       debug_return_bool(true);
+
+    switch (handle->fn_get_values(rule, "sudoOption", &val_array)) {
+    case 0:
+       break;
+    case ENOENT:
+       sudo_debug_printf(SUDO_DEBUG_INFO, "No result.");
+       debug_return_bool(true);
+    case ENOMEM:
+       goto oom;
+    default:
+       sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_get_values(sudoOption): != 0");
+       debug_return_bool(false);
+    }
+
+    /* Use sudoRole in place of file name in defaults. */
+    if (handle->fn_get_values(rule, "cn", &cn_array) == 0) {
+       if (cn_array[0] != NULL) {
+           char *cp;
+           if (asprintf(&cp, "sudoRole %s", cn_array[0]) == -1)
+               goto oom;
+           source = rcstr_dup(cp);
+           free(cp);
+           if (source == NULL)
+               goto oom;
+       }
+       handle->fn_free_values(cn_array);
+       cn_array = NULL;
+    }
+    if (source == NULL) {
+       if ((source = rcstr_dup("sudoRole UNKNOWN")) == NULL)
+           goto oom;
+    }
+
+    /* Walk through options, appending to defs. */
+    for (i = 0; val_array[i] != NULL; i++) {
+       char *copy, *var, *val;
+       int op;
+
+       /* XXX - should not need to copy */
+       if ((copy = strdup(val_array[i])) == NULL)
+           goto oom;
+       op = sudo_ldap_parse_option(copy, &var, &val);
+       if (!sudo_ldap_add_default(var, val, op, source, defs)) {
+           free(copy);
+           goto oom;
+       }
+       free(copy);
+    }
+    ret = true;
+    goto done;
+
+oom:
+    sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+
+done:
+    rcstr_delref(source);
+    handle->fn_free_values(val_array);
+    debug_return_bool(ret);
+}
+
+static struct sss_sudo_result *
+sudo_sss_result_get(struct sudo_nss *nss, struct passwd *pw)
+{
+    struct sudo_sss_handle *handle = nss->handle;
+    struct sss_sudo_result *sss_result = NULL;
+    uint32_t sss_error = 0, rc;
+    debug_decl(sudo_sss_result_get, SUDOERS_DEBUG_SSSD);
+
+    sudo_debug_printf(SUDO_DEBUG_DIAG, "  username=%s", pw->pw_name);
+    sudo_debug_printf(SUDO_DEBUG_DIAG, "domainname=%s",
+       handle->domainname ? handle->domainname : "NULL");
+
+    rc = handle->fn_send_recv(pw->pw_uid, pw->pw_name,
+       handle->domainname, &sss_error, &sss_result);
+    switch (rc) {
+    case 0:
+       switch (sss_error) {
+       case 0:
+           if (sss_result != NULL) {
+               sudo_debug_printf(SUDO_DEBUG_INFO, "Received %u rule(s)",
+                   sss_result->num_rules);
+           } else {
+               sudo_debug_printf(SUDO_DEBUG_INFO,
+                   "Internal error: sss_result == NULL && sss_error == 0");
+               debug_return_ptr(NULL);
+           }
+           break;
+       case ENOENT:
+           sudo_debug_printf(SUDO_DEBUG_INFO, "The user was not found in SSSD.");
+           debug_return_ptr(NULL);
+       default:
+           sudo_debug_printf(SUDO_DEBUG_INFO, "sss_error=%u\n", sss_error);
+           debug_return_ptr(NULL);
+       }
+       break;
+    case ENOMEM:
+       sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+       /* FALLTHROUGH */
+    default:
+       sudo_debug_printf(SUDO_DEBUG_INFO,
+           "handle->fn_send_recv: rc=%d", rc);
+       debug_return_ptr(NULL);
+    }
+
+    debug_return_ptr(sss_result);
+}
+
+/* sudo_nss implementation */
+// ok
+static int
+sudo_sss_open(struct sudo_nss *nss)
+{
+    struct sudo_sss_handle *handle;
+    static const char path[] = _PATH_SSSD_LIB"/libsss_sudo.so";
+    debug_decl(sudo_sss_open, SUDOERS_DEBUG_SSSD);
+
+    /* Create a handle container. */
+    handle = calloc(1, sizeof(struct sudo_sss_handle));
+    if (handle == NULL) {
+       sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+       debug_return_int(ENOMEM);
+    }
+
+    /* Load symbols */
+    handle->ssslib = sudo_dso_load(path, SUDO_DSO_LAZY);
+    if (handle->ssslib == NULL) {
+       const char *errstr = sudo_dso_strerror();
+       sudo_warnx(U_("unable to load %s: %s"), path,
+           errstr ? errstr : "unknown error");
+       sudo_warnx(U_("unable to initialize SSS source. Is SSSD installed on your machine?"));
+       free(handle);
+       debug_return_int(EFAULT);
+    }
+
+    handle->fn_send_recv =
+       sudo_dso_findsym(handle->ssslib, "sss_sudo_send_recv");
+    if (handle->fn_send_recv == NULL) {
+       sudo_warnx(U_("unable to find symbol \"%s\" in %s"), path,
+          "sss_sudo_send_recv");
+       free(handle);
+       debug_return_int(EFAULT);
+    }
+
+    handle->fn_send_recv_defaults =
+       sudo_dso_findsym(handle->ssslib, "sss_sudo_send_recv_defaults");
+    if (handle->fn_send_recv_defaults == NULL) {
+       sudo_warnx(U_("unable to find symbol \"%s\" in %s"), path,
+          "sss_sudo_send_recv_defaults");
+       free(handle);
+       debug_return_int(EFAULT);
+    }
+
+    handle->fn_free_result =
+       sudo_dso_findsym(handle->ssslib, "sss_sudo_free_result");
+    if (handle->fn_free_result == NULL) {
+       sudo_warnx(U_("unable to find symbol \"%s\" in %s"), path,
+          "sss_sudo_free_result");
+       free(handle);
+       debug_return_int(EFAULT);
+    }
+
+    handle->fn_get_values =
+       sudo_dso_findsym(handle->ssslib, "sss_sudo_get_values");
+    if (handle->fn_get_values == NULL) {
+       sudo_warnx(U_("unable to find symbol \"%s\" in %s"), path,
+          "sss_sudo_get_values");
+       free(handle);
+       debug_return_int(EFAULT);
+    }
+
+    handle->fn_free_values =
+       sudo_dso_findsym(handle->ssslib, "sss_sudo_free_values");
+    if (handle->fn_free_values == NULL) {
+       sudo_warnx(U_("unable to find symbol \"%s\" in %s"), path,
+          "sss_sudo_free_values");
+       free(handle);
+       debug_return_int(EFAULT);
+    }
+
+    nss->handle = handle;
+
+    /*
+     * If runhost is the same as the local host, check for ipa_hostname
+     * in sssd.conf and use it in preference to user_runhost.
+     */
+    if (strcasecmp(user_runhost, user_host) == 0) {
+       if (get_ipa_hostname(&handle->ipa_shost, &handle->ipa_host) == -1) {
+           free(handle);
+           debug_return_int(ENOMEM);
+       }
+    }
+
+    sudo_debug_printf(SUDO_DEBUG_DEBUG, "handle=%p", handle);
+
+    debug_return_int(0);
+}
+
+// ok
+static int
+sudo_sss_close(struct sudo_nss *nss)
+{
+    struct sudo_sss_handle *handle;
+    debug_decl(sudo_sss_close, SUDOERS_DEBUG_SSSD);
+
+    if (nss && nss->handle) {
+       handle = nss->handle;
+       sudo_dso_unload(handle->ssslib);
+       if (handle->pw != NULL)
+           sudo_pw_delref(handle->pw);
+       free(handle->ipa_host);
+       if (handle->ipa_host != handle->ipa_shost)
+           free(handle->ipa_shost);
+       free(handle);
+       nss->handle = NULL;
+
+       /* XXX - do in main module? */
+       free_userspecs(&nss->userspecs);
+       free_defaults(&nss->defaults);
+    }
+    debug_return_int(0);
+}
+
+/*
+ * Perform query for user and host and convert to sudoers parse tree.
+ */
+static int
+sudo_sss_query(struct sudo_nss *nss, struct passwd *pw)
+{
+    struct sudo_sss_handle *handle = nss->handle;
+    struct sss_sudo_result *sss_result = NULL;
+    int ret = 0;
+    debug_decl(sudo_sss_query, SUDOERS_DEBUG_SSSD);
+
+    /* Use cached result if it matches pw. */
+    if (handle->pw != NULL) {
+       if (pw == handle->pw)
+           goto done;
+       sudo_pw_delref(handle->pw);
+       handle->pw = NULL;
+    }
+
+    /* Free old userspecs, if any. */
+    free_userspecs(&nss->userspecs);
+
+    /* Fetch list of sudoRole entries that match user and host. */
+    sss_result = sudo_sss_result_get(nss, pw);
+
+    sudo_debug_printf(SUDO_DEBUG_DIAG,
+       "searching SSSD/LDAP for sudoers entries for user %s, host %s",
+        pw->pw_name, user_runhost);
+
+    if (sss_result == NULL)
+       goto done;
+
+    /* Stash a ref to the passwd struct in the handle. */
+    sudo_pw_addref(pw);
+    handle->pw = pw;
+
+    /* Convert to sudoers parse tree. */
+    if (!sss_to_sudoers(handle, sss_result, &nss->userspecs)) {
+       ret = -1;
+       goto done;
+    }
+
+done:
+    /* Cleanup */
+    handle->fn_free_result(sss_result);
+    if (ret == -1) {
+       free_userspecs(&nss->userspecs);
+       sudo_pw_delref(handle->pw);
+       handle->pw = NULL;
+    }
+
+    sudo_debug_printf(SUDO_DEBUG_DIAG, "Done with LDAP searches");
+
+    debug_return_int(ret);
+}
+
+// ok
+static int
+sudo_sss_parse(struct sudo_nss *nss)
+{
+    debug_decl(sudo_sss_parse, SUDOERS_DEBUG_SSSD);
+    debug_return_int(0);
+}
+
+static int
+sudo_sss_getdefs(struct sudo_nss *nss)
+{
+    struct sudo_sss_handle *handle = nss->handle;
+    struct sss_sudo_result *sss_result = NULL;
+    struct sss_sudo_rule   *sss_rule;
+    uint32_t sss_error;
+    unsigned int i;
+    int rc;
+    debug_decl(sudo_sss_getdefs, SUDOERS_DEBUG_SSSD);
+
+    if (handle == NULL)
+       debug_return_int(-1);
+
+    /* Free old defaults, if any. */
+    free_defaults(&nss->defaults);
+
+    sudo_debug_printf(SUDO_DEBUG_DIAG, "Looking for cn=defaults");
+
+    /* NOTE: these are global defaults, user ID and name are not used. */
+    rc = handle->fn_send_recv_defaults(sudo_user.pw->pw_uid,
+       sudo_user.pw->pw_name, &sss_error, &handle->domainname, &sss_result);
+    switch (rc) {
+    case 0:
+       break;
+    case ENOMEM:
+       sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+       /* FALLTHROUGH */
+    default:
+       sudo_debug_printf(SUDO_DEBUG_INFO,
+           "handle->fn_send_recv_defaults: rc=%d, sss_error=%u", rc, sss_error);
+       debug_return_int(-1);
+    }
+    if (sss_error != 0) {
+       if (sss_error == ENOENT) {
+           sudo_debug_printf(SUDO_DEBUG_INFO,
+               "The user was not found in SSSD.");
+           goto done;
+       }
+       sudo_debug_printf(SUDO_DEBUG_INFO, "sss_error=%u\n", sss_error);
+       goto bad;
+    }
+
+    for (i = 0; i < sss_result->num_rules; ++i) {
+        sudo_debug_printf(SUDO_DEBUG_DIAG,
+           "Parsing cn=defaults, %d/%d", i, sss_result->num_rules);
+        sss_rule = sss_result->rules + i;
+        if (!sudo_sss_parse_options(handle, sss_rule, &nss->defaults))
+           goto bad;
+    }
+
+done:
+    handle->fn_free_result(sss_result);
+    debug_return_int(0);
+bad:
+    handle->fn_free_result(sss_result);
+    debug_return_int(-1);
+}
+
+/* sudo_nss implementation */
+struct sudo_nss sudo_nss_sss = {
+    { NULL, NULL },
+    sudo_sss_open,
+    sudo_sss_close,
+    sudo_sss_parse,
+    sudo_sss_query,
+    sudo_sss_getdefs
+};
+
 #endif /* HAVE_SSSD */