summaryrefslogtreecommitdiffstats
path: root/nsswitch
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
commit4f5791ebd03eaec1c7da0865a383175b05102712 (patch)
tree8ce7b00f7a76baa386372422adebbe64510812d4 /nsswitch
parentInitial commit. (diff)
downloadsamba-upstream.tar.xz
samba-upstream.zip
Adding upstream version 2:4.17.12+dfsg.upstream/2%4.17.12+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--nsswitch/krb5_plugin/async_dns_krb5_locator.c445
-rw-r--r--nsswitch/krb5_plugin/winbind_krb5_localauth.c280
-rw-r--r--nsswitch/krb5_plugin/winbind_krb5_locator.c422
-rw-r--r--nsswitch/libwbclient/ABI/wbclient-0.10.sigs76
-rw-r--r--nsswitch/libwbclient/ABI/wbclient-0.11.sigs76
-rw-r--r--nsswitch/libwbclient/ABI/wbclient-0.12.sigs130
-rw-r--r--nsswitch/libwbclient/ABI/wbclient-0.13.sigs132
-rw-r--r--nsswitch/libwbclient/ABI/wbclient-0.14.sigs132
-rw-r--r--nsswitch/libwbclient/ABI/wbclient-0.15.sigs133
-rw-r--r--nsswitch/libwbclient/ABI/wbclient-0.9.sigs75
-rw-r--r--nsswitch/libwbclient/Doxyfile1297
-rw-r--r--nsswitch/libwbclient/libwbclient.h44
-rw-r--r--nsswitch/libwbclient/tests/wbclient.c1121
-rw-r--r--nsswitch/libwbclient/wbc_err_internal.h45
-rw-r--r--nsswitch/libwbclient/wbc_guid.c103
-rw-r--r--nsswitch/libwbclient/wbc_idmap.c550
-rw-r--r--nsswitch/libwbclient/wbc_pam.c1484
-rw-r--r--nsswitch/libwbclient/wbc_pwd.c722
-rw-r--r--nsswitch/libwbclient/wbc_sid.c1123
-rw-r--r--nsswitch/libwbclient/wbc_util.c920
-rw-r--r--nsswitch/libwbclient/wbclient.c344
-rw-r--r--nsswitch/libwbclient/wbclient.h2068
-rw-r--r--nsswitch/libwbclient/wbclient.pc.in11
-rw-r--r--nsswitch/libwbclient/wbclient_internal.h50
-rw-r--r--nsswitch/libwbclient/wscript57
-rw-r--r--nsswitch/nsstest.c500
-rw-r--r--nsswitch/nsstest.h119
-rw-r--r--nsswitch/pam_winbind.c3527
-rw-r--r--nsswitch/pam_winbind.h221
-rw-r--r--nsswitch/stress-nss-libwbclient.c169
-rwxr-xr-xnsswitch/tests/test_idmap_ad.sh248
-rwxr-xr-xnsswitch/tests/test_idmap_nss.sh41
-rwxr-xr-xnsswitch/tests/test_idmap_rfc2307.sh221
-rwxr-xr-xnsswitch/tests/test_idmap_rid.sh198
-rwxr-xr-xnsswitch/tests/test_rfc2307_mapping.sh187
-rwxr-xr-xnsswitch/tests/test_ticket_expiry.sh74
-rwxr-xr-xnsswitch/tests/test_wbinfo.sh321
-rwxr-xr-xnsswitch/tests/test_wbinfo_name_lookup.sh64
-rwxr-xr-xnsswitch/tests/test_wbinfo_sids_to_xids.sh32
-rwxr-xr-xnsswitch/tests/test_wbinfo_simple.sh25
-rwxr-xr-xnsswitch/tests/test_wbinfo_user_info.sh140
-rwxr-xr-xnsswitch/tests/test_wbinfo_user_info_cached.sh50
-rw-r--r--nsswitch/wb_common.c852
-rw-r--r--nsswitch/wb_reqtrans.c446
-rw-r--r--nsswitch/wb_reqtrans.h61
-rw-r--r--nsswitch/wbinfo.c3292
-rw-r--r--nsswitch/winbind_client.h57
-rw-r--r--nsswitch/winbind_nss.h82
-rw-r--r--nsswitch/winbind_nss_aix.c1129
-rw-r--r--nsswitch/winbind_nss_config.h67
-rw-r--r--nsswitch/winbind_nss_freebsd.c137
-rw-r--r--nsswitch/winbind_nss_hpux.h142
-rw-r--r--nsswitch/winbind_nss_linux.c1178
-rw-r--r--nsswitch/winbind_nss_linux.h56
-rw-r--r--nsswitch/winbind_nss_netbsd.c405
-rw-r--r--nsswitch/winbind_nss_netbsd.h40
-rw-r--r--nsswitch/winbind_nss_solaris.c665
-rw-r--r--nsswitch/winbind_nss_solaris.h37
-rw-r--r--nsswitch/winbind_struct_protocol.h535
-rw-r--r--nsswitch/wins.c400
-rw-r--r--nsswitch/wins_freebsd.c81
-rw-r--r--nsswitch/wscript_build176
-rw-r--r--nsswitch/wscript_configure31
63 files changed, 27846 insertions, 0 deletions
diff --git a/nsswitch/krb5_plugin/async_dns_krb5_locator.c b/nsswitch/krb5_plugin/async_dns_krb5_locator.c
new file mode 100644
index 0000000..7383b73
--- /dev/null
+++ b/nsswitch/krb5_plugin/async_dns_krb5_locator.c
@@ -0,0 +1,445 @@
+/*
+ Unix SMB/CIFS implementation.
+ Async DNS kerberos locator plugin
+ Copyright (C) Guenther Deschner 2007-2008
+ Copyright (C) Jeremy Allison 2020.
+
+ 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 "../../source3/include/includes.h"
+#include "../../source3/libsmb/namequery.h"
+
+#ifndef DEBUG_KRB5
+#undef DEBUG_KRB5
+#endif
+
+/* Uncomment to debug. */
+/* #define DEBUG_KRB5 1 */
+
+#if defined(HAVE_KRB5) && defined(HAVE_KRB5_LOCATE_PLUGIN_H)
+
+#ifdef HAVE_COM_ERR_H
+#include <com_err.h>
+#endif
+
+#include <krb5.h>
+#include <krb5/locate_plugin.h>
+
+#ifndef KRB5_PLUGIN_NO_HANDLE
+#define KRB5_PLUGIN_NO_HANDLE KRB5_KDC_UNREACH /* Heimdal */
+#endif
+
+struct singleton_realm_kdc_list_cache {
+ char *realm;
+ struct samba_sockaddr *kdc_list;
+ size_t num_kdcs;
+};
+
+static struct singleton_realm_kdc_list_cache *scache;
+
+static const char *get_service_from_locate_service_type(enum locate_service_type svc)
+{
+ switch (svc) {
+ case locate_service_kdc:
+ case locate_service_master_kdc:
+ return "88";
+ case locate_service_kadmin:
+ case locate_service_krb524:
+ /* not supported */
+ return NULL;
+ case locate_service_kpasswd:
+ return "464";
+ default:
+ break;
+ }
+ return NULL;
+
+}
+
+#ifdef DEBUG_KRB5
+static const char *locate_service_type_name(enum locate_service_type svc)
+{
+ switch (svc) {
+ case locate_service_kdc:
+ return "locate_service_kdc";
+ case locate_service_master_kdc:
+ return "locate_service_master_kdc";
+ case locate_service_kadmin:
+ return "locate_service_kadmin";
+ case locate_service_krb524:
+ return "locate_service_krb524";
+ case locate_service_kpasswd:
+ return "locate_service_kpasswd";
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static const char *socktype_name(int socktype)
+{
+ switch (socktype) {
+ case SOCK_STREAM:
+ return "SOCK_STREAM";
+ case SOCK_DGRAM:
+ return "SOCK_DGRAM";
+ default:
+ break;
+ }
+ return "unknown";
+}
+
+static const char *family_name(int family)
+{
+ switch (family) {
+ case AF_UNSPEC:
+ return "AF_UNSPEC";
+ case AF_INET:
+ return "AF_INET";
+#if defined(HAVE_IPV6)
+ case AF_INET6:
+ return "AF_INET6";
+#endif
+ default:
+ break;
+ }
+ return "unknown";
+}
+#endif
+
+/**
+ * Check input parameters, return KRB5_PLUGIN_NO_HANDLE for unsupported ones
+ *
+ * @param svc
+ * @param realm string
+ * @param socktype integer
+ * @param family integer
+ *
+ * @return integer.
+ */
+
+static int smb_krb5_adns_locator_lookup_sanity_check(
+ enum locate_service_type svc,
+ const char *realm,
+ int socktype,
+ int family)
+{
+ if (!realm || strlen(realm) == 0) {
+ return EINVAL;
+ }
+
+ switch (svc) {
+ case locate_service_kdc:
+ case locate_service_master_kdc:
+ break;
+ case locate_service_kadmin:
+ case locate_service_krb524:
+ case locate_service_kpasswd:
+ return KRB5_PLUGIN_NO_HANDLE;
+ default:
+ return EINVAL;
+ }
+
+ switch (family) {
+ case AF_UNSPEC:
+ case AF_INET:
+#if defined(HAVE_IPV6)
+ case AF_INET6:
+#endif
+ break;
+ default:
+ return EINVAL;
+ }
+
+ switch (socktype) {
+ case SOCK_STREAM:
+ case SOCK_DGRAM:
+ case 0: /* Heimdal uses that */
+ break;
+ default:
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * Call back into the MIT libraries with each address
+ * we found. Assume AD-DC's always support both UDP and
+ * TCP port 88 for KDC service.
+ */
+
+static krb5_error_code smb_krb5_adns_locator_call_cbfunc(
+ struct samba_sockaddr *kdcs,
+ size_t num_kdcs,
+ const char *service,
+ int socktype,
+ int (*cbfunc)(void *, int, struct sockaddr *),
+ void *cbdata)
+{
+ int ret = 0;
+ size_t i;
+
+ for (i = 0; i < num_kdcs; i++) {
+ struct sockaddr *sa = NULL;
+
+ if (kdcs[i].u.ss.ss_family == AF_INET) {
+ struct sockaddr_in *sin = &kdcs[i].u.in;
+ sin->sin_family = AF_INET;
+ sin->sin_port = htons(88);
+ sa = &kdcs[i].u.sa;
+ }
+#if defined(HAVE_IPV6)
+ if (kdcs[i].u.ss.ss_family == AF_INET6) {
+ struct sockaddr_in6 *sin6 = &kdcs[i].u.in6;
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = htons(88);
+ sa = &kdcs[i].u.sa;
+ }
+#else
+ else {
+ return KRB5_PLUGIN_NO_HANDLE;
+ }
+#endif
+
+#ifdef DEBUG_KRB5
+ {
+ char addr[INET6_ADDRSTRLEN];
+ fprintf(stderr, "[%5u]: "
+ "smb_krb5_adns_locator_call_cbfunc: "
+ "IP[%zu] %s\n",
+ (unsigned int)getpid(),
+ i,
+ print_sockaddr(addr,
+ sizeof(addr),
+ &kdcs[i].u.ss));
+ }
+#endif
+
+ /* Assume all AD-DC's do both UDP and TCP on port 88. */
+ ret = cbfunc(cbdata, socktype, sa);
+ if (ret) {
+#ifdef DEBUG_KRB5
+ fprintf(stderr, "[%5u]: "
+ "smb_krb5_adns_locator_call_cbfunc: "
+ "failed to call callback: %s (%d)\n",
+ (unsigned int)getpid(),
+ error_message(ret),
+ ret);
+#endif
+ break;
+ }
+ }
+ return ret;
+}
+
+/**
+ * PUBLIC INTERFACE: locate init
+ *
+ * @param context krb5_context
+ * @param privata_data pointer to private data pointer
+ *
+ * @return krb5_error_code.
+ */
+
+static krb5_error_code smb_krb5_adns_locator_init(krb5_context context,
+ void **private_data)
+{
+ static bool loaded_config;
+ if (!loaded_config) {
+ lp_load_global(get_dyn_CONFIGFILE());
+ loaded_config = true;
+ }
+#ifdef DEBUG_KRB5
+ fprintf(stderr,"[%5u]: smb_krb5_adns_locator_init\n",
+ (unsigned int)getpid());
+#endif
+ return 0;
+}
+
+/**
+ * PUBLIC INTERFACE: close locate
+ *
+ * @param private_data pointer to private data
+ *
+ * @return void.
+ */
+
+static void smb_krb5_adns_locator_close(void *private_data)
+{
+#ifdef DEBUG_KRB5
+ fprintf(stderr,"[%5u]: smb_krb5_adns_locator_close\n",
+ (unsigned int)getpid());
+#endif
+ return;
+}
+
+/**
+ * PUBLIC INTERFACE: locate lookup
+ *
+ * @param private_data pointer to private data
+ * @param svc enum locate_service_type.
+ * @param realm string
+ * @param socktype integer
+ * @param family integer
+ * @param cbfunc callback function to send back entries
+ * @param cbdata void pointer to cbdata
+ *
+ * @return krb5_error_code.
+ */
+
+static krb5_error_code smb_krb5_adns_locator_lookup(void *private_data,
+ enum locate_service_type svc,
+ const char *realm,
+ int socktype,
+ int family,
+ int (*cbfunc)(void *, int, struct sockaddr *),
+ void *cbdata)
+{
+ krb5_error_code ret;
+ const char *service = get_service_from_locate_service_type(svc);
+
+#ifdef DEBUG_KRB5
+ fprintf(stderr,"[%5u]: smb_krb5_adns_locator_lookup: called for '%s' "
+ "svc: '%s' (%d) "
+ "socktype: '%s' (%d), family: '%s' (%d)\n",
+ (unsigned int)getpid(),
+ realm,
+ locate_service_type_name(svc),
+ svc,
+ socktype_name(socktype),
+ socktype,
+ family_name(family),
+ family);
+#endif
+ ret = smb_krb5_adns_locator_lookup_sanity_check(svc,
+ realm,
+ socktype,
+ family);
+ if (ret) {
+#ifdef DEBUG_KRB5
+ fprintf(stderr, "[%5u]: smb_krb5_adns_locator_lookup: "
+ "returning ret: %s (%d)\n",
+ (unsigned int)getpid(),
+ error_message(ret),
+ ret);
+#endif
+ return ret;
+ }
+
+ /*
+ * If is a subsequent lookup for the same realm
+ * and we have a cache for this already, don't re-do
+ * the DNS SRV -> A/AAAA lookups.
+ *
+ * kinit does this a lot, it looks for UDP then TCP.
+ */
+
+ if ((scache == NULL) || strcmp(realm, scache->realm) != 0) {
+ /* Cache is NULL or a different realm lookup. */
+ NTSTATUS status;
+
+ /*
+ * We have a new lookup to do. As it's a singleton
+ * cache make sure we have no old cache.
+ */
+ TALLOC_FREE(scache);
+
+ scache = talloc_zero(NULL,
+ struct singleton_realm_kdc_list_cache);
+ if (scache == NULL) {
+ return KRB5_PLUGIN_NO_HANDLE;
+ }
+ scache->realm = talloc_strdup(scache, realm);
+ if (scache->realm == NULL) {
+ TALLOC_FREE(scache);
+ return KRB5_PLUGIN_NO_HANDLE;
+ }
+
+ status = get_kdc_list(scache,
+ realm,
+ NULL,
+ &scache->kdc_list,
+ &scache->num_kdcs);
+ if (!NT_STATUS_IS_OK(status)) {
+#ifdef DEBUG_KRB5
+ fprintf(stderr, "[%5u]: "
+ "smb_krb5_adns_locator_lookup: "
+ "get_kdc_list() for realm %s failed "
+ "with %s\n",
+ (unsigned int)getpid(),
+ realm,
+ nt_errstr(status));
+#endif
+ TALLOC_FREE(scache);
+ return KRB5_PLUGIN_NO_HANDLE;
+ }
+ if (scache->num_kdcs == 0) {
+ TALLOC_FREE(scache);
+ return KRB5_PLUGIN_NO_HANDLE;
+ }
+ }
+#ifdef DEBUG_KRB5
+ else {
+ fprintf(stderr, "[%5u]: "
+ "smb_krb5_adns_locator_lookup: "
+ "returning cached data for realm %s\n",
+ (unsigned int)getpid(),
+ realm);
+ }
+#endif
+ /*
+ * If we get here we know scache contains the right
+ * realm and non-null address list.
+ */
+
+#ifdef DEBUG_KRB5
+ fprintf(stderr, "[%5u]: smb_krb5_adns_locator_lookup: "
+ "got %zu IP addresses for realm %s\n",
+ (unsigned int)getpid(),
+ scache->num_kdcs,
+ scache->realm);
+#endif
+
+ /*
+ * Don't free kdc list on success, we're
+ * always returning from the cache.
+ */
+ return smb_krb5_adns_locator_call_cbfunc(scache->kdc_list,
+ scache->num_kdcs,
+ service,
+ socktype,
+ cbfunc,
+ cbdata);
+}
+
+#ifdef HEIMDAL_KRB5_LOCATE_PLUGIN_H
+#define SMB_KRB5_LOCATOR_SYMBOL_NAME resolve /* Heimdal */
+#else
+#define SMB_KRB5_LOCATOR_SYMBOL_NAME service_locator /* MIT */
+#endif
+
+_PUBLIC_ const krb5plugin_service_locate_ftable SMB_KRB5_LOCATOR_SYMBOL_NAME = {
+ .minor_version = 0,
+ .init = smb_krb5_adns_locator_init,
+ .fini = smb_krb5_adns_locator_close,
+#ifdef KRB5_PLUGIN_LOCATE_VERSION_2
+ .old_lookup = smb_krb5_adns_locator_lookup,
+#else
+ .lookup = smb_krb5_adns_locator_lookup,
+#endif
+};
+
+#endif
diff --git a/nsswitch/krb5_plugin/winbind_krb5_localauth.c b/nsswitch/krb5_plugin/winbind_krb5_localauth.c
new file mode 100644
index 0000000..751dfd1
--- /dev/null
+++ b/nsswitch/krb5_plugin/winbind_krb5_localauth.c
@@ -0,0 +1,280 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ A localauth plugin for MIT Kerberos
+
+ Copyright (C) 2018 Andreas Schneider <asn@samba.org>
+
+ 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 "replace.h"
+#include <krb5/localauth_plugin.h>
+#include <wbclient.h>
+#ifdef HAVE_COM_ERR_H
+#include <com_err.h>
+#endif
+
+struct krb5_localauth_moddata_st {
+ struct wbcContext *wbc_ctx;
+};
+
+/*
+ * Initialize the module data.
+ *
+ * This creates the wbclient context.
+ */
+static krb5_error_code winbind_init(krb5_context context,
+ krb5_localauth_moddata *data)
+{
+ krb5_localauth_moddata d;
+
+ *data = NULL;
+ d = malloc(sizeof(struct krb5_localauth_moddata_st));
+ if (d == NULL) {
+ return ENOMEM;
+ }
+
+ d->wbc_ctx = wbcCtxCreate();
+ if (d->wbc_ctx == NULL) {
+ free(d);
+ return ENOMEM;
+ }
+
+ wbcSetClientProcessName("krb5_localauth_plugin");
+
+ *data = d;
+
+ return 0;
+}
+
+/*
+ * Release resources used by module data.
+ */
+static void winbind_fini(krb5_context context, krb5_localauth_moddata data)
+{
+ wbcCtxFree(data->wbc_ctx);
+ free(data);
+ data = NULL;
+}
+
+/*
+ * Determine whether aname is authorized to log in as the local account lname.
+ *
+ * Return 0 if aname is authorized, EPERM if aname is authoritatively not
+ * authorized, KRB5_PLUGIN_NO_HANDLE if the module cannot determine whether
+ * aname is authorized, and any other error code for a serious failure to
+ * process the request. aname will be considered authorized if at least one
+ * module returns 0 and all other modules return KRB5_PLUGIN_NO_HANDLE.
+ */
+static krb5_error_code winbind_userok(krb5_context context,
+ krb5_localauth_moddata data,
+ krb5_const_principal aname,
+ const char *lname)
+{
+ krb5_error_code code = 0;
+ char *princ_str = NULL;
+ struct passwd *pwd = NULL;
+ uid_t princ_uid = (uid_t)-1;
+ uid_t lname_uid = (uid_t)-1;
+ wbcErr wbc_status;
+ int cmp;
+
+ code = krb5_unparse_name(context, aname, &princ_str);
+ if (code != 0) {
+ return code;
+ }
+
+ cmp = strcasecmp(princ_str, lname);
+ if (cmp == 0) {
+ goto out;
+ }
+
+ wbc_status = wbcCtxGetpwnam(data->wbc_ctx,
+ princ_str,
+ &pwd);
+ switch (wbc_status) {
+ case WBC_ERR_SUCCESS:
+ princ_uid = pwd->pw_uid;
+ code = 0;
+ break;
+ case WBC_ERR_UNKNOWN_USER:
+ /* match other insane libwbclient return codes */
+ case WBC_ERR_WINBIND_NOT_AVAILABLE:
+ case WBC_ERR_DOMAIN_NOT_FOUND:
+ code = KRB5_PLUGIN_NO_HANDLE;
+ break;
+ default:
+ code = EIO;
+ break;
+ }
+ wbcFreeMemory(pwd);
+ if (code != 0) {
+ goto out;
+ }
+
+ wbc_status = wbcCtxGetpwnam(data->wbc_ctx,
+ lname,
+ &pwd);
+ switch (wbc_status) {
+ case WBC_ERR_SUCCESS:
+ lname_uid = pwd->pw_uid;
+ break;
+ case WBC_ERR_UNKNOWN_USER:
+ /* match other insane libwbclient return codes */
+ case WBC_ERR_WINBIND_NOT_AVAILABLE:
+ case WBC_ERR_DOMAIN_NOT_FOUND:
+ code = KRB5_PLUGIN_NO_HANDLE;
+ break;
+ default:
+ code = EIO;
+ break;
+ }
+ wbcFreeMemory(pwd);
+ if (code != 0) {
+ goto out;
+ }
+
+ if (princ_uid != lname_uid) {
+ code = EPERM;
+ }
+
+ com_err("winbind_localauth",
+ code,
+ "Access %s: %s (uid=%u) %sequal to %s (uid=%u)",
+ code == 0 ? "granted" : "denied",
+ princ_str,
+ (unsigned int)princ_uid,
+ code == 0 ? "" : "not ",
+ lname,
+ (unsigned int)lname_uid);
+
+out:
+ krb5_free_unparsed_name(context, princ_str);
+
+ return code;
+}
+
+/*
+ * Determine the local account name corresponding to aname.
+ *
+ * Return 0 and set *lname_out if a mapping can be determined; the contents of
+ * *lname_out will later be released with a call to the module's free_string
+ * method. Return KRB5_LNAME_NOTRANS if no mapping can be determined. Return
+ * any other error code for a serious failure to process the request; this will
+ * halt the krb5_aname_to_localname operation.
+ *
+ * If the module's an2ln_types field is set, this method will only be invoked
+ * when a profile "auth_to_local" value references one of the module's types.
+ * type and residual will be set to the type and residual of the auth_to_local
+ * value.
+ *
+ * If the module's an2ln_types field is not set but the an2ln method is
+ * implemented, this method will be invoked independently of the profile's
+ * auth_to_local settings, with type and residual set to NULL. If multiple
+ * modules are registered with an2ln methods but no an2ln_types field, the
+ * order of invocation is not defined, but all such modules will be consulted
+ * before the built-in mechanisms are tried.
+ */
+static krb5_error_code winbind_an2ln(krb5_context context,
+ krb5_localauth_moddata data,
+ const char *type,
+ const char *residual,
+ krb5_const_principal aname,
+ char **lname_out)
+{
+ krb5_error_code code = 0;
+ char *princ_str = NULL;
+ char *name = NULL;
+ struct passwd *pwd = NULL;
+ wbcErr wbc_status;
+
+ code = krb5_unparse_name(context, aname, &princ_str);
+ if (code != 0) {
+ return code;
+ }
+
+ wbc_status = wbcCtxGetpwnam(data->wbc_ctx,
+ princ_str,
+ &pwd);
+ krb5_free_unparsed_name(context, princ_str);
+ switch (wbc_status) {
+ case WBC_ERR_SUCCESS:
+ name = strdup(pwd->pw_name);
+ code = 0;
+ break;
+ case WBC_ERR_UNKNOWN_USER:
+ /* match other insane libwbclient return codes */
+ case WBC_ERR_WINBIND_NOT_AVAILABLE:
+ case WBC_ERR_DOMAIN_NOT_FOUND:
+ code = KRB5_LNAME_NOTRANS;
+ break;
+ default:
+ code = EIO;
+ break;
+ }
+ wbcFreeMemory(pwd);
+ if (code != 0) {
+ return code;
+ }
+
+ if (name == NULL) {
+ return ENOMEM;
+ }
+
+ *lname_out = name;
+
+ return code;
+}
+
+/*
+ * Release the memory returned by an invocation of an2ln.
+ */
+static void winbind_free_string(krb5_context context,
+ krb5_localauth_moddata data,
+ char *str)
+{
+ free(str);
+}
+
+_PUBLIC_ krb5_error_code
+localauth_winbind_initvt(krb5_context context,
+ int maj_ver,
+ int min_ver,
+ krb5_plugin_vtable vtable);
+
+_PUBLIC_ krb5_error_code
+localauth_winbind_initvt(krb5_context context,
+ int maj_ver,
+ int min_ver,
+ krb5_plugin_vtable vtable)
+{
+ krb5_localauth_vtable vt = (krb5_localauth_vtable)vtable;
+
+ if (maj_ver != 1) {
+ com_err("winbind_localauth",
+ EINVAL,
+ "Failed to load, plugin API changed.");
+ return KRB5_PLUGIN_VER_NOTSUPP;
+ }
+
+ vt->init = winbind_init;
+ vt->fini = winbind_fini;
+ vt->name = "winbind";
+ vt->an2ln = winbind_an2ln;
+ vt->userok = winbind_userok;
+ vt->free_string = winbind_free_string;
+
+ return 0;
+}
diff --git a/nsswitch/krb5_plugin/winbind_krb5_locator.c b/nsswitch/krb5_plugin/winbind_krb5_locator.c
new file mode 100644
index 0000000..d65b50e
--- /dev/null
+++ b/nsswitch/krb5_plugin/winbind_krb5_locator.c
@@ -0,0 +1,422 @@
+/*
+ Unix SMB/CIFS implementation.
+ kerberos locator plugin
+ Copyright (C) Guenther Deschner 2007-2008
+
+ 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 "nsswitch/winbind_client.h"
+#include "libwbclient/wbclient.h"
+
+#ifndef DEBUG_KRB5
+#undef DEBUG_KRB5
+#endif
+
+#if defined(HAVE_KRB5) && defined(HAVE_KRB5_LOCATE_PLUGIN_H)
+
+#ifdef HAVE_COM_ERR_H
+#include <com_err.h>
+#endif
+
+#include <krb5.h>
+#include <krb5/locate_plugin.h>
+
+#ifndef KRB5_PLUGIN_NO_HANDLE
+#define KRB5_PLUGIN_NO_HANDLE KRB5_KDC_UNREACH /* Heimdal */
+#endif
+
+static const char *get_service_from_locate_service_type(enum locate_service_type svc)
+{
+ switch (svc) {
+ case locate_service_kdc:
+ case locate_service_master_kdc:
+ return "88";
+ case locate_service_kadmin:
+ case locate_service_krb524:
+ /* not supported */
+ return NULL;
+ case locate_service_kpasswd:
+ return "464";
+ default:
+ break;
+ }
+ return NULL;
+
+}
+
+#ifdef DEBUG_KRB5
+static const char *locate_service_type_name(enum locate_service_type svc)
+{
+ switch (svc) {
+ case locate_service_kdc:
+ return "locate_service_kdc";
+ case locate_service_master_kdc:
+ return "locate_service_master_kdc";
+ case locate_service_kadmin:
+ return "locate_service_kadmin";
+ case locate_service_krb524:
+ return "locate_service_krb524";
+ case locate_service_kpasswd:
+ return "locate_service_kpasswd";
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static const char *socktype_name(int socktype)
+{
+ switch (socktype) {
+ case SOCK_STREAM:
+ return "SOCK_STREAM";
+ case SOCK_DGRAM:
+ return "SOCK_DGRAM";
+ default:
+ break;
+ }
+ return "unknown";
+}
+
+static const char *family_name(int family)
+{
+ switch (family) {
+ case AF_UNSPEC:
+ return "AF_UNSPEC";
+ case AF_INET:
+ return "AF_INET";
+#if defined(HAVE_IPV6)
+ case AF_INET6:
+ return "AF_INET6";
+#endif
+ default:
+ break;
+ }
+ return "unknown";
+}
+#endif
+
+/**
+ * Check input parameters, return KRB5_PLUGIN_NO_HANDLE for unsupported ones
+ *
+ * @param svc
+ * @param realm string
+ * @param socktype integer
+ * @param family integer
+ *
+ * @return integer.
+ */
+
+static int smb_krb5_locator_lookup_sanity_check(enum locate_service_type svc,
+ const char *realm,
+ int socktype,
+ int family)
+{
+ if (!realm || strlen(realm) == 0) {
+ return EINVAL;
+ }
+
+ switch (svc) {
+ case locate_service_kdc:
+ case locate_service_master_kdc:
+ case locate_service_kpasswd:
+ break;
+ case locate_service_kadmin:
+ case locate_service_krb524:
+ return KRB5_PLUGIN_NO_HANDLE;
+ default:
+ return EINVAL;
+ }
+
+ switch (family) {
+ case AF_UNSPEC:
+ case AF_INET:
+#if defined(HAVE_IPV6)
+ case AF_INET6:
+#endif
+ break;
+ default:
+ return EINVAL;
+ }
+
+ switch (socktype) {
+ case SOCK_STREAM:
+ case SOCK_DGRAM:
+ case 0: /* Heimdal uses that */
+ break;
+ default:
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * Try to get addrinfo for a given host and call the krb5 callback
+ *
+ * @param name string
+ * @param service string
+ * @param in struct addrinfo hint
+ * @param cbfunc krb5 callback function
+ * @param cbdata void pointer cbdata
+ *
+ * @return krb5_error_code.
+ */
+
+static krb5_error_code smb_krb5_locator_call_cbfunc(const char *name,
+ const char *service,
+ struct addrinfo *in,
+ int (*cbfunc)(void *, int, struct sockaddr *),
+ void *cbdata)
+{
+ struct addrinfo *out = NULL;
+ int ret = 0;
+ struct addrinfo *res = NULL;
+ int count = 3;
+
+ while (count) {
+
+ ret = getaddrinfo(name, service, in, &out);
+ if (ret == 0) {
+ break;
+ }
+
+ if ((ret == EAI_AGAIN) && (count > 1)) {
+ count--;
+ continue;
+ }
+
+#ifdef DEBUG_KRB5
+ fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
+ "getaddrinfo failed: %s (%d)\n",
+ (unsigned int)getpid(), gai_strerror(ret), ret);
+#endif
+
+ return KRB5_PLUGIN_NO_HANDLE;
+ }
+
+ for (res = out; res; res = res->ai_next) {
+ if (!res->ai_addr || res->ai_addrlen == 0) {
+ continue;
+ }
+
+ ret = cbfunc(cbdata, res->ai_socktype, res->ai_addr);
+ if (ret) {
+#ifdef DEBUG_KRB5
+ fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
+ "failed to call callback: %s (%d)\n",
+ (unsigned int)getpid(), error_message(ret), ret);
+#endif
+ break;
+ }
+ }
+
+ if (out) {
+ freeaddrinfo(out);
+ }
+ return ret;
+}
+
+/**
+ * PUBLIC INTERFACE: locate init
+ *
+ * @param context krb5_context
+ * @param privata_data pointer to private data pointer
+ *
+ * @return krb5_error_code.
+ */
+
+static krb5_error_code smb_krb5_locator_init(krb5_context context,
+ void **private_data)
+{
+ return 0;
+}
+
+/**
+ * PUBLIC INTERFACE: close locate
+ *
+ * @param private_data pointer to private data
+ *
+ * @return void.
+ */
+
+static void smb_krb5_locator_close(void *private_data)
+{
+ return;
+}
+
+
+static bool ask_winbind(const char *realm, char **dcname)
+{
+ wbcErr wbc_status;
+ const char *dc = NULL;
+ struct wbcDomainControllerInfoEx *dc_info = NULL;
+ uint32_t flags;
+
+ flags = WBC_LOOKUP_DC_KDC_REQUIRED |
+ WBC_LOOKUP_DC_IS_DNS_NAME |
+ WBC_LOOKUP_DC_RETURN_DNS_NAME;
+
+ wbc_status = wbcLookupDomainControllerEx(realm, NULL, NULL, flags, &dc_info);
+
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+#ifdef DEBUG_KRB5
+ fprintf(stderr,"[%5u]: smb_krb5_locator_lookup: failed with: %s\n",
+ (unsigned int)getpid(), wbcErrorString(wbc_status));
+#endif
+ return false;
+ }
+
+ if (!dc && dc_info->dc_unc) {
+ dc = dc_info->dc_unc;
+ if (dc[0] == '\\') dc++;
+ if (dc[0] == '\\') dc++;
+ }
+
+ if (!dc) {
+ wbcFreeMemory(dc_info);
+ return false;
+ }
+
+ *dcname = strdup(dc);
+ if (!*dcname) {
+ wbcFreeMemory(dc_info);
+ return false;
+ }
+
+ wbcFreeMemory(dc_info);
+ return true;
+}
+
+/**
+ * PUBLIC INTERFACE: locate lookup
+ *
+ * @param private_data pointer to private data
+ * @param svc enum locate_service_type.
+ * @param realm string
+ * @param socktype integer
+ * @param family integer
+ * @param cbfunc callback function to send back entries
+ * @param cbdata void pointer to cbdata
+ *
+ * @return krb5_error_code.
+ */
+
+static krb5_error_code smb_krb5_locator_lookup(void *private_data,
+ enum locate_service_type svc,
+ const char *realm,
+ int socktype,
+ int family,
+ int (*cbfunc)(void *, int, struct sockaddr *),
+ void *cbdata)
+{
+ krb5_error_code ret;
+ struct addrinfo aihints;
+ char *kdc_name = NULL;
+ const char *service = get_service_from_locate_service_type(svc);
+
+ ZERO_STRUCT(aihints);
+
+#ifdef DEBUG_KRB5
+ fprintf(stderr,"[%5u]: smb_krb5_locator_lookup: called for '%s' "
+ "svc: '%s' (%d) "
+ "socktype: '%s' (%d), family: '%s' (%d)\n",
+ (unsigned int)getpid(), realm,
+ locate_service_type_name(svc), svc,
+ socktype_name(socktype), socktype,
+ family_name(family), family);
+#endif
+ ret = smb_krb5_locator_lookup_sanity_check(svc, realm, socktype,
+ family);
+ if (ret) {
+#ifdef DEBUG_KRB5
+ fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
+ "returning ret: %s (%d)\n",
+ (unsigned int)getpid(), error_message(ret), ret);
+#endif
+ return ret;
+ }
+
+ if (!winbind_env_set()) {
+ if (!ask_winbind(realm, &kdc_name)) {
+#ifdef DEBUG_KRB5
+ fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
+ "failed to query winbindd\n",
+ (unsigned int)getpid());
+#endif
+ goto failed;
+ }
+ } else {
+ const char *env = NULL;
+ char *var = NULL;
+ if (asprintf(&var, "%s_%s",
+ WINBINDD_LOCATOR_KDC_ADDRESS, realm) == -1) {
+ goto failed;
+ }
+ env = getenv(var);
+ if (!env) {
+#ifdef DEBUG_KRB5
+ fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
+ "failed to get kdc from env %s\n",
+ (unsigned int)getpid(), var);
+#endif
+ free(var);
+ goto failed;
+ }
+ free(var);
+
+ kdc_name = strdup(env);
+ if (!kdc_name) {
+ goto failed;
+ }
+ }
+#ifdef DEBUG_KRB5
+ fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
+ "got '%s' for '%s' from winbindd\n", (unsigned int)getpid(),
+ kdc_name, realm);
+#endif
+
+ aihints.ai_family = family;
+ aihints.ai_socktype = socktype;
+
+ ret = smb_krb5_locator_call_cbfunc(kdc_name,
+ service,
+ &aihints,
+ cbfunc, cbdata);
+ SAFE_FREE(kdc_name);
+
+ return ret;
+
+ failed:
+ return KRB5_PLUGIN_NO_HANDLE;
+}
+
+#ifdef HEIMDAL_KRB5_LOCATE_PLUGIN_H
+#define SMB_KRB5_LOCATOR_SYMBOL_NAME resolve /* Heimdal */
+#else
+#define SMB_KRB5_LOCATOR_SYMBOL_NAME service_locator /* MIT */
+#endif
+
+_PUBLIC_ const krb5plugin_service_locate_ftable SMB_KRB5_LOCATOR_SYMBOL_NAME = {
+ .minor_version = 0,
+ .init = smb_krb5_locator_init,
+ .fini = smb_krb5_locator_close,
+#ifdef KRB5_PLUGIN_LOCATE_VERSION_2
+ .old_lookup = smb_krb5_locator_lookup,
+#else
+ .lookup = smb_krb5_locator_lookup,
+#endif
+};
+
+#endif
diff --git a/nsswitch/libwbclient/ABI/wbclient-0.10.sigs b/nsswitch/libwbclient/ABI/wbclient-0.10.sigs
new file mode 100644
index 0000000..eda96f4
--- /dev/null
+++ b/nsswitch/libwbclient/ABI/wbclient-0.10.sigs
@@ -0,0 +1,76 @@
+wbcAddNamedBlob: wbcErr (size_t *, struct wbcNamedBlob **, const char *, uint32_t, uint8_t *, size_t)
+wbcAllocateGid: wbcErr (gid_t *)
+wbcAllocateMemory: void *(size_t, size_t, void (*)(void *))
+wbcAllocateStringArray: const char **(int)
+wbcAllocateUid: wbcErr (uid_t *)
+wbcAuthenticateUser: wbcErr (const char *, const char *)
+wbcAuthenticateUserEx: wbcErr (const struct wbcAuthUserParams *, struct wbcAuthUserInfo **, struct wbcAuthErrorInfo **)
+wbcChangeTrustCredentials: wbcErr (const char *, struct wbcAuthErrorInfo **)
+wbcChangeUserPassword: wbcErr (const char *, const char *, const char *)
+wbcChangeUserPasswordEx: wbcErr (const struct wbcChangePasswordParams *, struct wbcAuthErrorInfo **, enum wbcPasswordChangeRejectReason *, struct wbcUserPasswordPolicyInfo **)
+wbcCheckTrustCredentials: wbcErr (const char *, struct wbcAuthErrorInfo **)
+wbcCredentialCache: wbcErr (struct wbcCredentialCacheParams *, struct wbcCredentialCacheInfo **, struct wbcAuthErrorInfo **)
+wbcCredentialSave: wbcErr (const char *, const char *)
+wbcDcInfo: wbcErr (const char *, size_t *, const char ***, const char ***)
+wbcDomainInfo: wbcErr (const char *, struct wbcDomainInfo **)
+wbcEndgrent: wbcErr (void)
+wbcEndpwent: wbcErr (void)
+wbcErrorString: const char *(wbcErr)
+wbcFreeMemory: void (void *)
+wbcGetDisplayName: wbcErr (const struct wbcDomainSid *, char **, char **, enum wbcSidType *)
+wbcGetGroups: wbcErr (const char *, uint32_t *, gid_t **)
+wbcGetSidAliases: wbcErr (const struct wbcDomainSid *, struct wbcDomainSid *, uint32_t, uint32_t **, uint32_t *)
+wbcGetgrent: wbcErr (struct group **)
+wbcGetgrgid: wbcErr (gid_t, struct group **)
+wbcGetgrlist: wbcErr (struct group **)
+wbcGetgrnam: wbcErr (const char *, struct group **)
+wbcGetpwent: wbcErr (struct passwd **)
+wbcGetpwnam: wbcErr (const char *, struct passwd **)
+wbcGetpwsid: wbcErr (struct wbcDomainSid *, struct passwd **)
+wbcGetpwuid: wbcErr (uid_t, struct passwd **)
+wbcGidToSid: wbcErr (gid_t, struct wbcDomainSid *)
+wbcGuidToString: wbcErr (const struct wbcGuid *, char **)
+wbcInterfaceDetails: wbcErr (struct wbcInterfaceDetails **)
+wbcLibraryDetails: wbcErr (struct wbcLibraryDetails **)
+wbcListGroups: wbcErr (const char *, uint32_t *, const char ***)
+wbcListTrusts: wbcErr (struct wbcDomainInfo **, size_t *)
+wbcListUsers: wbcErr (const char *, uint32_t *, const char ***)
+wbcLogoffUser: wbcErr (const char *, uid_t, const char *)
+wbcLogoffUserEx: wbcErr (const struct wbcLogoffUserParams *, struct wbcAuthErrorInfo **)
+wbcLogonUser: wbcErr (const struct wbcLogonUserParams *, struct wbcLogonUserInfo **, struct wbcAuthErrorInfo **, struct wbcUserPasswordPolicyInfo **)
+wbcLookupDomainController: wbcErr (const char *, uint32_t, struct wbcDomainControllerInfo **)
+wbcLookupDomainControllerEx: wbcErr (const char *, struct wbcGuid *, const char *, uint32_t, struct wbcDomainControllerInfoEx **)
+wbcLookupName: wbcErr (const char *, const char *, struct wbcDomainSid *, enum wbcSidType *)
+wbcLookupRids: wbcErr (struct wbcDomainSid *, int, uint32_t *, const char **, const char ***, enum wbcSidType **)
+wbcLookupSid: wbcErr (const struct wbcDomainSid *, char **, char **, enum wbcSidType *)
+wbcLookupSids: wbcErr (const struct wbcDomainSid *, int, struct wbcDomainInfo **, int *, struct wbcTranslatedName **)
+wbcLookupUserSids: wbcErr (const struct wbcDomainSid *, bool, uint32_t *, struct wbcDomainSid **)
+wbcPing: wbcErr (void)
+wbcPingDc: wbcErr (const char *, struct wbcAuthErrorInfo **)
+wbcPingDc2: wbcErr (const char *, struct wbcAuthErrorInfo **, char **)
+wbcQueryGidToSid: wbcErr (gid_t, struct wbcDomainSid *)
+wbcQuerySidToGid: wbcErr (const struct wbcDomainSid *, gid_t *)
+wbcQuerySidToUid: wbcErr (const struct wbcDomainSid *, uid_t *)
+wbcQueryUidToSid: wbcErr (uid_t, struct wbcDomainSid *)
+wbcRemoveGidMapping: wbcErr (gid_t, const struct wbcDomainSid *)
+wbcRemoveUidMapping: wbcErr (uid_t, const struct wbcDomainSid *)
+wbcRequestResponse: wbcErr (int, struct winbindd_request *, struct winbindd_response *)
+wbcRequestResponsePriv: wbcErr (int, struct winbindd_request *, struct winbindd_response *)
+wbcResolveWinsByIP: wbcErr (const char *, char **)
+wbcResolveWinsByName: wbcErr (const char *, char **)
+wbcSetGidHwm: wbcErr (gid_t)
+wbcSetGidMapping: wbcErr (gid_t, const struct wbcDomainSid *)
+wbcSetUidHwm: wbcErr (uid_t)
+wbcSetUidMapping: wbcErr (uid_t, const struct wbcDomainSid *)
+wbcSetgrent: wbcErr (void)
+wbcSetpwent: wbcErr (void)
+wbcSidToGid: wbcErr (const struct wbcDomainSid *, gid_t *)
+wbcSidToString: wbcErr (const struct wbcDomainSid *, char **)
+wbcSidToStringBuf: int (const struct wbcDomainSid *, char *, int)
+wbcSidToUid: wbcErr (const struct wbcDomainSid *, uid_t *)
+wbcSidTypeString: const char *(enum wbcSidType)
+wbcSidsToUnixIds: wbcErr (const struct wbcDomainSid *, uint32_t, struct wbcUnixId *)
+wbcStrDup: char *(const char *)
+wbcStringToGuid: wbcErr (const char *, struct wbcGuid *)
+wbcStringToSid: wbcErr (const char *, struct wbcDomainSid *)
+wbcUidToSid: wbcErr (uid_t, struct wbcDomainSid *)
diff --git a/nsswitch/libwbclient/ABI/wbclient-0.11.sigs b/nsswitch/libwbclient/ABI/wbclient-0.11.sigs
new file mode 100644
index 0000000..eda96f4
--- /dev/null
+++ b/nsswitch/libwbclient/ABI/wbclient-0.11.sigs
@@ -0,0 +1,76 @@
+wbcAddNamedBlob: wbcErr (size_t *, struct wbcNamedBlob **, const char *, uint32_t, uint8_t *, size_t)
+wbcAllocateGid: wbcErr (gid_t *)
+wbcAllocateMemory: void *(size_t, size_t, void (*)(void *))
+wbcAllocateStringArray: const char **(int)
+wbcAllocateUid: wbcErr (uid_t *)
+wbcAuthenticateUser: wbcErr (const char *, const char *)
+wbcAuthenticateUserEx: wbcErr (const struct wbcAuthUserParams *, struct wbcAuthUserInfo **, struct wbcAuthErrorInfo **)
+wbcChangeTrustCredentials: wbcErr (const char *, struct wbcAuthErrorInfo **)
+wbcChangeUserPassword: wbcErr (const char *, const char *, const char *)
+wbcChangeUserPasswordEx: wbcErr (const struct wbcChangePasswordParams *, struct wbcAuthErrorInfo **, enum wbcPasswordChangeRejectReason *, struct wbcUserPasswordPolicyInfo **)
+wbcCheckTrustCredentials: wbcErr (const char *, struct wbcAuthErrorInfo **)
+wbcCredentialCache: wbcErr (struct wbcCredentialCacheParams *, struct wbcCredentialCacheInfo **, struct wbcAuthErrorInfo **)
+wbcCredentialSave: wbcErr (const char *, const char *)
+wbcDcInfo: wbcErr (const char *, size_t *, const char ***, const char ***)
+wbcDomainInfo: wbcErr (const char *, struct wbcDomainInfo **)
+wbcEndgrent: wbcErr (void)
+wbcEndpwent: wbcErr (void)
+wbcErrorString: const char *(wbcErr)
+wbcFreeMemory: void (void *)
+wbcGetDisplayName: wbcErr (const struct wbcDomainSid *, char **, char **, enum wbcSidType *)
+wbcGetGroups: wbcErr (const char *, uint32_t *, gid_t **)
+wbcGetSidAliases: wbcErr (const struct wbcDomainSid *, struct wbcDomainSid *, uint32_t, uint32_t **, uint32_t *)
+wbcGetgrent: wbcErr (struct group **)
+wbcGetgrgid: wbcErr (gid_t, struct group **)
+wbcGetgrlist: wbcErr (struct group **)
+wbcGetgrnam: wbcErr (const char *, struct group **)
+wbcGetpwent: wbcErr (struct passwd **)
+wbcGetpwnam: wbcErr (const char *, struct passwd **)
+wbcGetpwsid: wbcErr (struct wbcDomainSid *, struct passwd **)
+wbcGetpwuid: wbcErr (uid_t, struct passwd **)
+wbcGidToSid: wbcErr (gid_t, struct wbcDomainSid *)
+wbcGuidToString: wbcErr (const struct wbcGuid *, char **)
+wbcInterfaceDetails: wbcErr (struct wbcInterfaceDetails **)
+wbcLibraryDetails: wbcErr (struct wbcLibraryDetails **)
+wbcListGroups: wbcErr (const char *, uint32_t *, const char ***)
+wbcListTrusts: wbcErr (struct wbcDomainInfo **, size_t *)
+wbcListUsers: wbcErr (const char *, uint32_t *, const char ***)
+wbcLogoffUser: wbcErr (const char *, uid_t, const char *)
+wbcLogoffUserEx: wbcErr (const struct wbcLogoffUserParams *, struct wbcAuthErrorInfo **)
+wbcLogonUser: wbcErr (const struct wbcLogonUserParams *, struct wbcLogonUserInfo **, struct wbcAuthErrorInfo **, struct wbcUserPasswordPolicyInfo **)
+wbcLookupDomainController: wbcErr (const char *, uint32_t, struct wbcDomainControllerInfo **)
+wbcLookupDomainControllerEx: wbcErr (const char *, struct wbcGuid *, const char *, uint32_t, struct wbcDomainControllerInfoEx **)
+wbcLookupName: wbcErr (const char *, const char *, struct wbcDomainSid *, enum wbcSidType *)
+wbcLookupRids: wbcErr (struct wbcDomainSid *, int, uint32_t *, const char **, const char ***, enum wbcSidType **)
+wbcLookupSid: wbcErr (const struct wbcDomainSid *, char **, char **, enum wbcSidType *)
+wbcLookupSids: wbcErr (const struct wbcDomainSid *, int, struct wbcDomainInfo **, int *, struct wbcTranslatedName **)
+wbcLookupUserSids: wbcErr (const struct wbcDomainSid *, bool, uint32_t *, struct wbcDomainSid **)
+wbcPing: wbcErr (void)
+wbcPingDc: wbcErr (const char *, struct wbcAuthErrorInfo **)
+wbcPingDc2: wbcErr (const char *, struct wbcAuthErrorInfo **, char **)
+wbcQueryGidToSid: wbcErr (gid_t, struct wbcDomainSid *)
+wbcQuerySidToGid: wbcErr (const struct wbcDomainSid *, gid_t *)
+wbcQuerySidToUid: wbcErr (const struct wbcDomainSid *, uid_t *)
+wbcQueryUidToSid: wbcErr (uid_t, struct wbcDomainSid *)
+wbcRemoveGidMapping: wbcErr (gid_t, const struct wbcDomainSid *)
+wbcRemoveUidMapping: wbcErr (uid_t, const struct wbcDomainSid *)
+wbcRequestResponse: wbcErr (int, struct winbindd_request *, struct winbindd_response *)
+wbcRequestResponsePriv: wbcErr (int, struct winbindd_request *, struct winbindd_response *)
+wbcResolveWinsByIP: wbcErr (const char *, char **)
+wbcResolveWinsByName: wbcErr (const char *, char **)
+wbcSetGidHwm: wbcErr (gid_t)
+wbcSetGidMapping: wbcErr (gid_t, const struct wbcDomainSid *)
+wbcSetUidHwm: wbcErr (uid_t)
+wbcSetUidMapping: wbcErr (uid_t, const struct wbcDomainSid *)
+wbcSetgrent: wbcErr (void)
+wbcSetpwent: wbcErr (void)
+wbcSidToGid: wbcErr (const struct wbcDomainSid *, gid_t *)
+wbcSidToString: wbcErr (const struct wbcDomainSid *, char **)
+wbcSidToStringBuf: int (const struct wbcDomainSid *, char *, int)
+wbcSidToUid: wbcErr (const struct wbcDomainSid *, uid_t *)
+wbcSidTypeString: const char *(enum wbcSidType)
+wbcSidsToUnixIds: wbcErr (const struct wbcDomainSid *, uint32_t, struct wbcUnixId *)
+wbcStrDup: char *(const char *)
+wbcStringToGuid: wbcErr (const char *, struct wbcGuid *)
+wbcStringToSid: wbcErr (const char *, struct wbcDomainSid *)
+wbcUidToSid: wbcErr (uid_t, struct wbcDomainSid *)
diff --git a/nsswitch/libwbclient/ABI/wbclient-0.12.sigs b/nsswitch/libwbclient/ABI/wbclient-0.12.sigs
new file mode 100644
index 0000000..3b71917
--- /dev/null
+++ b/nsswitch/libwbclient/ABI/wbclient-0.12.sigs
@@ -0,0 +1,130 @@
+wbcAddNamedBlob: wbcErr (size_t *, struct wbcNamedBlob **, const char *, uint32_t, uint8_t *, size_t)
+wbcAllocateGid: wbcErr (gid_t *)
+wbcAllocateMemory: void *(size_t, size_t, void (*)(void *))
+wbcAllocateStringArray: const char **(int)
+wbcAllocateUid: wbcErr (uid_t *)
+wbcAuthenticateUser: wbcErr (const char *, const char *)
+wbcAuthenticateUserEx: wbcErr (const struct wbcAuthUserParams *, struct wbcAuthUserInfo **, struct wbcAuthErrorInfo **)
+wbcChangeTrustCredentials: wbcErr (const char *, struct wbcAuthErrorInfo **)
+wbcChangeUserPassword: wbcErr (const char *, const char *, const char *)
+wbcChangeUserPasswordEx: wbcErr (const struct wbcChangePasswordParams *, struct wbcAuthErrorInfo **, enum wbcPasswordChangeRejectReason *, struct wbcUserPasswordPolicyInfo **)
+wbcCheckTrustCredentials: wbcErr (const char *, struct wbcAuthErrorInfo **)
+wbcCredentialCache: wbcErr (struct wbcCredentialCacheParams *, struct wbcCredentialCacheInfo **, struct wbcAuthErrorInfo **)
+wbcCredentialSave: wbcErr (const char *, const char *)
+wbcCtxAllocateGid: wbcErr (struct wbcContext *, gid_t *)
+wbcCtxAllocateUid: wbcErr (struct wbcContext *, uid_t *)
+wbcCtxAuthenticateUser: wbcErr (struct wbcContext *, const char *, const char *)
+wbcCtxAuthenticateUserEx: wbcErr (struct wbcContext *, const struct wbcAuthUserParams *, struct wbcAuthUserInfo **, struct wbcAuthErrorInfo **)
+wbcCtxChangeTrustCredentials: wbcErr (struct wbcContext *, const char *, struct wbcAuthErrorInfo **)
+wbcCtxChangeUserPassword: wbcErr (struct wbcContext *, const char *, const char *, const char *)
+wbcCtxChangeUserPasswordEx: wbcErr (struct wbcContext *, const struct wbcChangePasswordParams *, struct wbcAuthErrorInfo **, enum wbcPasswordChangeRejectReason *, struct wbcUserPasswordPolicyInfo **)
+wbcCtxCheckTrustCredentials: wbcErr (struct wbcContext *, const char *, struct wbcAuthErrorInfo **)
+wbcCtxCreate: struct wbcContext *(void)
+wbcCtxCredentialCache: wbcErr (struct wbcContext *, struct wbcCredentialCacheParams *, struct wbcCredentialCacheInfo **, struct wbcAuthErrorInfo **)
+wbcCtxCredentialSave: wbcErr (struct wbcContext *, const char *, const char *)
+wbcCtxDcInfo: wbcErr (struct wbcContext *, const char *, size_t *, const char ***, const char ***)
+wbcCtxDomainInfo: wbcErr (struct wbcContext *, const char *, struct wbcDomainInfo **)
+wbcCtxEndgrent: wbcErr (struct wbcContext *)
+wbcCtxEndpwent: wbcErr (struct wbcContext *)
+wbcCtxFree: void (struct wbcContext *)
+wbcCtxGetDisplayName: wbcErr (struct wbcContext *, const struct wbcDomainSid *, char **, char **, enum wbcSidType *)
+wbcCtxGetGroups: wbcErr (struct wbcContext *, const char *, uint32_t *, gid_t **)
+wbcCtxGetSidAliases: wbcErr (struct wbcContext *, const struct wbcDomainSid *, struct wbcDomainSid *, uint32_t, uint32_t **, uint32_t *)
+wbcCtxGetgrent: wbcErr (struct wbcContext *, struct group **)
+wbcCtxGetgrgid: wbcErr (struct wbcContext *, gid_t, struct group **)
+wbcCtxGetgrlist: wbcErr (struct wbcContext *, struct group **)
+wbcCtxGetgrnam: wbcErr (struct wbcContext *, const char *, struct group **)
+wbcCtxGetpwent: wbcErr (struct wbcContext *, struct passwd **)
+wbcCtxGetpwnam: wbcErr (struct wbcContext *, const char *, struct passwd **)
+wbcCtxGetpwsid: wbcErr (struct wbcContext *, struct wbcDomainSid *, struct passwd **)
+wbcCtxGetpwuid: wbcErr (struct wbcContext *, uid_t, struct passwd **)
+wbcCtxGidToSid: wbcErr (struct wbcContext *, gid_t, struct wbcDomainSid *)
+wbcCtxInterfaceDetails: wbcErr (struct wbcContext *, struct wbcInterfaceDetails **)
+wbcCtxListGroups: wbcErr (struct wbcContext *, const char *, uint32_t *, const char ***)
+wbcCtxListTrusts: wbcErr (struct wbcContext *, struct wbcDomainInfo **, size_t *)
+wbcCtxListUsers: wbcErr (struct wbcContext *, const char *, uint32_t *, const char ***)
+wbcCtxLogoffUser: wbcErr (struct wbcContext *, const char *, uid_t, const char *)
+wbcCtxLogoffUserEx: wbcErr (struct wbcContext *, const struct wbcLogoffUserParams *, struct wbcAuthErrorInfo **)
+wbcCtxLogonUser: wbcErr (struct wbcContext *, const struct wbcLogonUserParams *, struct wbcLogonUserInfo **, struct wbcAuthErrorInfo **, struct wbcUserPasswordPolicyInfo **)
+wbcCtxLookupDomainController: wbcErr (struct wbcContext *, const char *, uint32_t, struct wbcDomainControllerInfo **)
+wbcCtxLookupDomainControllerEx: wbcErr (struct wbcContext *, const char *, struct wbcGuid *, const char *, uint32_t, struct wbcDomainControllerInfoEx **)
+wbcCtxLookupName: wbcErr (struct wbcContext *, const char *, const char *, struct wbcDomainSid *, enum wbcSidType *)
+wbcCtxLookupRids: wbcErr (struct wbcContext *, struct wbcDomainSid *, int, uint32_t *, const char **, const char ***, enum wbcSidType **)
+wbcCtxLookupSid: wbcErr (struct wbcContext *, const struct wbcDomainSid *, char **, char **, enum wbcSidType *)
+wbcCtxLookupSids: wbcErr (struct wbcContext *, const struct wbcDomainSid *, int, struct wbcDomainInfo **, int *, struct wbcTranslatedName **)
+wbcCtxLookupUserSids: wbcErr (struct wbcContext *, const struct wbcDomainSid *, bool, uint32_t *, struct wbcDomainSid **)
+wbcCtxPing: wbcErr (struct wbcContext *)
+wbcCtxPingDc: wbcErr (struct wbcContext *, const char *, struct wbcAuthErrorInfo **)
+wbcCtxPingDc2: wbcErr (struct wbcContext *, const char *, struct wbcAuthErrorInfo **, char **)
+wbcCtxResolveWinsByIP: wbcErr (struct wbcContext *, const char *, char **)
+wbcCtxResolveWinsByName: wbcErr (struct wbcContext *, const char *, char **)
+wbcCtxSetgrent: wbcErr (struct wbcContext *)
+wbcCtxSetpwent: wbcErr (struct wbcContext *)
+wbcCtxSidToGid: wbcErr (struct wbcContext *, const struct wbcDomainSid *, gid_t *)
+wbcCtxSidToUid: wbcErr (struct wbcContext *, const struct wbcDomainSid *, uid_t *)
+wbcCtxSidsToUnixIds: wbcErr (struct wbcContext *, const struct wbcDomainSid *, uint32_t, struct wbcUnixId *)
+wbcCtxUidToSid: wbcErr (struct wbcContext *, uid_t, struct wbcDomainSid *)
+wbcDcInfo: wbcErr (const char *, size_t *, const char ***, const char ***)
+wbcDomainInfo: wbcErr (const char *, struct wbcDomainInfo **)
+wbcEndgrent: wbcErr (void)
+wbcEndpwent: wbcErr (void)
+wbcErrorString: const char *(wbcErr)
+wbcFreeMemory: void (void *)
+wbcGetDisplayName: wbcErr (const struct wbcDomainSid *, char **, char **, enum wbcSidType *)
+wbcGetGlobalCtx: struct wbcContext *(void)
+wbcGetGroups: wbcErr (const char *, uint32_t *, gid_t **)
+wbcGetSidAliases: wbcErr (const struct wbcDomainSid *, struct wbcDomainSid *, uint32_t, uint32_t **, uint32_t *)
+wbcGetgrent: wbcErr (struct group **)
+wbcGetgrgid: wbcErr (gid_t, struct group **)
+wbcGetgrlist: wbcErr (struct group **)
+wbcGetgrnam: wbcErr (const char *, struct group **)
+wbcGetpwent: wbcErr (struct passwd **)
+wbcGetpwnam: wbcErr (const char *, struct passwd **)
+wbcGetpwsid: wbcErr (struct wbcDomainSid *, struct passwd **)
+wbcGetpwuid: wbcErr (uid_t, struct passwd **)
+wbcGidToSid: wbcErr (gid_t, struct wbcDomainSid *)
+wbcGuidToString: wbcErr (const struct wbcGuid *, char **)
+wbcInterfaceDetails: wbcErr (struct wbcInterfaceDetails **)
+wbcLibraryDetails: wbcErr (struct wbcLibraryDetails **)
+wbcListGroups: wbcErr (const char *, uint32_t *, const char ***)
+wbcListTrusts: wbcErr (struct wbcDomainInfo **, size_t *)
+wbcListUsers: wbcErr (const char *, uint32_t *, const char ***)
+wbcLogoffUser: wbcErr (const char *, uid_t, const char *)
+wbcLogoffUserEx: wbcErr (const struct wbcLogoffUserParams *, struct wbcAuthErrorInfo **)
+wbcLogonUser: wbcErr (const struct wbcLogonUserParams *, struct wbcLogonUserInfo **, struct wbcAuthErrorInfo **, struct wbcUserPasswordPolicyInfo **)
+wbcLookupDomainController: wbcErr (const char *, uint32_t, struct wbcDomainControllerInfo **)
+wbcLookupDomainControllerEx: wbcErr (const char *, struct wbcGuid *, const char *, uint32_t, struct wbcDomainControllerInfoEx **)
+wbcLookupName: wbcErr (const char *, const char *, struct wbcDomainSid *, enum wbcSidType *)
+wbcLookupRids: wbcErr (struct wbcDomainSid *, int, uint32_t *, const char **, const char ***, enum wbcSidType **)
+wbcLookupSid: wbcErr (const struct wbcDomainSid *, char **, char **, enum wbcSidType *)
+wbcLookupSids: wbcErr (const struct wbcDomainSid *, int, struct wbcDomainInfo **, int *, struct wbcTranslatedName **)
+wbcLookupUserSids: wbcErr (const struct wbcDomainSid *, bool, uint32_t *, struct wbcDomainSid **)
+wbcPing: wbcErr (void)
+wbcPingDc: wbcErr (const char *, struct wbcAuthErrorInfo **)
+wbcPingDc2: wbcErr (const char *, struct wbcAuthErrorInfo **, char **)
+wbcQueryGidToSid: wbcErr (gid_t, struct wbcDomainSid *)
+wbcQuerySidToGid: wbcErr (const struct wbcDomainSid *, gid_t *)
+wbcQuerySidToUid: wbcErr (const struct wbcDomainSid *, uid_t *)
+wbcQueryUidToSid: wbcErr (uid_t, struct wbcDomainSid *)
+wbcRemoveGidMapping: wbcErr (gid_t, const struct wbcDomainSid *)
+wbcRemoveUidMapping: wbcErr (uid_t, const struct wbcDomainSid *)
+wbcRequestResponse: wbcErr (struct wbcContext *, int, struct winbindd_request *, struct winbindd_response *)
+wbcRequestResponsePriv: wbcErr (struct wbcContext *, int, struct winbindd_request *, struct winbindd_response *)
+wbcResolveWinsByIP: wbcErr (const char *, char **)
+wbcResolveWinsByName: wbcErr (const char *, char **)
+wbcSetGidHwm: wbcErr (gid_t)
+wbcSetGidMapping: wbcErr (gid_t, const struct wbcDomainSid *)
+wbcSetUidHwm: wbcErr (uid_t)
+wbcSetUidMapping: wbcErr (uid_t, const struct wbcDomainSid *)
+wbcSetgrent: wbcErr (void)
+wbcSetpwent: wbcErr (void)
+wbcSidToGid: wbcErr (const struct wbcDomainSid *, gid_t *)
+wbcSidToString: wbcErr (const struct wbcDomainSid *, char **)
+wbcSidToStringBuf: int (const struct wbcDomainSid *, char *, int)
+wbcSidToUid: wbcErr (const struct wbcDomainSid *, uid_t *)
+wbcSidTypeString: const char *(enum wbcSidType)
+wbcSidsToUnixIds: wbcErr (const struct wbcDomainSid *, uint32_t, struct wbcUnixId *)
+wbcStrDup: char *(const char *)
+wbcStringToGuid: wbcErr (const char *, struct wbcGuid *)
+wbcStringToSid: wbcErr (const char *, struct wbcDomainSid *)
+wbcUidToSid: wbcErr (uid_t, struct wbcDomainSid *)
diff --git a/nsswitch/libwbclient/ABI/wbclient-0.13.sigs b/nsswitch/libwbclient/ABI/wbclient-0.13.sigs
new file mode 100644
index 0000000..b07a6a8
--- /dev/null
+++ b/nsswitch/libwbclient/ABI/wbclient-0.13.sigs
@@ -0,0 +1,132 @@
+wbcAddNamedBlob: wbcErr (size_t *, struct wbcNamedBlob **, const char *, uint32_t, uint8_t *, size_t)
+wbcAllocateGid: wbcErr (gid_t *)
+wbcAllocateMemory: void *(size_t, size_t, void (*)(void *))
+wbcAllocateStringArray: const char **(int)
+wbcAllocateUid: wbcErr (uid_t *)
+wbcAuthenticateUser: wbcErr (const char *, const char *)
+wbcAuthenticateUserEx: wbcErr (const struct wbcAuthUserParams *, struct wbcAuthUserInfo **, struct wbcAuthErrorInfo **)
+wbcChangeTrustCredentials: wbcErr (const char *, struct wbcAuthErrorInfo **)
+wbcChangeUserPassword: wbcErr (const char *, const char *, const char *)
+wbcChangeUserPasswordEx: wbcErr (const struct wbcChangePasswordParams *, struct wbcAuthErrorInfo **, enum wbcPasswordChangeRejectReason *, struct wbcUserPasswordPolicyInfo **)
+wbcCheckTrustCredentials: wbcErr (const char *, struct wbcAuthErrorInfo **)
+wbcCredentialCache: wbcErr (struct wbcCredentialCacheParams *, struct wbcCredentialCacheInfo **, struct wbcAuthErrorInfo **)
+wbcCredentialSave: wbcErr (const char *, const char *)
+wbcCtxAllocateGid: wbcErr (struct wbcContext *, gid_t *)
+wbcCtxAllocateUid: wbcErr (struct wbcContext *, uid_t *)
+wbcCtxAuthenticateUser: wbcErr (struct wbcContext *, const char *, const char *)
+wbcCtxAuthenticateUserEx: wbcErr (struct wbcContext *, const struct wbcAuthUserParams *, struct wbcAuthUserInfo **, struct wbcAuthErrorInfo **)
+wbcCtxChangeTrustCredentials: wbcErr (struct wbcContext *, const char *, struct wbcAuthErrorInfo **)
+wbcCtxChangeUserPassword: wbcErr (struct wbcContext *, const char *, const char *, const char *)
+wbcCtxChangeUserPasswordEx: wbcErr (struct wbcContext *, const struct wbcChangePasswordParams *, struct wbcAuthErrorInfo **, enum wbcPasswordChangeRejectReason *, struct wbcUserPasswordPolicyInfo **)
+wbcCtxCheckTrustCredentials: wbcErr (struct wbcContext *, const char *, struct wbcAuthErrorInfo **)
+wbcCtxCreate: struct wbcContext *(void)
+wbcCtxCredentialCache: wbcErr (struct wbcContext *, struct wbcCredentialCacheParams *, struct wbcCredentialCacheInfo **, struct wbcAuthErrorInfo **)
+wbcCtxCredentialSave: wbcErr (struct wbcContext *, const char *, const char *)
+wbcCtxDcInfo: wbcErr (struct wbcContext *, const char *, size_t *, const char ***, const char ***)
+wbcCtxDomainInfo: wbcErr (struct wbcContext *, const char *, struct wbcDomainInfo **)
+wbcCtxEndgrent: wbcErr (struct wbcContext *)
+wbcCtxEndpwent: wbcErr (struct wbcContext *)
+wbcCtxFree: void (struct wbcContext *)
+wbcCtxGetDisplayName: wbcErr (struct wbcContext *, const struct wbcDomainSid *, char **, char **, enum wbcSidType *)
+wbcCtxGetGroups: wbcErr (struct wbcContext *, const char *, uint32_t *, gid_t **)
+wbcCtxGetSidAliases: wbcErr (struct wbcContext *, const struct wbcDomainSid *, struct wbcDomainSid *, uint32_t, uint32_t **, uint32_t *)
+wbcCtxGetgrent: wbcErr (struct wbcContext *, struct group **)
+wbcCtxGetgrgid: wbcErr (struct wbcContext *, gid_t, struct group **)
+wbcCtxGetgrlist: wbcErr (struct wbcContext *, struct group **)
+wbcCtxGetgrnam: wbcErr (struct wbcContext *, const char *, struct group **)
+wbcCtxGetpwent: wbcErr (struct wbcContext *, struct passwd **)
+wbcCtxGetpwnam: wbcErr (struct wbcContext *, const char *, struct passwd **)
+wbcCtxGetpwsid: wbcErr (struct wbcContext *, struct wbcDomainSid *, struct passwd **)
+wbcCtxGetpwuid: wbcErr (struct wbcContext *, uid_t, struct passwd **)
+wbcCtxGidToSid: wbcErr (struct wbcContext *, gid_t, struct wbcDomainSid *)
+wbcCtxInterfaceDetails: wbcErr (struct wbcContext *, struct wbcInterfaceDetails **)
+wbcCtxListGroups: wbcErr (struct wbcContext *, const char *, uint32_t *, const char ***)
+wbcCtxListTrusts: wbcErr (struct wbcContext *, struct wbcDomainInfo **, size_t *)
+wbcCtxListUsers: wbcErr (struct wbcContext *, const char *, uint32_t *, const char ***)
+wbcCtxLogoffUser: wbcErr (struct wbcContext *, const char *, uid_t, const char *)
+wbcCtxLogoffUserEx: wbcErr (struct wbcContext *, const struct wbcLogoffUserParams *, struct wbcAuthErrorInfo **)
+wbcCtxLogonUser: wbcErr (struct wbcContext *, const struct wbcLogonUserParams *, struct wbcLogonUserInfo **, struct wbcAuthErrorInfo **, struct wbcUserPasswordPolicyInfo **)
+wbcCtxLookupDomainController: wbcErr (struct wbcContext *, const char *, uint32_t, struct wbcDomainControllerInfo **)
+wbcCtxLookupDomainControllerEx: wbcErr (struct wbcContext *, const char *, struct wbcGuid *, const char *, uint32_t, struct wbcDomainControllerInfoEx **)
+wbcCtxLookupName: wbcErr (struct wbcContext *, const char *, const char *, struct wbcDomainSid *, enum wbcSidType *)
+wbcCtxLookupRids: wbcErr (struct wbcContext *, struct wbcDomainSid *, int, uint32_t *, const char **, const char ***, enum wbcSidType **)
+wbcCtxLookupSid: wbcErr (struct wbcContext *, const struct wbcDomainSid *, char **, char **, enum wbcSidType *)
+wbcCtxLookupSids: wbcErr (struct wbcContext *, const struct wbcDomainSid *, int, struct wbcDomainInfo **, int *, struct wbcTranslatedName **)
+wbcCtxLookupUserSids: wbcErr (struct wbcContext *, const struct wbcDomainSid *, bool, uint32_t *, struct wbcDomainSid **)
+wbcCtxPing: wbcErr (struct wbcContext *)
+wbcCtxPingDc: wbcErr (struct wbcContext *, const char *, struct wbcAuthErrorInfo **)
+wbcCtxPingDc2: wbcErr (struct wbcContext *, const char *, struct wbcAuthErrorInfo **, char **)
+wbcCtxResolveWinsByIP: wbcErr (struct wbcContext *, const char *, char **)
+wbcCtxResolveWinsByName: wbcErr (struct wbcContext *, const char *, char **)
+wbcCtxSetgrent: wbcErr (struct wbcContext *)
+wbcCtxSetpwent: wbcErr (struct wbcContext *)
+wbcCtxSidToGid: wbcErr (struct wbcContext *, const struct wbcDomainSid *, gid_t *)
+wbcCtxSidToUid: wbcErr (struct wbcContext *, const struct wbcDomainSid *, uid_t *)
+wbcCtxSidsToUnixIds: wbcErr (struct wbcContext *, const struct wbcDomainSid *, uint32_t, struct wbcUnixId *)
+wbcCtxUidToSid: wbcErr (struct wbcContext *, uid_t, struct wbcDomainSid *)
+wbcCtxUnixIdsToSids: wbcErr (struct wbcContext *, const struct wbcUnixId *, uint32_t, struct wbcDomainSid *)
+wbcDcInfo: wbcErr (const char *, size_t *, const char ***, const char ***)
+wbcDomainInfo: wbcErr (const char *, struct wbcDomainInfo **)
+wbcEndgrent: wbcErr (void)
+wbcEndpwent: wbcErr (void)
+wbcErrorString: const char *(wbcErr)
+wbcFreeMemory: void (void *)
+wbcGetDisplayName: wbcErr (const struct wbcDomainSid *, char **, char **, enum wbcSidType *)
+wbcGetGlobalCtx: struct wbcContext *(void)
+wbcGetGroups: wbcErr (const char *, uint32_t *, gid_t **)
+wbcGetSidAliases: wbcErr (const struct wbcDomainSid *, struct wbcDomainSid *, uint32_t, uint32_t **, uint32_t *)
+wbcGetgrent: wbcErr (struct group **)
+wbcGetgrgid: wbcErr (gid_t, struct group **)
+wbcGetgrlist: wbcErr (struct group **)
+wbcGetgrnam: wbcErr (const char *, struct group **)
+wbcGetpwent: wbcErr (struct passwd **)
+wbcGetpwnam: wbcErr (const char *, struct passwd **)
+wbcGetpwsid: wbcErr (struct wbcDomainSid *, struct passwd **)
+wbcGetpwuid: wbcErr (uid_t, struct passwd **)
+wbcGidToSid: wbcErr (gid_t, struct wbcDomainSid *)
+wbcGuidToString: wbcErr (const struct wbcGuid *, char **)
+wbcInterfaceDetails: wbcErr (struct wbcInterfaceDetails **)
+wbcLibraryDetails: wbcErr (struct wbcLibraryDetails **)
+wbcListGroups: wbcErr (const char *, uint32_t *, const char ***)
+wbcListTrusts: wbcErr (struct wbcDomainInfo **, size_t *)
+wbcListUsers: wbcErr (const char *, uint32_t *, const char ***)
+wbcLogoffUser: wbcErr (const char *, uid_t, const char *)
+wbcLogoffUserEx: wbcErr (const struct wbcLogoffUserParams *, struct wbcAuthErrorInfo **)
+wbcLogonUser: wbcErr (const struct wbcLogonUserParams *, struct wbcLogonUserInfo **, struct wbcAuthErrorInfo **, struct wbcUserPasswordPolicyInfo **)
+wbcLookupDomainController: wbcErr (const char *, uint32_t, struct wbcDomainControllerInfo **)
+wbcLookupDomainControllerEx: wbcErr (const char *, struct wbcGuid *, const char *, uint32_t, struct wbcDomainControllerInfoEx **)
+wbcLookupName: wbcErr (const char *, const char *, struct wbcDomainSid *, enum wbcSidType *)
+wbcLookupRids: wbcErr (struct wbcDomainSid *, int, uint32_t *, const char **, const char ***, enum wbcSidType **)
+wbcLookupSid: wbcErr (const struct wbcDomainSid *, char **, char **, enum wbcSidType *)
+wbcLookupSids: wbcErr (const struct wbcDomainSid *, int, struct wbcDomainInfo **, int *, struct wbcTranslatedName **)
+wbcLookupUserSids: wbcErr (const struct wbcDomainSid *, bool, uint32_t *, struct wbcDomainSid **)
+wbcPing: wbcErr (void)
+wbcPingDc: wbcErr (const char *, struct wbcAuthErrorInfo **)
+wbcPingDc2: wbcErr (const char *, struct wbcAuthErrorInfo **, char **)
+wbcQueryGidToSid: wbcErr (gid_t, struct wbcDomainSid *)
+wbcQuerySidToGid: wbcErr (const struct wbcDomainSid *, gid_t *)
+wbcQuerySidToUid: wbcErr (const struct wbcDomainSid *, uid_t *)
+wbcQueryUidToSid: wbcErr (uid_t, struct wbcDomainSid *)
+wbcRemoveGidMapping: wbcErr (gid_t, const struct wbcDomainSid *)
+wbcRemoveUidMapping: wbcErr (uid_t, const struct wbcDomainSid *)
+wbcRequestResponse: wbcErr (struct wbcContext *, int, struct winbindd_request *, struct winbindd_response *)
+wbcRequestResponsePriv: wbcErr (struct wbcContext *, int, struct winbindd_request *, struct winbindd_response *)
+wbcResolveWinsByIP: wbcErr (const char *, char **)
+wbcResolveWinsByName: wbcErr (const char *, char **)
+wbcSetGidHwm: wbcErr (gid_t)
+wbcSetGidMapping: wbcErr (gid_t, const struct wbcDomainSid *)
+wbcSetUidHwm: wbcErr (uid_t)
+wbcSetUidMapping: wbcErr (uid_t, const struct wbcDomainSid *)
+wbcSetgrent: wbcErr (void)
+wbcSetpwent: wbcErr (void)
+wbcSidToGid: wbcErr (const struct wbcDomainSid *, gid_t *)
+wbcSidToString: wbcErr (const struct wbcDomainSid *, char **)
+wbcSidToStringBuf: int (const struct wbcDomainSid *, char *, int)
+wbcSidToUid: wbcErr (const struct wbcDomainSid *, uid_t *)
+wbcSidTypeString: const char *(enum wbcSidType)
+wbcSidsToUnixIds: wbcErr (const struct wbcDomainSid *, uint32_t, struct wbcUnixId *)
+wbcStrDup: char *(const char *)
+wbcStringToGuid: wbcErr (const char *, struct wbcGuid *)
+wbcStringToSid: wbcErr (const char *, struct wbcDomainSid *)
+wbcUidToSid: wbcErr (uid_t, struct wbcDomainSid *)
+wbcUnixIdsToSids: wbcErr (const struct wbcUnixId *, uint32_t, struct wbcDomainSid *)
diff --git a/nsswitch/libwbclient/ABI/wbclient-0.14.sigs b/nsswitch/libwbclient/ABI/wbclient-0.14.sigs
new file mode 100644
index 0000000..b07a6a8
--- /dev/null
+++ b/nsswitch/libwbclient/ABI/wbclient-0.14.sigs
@@ -0,0 +1,132 @@
+wbcAddNamedBlob: wbcErr (size_t *, struct wbcNamedBlob **, const char *, uint32_t, uint8_t *, size_t)
+wbcAllocateGid: wbcErr (gid_t *)
+wbcAllocateMemory: void *(size_t, size_t, void (*)(void *))
+wbcAllocateStringArray: const char **(int)
+wbcAllocateUid: wbcErr (uid_t *)
+wbcAuthenticateUser: wbcErr (const char *, const char *)
+wbcAuthenticateUserEx: wbcErr (const struct wbcAuthUserParams *, struct wbcAuthUserInfo **, struct wbcAuthErrorInfo **)
+wbcChangeTrustCredentials: wbcErr (const char *, struct wbcAuthErrorInfo **)
+wbcChangeUserPassword: wbcErr (const char *, const char *, const char *)
+wbcChangeUserPasswordEx: wbcErr (const struct wbcChangePasswordParams *, struct wbcAuthErrorInfo **, enum wbcPasswordChangeRejectReason *, struct wbcUserPasswordPolicyInfo **)
+wbcCheckTrustCredentials: wbcErr (const char *, struct wbcAuthErrorInfo **)
+wbcCredentialCache: wbcErr (struct wbcCredentialCacheParams *, struct wbcCredentialCacheInfo **, struct wbcAuthErrorInfo **)
+wbcCredentialSave: wbcErr (const char *, const char *)
+wbcCtxAllocateGid: wbcErr (struct wbcContext *, gid_t *)
+wbcCtxAllocateUid: wbcErr (struct wbcContext *, uid_t *)
+wbcCtxAuthenticateUser: wbcErr (struct wbcContext *, const char *, const char *)
+wbcCtxAuthenticateUserEx: wbcErr (struct wbcContext *, const struct wbcAuthUserParams *, struct wbcAuthUserInfo **, struct wbcAuthErrorInfo **)
+wbcCtxChangeTrustCredentials: wbcErr (struct wbcContext *, const char *, struct wbcAuthErrorInfo **)
+wbcCtxChangeUserPassword: wbcErr (struct wbcContext *, const char *, const char *, const char *)
+wbcCtxChangeUserPasswordEx: wbcErr (struct wbcContext *, const struct wbcChangePasswordParams *, struct wbcAuthErrorInfo **, enum wbcPasswordChangeRejectReason *, struct wbcUserPasswordPolicyInfo **)
+wbcCtxCheckTrustCredentials: wbcErr (struct wbcContext *, const char *, struct wbcAuthErrorInfo **)
+wbcCtxCreate: struct wbcContext *(void)
+wbcCtxCredentialCache: wbcErr (struct wbcContext *, struct wbcCredentialCacheParams *, struct wbcCredentialCacheInfo **, struct wbcAuthErrorInfo **)
+wbcCtxCredentialSave: wbcErr (struct wbcContext *, const char *, const char *)
+wbcCtxDcInfo: wbcErr (struct wbcContext *, const char *, size_t *, const char ***, const char ***)
+wbcCtxDomainInfo: wbcErr (struct wbcContext *, const char *, struct wbcDomainInfo **)
+wbcCtxEndgrent: wbcErr (struct wbcContext *)
+wbcCtxEndpwent: wbcErr (struct wbcContext *)
+wbcCtxFree: void (struct wbcContext *)
+wbcCtxGetDisplayName: wbcErr (struct wbcContext *, const struct wbcDomainSid *, char **, char **, enum wbcSidType *)
+wbcCtxGetGroups: wbcErr (struct wbcContext *, const char *, uint32_t *, gid_t **)
+wbcCtxGetSidAliases: wbcErr (struct wbcContext *, const struct wbcDomainSid *, struct wbcDomainSid *, uint32_t, uint32_t **, uint32_t *)
+wbcCtxGetgrent: wbcErr (struct wbcContext *, struct group **)
+wbcCtxGetgrgid: wbcErr (struct wbcContext *, gid_t, struct group **)
+wbcCtxGetgrlist: wbcErr (struct wbcContext *, struct group **)
+wbcCtxGetgrnam: wbcErr (struct wbcContext *, const char *, struct group **)
+wbcCtxGetpwent: wbcErr (struct wbcContext *, struct passwd **)
+wbcCtxGetpwnam: wbcErr (struct wbcContext *, const char *, struct passwd **)
+wbcCtxGetpwsid: wbcErr (struct wbcContext *, struct wbcDomainSid *, struct passwd **)
+wbcCtxGetpwuid: wbcErr (struct wbcContext *, uid_t, struct passwd **)
+wbcCtxGidToSid: wbcErr (struct wbcContext *, gid_t, struct wbcDomainSid *)
+wbcCtxInterfaceDetails: wbcErr (struct wbcContext *, struct wbcInterfaceDetails **)
+wbcCtxListGroups: wbcErr (struct wbcContext *, const char *, uint32_t *, const char ***)
+wbcCtxListTrusts: wbcErr (struct wbcContext *, struct wbcDomainInfo **, size_t *)
+wbcCtxListUsers: wbcErr (struct wbcContext *, const char *, uint32_t *, const char ***)
+wbcCtxLogoffUser: wbcErr (struct wbcContext *, const char *, uid_t, const char *)
+wbcCtxLogoffUserEx: wbcErr (struct wbcContext *, const struct wbcLogoffUserParams *, struct wbcAuthErrorInfo **)
+wbcCtxLogonUser: wbcErr (struct wbcContext *, const struct wbcLogonUserParams *, struct wbcLogonUserInfo **, struct wbcAuthErrorInfo **, struct wbcUserPasswordPolicyInfo **)
+wbcCtxLookupDomainController: wbcErr (struct wbcContext *, const char *, uint32_t, struct wbcDomainControllerInfo **)
+wbcCtxLookupDomainControllerEx: wbcErr (struct wbcContext *, const char *, struct wbcGuid *, const char *, uint32_t, struct wbcDomainControllerInfoEx **)
+wbcCtxLookupName: wbcErr (struct wbcContext *, const char *, const char *, struct wbcDomainSid *, enum wbcSidType *)
+wbcCtxLookupRids: wbcErr (struct wbcContext *, struct wbcDomainSid *, int, uint32_t *, const char **, const char ***, enum wbcSidType **)
+wbcCtxLookupSid: wbcErr (struct wbcContext *, const struct wbcDomainSid *, char **, char **, enum wbcSidType *)
+wbcCtxLookupSids: wbcErr (struct wbcContext *, const struct wbcDomainSid *, int, struct wbcDomainInfo **, int *, struct wbcTranslatedName **)
+wbcCtxLookupUserSids: wbcErr (struct wbcContext *, const struct wbcDomainSid *, bool, uint32_t *, struct wbcDomainSid **)
+wbcCtxPing: wbcErr (struct wbcContext *)
+wbcCtxPingDc: wbcErr (struct wbcContext *, const char *, struct wbcAuthErrorInfo **)
+wbcCtxPingDc2: wbcErr (struct wbcContext *, const char *, struct wbcAuthErrorInfo **, char **)
+wbcCtxResolveWinsByIP: wbcErr (struct wbcContext *, const char *, char **)
+wbcCtxResolveWinsByName: wbcErr (struct wbcContext *, const char *, char **)
+wbcCtxSetgrent: wbcErr (struct wbcContext *)
+wbcCtxSetpwent: wbcErr (struct wbcContext *)
+wbcCtxSidToGid: wbcErr (struct wbcContext *, const struct wbcDomainSid *, gid_t *)
+wbcCtxSidToUid: wbcErr (struct wbcContext *, const struct wbcDomainSid *, uid_t *)
+wbcCtxSidsToUnixIds: wbcErr (struct wbcContext *, const struct wbcDomainSid *, uint32_t, struct wbcUnixId *)
+wbcCtxUidToSid: wbcErr (struct wbcContext *, uid_t, struct wbcDomainSid *)
+wbcCtxUnixIdsToSids: wbcErr (struct wbcContext *, const struct wbcUnixId *, uint32_t, struct wbcDomainSid *)
+wbcDcInfo: wbcErr (const char *, size_t *, const char ***, const char ***)
+wbcDomainInfo: wbcErr (const char *, struct wbcDomainInfo **)
+wbcEndgrent: wbcErr (void)
+wbcEndpwent: wbcErr (void)
+wbcErrorString: const char *(wbcErr)
+wbcFreeMemory: void (void *)
+wbcGetDisplayName: wbcErr (const struct wbcDomainSid *, char **, char **, enum wbcSidType *)
+wbcGetGlobalCtx: struct wbcContext *(void)
+wbcGetGroups: wbcErr (const char *, uint32_t *, gid_t **)
+wbcGetSidAliases: wbcErr (const struct wbcDomainSid *, struct wbcDomainSid *, uint32_t, uint32_t **, uint32_t *)
+wbcGetgrent: wbcErr (struct group **)
+wbcGetgrgid: wbcErr (gid_t, struct group **)
+wbcGetgrlist: wbcErr (struct group **)
+wbcGetgrnam: wbcErr (const char *, struct group **)
+wbcGetpwent: wbcErr (struct passwd **)
+wbcGetpwnam: wbcErr (const char *, struct passwd **)
+wbcGetpwsid: wbcErr (struct wbcDomainSid *, struct passwd **)
+wbcGetpwuid: wbcErr (uid_t, struct passwd **)
+wbcGidToSid: wbcErr (gid_t, struct wbcDomainSid *)
+wbcGuidToString: wbcErr (const struct wbcGuid *, char **)
+wbcInterfaceDetails: wbcErr (struct wbcInterfaceDetails **)
+wbcLibraryDetails: wbcErr (struct wbcLibraryDetails **)
+wbcListGroups: wbcErr (const char *, uint32_t *, const char ***)
+wbcListTrusts: wbcErr (struct wbcDomainInfo **, size_t *)
+wbcListUsers: wbcErr (const char *, uint32_t *, const char ***)
+wbcLogoffUser: wbcErr (const char *, uid_t, const char *)
+wbcLogoffUserEx: wbcErr (const struct wbcLogoffUserParams *, struct wbcAuthErrorInfo **)
+wbcLogonUser: wbcErr (const struct wbcLogonUserParams *, struct wbcLogonUserInfo **, struct wbcAuthErrorInfo **, struct wbcUserPasswordPolicyInfo **)
+wbcLookupDomainController: wbcErr (const char *, uint32_t, struct wbcDomainControllerInfo **)
+wbcLookupDomainControllerEx: wbcErr (const char *, struct wbcGuid *, const char *, uint32_t, struct wbcDomainControllerInfoEx **)
+wbcLookupName: wbcErr (const char *, const char *, struct wbcDomainSid *, enum wbcSidType *)
+wbcLookupRids: wbcErr (struct wbcDomainSid *, int, uint32_t *, const char **, const char ***, enum wbcSidType **)
+wbcLookupSid: wbcErr (const struct wbcDomainSid *, char **, char **, enum wbcSidType *)
+wbcLookupSids: wbcErr (const struct wbcDomainSid *, int, struct wbcDomainInfo **, int *, struct wbcTranslatedName **)
+wbcLookupUserSids: wbcErr (const struct wbcDomainSid *, bool, uint32_t *, struct wbcDomainSid **)
+wbcPing: wbcErr (void)
+wbcPingDc: wbcErr (const char *, struct wbcAuthErrorInfo **)
+wbcPingDc2: wbcErr (const char *, struct wbcAuthErrorInfo **, char **)
+wbcQueryGidToSid: wbcErr (gid_t, struct wbcDomainSid *)
+wbcQuerySidToGid: wbcErr (const struct wbcDomainSid *, gid_t *)
+wbcQuerySidToUid: wbcErr (const struct wbcDomainSid *, uid_t *)
+wbcQueryUidToSid: wbcErr (uid_t, struct wbcDomainSid *)
+wbcRemoveGidMapping: wbcErr (gid_t, const struct wbcDomainSid *)
+wbcRemoveUidMapping: wbcErr (uid_t, const struct wbcDomainSid *)
+wbcRequestResponse: wbcErr (struct wbcContext *, int, struct winbindd_request *, struct winbindd_response *)
+wbcRequestResponsePriv: wbcErr (struct wbcContext *, int, struct winbindd_request *, struct winbindd_response *)
+wbcResolveWinsByIP: wbcErr (const char *, char **)
+wbcResolveWinsByName: wbcErr (const char *, char **)
+wbcSetGidHwm: wbcErr (gid_t)
+wbcSetGidMapping: wbcErr (gid_t, const struct wbcDomainSid *)
+wbcSetUidHwm: wbcErr (uid_t)
+wbcSetUidMapping: wbcErr (uid_t, const struct wbcDomainSid *)
+wbcSetgrent: wbcErr (void)
+wbcSetpwent: wbcErr (void)
+wbcSidToGid: wbcErr (const struct wbcDomainSid *, gid_t *)
+wbcSidToString: wbcErr (const struct wbcDomainSid *, char **)
+wbcSidToStringBuf: int (const struct wbcDomainSid *, char *, int)
+wbcSidToUid: wbcErr (const struct wbcDomainSid *, uid_t *)
+wbcSidTypeString: const char *(enum wbcSidType)
+wbcSidsToUnixIds: wbcErr (const struct wbcDomainSid *, uint32_t, struct wbcUnixId *)
+wbcStrDup: char *(const char *)
+wbcStringToGuid: wbcErr (const char *, struct wbcGuid *)
+wbcStringToSid: wbcErr (const char *, struct wbcDomainSid *)
+wbcUidToSid: wbcErr (uid_t, struct wbcDomainSid *)
+wbcUnixIdsToSids: wbcErr (const struct wbcUnixId *, uint32_t, struct wbcDomainSid *)
diff --git a/nsswitch/libwbclient/ABI/wbclient-0.15.sigs b/nsswitch/libwbclient/ABI/wbclient-0.15.sigs
new file mode 100644
index 0000000..a3019b5
--- /dev/null
+++ b/nsswitch/libwbclient/ABI/wbclient-0.15.sigs
@@ -0,0 +1,133 @@
+wbcAddNamedBlob: wbcErr (size_t *, struct wbcNamedBlob **, const char *, uint32_t, uint8_t *, size_t)
+wbcAllocateGid: wbcErr (gid_t *)
+wbcAllocateMemory: void *(size_t, size_t, void (*)(void *))
+wbcAllocateStringArray: const char **(int)
+wbcAllocateUid: wbcErr (uid_t *)
+wbcAuthenticateUser: wbcErr (const char *, const char *)
+wbcAuthenticateUserEx: wbcErr (const struct wbcAuthUserParams *, struct wbcAuthUserInfo **, struct wbcAuthErrorInfo **)
+wbcChangeTrustCredentials: wbcErr (const char *, struct wbcAuthErrorInfo **)
+wbcChangeUserPassword: wbcErr (const char *, const char *, const char *)
+wbcChangeUserPasswordEx: wbcErr (const struct wbcChangePasswordParams *, struct wbcAuthErrorInfo **, enum wbcPasswordChangeRejectReason *, struct wbcUserPasswordPolicyInfo **)
+wbcCheckTrustCredentials: wbcErr (const char *, struct wbcAuthErrorInfo **)
+wbcCredentialCache: wbcErr (struct wbcCredentialCacheParams *, struct wbcCredentialCacheInfo **, struct wbcAuthErrorInfo **)
+wbcCredentialSave: wbcErr (const char *, const char *)
+wbcCtxAllocateGid: wbcErr (struct wbcContext *, gid_t *)
+wbcCtxAllocateUid: wbcErr (struct wbcContext *, uid_t *)
+wbcCtxAuthenticateUser: wbcErr (struct wbcContext *, const char *, const char *)
+wbcCtxAuthenticateUserEx: wbcErr (struct wbcContext *, const struct wbcAuthUserParams *, struct wbcAuthUserInfo **, struct wbcAuthErrorInfo **)
+wbcCtxChangeTrustCredentials: wbcErr (struct wbcContext *, const char *, struct wbcAuthErrorInfo **)
+wbcCtxChangeUserPassword: wbcErr (struct wbcContext *, const char *, const char *, const char *)
+wbcCtxChangeUserPasswordEx: wbcErr (struct wbcContext *, const struct wbcChangePasswordParams *, struct wbcAuthErrorInfo **, enum wbcPasswordChangeRejectReason *, struct wbcUserPasswordPolicyInfo **)
+wbcCtxCheckTrustCredentials: wbcErr (struct wbcContext *, const char *, struct wbcAuthErrorInfo **)
+wbcCtxCreate: struct wbcContext *(void)
+wbcCtxCredentialCache: wbcErr (struct wbcContext *, struct wbcCredentialCacheParams *, struct wbcCredentialCacheInfo **, struct wbcAuthErrorInfo **)
+wbcCtxCredentialSave: wbcErr (struct wbcContext *, const char *, const char *)
+wbcCtxDcInfo: wbcErr (struct wbcContext *, const char *, size_t *, const char ***, const char ***)
+wbcCtxDomainInfo: wbcErr (struct wbcContext *, const char *, struct wbcDomainInfo **)
+wbcCtxEndgrent: wbcErr (struct wbcContext *)
+wbcCtxEndpwent: wbcErr (struct wbcContext *)
+wbcCtxFree: void (struct wbcContext *)
+wbcCtxGetDisplayName: wbcErr (struct wbcContext *, const struct wbcDomainSid *, char **, char **, enum wbcSidType *)
+wbcCtxGetGroups: wbcErr (struct wbcContext *, const char *, uint32_t *, gid_t **)
+wbcCtxGetSidAliases: wbcErr (struct wbcContext *, const struct wbcDomainSid *, struct wbcDomainSid *, uint32_t, uint32_t **, uint32_t *)
+wbcCtxGetgrent: wbcErr (struct wbcContext *, struct group **)
+wbcCtxGetgrgid: wbcErr (struct wbcContext *, gid_t, struct group **)
+wbcCtxGetgrlist: wbcErr (struct wbcContext *, struct group **)
+wbcCtxGetgrnam: wbcErr (struct wbcContext *, const char *, struct group **)
+wbcCtxGetpwent: wbcErr (struct wbcContext *, struct passwd **)
+wbcCtxGetpwnam: wbcErr (struct wbcContext *, const char *, struct passwd **)
+wbcCtxGetpwsid: wbcErr (struct wbcContext *, struct wbcDomainSid *, struct passwd **)
+wbcCtxGetpwuid: wbcErr (struct wbcContext *, uid_t, struct passwd **)
+wbcCtxGidToSid: wbcErr (struct wbcContext *, gid_t, struct wbcDomainSid *)
+wbcCtxInterfaceDetails: wbcErr (struct wbcContext *, struct wbcInterfaceDetails **)
+wbcCtxListGroups: wbcErr (struct wbcContext *, const char *, uint32_t *, const char ***)
+wbcCtxListTrusts: wbcErr (struct wbcContext *, struct wbcDomainInfo **, size_t *)
+wbcCtxListUsers: wbcErr (struct wbcContext *, const char *, uint32_t *, const char ***)
+wbcCtxLogoffUser: wbcErr (struct wbcContext *, const char *, uid_t, const char *)
+wbcCtxLogoffUserEx: wbcErr (struct wbcContext *, const struct wbcLogoffUserParams *, struct wbcAuthErrorInfo **)
+wbcCtxLogonUser: wbcErr (struct wbcContext *, const struct wbcLogonUserParams *, struct wbcLogonUserInfo **, struct wbcAuthErrorInfo **, struct wbcUserPasswordPolicyInfo **)
+wbcCtxLookupDomainController: wbcErr (struct wbcContext *, const char *, uint32_t, struct wbcDomainControllerInfo **)
+wbcCtxLookupDomainControllerEx: wbcErr (struct wbcContext *, const char *, struct wbcGuid *, const char *, uint32_t, struct wbcDomainControllerInfoEx **)
+wbcCtxLookupName: wbcErr (struct wbcContext *, const char *, const char *, struct wbcDomainSid *, enum wbcSidType *)
+wbcCtxLookupRids: wbcErr (struct wbcContext *, struct wbcDomainSid *, int, uint32_t *, const char **, const char ***, enum wbcSidType **)
+wbcCtxLookupSid: wbcErr (struct wbcContext *, const struct wbcDomainSid *, char **, char **, enum wbcSidType *)
+wbcCtxLookupSids: wbcErr (struct wbcContext *, const struct wbcDomainSid *, int, struct wbcDomainInfo **, int *, struct wbcTranslatedName **)
+wbcCtxLookupUserSids: wbcErr (struct wbcContext *, const struct wbcDomainSid *, bool, uint32_t *, struct wbcDomainSid **)
+wbcCtxPing: wbcErr (struct wbcContext *)
+wbcCtxPingDc: wbcErr (struct wbcContext *, const char *, struct wbcAuthErrorInfo **)
+wbcCtxPingDc2: wbcErr (struct wbcContext *, const char *, struct wbcAuthErrorInfo **, char **)
+wbcCtxResolveWinsByIP: wbcErr (struct wbcContext *, const char *, char **)
+wbcCtxResolveWinsByName: wbcErr (struct wbcContext *, const char *, char **)
+wbcCtxSetgrent: wbcErr (struct wbcContext *)
+wbcCtxSetpwent: wbcErr (struct wbcContext *)
+wbcCtxSidToGid: wbcErr (struct wbcContext *, const struct wbcDomainSid *, gid_t *)
+wbcCtxSidToUid: wbcErr (struct wbcContext *, const struct wbcDomainSid *, uid_t *)
+wbcCtxSidsToUnixIds: wbcErr (struct wbcContext *, const struct wbcDomainSid *, uint32_t, struct wbcUnixId *)
+wbcCtxUidToSid: wbcErr (struct wbcContext *, uid_t, struct wbcDomainSid *)
+wbcCtxUnixIdsToSids: wbcErr (struct wbcContext *, const struct wbcUnixId *, uint32_t, struct wbcDomainSid *)
+wbcDcInfo: wbcErr (const char *, size_t *, const char ***, const char ***)
+wbcDomainInfo: wbcErr (const char *, struct wbcDomainInfo **)
+wbcEndgrent: wbcErr (void)
+wbcEndpwent: wbcErr (void)
+wbcErrorString: const char *(wbcErr)
+wbcFreeMemory: void (void *)
+wbcGetDisplayName: wbcErr (const struct wbcDomainSid *, char **, char **, enum wbcSidType *)
+wbcGetGlobalCtx: struct wbcContext *(void)
+wbcGetGroups: wbcErr (const char *, uint32_t *, gid_t **)
+wbcGetSidAliases: wbcErr (const struct wbcDomainSid *, struct wbcDomainSid *, uint32_t, uint32_t **, uint32_t *)
+wbcGetgrent: wbcErr (struct group **)
+wbcGetgrgid: wbcErr (gid_t, struct group **)
+wbcGetgrlist: wbcErr (struct group **)
+wbcGetgrnam: wbcErr (const char *, struct group **)
+wbcGetpwent: wbcErr (struct passwd **)
+wbcGetpwnam: wbcErr (const char *, struct passwd **)
+wbcGetpwsid: wbcErr (struct wbcDomainSid *, struct passwd **)
+wbcGetpwuid: wbcErr (uid_t, struct passwd **)
+wbcGidToSid: wbcErr (gid_t, struct wbcDomainSid *)
+wbcGuidToString: wbcErr (const struct wbcGuid *, char **)
+wbcInterfaceDetails: wbcErr (struct wbcInterfaceDetails **)
+wbcLibraryDetails: wbcErr (struct wbcLibraryDetails **)
+wbcListGroups: wbcErr (const char *, uint32_t *, const char ***)
+wbcListTrusts: wbcErr (struct wbcDomainInfo **, size_t *)
+wbcListUsers: wbcErr (const char *, uint32_t *, const char ***)
+wbcLogoffUser: wbcErr (const char *, uid_t, const char *)
+wbcLogoffUserEx: wbcErr (const struct wbcLogoffUserParams *, struct wbcAuthErrorInfo **)
+wbcLogonUser: wbcErr (const struct wbcLogonUserParams *, struct wbcLogonUserInfo **, struct wbcAuthErrorInfo **, struct wbcUserPasswordPolicyInfo **)
+wbcLookupDomainController: wbcErr (const char *, uint32_t, struct wbcDomainControllerInfo **)
+wbcLookupDomainControllerEx: wbcErr (const char *, struct wbcGuid *, const char *, uint32_t, struct wbcDomainControllerInfoEx **)
+wbcLookupName: wbcErr (const char *, const char *, struct wbcDomainSid *, enum wbcSidType *)
+wbcLookupRids: wbcErr (struct wbcDomainSid *, int, uint32_t *, const char **, const char ***, enum wbcSidType **)
+wbcLookupSid: wbcErr (const struct wbcDomainSid *, char **, char **, enum wbcSidType *)
+wbcLookupSids: wbcErr (const struct wbcDomainSid *, int, struct wbcDomainInfo **, int *, struct wbcTranslatedName **)
+wbcLookupUserSids: wbcErr (const struct wbcDomainSid *, bool, uint32_t *, struct wbcDomainSid **)
+wbcPing: wbcErr (void)
+wbcPingDc: wbcErr (const char *, struct wbcAuthErrorInfo **)
+wbcPingDc2: wbcErr (const char *, struct wbcAuthErrorInfo **, char **)
+wbcQueryGidToSid: wbcErr (gid_t, struct wbcDomainSid *)
+wbcQuerySidToGid: wbcErr (const struct wbcDomainSid *, gid_t *)
+wbcQuerySidToUid: wbcErr (const struct wbcDomainSid *, uid_t *)
+wbcQueryUidToSid: wbcErr (uid_t, struct wbcDomainSid *)
+wbcRemoveGidMapping: wbcErr (gid_t, const struct wbcDomainSid *)
+wbcRemoveUidMapping: wbcErr (uid_t, const struct wbcDomainSid *)
+wbcRequestResponse: wbcErr (struct wbcContext *, int, struct winbindd_request *, struct winbindd_response *)
+wbcRequestResponsePriv: wbcErr (struct wbcContext *, int, struct winbindd_request *, struct winbindd_response *)
+wbcResolveWinsByIP: wbcErr (const char *, char **)
+wbcResolveWinsByName: wbcErr (const char *, char **)
+wbcSetClientProcessName: void (const char *)
+wbcSetGidHwm: wbcErr (gid_t)
+wbcSetGidMapping: wbcErr (gid_t, const struct wbcDomainSid *)
+wbcSetUidHwm: wbcErr (uid_t)
+wbcSetUidMapping: wbcErr (uid_t, const struct wbcDomainSid *)
+wbcSetgrent: wbcErr (void)
+wbcSetpwent: wbcErr (void)
+wbcSidToGid: wbcErr (const struct wbcDomainSid *, gid_t *)
+wbcSidToString: wbcErr (const struct wbcDomainSid *, char **)
+wbcSidToStringBuf: int (const struct wbcDomainSid *, char *, int)
+wbcSidToUid: wbcErr (const struct wbcDomainSid *, uid_t *)
+wbcSidTypeString: const char *(enum wbcSidType)
+wbcSidsToUnixIds: wbcErr (const struct wbcDomainSid *, uint32_t, struct wbcUnixId *)
+wbcStrDup: char *(const char *)
+wbcStringToGuid: wbcErr (const char *, struct wbcGuid *)
+wbcStringToSid: wbcErr (const char *, struct wbcDomainSid *)
+wbcUidToSid: wbcErr (uid_t, struct wbcDomainSid *)
+wbcUnixIdsToSids: wbcErr (const struct wbcUnixId *, uint32_t, struct wbcDomainSid *)
diff --git a/nsswitch/libwbclient/ABI/wbclient-0.9.sigs b/nsswitch/libwbclient/ABI/wbclient-0.9.sigs
new file mode 100644
index 0000000..ec25e76
--- /dev/null
+++ b/nsswitch/libwbclient/ABI/wbclient-0.9.sigs
@@ -0,0 +1,75 @@
+wbcAddNamedBlob: wbcErr (size_t *, struct wbcNamedBlob **, const char *, uint32_t, uint8_t *, size_t)
+wbcAllocateGid: wbcErr (gid_t *)
+wbcAllocateMemory: void *(size_t, size_t, void (*)(void *))
+wbcAllocateStringArray: const char **(int)
+wbcAllocateUid: wbcErr (uid_t *)
+wbcAuthenticateUser: wbcErr (const char *, const char *)
+wbcAuthenticateUserEx: wbcErr (const struct wbcAuthUserParams *, struct wbcAuthUserInfo **, struct wbcAuthErrorInfo **)
+wbcChangeTrustCredentials: wbcErr (const char *, struct wbcAuthErrorInfo **)
+wbcChangeUserPassword: wbcErr (const char *, const char *, const char *)
+wbcChangeUserPasswordEx: wbcErr (const struct wbcChangePasswordParams *, struct wbcAuthErrorInfo **, enum wbcPasswordChangeRejectReason *, struct wbcUserPasswordPolicyInfo **)
+wbcCheckTrustCredentials: wbcErr (const char *, struct wbcAuthErrorInfo **)
+wbcCredentialCache: wbcErr (struct wbcCredentialCacheParams *, struct wbcCredentialCacheInfo **, struct wbcAuthErrorInfo **)
+wbcCredentialSave: wbcErr (const char *, const char *)
+wbcDcInfo: wbcErr (const char *, size_t *, const char ***, const char ***)
+wbcDomainInfo: wbcErr (const char *, struct wbcDomainInfo **)
+wbcEndgrent: wbcErr (void)
+wbcEndpwent: wbcErr (void)
+wbcErrorString: const char *(wbcErr)
+wbcFreeMemory: void (void *)
+wbcGetDisplayName: wbcErr (const struct wbcDomainSid *, char **, char **, enum wbcSidType *)
+wbcGetGroups: wbcErr (const char *, uint32_t *, gid_t **)
+wbcGetSidAliases: wbcErr (const struct wbcDomainSid *, struct wbcDomainSid *, uint32_t, uint32_t **, uint32_t *)
+wbcGetgrent: wbcErr (struct group **)
+wbcGetgrgid: wbcErr (gid_t, struct group **)
+wbcGetgrlist: wbcErr (struct group **)
+wbcGetgrnam: wbcErr (const char *, struct group **)
+wbcGetpwent: wbcErr (struct passwd **)
+wbcGetpwnam: wbcErr (const char *, struct passwd **)
+wbcGetpwsid: wbcErr (struct wbcDomainSid *, struct passwd **)
+wbcGetpwuid: wbcErr (uid_t, struct passwd **)
+wbcGidToSid: wbcErr (gid_t, struct wbcDomainSid *)
+wbcGuidToString: wbcErr (const struct wbcGuid *, char **)
+wbcInterfaceDetails: wbcErr (struct wbcInterfaceDetails **)
+wbcLibraryDetails: wbcErr (struct wbcLibraryDetails **)
+wbcListGroups: wbcErr (const char *, uint32_t *, const char ***)
+wbcListTrusts: wbcErr (struct wbcDomainInfo **, size_t *)
+wbcListUsers: wbcErr (const char *, uint32_t *, const char ***)
+wbcLogoffUser: wbcErr (const char *, uid_t, const char *)
+wbcLogoffUserEx: wbcErr (const struct wbcLogoffUserParams *, struct wbcAuthErrorInfo **)
+wbcLogonUser: wbcErr (const struct wbcLogonUserParams *, struct wbcLogonUserInfo **, struct wbcAuthErrorInfo **, struct wbcUserPasswordPolicyInfo **)
+wbcLookupDomainController: wbcErr (const char *, uint32_t, struct wbcDomainControllerInfo **)
+wbcLookupDomainControllerEx: wbcErr (const char *, struct wbcGuid *, const char *, uint32_t, struct wbcDomainControllerInfoEx **)
+wbcLookupName: wbcErr (const char *, const char *, struct wbcDomainSid *, enum wbcSidType *)
+wbcLookupRids: wbcErr (struct wbcDomainSid *, int, uint32_t *, const char **, const char ***, enum wbcSidType **)
+wbcLookupSid: wbcErr (const struct wbcDomainSid *, char **, char **, enum wbcSidType *)
+wbcLookupSids: wbcErr (const struct wbcDomainSid *, int, struct wbcDomainInfo **, int *, struct wbcTranslatedName **)
+wbcLookupUserSids: wbcErr (const struct wbcDomainSid *, bool, uint32_t *, struct wbcDomainSid **)
+wbcPing: wbcErr (void)
+wbcPingDc: wbcErr (const char *, struct wbcAuthErrorInfo **)
+wbcQueryGidToSid: wbcErr (gid_t, struct wbcDomainSid *)
+wbcQuerySidToGid: wbcErr (const struct wbcDomainSid *, gid_t *)
+wbcQuerySidToUid: wbcErr (const struct wbcDomainSid *, uid_t *)
+wbcQueryUidToSid: wbcErr (uid_t, struct wbcDomainSid *)
+wbcRemoveGidMapping: wbcErr (gid_t, const struct wbcDomainSid *)
+wbcRemoveUidMapping: wbcErr (uid_t, const struct wbcDomainSid *)
+wbcRequestResponse: wbcErr (int, struct winbindd_request *, struct winbindd_response *)
+wbcRequestResponsePriv: wbcErr (int, struct winbindd_request *, struct winbindd_response *)
+wbcResolveWinsByIP: wbcErr (const char *, char **)
+wbcResolveWinsByName: wbcErr (const char *, char **)
+wbcSetGidHwm: wbcErr (gid_t)
+wbcSetGidMapping: wbcErr (gid_t, const struct wbcDomainSid *)
+wbcSetUidHwm: wbcErr (uid_t)
+wbcSetUidMapping: wbcErr (uid_t, const struct wbcDomainSid *)
+wbcSetgrent: wbcErr (void)
+wbcSetpwent: wbcErr (void)
+wbcSidToGid: wbcErr (const struct wbcDomainSid *, gid_t *)
+wbcSidToString: wbcErr (const struct wbcDomainSid *, char **)
+wbcSidToStringBuf: int (const struct wbcDomainSid *, char *, int)
+wbcSidToUid: wbcErr (const struct wbcDomainSid *, uid_t *)
+wbcSidTypeString: const char *(enum wbcSidType)
+wbcSidsToUnixIds: wbcErr (const struct wbcDomainSid *, uint32_t, struct wbcUnixId *)
+wbcStrDup: char *(const char *)
+wbcStringToGuid: wbcErr (const char *, struct wbcGuid *)
+wbcStringToSid: wbcErr (const char *, struct wbcDomainSid *)
+wbcUidToSid: wbcErr (uid_t, struct wbcDomainSid *)
diff --git a/nsswitch/libwbclient/Doxyfile b/nsswitch/libwbclient/Doxyfile
new file mode 100644
index 0000000..529a16f
--- /dev/null
+++ b/nsswitch/libwbclient/Doxyfile
@@ -0,0 +1,1297 @@
+# Doxyfile 1.5.3
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file that
+# follow. The default is UTF-8 which is also the encoding used for all text before
+# the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into
+# libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of
+# possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = Samba
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = HEAD
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = dox
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian,
+# Italian, Japanese, Japanese-en (Japanese with English messages), Korean,
+# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian,
+# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH = $(PWD)/
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member
+# documentation.
+
+DETAILS_AT_TOP = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for Java.
+# For instance, namespaces will be presented as packages, qualified scopes
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to
+# include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = YES
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be extracted
+# and appear in the documentation as a namespace called 'anonymous_namespace{file}',
+# where file will be replaced with the base name of the file that contains the anonymous
+# namespace. By default anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = YES
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = YES
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = NO
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from the
+# version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = YES
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = NO
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = NO
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text "
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = .
+
+# This tag can be used to specify the character encoding of the source files that
+# doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default
+# input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding.
+# See http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py
+
+FILE_PATTERNS = *.c \
+ *.h \
+ *.idl
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE = include/includes.h \
+ include/proto.h
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the output.
+# The symbol name can be a fully qualified name, a word, or if the wildcard * is used,
+# a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output. If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO. If you have enabled CALL_GRAPH or CALLER_GRAPH
+# then you must also enable this option. If you don't then doxygen will produce
+# a warning and turn it on anyway
+
+SOURCE_BROWSER = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = YES
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = NO
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default)
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default)
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code. Otherwise they will link to the documentstion.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 1
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = .
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 3
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = YES
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader. This is useful
+# if you want to understand what is going on. On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = NO
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to
+# produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to
+# specify the directory where the mscgen tool resides. If left empty the tool is assumed to
+# be found in the default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will
+# generate a call dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will
+# generate a caller dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the number
+# of direct children of the root node in a graph is already larger than
+# MAX_DOT_GRAPH_NOTES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, which results in a white background.
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = NO
diff --git a/nsswitch/libwbclient/libwbclient.h b/nsswitch/libwbclient/libwbclient.h
new file mode 100644
index 0000000..867eb4d
--- /dev/null
+++ b/nsswitch/libwbclient/libwbclient.h
@@ -0,0 +1,44 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind client API
+
+ Copyright (C) Gerald (Jerry) Carter 2007
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _LIBWBCLIENT_H
+#define _LIBWBCLIENT_H
+
+/* Super header including necessary public and private header files
+ for building the wbclient library. __DO NOT__ define anything
+ in this file. Only include other headers. */
+
+/* Winbind headers */
+
+#include "nsswitch/winbind_nss_config.h"
+#include "nsswitch/winbind_struct_protocol.h"
+
+/* Public headers */
+
+#include "wbclient.h"
+
+/* Private headers */
+
+#include "wbc_err_internal.h"
+#include "wbclient_internal.h"
+
+
+#endif /* _LIBWBCLIENT_H */
diff --git a/nsswitch/libwbclient/tests/wbclient.c b/nsswitch/libwbclient/tests/wbclient.c
new file mode 100644
index 0000000..01fa289
--- /dev/null
+++ b/nsswitch/libwbclient/tests/wbclient.c
@@ -0,0 +1,1121 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB torture tester
+ Copyright (C) Guenther Deschner 2009-2010
+
+ 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 "source3/include/includes.h"
+#include "lib/replace/replace.h"
+#include "libcli/util/ntstatus.h"
+#include "libcli/util/werror.h"
+#include "lib/util/data_blob.h"
+#include "lib/util/time.h"
+#include "libcli/resolve/resolve.h"
+#include "nsswitch/libwbclient/wbclient.h"
+#include "nsswitch/winbind_client.h"
+#include "torture/smbtorture.h"
+#include "torture/winbind/proto.h"
+#include "lib/util/util_net.h"
+#include "lib/util/charset/charset.h"
+#include "libcli/auth/libcli_auth.h"
+#include "lib/param/param.h"
+#include "lib/util/samba_util.h"
+#include "auth/credentials/credentials.h"
+#include "lib/cmdline/cmdline.h"
+#include "winbindd.h"
+
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+#define WBC_ERROR_EQUAL(x,y) (x == y)
+
+#define torture_assert_wbc_equal(torture_ctx, got, expected, cmt, cmt_arg) \
+ do { wbcErr __got = got, __expected = expected; \
+ if (!WBC_ERROR_EQUAL(__got, __expected)) { \
+ torture_result(torture_ctx, TORTURE_FAIL, __location__": "#got" was %s, expected %s: " cmt, wbcErrorString(__got), wbcErrorString(__expected), cmt_arg); \
+ return false; \
+ } \
+ } while (0)
+
+#define torture_assert_wbc_ok(torture_ctx,expr,cmt,cmt_arg) \
+ torture_assert_wbc_equal(torture_ctx,expr,WBC_ERR_SUCCESS,cmt,cmt_arg)
+
+#define torture_assert_wbc_equal_goto_fail(torture_ctx, got, expected, cmt, cmt_arg) \
+ do { wbcErr __got = got, __expected = expected; \
+ if (!WBC_ERROR_EQUAL(__got, __expected)) { \
+ torture_result(torture_ctx, TORTURE_FAIL, __location__": "#got" was %s, expected %s: " cmt, wbcErrorString(__got), wbcErrorString(__expected), cmt_arg); \
+ goto fail; \
+ } \
+ } while (0)
+
+#define torture_assert_wbc_ok_goto_fail(torture_ctx,expr,cmt,cmt_arg) \
+ torture_assert_wbc_equal_goto_fail(torture_ctx,expr,WBC_ERR_SUCCESS,cmt,cmt_arg)
+
+#define torture_assert_str_equal_goto_fail(torture_ctx,got,expected,cmt)\
+ do { const char *__got = (got), *__expected = (expected); \
+ if (strcmp(__got, __expected) != 0) { \
+ torture_result(torture_ctx, TORTURE_FAIL, \
+ __location__": "#got" was %s, expected %s: %s", \
+ __got, __expected, cmt); \
+ goto fail;; \
+ } \
+ } while(0)
+
+static bool test_wbc_ping(struct torture_context *tctx)
+{
+ torture_assert_wbc_ok(tctx, wbcPing(),
+ "%s", "wbcPing failed");
+
+ return true;
+}
+
+static bool test_wbc_pingdc(struct torture_context *tctx)
+{
+ struct wbcInterfaceDetails *details = NULL;
+ wbcErr ret = false;
+
+ torture_assert_wbc_equal_goto_fail(tctx,
+ wbcPingDc("random_string", NULL),
+ WBC_ERR_DOMAIN_NOT_FOUND,
+ "%s",
+ "wbcPingDc failed");
+ torture_assert_wbc_ok_goto_fail(tctx, wbcPingDc(NULL, NULL),
+ "%s", "wbcPingDc failed");
+
+ torture_assert_wbc_ok_goto_fail(tctx, wbcInterfaceDetails(&details),
+ "%s", "wbcInterfaceDetails failed");
+ torture_assert_goto(tctx, details, ret, fail,
+ "wbcInterfaceDetails returned NULL pointer");
+ torture_assert_goto(tctx, details->netbios_domain, ret, fail,
+ "wbcInterfaceDetails returned NULL netbios_domain");
+
+ torture_assert_wbc_ok_goto_fail(tctx,
+ wbcPingDc(details->netbios_domain, NULL),
+ "wbcPingDc(%s) failed",
+ details->netbios_domain);
+
+ torture_assert_wbc_ok_goto_fail(tctx,
+ wbcPingDc("BUILTIN", NULL),
+ "%s",
+ "wbcPingDc(BUILTIN) failed");
+
+ ret = true;
+fail:
+ wbcFreeMemory(details);
+ return ret;
+}
+
+static bool test_wbc_pingdc2(struct torture_context *tctx)
+{
+ struct wbcInterfaceDetails *details = NULL;
+ char *name = NULL;
+ wbcErr ret = false;
+
+ torture_assert_wbc_equal_goto_fail(tctx,
+ wbcPingDc2("random_string", NULL, &name),
+ WBC_ERR_DOMAIN_NOT_FOUND,
+ "%s",
+ "wbcPingDc2 failed");
+ torture_assert_wbc_ok_goto_fail(tctx,
+ wbcPingDc2(NULL, NULL, &name),
+ "%s",
+ "wbcPingDc2 failed");
+ wbcFreeMemory(name);
+ name = NULL;
+
+ torture_assert_wbc_ok_goto_fail(tctx,
+ wbcInterfaceDetails(&details),
+ "%s",
+ "wbcInterfaceDetails failed");
+ torture_assert_goto(tctx,
+ details,
+ ret,
+ fail,
+ "wbcInterfaceDetails returned NULL pointer");
+ torture_assert_goto(tctx,
+ details->netbios_domain,
+ ret,
+ fail,
+ "wbcInterfaceDetails returned NULL netbios_domain");
+
+ torture_assert_wbc_ok_goto_fail(tctx,
+ wbcPingDc2(details->netbios_domain, NULL, &name),
+ "wbcPingDc2(%s) failed",
+ details->netbios_domain);
+ wbcFreeMemory(name);
+ name = NULL;
+
+ torture_assert_wbc_ok_goto_fail(tctx,
+ wbcPingDc2("BUILTIN", NULL, &name),
+ "%s",
+ "wbcPingDc2(BUILTIN) failed");
+
+ ret = true;
+fail:
+ wbcFreeMemory(name);
+ wbcFreeMemory(details);
+
+ return ret;
+}
+
+static bool test_wbc_library_details(struct torture_context *tctx)
+{
+ struct wbcLibraryDetails *details;
+
+ torture_assert_wbc_ok(tctx, wbcLibraryDetails(&details),
+ "%s", "wbcLibraryDetails failed");
+ torture_assert(tctx, details,
+ "wbcLibraryDetails returned NULL pointer");
+
+ wbcFreeMemory(details);
+
+ return true;
+}
+
+static bool test_wbc_interface_details(struct torture_context *tctx)
+{
+ struct wbcInterfaceDetails *details;
+
+ torture_assert_wbc_ok(tctx, wbcInterfaceDetails(&details),
+ "%s", "wbcInterfaceDetails failed");
+ torture_assert(tctx, details,
+ "wbcInterfaceDetails returned NULL pointer");
+
+ wbcFreeMemory(details);
+
+ return true;
+}
+
+static bool test_wbc_sidtypestring(struct torture_context *tctx)
+{
+ torture_assert_str_equal(tctx, wbcSidTypeString(WBC_SID_NAME_USE_NONE),
+ "SID_NONE", "SID_NONE failed");
+ torture_assert_str_equal(tctx, wbcSidTypeString(WBC_SID_NAME_USER),
+ "SID_USER", "SID_USER failed");
+ torture_assert_str_equal(tctx, wbcSidTypeString(WBC_SID_NAME_DOM_GRP),
+ "SID_DOM_GROUP", "SID_DOM_GROUP failed");
+ torture_assert_str_equal(tctx, wbcSidTypeString(WBC_SID_NAME_DOMAIN),
+ "SID_DOMAIN", "SID_DOMAIN failed");
+ torture_assert_str_equal(tctx, wbcSidTypeString(WBC_SID_NAME_ALIAS),
+ "SID_ALIAS", "SID_ALIAS failed");
+ torture_assert_str_equal(tctx, wbcSidTypeString(WBC_SID_NAME_WKN_GRP),
+ "SID_WKN_GROUP", "SID_WKN_GROUP failed");
+ torture_assert_str_equal(tctx, wbcSidTypeString(WBC_SID_NAME_DELETED),
+ "SID_DELETED", "SID_DELETED failed");
+ torture_assert_str_equal(tctx, wbcSidTypeString(WBC_SID_NAME_INVALID),
+ "SID_INVALID", "SID_INVALID failed");
+ torture_assert_str_equal(tctx, wbcSidTypeString(WBC_SID_NAME_UNKNOWN),
+ "SID_UNKNOWN", "SID_UNKNOWN failed");
+ torture_assert_str_equal(tctx, wbcSidTypeString(WBC_SID_NAME_COMPUTER),
+ "SID_COMPUTER", "SID_COMPUTER failed");
+ torture_assert_str_equal(tctx, wbcSidTypeString(WBC_SID_NAME_LABEL),
+ "SID_LABEL", "SID_LABEL failed");
+ return true;
+}
+
+static bool test_wbc_sidtostring(struct torture_context *tctx)
+{
+ struct wbcDomainSid sid;
+ const char *sid_string = "S-1-5-32";
+ char *sid_string2;
+
+ torture_assert_wbc_ok(tctx, wbcStringToSid(sid_string, &sid),
+ "wbcStringToSid of %s failed", sid_string);
+ torture_assert_wbc_ok(tctx, wbcSidToString(&sid, &sid_string2),
+ "wbcSidToString of %s failed", sid_string);
+ torture_assert_str_equal(tctx, sid_string, sid_string2,
+ "sid strings differ");
+ wbcFreeMemory(sid_string2);
+
+ return true;
+}
+
+static bool test_wbc_guidtostring(struct torture_context *tctx)
+{
+ struct wbcGuid guid;
+ const char *guid_string = "f7cf07b4-1487-45c7-824d-8b18cc580811";
+ char *guid_string2;
+
+ torture_assert_wbc_ok(tctx, wbcStringToGuid(guid_string, &guid),
+ "wbcStringToGuid of %s failed", guid_string);
+ torture_assert_wbc_ok(tctx, wbcGuidToString(&guid, &guid_string2),
+ "wbcGuidToString of %s failed", guid_string);
+ torture_assert_str_equal(tctx, guid_string, guid_string2,
+ "guid strings differ");
+ wbcFreeMemory(guid_string2);
+
+ return true;
+}
+
+static bool test_wbc_domain_info(struct torture_context *tctx)
+{
+ struct wbcDomainInfo *info = NULL;
+ struct wbcInterfaceDetails *details = NULL;
+ wbcErr ret = false;
+
+ torture_assert_wbc_ok_goto_fail(tctx,
+ wbcInterfaceDetails(&details),
+ "%s",
+ "wbcInterfaceDetails failed");
+ torture_assert_wbc_ok_goto_fail(tctx,
+ wbcDomainInfo(details->netbios_domain, &info),
+ "%s",
+ "wbcDomainInfo failed");
+
+ torture_assert_goto(tctx,
+ info,
+ ret,
+ fail,
+ "wbcDomainInfo returned NULL pointer");
+
+ ret = true;
+fail:
+ wbcFreeMemory(details);
+ wbcFreeMemory(info);
+
+ return ret;
+}
+
+static bool test_wbc_users(struct torture_context *tctx)
+{
+ const char *domain_name = NULL;
+ uint32_t num_users;
+ const char **users = NULL;
+ uint32_t i;
+ struct wbcInterfaceDetails *details = NULL;
+ struct wbcDomainSid *sids = NULL;
+ char *domain = NULL;
+ char *name = NULL;
+ char *sid_string = NULL;
+ wbcErr ret = false;
+ char separator;
+
+ torture_assert_wbc_ok(tctx, wbcInterfaceDetails(&details),
+ "%s", "wbcInterfaceDetails failed");
+
+ domain_name = talloc_strdup(tctx, details->netbios_domain);
+ torture_assert_goto(tctx,
+ domain_name != NULL,
+ ret,
+ fail,
+ "Failed to allocate domain_name");
+ separator = details->winbind_separator;
+ wbcFreeMemory(details);
+ details = NULL;
+
+ torture_assert_wbc_ok_goto_fail(tctx,
+ wbcListUsers(domain_name, &num_users, &users),
+ "%s",
+ "wbcListUsers failed");
+ torture_assert_goto(tctx,
+ !(num_users > 0 && !users),
+ ret,
+ fail,
+ "wbcListUsers returned invalid results");
+
+ for (i = 0; i < MIN(num_users, 100); i++) {
+ struct wbcDomainSid sid;
+ enum wbcSidType name_type;
+ uint32_t num_sids;
+ const char *user;
+ char *c;
+
+ c = strchr(users[i], separator);
+
+ if (c == NULL) {
+ /*
+ * NT4 DC
+ * user name does not contain DOMAIN SEPARATOR prefix.
+ */
+
+ user = users[i];
+ } else {
+ /*
+ * AD DC
+ * user name starts with DOMAIN SEPARATOR prefix.
+ */
+ const char *dom;
+
+ *c = '\0';
+ dom = users[i];
+ user = c + 1;
+
+ torture_assert_str_equal_goto(tctx, dom, domain_name,
+ ret, fail, "Domain part "
+ "of user name does not "
+ "match domain name.\n");
+ }
+
+ torture_assert_wbc_ok_goto_fail(tctx,
+ wbcLookupName(domain_name, user,
+ &sid, &name_type),
+ "wbcLookupName of %s failed",
+ users[i]);
+ torture_assert_int_equal_goto(tctx,
+ name_type, WBC_SID_NAME_USER,
+ ret,
+ fail,
+ "wbcLookupName expected WBC_SID_NAME_USER");
+ wbcSidToString(&sid, &sid_string);
+ torture_assert_wbc_ok_goto_fail(tctx,
+ wbcLookupSid(&sid,
+ &domain,
+ &name,
+ &name_type),
+ "wbcLookupSid of %s failed",
+ sid_string);
+ torture_assert_int_equal_goto(tctx,
+ name_type, WBC_SID_NAME_USER,
+ ret,
+ fail,
+ "wbcLookupSid of expected WBC_SID_NAME_USER");
+ torture_assert_goto(tctx,
+ name,
+ ret,
+ fail,
+ "wbcLookupSid returned no name");
+ wbcFreeMemory(domain);
+ domain = NULL;
+ wbcFreeMemory(name);
+ name = NULL;
+
+ torture_assert_wbc_ok_goto_fail(tctx,
+ wbcLookupUserSids(&sid, true, &num_sids, &sids),
+ "wbcLookupUserSids of %s failed", sid_string);
+ torture_assert_wbc_ok_goto_fail(tctx,
+ wbcGetDisplayName(&sid,
+ &domain,
+ &name,
+ &name_type),
+ "wbcGetDisplayName of %s failed",
+ sid_string);
+ wbcFreeMemory(domain);
+ domain = NULL;
+ wbcFreeMemory(name);
+ name = NULL;
+ wbcFreeMemory(sids);
+ sids = NULL;
+ wbcFreeMemory(sid_string);
+ sid_string = NULL;
+ }
+
+ ret = true;
+fail:
+ wbcFreeMemory(details);
+ wbcFreeMemory(users);
+ wbcFreeMemory(domain);
+ wbcFreeMemory(name);
+ wbcFreeMemory(sids);
+ wbcFreeMemory(sid_string);
+
+ return ret;
+}
+
+static bool test_wbc_groups(struct torture_context *tctx)
+{
+ wbcErr ret = false;
+ const char *domain_name = NULL;
+ uint32_t num_groups;
+ const char **groups = NULL;
+ uint32_t i;
+ struct wbcInterfaceDetails *details = NULL;
+ char *domain = NULL;
+ char *name = NULL;
+ char *sid_string = NULL;
+ char separator;
+
+ torture_assert_wbc_ok(tctx, wbcInterfaceDetails(&details),
+ "%s", "wbcInterfaceDetails failed");
+
+ domain_name = talloc_strdup(tctx, details->netbios_domain);
+ torture_assert_goto(tctx,
+ domain_name != NULL,
+ ret,
+ fail,
+ "Failed to allocate domain_name");
+ separator = details->winbind_separator;
+ wbcFreeMemory(details);
+ details = NULL;
+
+ torture_assert_wbc_ok_goto_fail(tctx,
+ wbcListGroups(domain_name, &num_groups, &groups),
+ "wbcListGroups in %s failed",
+ domain_name);
+ torture_assert_goto(tctx,
+ !(num_groups > 0 && !groups),
+ ret,
+ fail,
+ "wbcListGroups returned invalid results");
+
+ for (i=0; i < MIN(num_groups,100); i++) {
+ struct wbcDomainSid sid;
+ enum wbcSidType name_type;
+ const char *group;
+ char *c;
+
+ c = strchr(groups[i], separator);
+
+ if (c == NULL) {
+ /*
+ * NT4 DC
+ * group name does not contain DOMAIN SEPARATOR prefix.
+ */
+
+ group = groups[i];
+ } else {
+ /*
+ * AD DC
+ * group name starts with DOMAIN SEPARATOR prefix.
+ */
+ const char *dom;
+
+
+ *c = '\0';
+ dom = groups[i];
+ group = c + 1;
+
+ torture_assert_str_equal_goto(tctx, dom, domain_name,
+ ret, fail, "Domain part "
+ "of group name does not "
+ "match domain name.\n");
+ }
+
+ torture_assert_wbc_ok_goto_fail(tctx,
+ wbcLookupName(domain_name,
+ group,
+ &sid,
+ &name_type),
+ "wbcLookupName for %s failed",
+ domain_name);
+ wbcSidToString(&sid, &sid_string);
+ torture_assert_wbc_ok_goto_fail(tctx,
+ wbcLookupSid(&sid,
+ &domain,
+ &name,
+ &name_type),
+ "wbcLookupSid of %s failed",
+ sid_string);
+ torture_assert_goto(tctx,
+ name,
+ ret,
+ fail,
+ "wbcLookupSid returned no name");
+
+ wbcFreeMemory(domain);
+ domain = NULL;
+ wbcFreeMemory(name);
+ name = NULL;
+ wbcFreeMemory(sid_string);
+ sid_string = NULL;
+ }
+
+ ret = true;
+fail:
+ wbcFreeMemory(details);
+ wbcFreeMemory(groups);
+ wbcFreeMemory(domain);
+ wbcFreeMemory(name);
+ wbcFreeMemory(sid_string);
+
+ return ret;
+}
+
+static bool test_wbc_trusts(struct torture_context *tctx)
+{
+ struct wbcDomainInfo *domains = NULL;
+ struct wbcAuthErrorInfo *error = NULL;
+ size_t num_domains;
+ uint32_t i;
+ wbcErr ret = false;
+
+ torture_assert_wbc_ok_goto_fail(tctx,
+ wbcListTrusts(&domains, &num_domains),
+ "%s",
+ "wbcListTrusts failed");
+ torture_assert_goto(tctx,
+ !(num_domains > 0 && !domains),
+ ret,
+ fail,
+ "wbcListTrusts returned invalid results");
+
+ for (i=0; i < MIN(num_domains,100); i++) {
+
+ /*
+ struct wbcDomainSid sid;
+ enum wbcSidType name_type;
+ char *domain;
+ char *name;
+ */
+ torture_assert_wbc_ok_goto_fail(tctx,
+ wbcCheckTrustCredentials(domains[i].short_name,
+ &error),
+ "%s",
+ "wbcCheckTrustCredentials failed");
+ /*
+ torture_assert_wbc_ok(tctx, wbcLookupName(domains[i].short_name, NULL, &sid, &name_type),
+ "wbcLookupName failed");
+ torture_assert_int_equal(tctx, name_type, WBC_SID_NAME_DOMAIN,
+ "wbcLookupName expected WBC_SID_NAME_DOMAIN");
+ torture_assert_wbc_ok(tctx, wbcLookupSid(&sid, &domain, &name, &name_type),
+ "wbcLookupSid failed");
+ torture_assert_int_equal(tctx, name_type, WBC_SID_NAME_DOMAIN,
+ "wbcLookupSid expected WBC_SID_NAME_DOMAIN");
+ torture_assert(tctx, name,
+ "wbcLookupSid returned no name");
+ */
+ wbcFreeMemory(error);
+ error = NULL;
+ }
+
+ ret = true;
+fail:
+ wbcFreeMemory(domains);
+ wbcFreeMemory(error);
+
+ return ret;
+}
+
+static bool test_wbc_lookupdc(struct torture_context *tctx)
+{
+ const char *domain_name = NULL;
+ struct wbcInterfaceDetails *details;
+ struct wbcDomainControllerInfo *dc_info;
+
+ torture_assert_wbc_ok(tctx, wbcInterfaceDetails(&details),
+ "%s", "wbcInterfaceDetails failed");
+
+ domain_name = talloc_strdup(tctx, details->netbios_domain);
+ wbcFreeMemory(details);
+
+ torture_assert_wbc_ok(tctx, wbcLookupDomainController(domain_name, 0, &dc_info),
+ "wbcLookupDomainController for %s failed", domain_name);
+ wbcFreeMemory(dc_info);
+
+ return true;
+}
+
+static bool test_wbc_lookupdcex(struct torture_context *tctx)
+{
+ const char *domain_name = NULL;
+ struct wbcInterfaceDetails *details;
+ struct wbcDomainControllerInfoEx *dc_info;
+
+ torture_assert_wbc_ok(tctx, wbcInterfaceDetails(&details),
+ "%s", "wbcInterfaceDetails failed");
+
+ domain_name = talloc_strdup(tctx, details->netbios_domain);
+ wbcFreeMemory(details);
+
+ torture_assert_wbc_ok(tctx, wbcLookupDomainControllerEx(domain_name, NULL, NULL, 0, &dc_info),
+ "wbcLookupDomainControllerEx for %s failed", domain_name);
+ wbcFreeMemory(dc_info);
+
+ return true;
+}
+
+static bool test_wbc_resolve_winsbyname(struct torture_context *tctx)
+{
+ const char *name;
+ char *ip;
+ wbcErr ret;
+
+ name = torture_setting_string(tctx, "host", NULL);
+
+ torture_comment(tctx, "test-WinsByName: host='%s'\n", name);
+
+ ret = wbcResolveWinsByName(name, &ip);
+
+ if (is_ipaddress(name)) {
+ torture_assert_wbc_equal(tctx, ret, WBC_ERR_DOMAIN_NOT_FOUND, "wbcResolveWinsByName of %s failed", name);
+ } else {
+ torture_assert_wbc_ok(tctx, ret, "wbcResolveWinsByName for %s failed", name);
+ }
+
+ return true;
+}
+
+static bool test_wbc_resolve_winsbyip(struct torture_context *tctx)
+{
+ const char *ip;
+ const char *host;
+ struct nbt_name nbt_name;
+ char *name;
+ wbcErr ret;
+ NTSTATUS status;
+
+ host = torture_setting_string(tctx, "host", NULL);
+
+ torture_comment(tctx, "test-WinsByIp: host='%s'\n", host);
+
+ make_nbt_name_server(&nbt_name, host);
+
+ status = resolve_name_ex(lpcfg_resolve_context(tctx->lp_ctx),
+ 0, 0, &nbt_name, tctx, &ip, tctx->ev);
+ torture_assert_ntstatus_ok(tctx, status,
+ talloc_asprintf(tctx,"Failed to resolve %s: %s",
+ nbt_name.name, nt_errstr(status)));
+
+ torture_comment(tctx, "test-WinsByIp: ip='%s'\n", ip);
+
+ ret = wbcResolveWinsByIP(ip, &name);
+
+ torture_assert_wbc_ok(tctx, ret, "wbcResolveWinsByIP for %s failed", ip);
+
+ wbcFreeMemory(name);
+
+ return true;
+}
+
+static bool test_wbc_lookup_rids(struct torture_context *tctx)
+{
+ struct wbcDomainSid builtin;
+ uint32_t rids[2] = { 544, 545 };
+ const char *domain_name = NULL;
+ const char **names = NULL;
+ enum wbcSidType *types;
+ wbcErr ret = false;
+
+ wbcStringToSid("S-1-5-32", &builtin);
+
+ ret = wbcLookupRids(&builtin, 2, rids, &domain_name, &names,
+ &types);
+ torture_assert_wbc_ok_goto_fail(
+ tctx, ret, "%s", "wbcLookupRids for 544 and 545 failed");
+
+ torture_assert_str_equal(
+ tctx, names[0], "Administrators",
+ "S-1-5-32-544 not mapped to 'Administrators'");
+ torture_assert_str_equal_goto_fail(
+ tctx, names[1], "Users", "S-1-5-32-545 not mapped to 'Users'");
+
+ ret = true;
+fail:
+ wbcFreeMemory(discard_const_p(char ,domain_name));
+ wbcFreeMemory(names);
+ wbcFreeMemory(types);
+ return ret;
+}
+
+static bool test_wbc_get_sidaliases(struct torture_context *tctx)
+{
+ struct wbcDomainSid builtin;
+ struct wbcDomainInfo *info = NULL;
+ struct wbcInterfaceDetails *details = NULL;
+ struct wbcDomainSid sids[2];
+ uint32_t *rids = NULL;
+ uint32_t num_rids;
+ wbcErr ret = false;
+
+ torture_assert_wbc_ok_goto_fail(tctx,
+ wbcInterfaceDetails(&details),
+ "%s",
+ "wbcInterfaceDetails failed");
+ torture_assert_wbc_ok_goto_fail(tctx,
+ wbcDomainInfo(details->netbios_domain, &info),
+ "wbcDomainInfo of %s failed",
+ details->netbios_domain);
+
+ sids[0] = info->sid;
+ sids[0].sub_auths[sids[0].num_auths++] = 500;
+ sids[1] = info->sid;
+ sids[1].sub_auths[sids[1].num_auths++] = 512;
+
+ torture_assert_wbc_ok_goto_fail(tctx,
+ wbcStringToSid("S-1-5-32", &builtin),
+ "wbcStringToSid of %s failed",
+ "S-1-5-32");
+
+ ret = wbcGetSidAliases(&builtin, sids, 2, &rids, &num_rids);
+ torture_assert_wbc_ok_goto_fail(tctx,
+ ret,
+ "%s",
+ "wbcGetSidAliases failed");
+
+ ret = true;
+fail:
+ wbcFreeMemory(details);
+ wbcFreeMemory(info);
+ wbcFreeMemory(rids);
+ return ret;
+}
+
+static bool test_wbc_authenticate_user_int(struct torture_context *tctx,
+ const char *correct_password)
+{
+ struct wbcAuthUserParams params;
+ struct wbcAuthUserInfo *info = NULL;
+ struct wbcAuthErrorInfo *error = NULL;
+ wbcErr ret;
+ struct cli_credentials *creds = samba_cmdline_get_creds();
+
+ ret = wbcAuthenticateUser(cli_credentials_get_username(
+ creds), correct_password);
+ torture_assert_wbc_equal(tctx, ret, WBC_ERR_SUCCESS,
+ "wbcAuthenticateUser of %s failed",
+ cli_credentials_get_username(creds));
+
+ ZERO_STRUCT(params);
+ params.account_name =
+ cli_credentials_get_username(creds);
+ params.level = WBC_AUTH_USER_LEVEL_PLAIN;
+ params.password.plaintext = correct_password;
+
+ ret = wbcAuthenticateUserEx(&params, &info, &error);
+ torture_assert_wbc_equal(tctx, ret, WBC_ERR_SUCCESS,
+ "wbcAuthenticateUserEx of %s failed", params.account_name);
+ wbcFreeMemory(info);
+ info = NULL;
+
+ wbcFreeMemory(error);
+ error = NULL;
+
+ params.password.plaintext = "wrong";
+ ret = wbcAuthenticateUserEx(&params, &info, &error);
+ torture_assert_wbc_equal(tctx, ret, WBC_ERR_AUTH_ERROR,
+ "wbcAuthenticateUserEx for %s succeeded where it "
+ "should have failed", params.account_name);
+ wbcFreeMemory(info);
+ info = NULL;
+
+ wbcFreeMemory(error);
+ error = NULL;
+
+ return true;
+}
+
+static bool test_wbc_authenticate_user(struct torture_context *tctx)
+{
+ struct cli_credentials *creds = samba_cmdline_get_creds();
+
+ return test_wbc_authenticate_user_int(tctx,
+ cli_credentials_get_password(creds));
+}
+
+static bool test_wbc_change_password(struct torture_context *tctx)
+{
+ wbcErr ret;
+ struct cli_credentials *creds = samba_cmdline_get_creds();
+ const char *oldpass =
+ cli_credentials_get_password(creds);
+ const char *newpass = "Koo8irei%$";
+
+ struct samr_CryptPassword new_nt_password;
+ struct samr_CryptPassword new_lm_password;
+ struct samr_Password old_nt_hash_enc;
+ struct samr_Password old_lanman_hash_enc;
+
+ gnutls_cipher_hd_t cipher_hnd = NULL;
+
+ uint8_t old_nt_hash[16];
+ uint8_t old_lanman_hash[16];
+ uint8_t new_nt_hash[16];
+ uint8_t new_lanman_hash[16];
+ gnutls_datum_t old_nt_key = {
+ .data = old_nt_hash,
+ .size = sizeof(old_nt_hash),
+ };
+
+ struct wbcChangePasswordParams params;
+
+ if (oldpass == NULL) {
+ torture_skip(tctx,
+ "skipping wbcChangeUserPassword test as old password cannot be retrieved\n");
+ }
+
+ ZERO_STRUCT(params);
+
+ E_md4hash(oldpass, old_nt_hash);
+ E_md4hash(newpass, new_nt_hash);
+
+ if (lpcfg_client_lanman_auth(tctx->lp_ctx) &&
+ E_deshash(newpass, new_lanman_hash) &&
+ E_deshash(oldpass, old_lanman_hash)) {
+
+ /* E_deshash returns false for 'long' passwords (> 14
+ DOS chars). This allows us to match Win2k, which
+ does not store a LM hash for these passwords (which
+ would reduce the effective password length to 14) */
+
+ encode_pw_buffer(new_lm_password.data, newpass, STR_UNICODE);
+
+ gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &old_nt_key,
+ NULL);
+ gnutls_cipher_encrypt(cipher_hnd,
+ new_lm_password.data,
+ 516);
+ gnutls_cipher_deinit(cipher_hnd);
+
+ E_old_pw_hash(new_nt_hash, old_lanman_hash,
+ old_lanman_hash_enc.hash);
+
+ params.old_password.response.old_lm_hash_enc_length =
+ sizeof(old_lanman_hash_enc.hash);
+ params.old_password.response.old_lm_hash_enc_data =
+ old_lanman_hash_enc.hash;
+ params.new_password.response.lm_length =
+ sizeof(new_lm_password.data);
+ params.new_password.response.lm_data =
+ new_lm_password.data;
+ } else {
+ ZERO_STRUCT(new_lm_password);
+ ZERO_STRUCT(old_lanman_hash_enc);
+ }
+
+ encode_pw_buffer(new_nt_password.data, newpass, STR_UNICODE);
+
+ gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &old_nt_key,
+ NULL);
+ gnutls_cipher_encrypt(cipher_hnd,
+ new_nt_password.data,
+ 516);
+ gnutls_cipher_deinit(cipher_hnd);
+
+ E_old_pw_hash(new_nt_hash, old_nt_hash, old_nt_hash_enc.hash);
+
+ params.old_password.response.old_nt_hash_enc_length =
+ sizeof(old_nt_hash_enc.hash);
+ params.old_password.response.old_nt_hash_enc_data =
+ old_nt_hash_enc.hash;
+ params.new_password.response.nt_length = sizeof(new_nt_password.data);
+ params.new_password.response.nt_data = new_nt_password.data;
+
+ params.level = WBC_CHANGE_PASSWORD_LEVEL_RESPONSE;
+ params.account_name =
+ cli_credentials_get_username(creds);
+ params.domain_name =
+ cli_credentials_get_domain(creds);
+
+ ret = wbcChangeUserPasswordEx(&params, NULL, NULL, NULL);
+ torture_assert_wbc_equal(tctx, ret, WBC_ERR_SUCCESS,
+ "wbcChangeUserPassword for %s failed", params.account_name);
+
+ if (!test_wbc_authenticate_user_int(tctx, newpass)) {
+ return false;
+ }
+
+ ret = wbcChangeUserPassword(
+ cli_credentials_get_username(creds),
+ newpass,
+ cli_credentials_get_password(creds));
+ torture_assert_wbc_equal(tctx, ret, WBC_ERR_SUCCESS,
+ "wbcChangeUserPassword for %s failed", params.account_name);
+
+ return test_wbc_authenticate_user_int(tctx,
+ cli_credentials_get_password(creds));
+}
+
+static bool test_wbc_logon_user(struct torture_context *tctx)
+{
+ struct wbcLogonUserParams params;
+ struct wbcLogonUserInfo *info = NULL;
+ struct wbcAuthErrorInfo *error = NULL;
+ struct wbcUserPasswordPolicyInfo *policy = NULL;
+ struct wbcInterfaceDetails *iface;
+ struct wbcDomainSid sid;
+ enum wbcSidType sidtype;
+ char *sidstr;
+ wbcErr ret;
+ struct cli_credentials *creds = samba_cmdline_get_creds();
+ uint32_t i, flags = 0;
+ const char *expected_unix_username = NULL;
+ const char *unix_username = NULL;
+
+ ZERO_STRUCT(params);
+
+ ret = wbcLogonUser(&params, &info, &error, &policy);
+ torture_assert_wbc_equal(tctx, ret, WBC_ERR_INVALID_PARAM,
+ "%s", "wbcLogonUser succeeded for NULL where it should "
+ "have failed");
+
+ params.username =
+ cli_credentials_get_username(creds);
+ params.password =
+ cli_credentials_get_password(creds);
+
+ ret = wbcAddNamedBlob(&params.num_blobs, &params.blobs,
+ "foo", 0, discard_const_p(uint8_t, "bar"), 4);
+ torture_assert_wbc_equal(tctx, ret, WBC_ERR_SUCCESS,
+ "%s", "wbcAddNamedBlob failed");
+
+ ret = wbcLogonUser(&params, &info, &error, &policy);
+ torture_assert_wbc_equal(tctx, ret, WBC_ERR_SUCCESS,
+ "wbcLogonUser for %s failed", params.username);
+ wbcFreeMemory(info); info = NULL;
+ wbcFreeMemory(error); error = NULL;
+ wbcFreeMemory(policy); policy = NULL;
+
+ params.password = "wrong";
+
+ ret = wbcLogonUser(&params, &info, &error, &policy);
+ torture_assert_wbc_equal(tctx, ret, WBC_ERR_AUTH_ERROR,
+ "wbcLogonUser for %s should have failed with "
+ "WBC_ERR_AUTH_ERROR", params.username);
+ wbcFreeMemory(info); info = NULL;
+ wbcFreeMemory(error); error = NULL;
+ wbcFreeMemory(policy); policy = NULL;
+
+ ret = wbcAddNamedBlob(&params.num_blobs, &params.blobs,
+ "membership_of", 0,
+ discard_const_p(uint8_t, "S-1-2-3-4"),
+ strlen("S-1-2-3-4")+1);
+ torture_assert_wbc_equal(tctx, ret, WBC_ERR_SUCCESS,
+ "%s", "wbcAddNamedBlob failed");
+ params.password =
+ cli_credentials_get_password(creds);
+ ret = wbcLogonUser(&params, &info, &error, &policy);
+ torture_assert_wbc_equal(tctx, ret, WBC_ERR_AUTH_ERROR,
+ "wbcLogonUser for %s should have failed with "
+ "WBC_ERR_AUTH_ERROR", params.username);
+ wbcFreeMemory(info); info = NULL;
+ wbcFreeMemory(error); error = NULL;
+ wbcFreeMemory(policy); policy = NULL;
+ wbcFreeMemory(params.blobs);
+ params.blobs = NULL; params.num_blobs = 0;
+
+ ret = wbcInterfaceDetails(&iface);
+ torture_assert_wbc_equal(tctx, ret, WBC_ERR_SUCCESS,
+ "%s", "wbcInterfaceDetails failed");
+
+ ret = wbcLookupName(iface->netbios_domain,
+ cli_credentials_get_username(creds),
+ &sid,
+ &sidtype);
+ wbcFreeMemory(iface);
+ torture_assert_wbc_equal(tctx, ret, WBC_ERR_SUCCESS,
+ "wbcLookupName for %s failed",
+ cli_credentials_get_username(creds));
+
+ ret = wbcSidToString(&sid, &sidstr);
+ torture_assert_wbc_equal(tctx, ret, WBC_ERR_SUCCESS,
+ "%s", "wbcSidToString failed");
+
+ ret = wbcAddNamedBlob(&params.num_blobs, &params.blobs,
+ "membership_of", 0,
+ (uint8_t *)sidstr, strlen(sidstr)+1);
+ torture_assert_wbc_equal(tctx, ret, WBC_ERR_SUCCESS,
+ "%s", "wbcAddNamedBlob failed");
+ wbcFreeMemory(sidstr);
+ params.password =
+ cli_credentials_get_password(creds);
+ ret = wbcLogonUser(&params, &info, &error, &policy);
+ torture_assert_wbc_equal(tctx, ret, WBC_ERR_SUCCESS,
+ "wbcLogonUser for %s failed", params.username);
+ wbcFreeMemory(info); info = NULL;
+ wbcFreeMemory(error); error = NULL;
+ wbcFreeMemory(policy); policy = NULL;
+ wbcFreeMemory(params.blobs);
+ params.blobs = NULL; params.num_blobs = 0;
+
+ /* Test WBFLAG_PAM_UNIX_NAME */
+ params.username = cli_credentials_get_username(creds);
+ params.password = cli_credentials_get_password(creds);
+ flags = WBFLAG_PAM_UNIX_NAME;
+
+ torture_assert(tctx,
+ lp_load_global(lpcfg_configfile(tctx->lp_ctx)),
+ "lp_load_global() failed\n");
+ expected_unix_username = fill_domain_username_talloc(tctx,
+ cli_credentials_get_domain(creds),
+ cli_credentials_get_username(creds),
+ true);
+
+ ret = wbcAddNamedBlob(&params.num_blobs, &params.blobs, "flags", 0,
+ (uint8_t *)&flags, sizeof(flags));
+ torture_assert_wbc_equal(tctx, ret, WBC_ERR_SUCCESS,
+ "%s", "wbcAddNamedBlob failed");
+
+ ret = wbcLogonUser(&params, &info, &error, &policy);
+ torture_assert_wbc_equal(tctx, ret, WBC_ERR_SUCCESS,
+ "wbcLogonUser for %s failed",
+ params.username);
+
+ for (unix_username=NULL, i=0; i<info->num_blobs; i++) {
+ torture_comment(tctx, "Found named blob '%s'\n", info->blobs[i].name);
+ if (strequal(info->blobs[i].name, "unix_username")) {
+ unix_username = (const char *)info->blobs[i].blob.data;
+ }
+ }
+ torture_assert_not_null(tctx, unix_username,
+ "wbcLogonUserInfo does not have unix_username blob\n");
+ torture_assert_str_equal(tctx, unix_username,
+ expected_unix_username,
+ "Unexpected unix_username");
+ wbcFreeMemory(info); info = NULL;
+ wbcFreeMemory(error); error = NULL;
+ wbcFreeMemory(policy); policy = NULL;
+ wbcFreeMemory(params.blobs);
+ params.blobs = NULL; params.num_blobs = 0;
+
+ return true;
+}
+
+static bool test_wbc_getgroups(struct torture_context *tctx)
+{
+ wbcErr ret;
+ uint32_t num_groups;
+ gid_t *groups;
+ struct cli_credentials *creds = samba_cmdline_get_creds();
+
+ ret = wbcGetGroups(
+ cli_credentials_get_username(creds),
+ &num_groups,
+ &groups);
+ torture_assert_wbc_equal(tctx, ret, WBC_ERR_SUCCESS,
+ "wbcGetGroups for %s failed",
+ cli_credentials_get_username(creds));
+ wbcFreeMemory(groups);
+ return true;
+}
+
+struct torture_suite *torture_wbclient(TALLOC_CTX *ctx)
+{
+ struct torture_suite *suite = torture_suite_create(ctx, "wbclient");
+
+ torture_suite_add_simple_test(suite, "wbcPing", test_wbc_ping);
+ torture_suite_add_simple_test(suite, "wbcPingDc", test_wbc_pingdc);
+ torture_suite_add_simple_test(suite, "wbcPingDc2", test_wbc_pingdc2);
+ torture_suite_add_simple_test(suite, "wbcLibraryDetails", test_wbc_library_details);
+ torture_suite_add_simple_test(suite, "wbcInterfaceDetails", test_wbc_interface_details);
+ torture_suite_add_simple_test(suite, "wbcSidTypeString", test_wbc_sidtypestring);
+ torture_suite_add_simple_test(suite, "wbcSidToString", test_wbc_sidtostring);
+ torture_suite_add_simple_test(suite, "wbcGuidToString", test_wbc_guidtostring);
+ torture_suite_add_simple_test(suite, "wbcDomainInfo", test_wbc_domain_info);
+ torture_suite_add_simple_test(suite, "wbcListUsers", test_wbc_users);
+ torture_suite_add_simple_test(suite, "wbcListGroups", test_wbc_groups);
+ torture_suite_add_simple_test(suite, "wbcListTrusts", test_wbc_trusts);
+ torture_suite_add_simple_test(suite, "wbcLookupDomainController", test_wbc_lookupdc);
+ torture_suite_add_simple_test(suite, "wbcLookupDomainControllerEx", test_wbc_lookupdcex);
+ torture_suite_add_simple_test(suite, "wbcResolveWinsByName", test_wbc_resolve_winsbyname);
+ torture_suite_add_simple_test(suite, "wbcResolveWinsByIP", test_wbc_resolve_winsbyip);
+ torture_suite_add_simple_test(suite, "wbcLookupRids",
+ test_wbc_lookup_rids);
+ torture_suite_add_simple_test(suite, "wbcGetSidAliases",
+ test_wbc_get_sidaliases);
+ torture_suite_add_simple_test(suite, "wbcAuthenticateUser",
+ test_wbc_authenticate_user);
+ torture_suite_add_simple_test(suite, "wbcLogonUser",
+ test_wbc_logon_user);
+ torture_suite_add_simple_test(suite, "wbcChangeUserPassword",
+ test_wbc_change_password);
+ torture_suite_add_simple_test(suite, "wbcGetGroups",
+ test_wbc_getgroups);
+
+ return suite;
+}
diff --git a/nsswitch/libwbclient/wbc_err_internal.h b/nsswitch/libwbclient/wbc_err_internal.h
new file mode 100644
index 0000000..dd8e7f2
--- /dev/null
+++ b/nsswitch/libwbclient/wbc_err_internal.h
@@ -0,0 +1,45 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind client API
+
+ Copyright (C) Gerald (Jerry) Carter 2007
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _WBC_ERR_INTERNAL_H
+#define _WBC_ERR_INTERNAL_H
+
+/* Private macros */
+
+#define BAIL_ON_WBC_ERROR(x) \
+ do { \
+ if (!WBC_ERROR_IS_OK(x)) { \
+ goto done; \
+ } \
+ } while(0)
+
+#define BAIL_ON_PTR_ERROR(x, status) \
+ do { \
+ if ((x) == NULL) { \
+ status = WBC_ERR_NO_MEMORY; \
+ goto done; \
+ } else { \
+ status = WBC_ERR_SUCCESS; \
+ } \
+ } while (0)
+
+
+#endif /* _WBC_ERR_INTERNAL_H */
diff --git a/nsswitch/libwbclient/wbc_guid.c b/nsswitch/libwbclient/wbc_guid.c
new file mode 100644
index 0000000..72701c8
--- /dev/null
+++ b/nsswitch/libwbclient/wbc_guid.c
@@ -0,0 +1,103 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind client API
+
+ Copyright (C) Gerald (Jerry) Carter 2007
+
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Required Headers */
+
+#include "replace.h"
+#include "libwbclient.h"
+
+/* Convert a binary GUID to a character string */
+_PUBLIC_
+wbcErr wbcGuidToString(const struct wbcGuid *guid,
+ char **guid_string)
+{
+ char *result;
+
+ result = (char *)wbcAllocateMemory(37, 1, NULL);
+ if (result == NULL) {
+ return WBC_ERR_NO_MEMORY;
+ }
+ snprintf(result, 37,
+ "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ guid->time_low, guid->time_mid,
+ guid->time_hi_and_version,
+ guid->clock_seq[0],
+ guid->clock_seq[1],
+ guid->node[0], guid->node[1],
+ guid->node[2], guid->node[3],
+ guid->node[4], guid->node[5]);
+ *guid_string = result;
+
+ return WBC_ERR_SUCCESS;
+}
+
+/* @brief Convert a character string to a binary GUID */
+_PUBLIC_
+wbcErr wbcStringToGuid(const char *str,
+ struct wbcGuid *guid)
+{
+ unsigned int time_low;
+ unsigned int time_mid, time_hi_and_version;
+ unsigned int clock_seq[2];
+ unsigned int node[6];
+ int i;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ if (!guid) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (!str) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (11 == sscanf(str, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ &time_low, &time_mid, &time_hi_and_version,
+ &clock_seq[0], &clock_seq[1],
+ &node[0], &node[1], &node[2], &node[3], &node[4], &node[5])) {
+ wbc_status = WBC_ERR_SUCCESS;
+ } else if (11 == sscanf(str, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
+ &time_low, &time_mid, &time_hi_and_version,
+ &clock_seq[0], &clock_seq[1],
+ &node[0], &node[1], &node[2], &node[3], &node[4], &node[5])) {
+ wbc_status = WBC_ERR_SUCCESS;
+ }
+
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ guid->time_low = time_low;
+ guid->time_mid = time_mid;
+ guid->time_hi_and_version = time_hi_and_version;
+ guid->clock_seq[0] = clock_seq[0];
+ guid->clock_seq[1] = clock_seq[1];
+
+ for (i=0;i<6;i++) {
+ guid->node[i] = node[i];
+ }
+
+ wbc_status = WBC_ERR_SUCCESS;
+
+done:
+ return wbc_status;
+}
diff --git a/nsswitch/libwbclient/wbc_idmap.c b/nsswitch/libwbclient/wbc_idmap.c
new file mode 100644
index 0000000..c3acced
--- /dev/null
+++ b/nsswitch/libwbclient/wbc_idmap.c
@@ -0,0 +1,550 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind client API
+
+ Copyright (C) Gerald (Jerry) Carter 2007
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Required Headers */
+
+#include "replace.h"
+#include "libwbclient.h"
+#include "../winbind_client.h"
+#include "lib/util/smb_strtox.h"
+
+/* Convert a Windows SID to a Unix uid, allocating an uid if needed */
+_PUBLIC_
+wbcErr wbcCtxSidToUid(struct wbcContext *ctx, const struct wbcDomainSid *sid,
+ uid_t *puid)
+{
+ struct wbcUnixId xid;
+ wbcErr wbc_status;
+
+ if (!sid || !puid) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ wbc_status = wbcCtxSidsToUnixIds(ctx, sid, 1, &xid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ goto done;
+ }
+
+ if ((xid.type == WBC_ID_TYPE_UID) || (xid.type == WBC_ID_TYPE_BOTH)) {
+ *puid = xid.id.uid;
+ wbc_status = WBC_ERR_SUCCESS;
+ } else {
+ wbc_status = WBC_ERR_DOMAIN_NOT_FOUND;
+ }
+
+ done:
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcSidToUid(const struct wbcDomainSid *sid, uid_t *puid)
+{
+ return wbcCtxSidToUid(NULL, sid, puid);
+}
+
+/* Convert a Windows SID to a Unix uid if there already is a mapping */
+_PUBLIC_
+wbcErr wbcQuerySidToUid(const struct wbcDomainSid *sid,
+ uid_t *puid)
+{
+ return WBC_ERR_NOT_IMPLEMENTED;
+}
+
+/* Convert a Unix uid to a Windows SID, allocating a SID if needed */
+_PUBLIC_
+wbcErr wbcCtxUidToSid(struct wbcContext *ctx, uid_t uid,
+ struct wbcDomainSid *psid)
+{
+ struct wbcUnixId xid;
+ struct wbcDomainSid sid;
+ struct wbcDomainSid null_sid = { 0 };
+ wbcErr wbc_status;
+
+ if (!psid) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ xid = (struct wbcUnixId) { .type = WBC_ID_TYPE_UID, .id.uid = uid };
+
+ wbc_status = wbcCtxUnixIdsToSids(ctx, &xid, 1, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ goto done;
+ }
+
+ if (memcmp(&sid, &null_sid, sizeof(sid)) != 0) {
+ *psid = sid;
+ } else {
+ wbc_status = WBC_ERR_DOMAIN_NOT_FOUND;
+ }
+
+done:
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcUidToSid(uid_t uid, struct wbcDomainSid *sid)
+{
+ return wbcCtxUidToSid(NULL, uid, sid);
+}
+
+/* Convert a Unix uid to a Windows SID if there already is a mapping */
+_PUBLIC_
+wbcErr wbcQueryUidToSid(uid_t uid,
+ struct wbcDomainSid *sid)
+{
+ return WBC_ERR_NOT_IMPLEMENTED;
+}
+
+/** @brief Convert a Windows SID to a Unix gid, allocating a gid if needed
+ *
+ * @param *sid Pointer to the domain SID to be resolved
+ * @param *pgid Pointer to the resolved gid_t value
+ *
+ * @return #wbcErr
+ *
+ **/
+
+_PUBLIC_
+wbcErr wbcCtxSidToGid(struct wbcContext *ctx, const struct wbcDomainSid *sid,
+ gid_t *pgid)
+{
+ struct wbcUnixId xid;
+ wbcErr wbc_status;
+
+ if (!sid || !pgid) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ wbc_status = wbcCtxSidsToUnixIds(ctx, sid, 1, &xid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ goto done;
+ }
+
+ if ((xid.type == WBC_ID_TYPE_GID) || (xid.type == WBC_ID_TYPE_BOTH)) {
+ *pgid = xid.id.gid;
+ wbc_status = WBC_ERR_SUCCESS;
+ } else {
+ wbc_status = WBC_ERR_DOMAIN_NOT_FOUND;
+ }
+
+ done:
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcSidToGid(const struct wbcDomainSid *sid, gid_t *pgid)
+{
+ return wbcCtxSidToGid(NULL, sid, pgid);
+}
+
+/* Convert a Windows SID to a Unix gid if there already is a mapping */
+
+_PUBLIC_
+wbcErr wbcQuerySidToGid(const struct wbcDomainSid *sid,
+ gid_t *pgid)
+{
+ return WBC_ERR_NOT_IMPLEMENTED;
+}
+
+
+/* Convert a Unix gid to a Windows SID, allocating a SID if needed */
+_PUBLIC_
+wbcErr wbcCtxGidToSid(struct wbcContext *ctx, gid_t gid,
+ struct wbcDomainSid *psid)
+{
+ struct wbcUnixId xid;
+ struct wbcDomainSid sid;
+ struct wbcDomainSid null_sid = { 0 };
+ wbcErr wbc_status;
+
+ if (!psid) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ xid = (struct wbcUnixId) { .type = WBC_ID_TYPE_GID, .id.gid = gid };
+
+ wbc_status = wbcCtxUnixIdsToSids(ctx, &xid, 1, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ goto done;
+ }
+
+ if (memcmp(&sid, &null_sid, sizeof(sid)) != 0) {
+ *psid = sid;
+ } else {
+ wbc_status = WBC_ERR_DOMAIN_NOT_FOUND;
+ }
+
+done:
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcGidToSid(gid_t gid, struct wbcDomainSid *sid)
+{
+ return wbcCtxGidToSid(NULL, gid, sid);
+}
+
+/* Convert a Unix gid to a Windows SID if there already is a mapping */
+_PUBLIC_
+wbcErr wbcQueryGidToSid(gid_t gid,
+ struct wbcDomainSid *sid)
+{
+ return WBC_ERR_NOT_IMPLEMENTED;
+}
+
+/* Obtain a new uid from Winbind */
+_PUBLIC_
+wbcErr wbcCtxAllocateUid(struct wbcContext *ctx, uid_t *puid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ if (!puid)
+ return WBC_ERR_INVALID_PARAM;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Make request */
+
+ wbc_status = wbcRequestResponsePriv(ctx, WINBINDD_ALLOCATE_UID,
+ &request, &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ /* Copy out result */
+ *puid = response.data.uid;
+
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcAllocateUid(uid_t *puid)
+{
+ return wbcCtxAllocateUid(NULL, puid);
+}
+
+/* Obtain a new gid from Winbind */
+_PUBLIC_
+wbcErr wbcCtxAllocateGid(struct wbcContext *ctx, gid_t *pgid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ if (!pgid)
+ return WBC_ERR_INVALID_PARAM;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Make request */
+
+ wbc_status = wbcRequestResponsePriv(ctx, WINBINDD_ALLOCATE_GID,
+ &request, &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ /* Copy out result */
+ *pgid = response.data.gid;
+
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcAllocateGid(gid_t *pgid)
+{
+ return wbcCtxAllocateGid(NULL, pgid);
+}
+
+/* we can't include smb.h here... */
+#define _ID_TYPE_UID 1
+#define _ID_TYPE_GID 2
+
+/* Set an user id mapping - not implemented any more */
+_PUBLIC_
+wbcErr wbcSetUidMapping(uid_t uid, const struct wbcDomainSid *sid)
+{
+ return WBC_ERR_NOT_IMPLEMENTED;
+}
+
+/* Set a group id mapping - not implemented any more */
+_PUBLIC_
+wbcErr wbcSetGidMapping(gid_t gid, const struct wbcDomainSid *sid)
+{
+ return WBC_ERR_NOT_IMPLEMENTED;
+}
+
+/* Remove a user id mapping - not implemented any more */
+_PUBLIC_
+wbcErr wbcRemoveUidMapping(uid_t uid, const struct wbcDomainSid *sid)
+{
+ return WBC_ERR_NOT_IMPLEMENTED;
+}
+
+/* Remove a group id mapping - not implemented any more */
+_PUBLIC_
+wbcErr wbcRemoveGidMapping(gid_t gid, const struct wbcDomainSid *sid)
+{
+ return WBC_ERR_NOT_IMPLEMENTED;
+}
+
+/* Set the highwater mark for allocated uids - not implemented any more */
+_PUBLIC_
+wbcErr wbcSetUidHwm(uid_t uid_hwm)
+{
+ return WBC_ERR_NOT_IMPLEMENTED;
+}
+
+/* Set the highwater mark for allocated gids - not implemented any more */
+_PUBLIC_
+wbcErr wbcSetGidHwm(gid_t gid_hwm)
+{
+ return WBC_ERR_NOT_IMPLEMENTED;
+}
+
+/* Convert a list of SIDs */
+_PUBLIC_
+wbcErr wbcCtxSidsToUnixIds(struct wbcContext *ctx,
+ const struct wbcDomainSid *sids,
+ uint32_t num_sids, struct wbcUnixId *ids)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ int buflen, extra_len;
+ uint32_t i;
+ char *sidlist, *p, *extra_data;
+
+ buflen = num_sids * (WBC_SID_STRING_BUFLEN + 1) + 1;
+
+ sidlist = (char *)malloc(buflen);
+ if (sidlist == NULL) {
+ return WBC_ERR_NO_MEMORY;
+ }
+
+ p = sidlist;
+
+ for (i=0; i<num_sids; i++) {
+ int remaining;
+ int len;
+
+ remaining = buflen - (p - sidlist);
+
+ len = wbcSidToStringBuf(&sids[i], p, remaining);
+ if (len > remaining) {
+ free(sidlist);
+ return WBC_ERR_UNKNOWN_FAILURE;
+ }
+
+ p += len;
+ *p++ = '\n';
+ }
+ *p++ = '\0';
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.extra_data.data = sidlist;
+ request.extra_len = p - sidlist;
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_SIDS_TO_XIDS,
+ &request, &response);
+ free(sidlist);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return wbc_status;
+ }
+
+ extra_len = response.length - sizeof(struct winbindd_response);
+ extra_data = (char *)response.extra_data.data;
+
+ if ((extra_len <= 0) || (extra_data[extra_len-1] != '\0')) {
+ goto wbc_err_invalid;
+ }
+
+ p = extra_data;
+
+ for (i=0; i<num_sids; i++) {
+ struct wbcUnixId *id = &ids[i];
+ char *q;
+ int error = 0;
+
+ switch (p[0]) {
+ case 'U':
+ id->type = WBC_ID_TYPE_UID;
+ id->id.uid = smb_strtoul(p+1,
+ &q,
+ 10,
+ &error,
+ SMB_STR_STANDARD);
+ break;
+ case 'G':
+ id->type = WBC_ID_TYPE_GID;
+ id->id.gid = smb_strtoul(p+1,
+ &q,
+ 10,
+ &error,
+ SMB_STR_STANDARD);
+ break;
+ case 'B':
+ id->type = WBC_ID_TYPE_BOTH;
+ id->id.uid = smb_strtoul(p+1,
+ &q,
+ 10,
+ &error,
+ SMB_STR_STANDARD);
+ break;
+ default:
+ id->type = WBC_ID_TYPE_NOT_SPECIFIED;
+ q = strchr(p, '\n');
+ break;
+ };
+ if (q == NULL || q[0] != '\n' || error != 0) {
+ goto wbc_err_invalid;
+ }
+ p = q+1;
+ }
+ wbc_status = WBC_ERR_SUCCESS;
+ goto done;
+
+wbc_err_invalid:
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+done:
+ winbindd_free_response(&response);
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcSidsToUnixIds(const struct wbcDomainSid *sids, uint32_t num_sids,
+ struct wbcUnixId *ids)
+{
+ return wbcCtxSidsToUnixIds(NULL, sids, num_sids, ids);
+}
+
+_PUBLIC_
+wbcErr wbcCtxUnixIdsToSids(struct wbcContext *ctx,
+ const struct wbcUnixId *ids, uint32_t num_ids,
+ struct wbcDomainSid *sids)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status;
+ char *buf;
+ char *s;
+ const size_t sidlen = (1 /* U/G */ + 10 /* 2^32 */ + 1 /* \n */);
+ size_t ofs, buflen;
+ uint32_t i;
+
+ if (num_ids > SIZE_MAX / sidlen) {
+ return WBC_ERR_NO_MEMORY; /* overflow */
+ }
+ buflen = num_ids * sidlen;
+
+ buflen += 1; /* trailing \0 */
+ if (buflen < 1) {
+ return WBC_ERR_NO_MEMORY; /* overflow */
+ }
+
+ buf = malloc(buflen);
+ if (buf == NULL) {
+ return WBC_ERR_NO_MEMORY;
+ }
+
+ ofs = 0;
+
+ for (i=0; i<num_ids; i++) {
+ const struct wbcUnixId *id = &ids[i];
+ int len;
+
+ switch (id->type) {
+ case WBC_ID_TYPE_UID:
+ len = snprintf(buf+ofs, buflen-ofs, "U%"PRIu32"\n",
+ (uint32_t)id->id.uid);
+ break;
+ case WBC_ID_TYPE_GID:
+ len = snprintf(buf+ofs, buflen-ofs, "G%"PRIu32"\n",
+ (uint32_t)id->id.gid);
+ break;
+ default:
+ free(buf);
+ return WBC_ERR_INVALID_PARAM;
+ }
+
+ if (len + ofs >= buflen) { /* >= for the terminating '\0' */
+ free(buf);
+ return WBC_ERR_UNKNOWN_FAILURE;
+ }
+ ofs += len;
+ }
+
+ request = (struct winbindd_request) {
+ .extra_data.data = buf, .extra_len = ofs+1
+ };
+ response = (struct winbindd_response) {0};
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_XIDS_TO_SIDS,
+ &request, &response);
+ free(buf);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return wbc_status;
+ }
+
+ s = response.extra_data.data;
+ for (i=0; i<num_ids; i++) {
+ char *n = strchr(s, '\n');
+
+ if (n == NULL) {
+ goto fail;
+ }
+ *n = '\0';
+
+ wbc_status = wbcStringToSid(s, &sids[i]);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ sids[i] = (struct wbcDomainSid) {0};
+ }
+ s = n+1;
+ }
+
+ wbc_status = WBC_ERR_SUCCESS;
+fail:
+ winbindd_free_response(&response);
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcUnixIdsToSids(const struct wbcUnixId *ids, uint32_t num_ids,
+ struct wbcDomainSid *sids)
+{
+ return wbcCtxUnixIdsToSids(NULL, ids, num_ids, sids);
+}
diff --git a/nsswitch/libwbclient/wbc_pam.c b/nsswitch/libwbclient/wbc_pam.c
new file mode 100644
index 0000000..4df0ffe
--- /dev/null
+++ b/nsswitch/libwbclient/wbc_pam.c
@@ -0,0 +1,1484 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind client API
+
+ Copyright (C) Gerald (Jerry) Carter 2007
+ Copyright (C) Guenther Deschner 2008
+ Copyright (C) Volker Lendecke 2009
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Required Headers */
+
+#include "replace.h"
+#include "libwbclient.h"
+#include "../winbind_client.h"
+
+/* Authenticate a username/password pair */
+_PUBLIC_
+wbcErr wbcCtxAuthenticateUser(struct wbcContext *ctx,
+ const char *username, const char *password)
+{
+ wbcErr wbc_status = WBC_ERR_SUCCESS;
+ struct wbcAuthUserParams params;
+
+ ZERO_STRUCT(params);
+
+ params.account_name = username;
+ params.level = WBC_AUTH_USER_LEVEL_PLAIN;
+ params.password.plaintext = password;
+
+ wbc_status = wbcCtxAuthenticateUserEx(ctx, &params, NULL, NULL);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+done:
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcAuthenticateUser(const char *username, const char *password)
+{
+ return wbcCtxAuthenticateUser(NULL, username, password);
+}
+
+static bool sid_attr_compose(struct wbcSidWithAttr *s,
+ const struct wbcDomainSid *d,
+ uint32_t rid, uint32_t attr)
+{
+ if (d->num_auths >= WBC_MAXSUBAUTHS) {
+ return false;
+ }
+ s->sid = *d;
+ s->sid.sub_auths[s->sid.num_auths++] = rid;
+ s->attributes = attr;
+ return true;
+}
+
+static void wbcAuthUserInfoDestructor(void *ptr)
+{
+ struct wbcAuthUserInfo *i = (struct wbcAuthUserInfo *)ptr;
+ free(i->account_name);
+ free(i->user_principal);
+ free(i->full_name);
+ free(i->domain_name);
+ free(i->dns_domain_name);
+ free(i->logon_server);
+ free(i->logon_script);
+ free(i->profile_path);
+ free(i->home_directory);
+ free(i->home_drive);
+ free(i->sids);
+}
+
+static wbcErr wbc_create_auth_info(const struct winbindd_response *resp,
+ struct wbcAuthUserInfo **_i)
+{
+ wbcErr wbc_status = WBC_ERR_SUCCESS;
+ struct wbcAuthUserInfo *i;
+ struct wbcDomainSid domain_sid;
+ char *p;
+ uint32_t sn = 0;
+ uint32_t j;
+
+ i = (struct wbcAuthUserInfo *)wbcAllocateMemory(
+ 1, sizeof(struct wbcAuthUserInfo),
+ wbcAuthUserInfoDestructor);
+ BAIL_ON_PTR_ERROR(i, wbc_status);
+
+ i->user_flags = resp->data.auth.info3.user_flgs;
+
+ i->account_name = strdup(resp->data.auth.info3.user_name);
+ BAIL_ON_PTR_ERROR(i->account_name, wbc_status);
+ if (resp->data.auth.validation_level == 6) {
+ i->user_principal = strdup(resp->data.auth.info6.principal_name);
+ BAIL_ON_PTR_ERROR(i->user_principal, wbc_status);
+ } else {
+ i->user_principal = NULL;
+ }
+ i->full_name = strdup(resp->data.auth.info3.full_name);
+ BAIL_ON_PTR_ERROR(i->full_name, wbc_status);
+ i->domain_name = strdup(resp->data.auth.info3.logon_dom);
+ BAIL_ON_PTR_ERROR(i->domain_name, wbc_status);
+ if (resp->data.auth.validation_level == 6) {
+ i->dns_domain_name = strdup(resp->data.auth.info6.dns_domainname);
+ BAIL_ON_PTR_ERROR(i->dns_domain_name, wbc_status);
+ } else {
+ i->dns_domain_name = NULL;
+ }
+
+ i->acct_flags = resp->data.auth.info3.acct_flags;
+ memcpy(i->user_session_key,
+ resp->data.auth.user_session_key,
+ sizeof(i->user_session_key));
+ memcpy(i->lm_session_key,
+ resp->data.auth.first_8_lm_hash,
+ sizeof(i->lm_session_key));
+
+ i->logon_count = resp->data.auth.info3.logon_count;
+ i->bad_password_count = resp->data.auth.info3.bad_pw_count;
+
+ i->logon_time = resp->data.auth.info3.logon_time;
+ i->logoff_time = resp->data.auth.info3.logoff_time;
+ i->kickoff_time = resp->data.auth.info3.kickoff_time;
+ i->pass_last_set_time = resp->data.auth.info3.pass_last_set_time;
+ i->pass_can_change_time = resp->data.auth.info3.pass_can_change_time;
+ i->pass_must_change_time= resp->data.auth.info3.pass_must_change_time;
+
+ i->logon_server = strdup(resp->data.auth.info3.logon_srv);
+ BAIL_ON_PTR_ERROR(i->logon_server, wbc_status);
+ i->logon_script = strdup(resp->data.auth.info3.logon_script);
+ BAIL_ON_PTR_ERROR(i->logon_script, wbc_status);
+ i->profile_path = strdup(resp->data.auth.info3.profile_path);
+ BAIL_ON_PTR_ERROR(i->profile_path, wbc_status);
+ i->home_directory= strdup(resp->data.auth.info3.home_dir);
+ BAIL_ON_PTR_ERROR(i->home_directory, wbc_status);
+ i->home_drive = strdup(resp->data.auth.info3.dir_drive);
+ BAIL_ON_PTR_ERROR(i->home_drive, wbc_status);
+
+ i->num_sids = 2;
+ i->num_sids += resp->data.auth.info3.num_groups;
+ i->num_sids += resp->data.auth.info3.num_other_sids;
+
+ i->sids = (struct wbcSidWithAttr *)calloc(
+ sizeof(struct wbcSidWithAttr), i->num_sids);
+ BAIL_ON_PTR_ERROR(i->sids, wbc_status);
+
+ wbc_status = wbcStringToSid(resp->data.auth.info3.dom_sid,
+ &domain_sid);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ sn = 0;
+ if (!sid_attr_compose(&i->sids[sn], &domain_sid,
+ resp->data.auth.info3.user_rid, 0)) {
+ wbc_status = WBC_ERR_INVALID_SID;
+ goto done;
+ }
+ sn++;
+ if (!sid_attr_compose(&i->sids[sn], &domain_sid,
+ resp->data.auth.info3.group_rid, 0)) {
+ wbc_status = WBC_ERR_INVALID_SID;
+ goto done;
+ }
+ sn++;
+
+ p = (char *)resp->extra_data.data;
+ if (!p) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ for (j=0; j < resp->data.auth.info3.num_groups; j++) {
+ uint32_t rid;
+ uint32_t attrs;
+ int ret;
+ char *s = p;
+ char *e = strchr(p, '\n');
+ if (!e) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ e[0] = '\0';
+ p = &e[1];
+
+ ret = sscanf(s, "0x%08X:0x%08X", &rid, &attrs);
+ if (ret != 2) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (!sid_attr_compose(&i->sids[sn], &domain_sid,
+ rid, attrs)) {
+ wbc_status = WBC_ERR_INVALID_SID;
+ goto done;
+ }
+ sn++;
+ }
+
+ for (j=0; j < resp->data.auth.info3.num_other_sids; j++) {
+ uint32_t attrs;
+ int ret;
+ char *s = p;
+ char *a;
+ char *e = strchr(p, '\n');
+ if (!e) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ e[0] = '\0';
+ p = &e[1];
+
+ e = strchr(s, ':');
+ if (!e) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ e[0] = '\0';
+ a = &e[1];
+
+ ret = sscanf(a, "0x%08X",
+ &attrs);
+ if (ret != 1) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ wbc_status = wbcStringToSid(s, &i->sids[sn].sid);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ i->sids[sn].attributes = attrs;
+ sn++;
+ }
+
+ i->num_sids = sn;
+
+ *_i = i;
+ i = NULL;
+done:
+ wbcFreeMemory(i);
+ return wbc_status;
+}
+
+static void wbcAuthErrorInfoDestructor(void *ptr)
+{
+ struct wbcAuthErrorInfo *e = (struct wbcAuthErrorInfo *)ptr;
+ free(e->nt_string);
+ free(e->display_string);
+}
+
+static wbcErr wbc_create_error_info(const struct winbindd_response *resp,
+ struct wbcAuthErrorInfo **_e)
+{
+ wbcErr wbc_status = WBC_ERR_SUCCESS;
+ struct wbcAuthErrorInfo *e;
+
+ e = (struct wbcAuthErrorInfo *)wbcAllocateMemory(
+ 1, sizeof(struct wbcAuthErrorInfo),
+ wbcAuthErrorInfoDestructor);
+ BAIL_ON_PTR_ERROR(e, wbc_status);
+
+ e->nt_status = resp->data.auth.nt_status;
+ e->pam_error = resp->data.auth.pam_error;
+ e->authoritative = resp->data.auth.authoritative;
+ e->nt_string = strdup(resp->data.auth.nt_status_string);
+ BAIL_ON_PTR_ERROR(e->nt_string, wbc_status);
+
+ e->display_string = strdup(resp->data.auth.error_string);
+ BAIL_ON_PTR_ERROR(e->display_string, wbc_status);
+
+ *_e = e;
+ e = NULL;
+
+done:
+ wbcFreeMemory(e);
+ return wbc_status;
+}
+
+static wbcErr wbc_create_password_policy_info(const struct winbindd_response *resp,
+ struct wbcUserPasswordPolicyInfo **_i)
+{
+ wbcErr wbc_status = WBC_ERR_SUCCESS;
+ struct wbcUserPasswordPolicyInfo *i;
+
+ i = (struct wbcUserPasswordPolicyInfo *)wbcAllocateMemory(
+ 1, sizeof(struct wbcUserPasswordPolicyInfo), NULL);
+ BAIL_ON_PTR_ERROR(i, wbc_status);
+
+ i->min_passwordage = resp->data.auth.policy.min_passwordage;
+ i->min_length_password = resp->data.auth.policy.min_length_password;
+ i->password_history = resp->data.auth.policy.password_history;
+ i->password_properties = resp->data.auth.policy.password_properties;
+ i->expire = resp->data.auth.policy.expire;
+
+ *_i = i;
+ i = NULL;
+
+done:
+ wbcFreeMemory(i);
+ return wbc_status;
+}
+
+static void wbcLogonUserInfoDestructor(void *ptr)
+{
+ struct wbcLogonUserInfo *i = (struct wbcLogonUserInfo *)ptr;
+ wbcFreeMemory(i->info);
+ wbcFreeMemory(i->blobs);
+}
+
+static wbcErr wbc_create_logon_info(struct winbindd_response *resp,
+ struct wbcLogonUserInfo **_i)
+{
+ wbcErr wbc_status = WBC_ERR_SUCCESS;
+ struct wbcLogonUserInfo *i;
+
+ i = (struct wbcLogonUserInfo *)wbcAllocateMemory(
+ 1, sizeof(struct wbcLogonUserInfo),
+ wbcLogonUserInfoDestructor);
+ BAIL_ON_PTR_ERROR(i, wbc_status);
+
+ wbc_status = wbc_create_auth_info(resp, &i->info);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ if (resp->data.auth.krb5ccname[0] != '\0') {
+ wbc_status = wbcAddNamedBlob(&i->num_blobs,
+ &i->blobs,
+ "krb5ccname",
+ 0,
+ (uint8_t *)resp->data.auth.krb5ccname,
+ strlen(resp->data.auth.krb5ccname)+1);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (resp->data.auth.unix_username[0] != '\0') {
+ wbc_status = wbcAddNamedBlob(&i->num_blobs,
+ &i->blobs,
+ "unix_username",
+ 0,
+ (uint8_t *)resp->data.auth.unix_username,
+ strlen(resp->data.auth.unix_username)+1);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ *_i = i;
+ i = NULL;
+done:
+ wbcFreeMemory(i);
+ return wbc_status;
+}
+
+
+/* Authenticate with more detailed information */
+_PUBLIC_
+wbcErr wbcCtxAuthenticateUserEx(struct wbcContext *ctx,
+ const struct wbcAuthUserParams *params,
+ struct wbcAuthUserInfo **info,
+ struct wbcAuthErrorInfo **error)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ int cmd = 0;
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (error) {
+ *error = NULL;
+ }
+
+ if (!params) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (params->level != WBC_AUTH_USER_LEVEL_PAC && !params->account_name) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* Initialize request */
+
+ switch (params->level) {
+ case WBC_AUTH_USER_LEVEL_PLAIN:
+ cmd = WINBINDD_PAM_AUTH;
+ request.flags = WBFLAG_PAM_INFO3_TEXT |
+ WBFLAG_PAM_USER_SESSION_KEY |
+ WBFLAG_PAM_LMKEY;
+
+ if (!params->password.plaintext) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (params->domain_name && params->domain_name[0]) {
+ /* We need to get the winbind separator :-( */
+ struct winbindd_response sep_response;
+
+ ZERO_STRUCT(sep_response);
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_INFO,
+ NULL, &sep_response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ snprintf(request.data.auth.user,
+ sizeof(request.data.auth.user)-1,
+ "%s%c%s",
+ params->domain_name,
+ sep_response.data.info.winbind_separator,
+ params->account_name);
+ } else {
+ strncpy(request.data.auth.user,
+ params->account_name,
+ sizeof(request.data.auth.user)-1);
+ }
+
+ strncpy(request.data.auth.pass,
+ params->password.plaintext,
+ sizeof(request.data.auth.pass)-1);
+ break;
+
+ case WBC_AUTH_USER_LEVEL_HASH:
+ wbc_status = WBC_ERR_NOT_IMPLEMENTED;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ break;
+
+ case WBC_AUTH_USER_LEVEL_RESPONSE:
+ cmd = WINBINDD_PAM_AUTH_CRAP;
+ request.flags = WBFLAG_PAM_INFO3_TEXT |
+ WBFLAG_PAM_USER_SESSION_KEY |
+ WBFLAG_PAM_LMKEY;
+
+ if (params->password.response.lm_length &&
+ !params->password.response.lm_data) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ if (params->password.response.lm_length == 0 &&
+ params->password.response.lm_data) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (params->password.response.nt_length &&
+ !params->password.response.nt_data) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ if (params->password.response.nt_length == 0&&
+ params->password.response.nt_data) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ strncpy(request.data.auth_crap.user,
+ params->account_name,
+ sizeof(request.data.auth_crap.user)-1);
+ if (params->domain_name) {
+ strncpy(request.data.auth_crap.domain,
+ params->domain_name,
+ sizeof(request.data.auth_crap.domain)-1);
+ }
+ if (params->workstation_name) {
+ strncpy(request.data.auth_crap.workstation,
+ params->workstation_name,
+ sizeof(request.data.auth_crap.workstation)-1);
+ }
+
+ request.data.auth_crap.logon_parameters =
+ params->parameter_control;
+
+ memcpy(request.data.auth_crap.chal,
+ params->password.response.challenge,
+ sizeof(request.data.auth_crap.chal));
+
+ request.data.auth_crap.lm_resp_len =
+ MIN(params->password.response.lm_length,
+ sizeof(request.data.auth_crap.lm_resp));
+ if (params->password.response.lm_data) {
+ memcpy(request.data.auth_crap.lm_resp,
+ params->password.response.lm_data,
+ request.data.auth_crap.lm_resp_len);
+ }
+ request.data.auth_crap.nt_resp_len = params->password.response.nt_length;
+ if (params->password.response.nt_length > sizeof(request.data.auth_crap.nt_resp)) {
+ request.flags |= WBFLAG_BIG_NTLMV2_BLOB;
+ request.extra_len = params->password.response.nt_length;
+ request.extra_data.data = (char *)malloc(
+ request.extra_len);
+ if (request.extra_data.data == NULL) {
+ wbc_status = WBC_ERR_NO_MEMORY;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ memcpy(request.extra_data.data,
+ params->password.response.nt_data,
+ request.data.auth_crap.nt_resp_len);
+ } else if (params->password.response.nt_data) {
+ memcpy(request.data.auth_crap.nt_resp,
+ params->password.response.nt_data,
+ request.data.auth_crap.nt_resp_len);
+ }
+ break;
+
+ case WBC_AUTH_USER_LEVEL_PAC:
+ cmd = WINBINDD_PAM_AUTH_CRAP;
+ request.flags = WBFLAG_PAM_AUTH_PAC | WBFLAG_PAM_INFO3_TEXT;
+ request.extra_data.data = malloc(params->password.pac.length);
+ if (request.extra_data.data == NULL) {
+ wbc_status = WBC_ERR_NO_MEMORY;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ memcpy(request.extra_data.data, params->password.pac.data,
+ params->password.pac.length);
+ request.extra_len = params->password.pac.length;
+ break;
+
+ default:
+ break;
+ }
+
+ if (cmd == 0) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (params->flags) {
+ request.flags |= params->flags;
+ }
+
+ if (cmd == WINBINDD_PAM_AUTH_CRAP) {
+ wbc_status = wbcRequestResponsePriv(ctx, cmd,
+ &request, &response);
+ } else {
+ wbc_status = wbcRequestResponse(ctx, cmd,
+ &request, &response);
+ }
+ if (response.data.auth.nt_status != 0) {
+ if (error) {
+ wbc_status = wbc_create_error_info(&response,
+ error);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ wbc_status = WBC_ERR_AUTH_ERROR;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ if (info) {
+ wbc_status = wbc_create_auth_info(&response, info);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+done:
+ winbindd_free_response(&response);
+
+ free(request.extra_data.data);
+
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcAuthenticateUserEx(const struct wbcAuthUserParams *params,
+ struct wbcAuthUserInfo **info,
+ struct wbcAuthErrorInfo **error)
+{
+ return wbcCtxAuthenticateUserEx(NULL, params, info, error);
+}
+
+/* Trigger a verification of the trust credentials of a specific domain */
+_PUBLIC_
+wbcErr wbcCtxCheckTrustCredentials(struct wbcContext *ctx, const char *domain,
+ struct wbcAuthErrorInfo **error)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (domain) {
+ strncpy(request.domain_name, domain,
+ sizeof(request.domain_name)-1);
+ }
+
+ /* Send request */
+
+ wbc_status = wbcRequestResponsePriv(ctx, WINBINDD_CHECK_MACHACC,
+ &request, &response);
+ if (response.data.auth.nt_status != 0) {
+ if (error) {
+ wbc_status = wbc_create_error_info(&response,
+ error);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ wbc_status = WBC_ERR_AUTH_ERROR;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ done:
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcCheckTrustCredentials(const char *domain,
+ struct wbcAuthErrorInfo **error)
+{
+ return wbcCtxCheckTrustCredentials(NULL, domain, error);
+}
+
+/* Trigger a change of the trust credentials for a specific domain */
+_PUBLIC_
+wbcErr wbcCtxChangeTrustCredentials(struct wbcContext *ctx, const char *domain,
+ struct wbcAuthErrorInfo **error)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (domain) {
+ strncpy(request.domain_name, domain,
+ sizeof(request.domain_name)-1);
+ }
+
+ /* Send request */
+
+ wbc_status = wbcRequestResponsePriv(ctx, WINBINDD_CHANGE_MACHACC,
+ &request, &response);
+ if (response.data.auth.nt_status != 0) {
+ if (error) {
+ wbc_status = wbc_create_error_info(&response,
+ error);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ wbc_status = WBC_ERR_AUTH_ERROR;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ done:
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcChangeTrustCredentials(const char *domain,
+ struct wbcAuthErrorInfo **error)
+{
+ return wbcCtxChangeTrustCredentials(NULL, domain, error);
+}
+
+/*
+ * Trigger a no-op NETLOGON call. Lightweight version of
+ * wbcCheckTrustCredentials
+ */
+_PUBLIC_
+wbcErr wbcCtxPingDc(struct wbcContext *ctx, const char *domain,
+ struct wbcAuthErrorInfo **error)
+{
+ return wbcCtxPingDc2(ctx, domain, error, NULL);
+}
+
+_PUBLIC_
+wbcErr wbcPingDc(const char *domain, struct wbcAuthErrorInfo **error)
+{
+ return wbcPingDc2(domain, error, NULL);
+}
+
+/*
+ * Trigger a no-op NETLOGON call. Lightweight version of
+ * wbcCheckTrustCredentials, optionally return attempted DC
+ */
+_PUBLIC_
+wbcErr wbcCtxPingDc2(struct wbcContext *ctx, const char *domain,
+ struct wbcAuthErrorInfo **error, char **dcname)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (domain) {
+ strncpy(request.domain_name, domain,
+ sizeof(request.domain_name)-1);
+ }
+
+ /* Send request */
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_PING_DC,
+ &request,
+ &response);
+
+ if (dcname && response.extra_data.data) {
+ size_t len;
+
+ len = response.length - sizeof(struct winbindd_response);
+ *dcname = wbcAllocateMemory(1, len, NULL);
+ BAIL_ON_PTR_ERROR(*dcname, wbc_status);
+
+ strlcpy(*dcname, response.extra_data.data, len);
+ }
+
+ if (response.data.auth.nt_status != 0) {
+ if (error) {
+ wbc_status = wbc_create_error_info(&response,
+ error);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ wbc_status = WBC_ERR_AUTH_ERROR;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ done:
+ winbindd_free_response(&response);
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcPingDc2(const char *domain, struct wbcAuthErrorInfo **error,
+ char **dcname)
+{
+ return wbcCtxPingDc2(NULL, domain, error, dcname);
+}
+
+/* Trigger an extended logoff notification to Winbind for a specific user */
+_PUBLIC_
+wbcErr wbcCtxLogoffUserEx(struct wbcContext *ctx,
+ const struct wbcLogoffUserParams *params,
+ struct wbcAuthErrorInfo **error)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ size_t i;
+
+ /* validate input */
+
+ if (!params || !params->username) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if ((params->num_blobs > 0) && (params->blobs == NULL)) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ if ((params->num_blobs == 0) && (params->blobs != NULL)) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ strncpy(request.data.logoff.user, params->username,
+ sizeof(request.data.logoff.user)-1);
+
+ for (i=0; i<params->num_blobs; i++) {
+
+ if (strcasecmp(params->blobs[i].name, "ccfilename") == 0) {
+ if (params->blobs[i].blob.data) {
+ strncpy(request.data.logoff.krb5ccname,
+ (const char *)params->blobs[i].blob.data,
+ sizeof(request.data.logoff.krb5ccname) - 1);
+ }
+ continue;
+ }
+
+ if (strcasecmp(params->blobs[i].name, "user_uid") == 0) {
+ if (params->blobs[i].blob.data) {
+ memcpy(&request.data.logoff.uid,
+ params->blobs[i].blob.data,
+ MIN(params->blobs[i].blob.length,
+ sizeof(request.data.logoff.uid)));
+ }
+ continue;
+ }
+
+ if (strcasecmp(params->blobs[i].name, "flags") == 0) {
+ if (params->blobs[i].blob.data) {
+ memcpy(&request.flags,
+ params->blobs[i].blob.data,
+ MIN(params->blobs[i].blob.length,
+ sizeof(request.flags)));
+ }
+ continue;
+ }
+ }
+
+ /* Send request */
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_PAM_LOGOFF,
+ &request,
+ &response);
+
+ /* Take the response above and return it to the caller */
+ if (response.data.auth.nt_status != 0) {
+ if (error) {
+ wbc_status = wbc_create_error_info(&response,
+ error);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ wbc_status = WBC_ERR_AUTH_ERROR;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ done:
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcLogoffUserEx(const struct wbcLogoffUserParams *params,
+ struct wbcAuthErrorInfo **error)
+{
+ return wbcCtxLogoffUserEx(NULL, params, error);
+}
+
+/* Trigger a logoff notification to Winbind for a specific user */
+_PUBLIC_
+wbcErr wbcCtxLogoffUser(struct wbcContext *ctx,
+ const char *username, uid_t uid,
+ const char *ccfilename)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ /* validate input */
+
+ if (!username) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ strncpy(request.data.logoff.user, username,
+ sizeof(request.data.logoff.user)-1);
+ request.data.logoff.uid = uid;
+
+ if (ccfilename) {
+ strncpy(request.data.logoff.krb5ccname, ccfilename,
+ sizeof(request.data.logoff.krb5ccname)-1);
+ }
+
+ /* Send request */
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_PAM_LOGOFF,
+ &request,
+ &response);
+
+ /* Take the response above and return it to the caller */
+
+ done:
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcLogoffUser(const char *username,
+ uid_t uid,
+ const char *ccfilename)
+{
+ return wbcCtxLogoffUser(NULL, username, uid, ccfilename);
+}
+
+/* Change a password for a user with more detailed information upon failure */
+_PUBLIC_
+wbcErr wbcCtxChangeUserPasswordEx(struct wbcContext *ctx,
+ const struct wbcChangePasswordParams *params,
+ struct wbcAuthErrorInfo **error,
+ enum wbcPasswordChangeRejectReason *reject_reason,
+ struct wbcUserPasswordPolicyInfo **policy)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ int cmd = 0;
+
+ /* validate input */
+
+ if (!params->account_name) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ goto done;
+ }
+
+ if (error) {
+ *error = NULL;
+ }
+
+ if (policy) {
+ *policy = NULL;
+ }
+
+ if (reject_reason) {
+ *reject_reason = -1;
+ }
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ switch (params->level) {
+ case WBC_CHANGE_PASSWORD_LEVEL_PLAIN:
+ cmd = WINBINDD_PAM_CHAUTHTOK;
+
+ if (!params->account_name) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ goto done;
+ }
+
+ strncpy(request.data.chauthtok.user, params->account_name,
+ sizeof(request.data.chauthtok.user) - 1);
+
+ if (params->old_password.plaintext) {
+ strncpy(request.data.chauthtok.oldpass,
+ params->old_password.plaintext,
+ sizeof(request.data.chauthtok.oldpass) - 1);
+ }
+
+ if (params->new_password.plaintext) {
+ strncpy(request.data.chauthtok.newpass,
+ params->new_password.plaintext,
+ sizeof(request.data.chauthtok.newpass) - 1);
+ }
+ break;
+
+ case WBC_CHANGE_PASSWORD_LEVEL_RESPONSE:
+ cmd = WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP;
+
+ if (!params->account_name || !params->domain_name) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ goto done;
+ }
+
+ if (params->old_password.response.old_lm_hash_enc_length &&
+ !params->old_password.response.old_lm_hash_enc_data) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ goto done;
+ }
+
+ if (params->old_password.response.old_lm_hash_enc_length == 0 &&
+ params->old_password.response.old_lm_hash_enc_data) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ goto done;
+ }
+
+ if (params->old_password.response.old_nt_hash_enc_length &&
+ !params->old_password.response.old_nt_hash_enc_data) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ goto done;
+ }
+
+ if (params->old_password.response.old_nt_hash_enc_length == 0 &&
+ params->old_password.response.old_nt_hash_enc_data) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ goto done;
+ }
+
+ if (params->new_password.response.lm_length &&
+ !params->new_password.response.lm_data) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ goto done;
+ }
+
+ if (params->new_password.response.lm_length == 0 &&
+ params->new_password.response.lm_data) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ goto done;
+ }
+
+ if (params->new_password.response.nt_length &&
+ !params->new_password.response.nt_data) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ goto done;
+ }
+
+ if (params->new_password.response.nt_length == 0 &&
+ params->new_password.response.nt_data) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ goto done;
+ }
+
+ strncpy(request.data.chng_pswd_auth_crap.user,
+ params->account_name,
+ sizeof(request.data.chng_pswd_auth_crap.user) - 1);
+
+ strncpy(request.data.chng_pswd_auth_crap.domain,
+ params->domain_name,
+ sizeof(request.data.chng_pswd_auth_crap.domain) - 1);
+
+ if (params->new_password.response.nt_data) {
+ request.data.chng_pswd_auth_crap.new_nt_pswd_len =
+ params->new_password.response.nt_length;
+ memcpy(request.data.chng_pswd_auth_crap.new_nt_pswd,
+ params->new_password.response.nt_data,
+ request.data.chng_pswd_auth_crap.new_nt_pswd_len);
+ }
+
+ if (params->new_password.response.lm_data) {
+ request.data.chng_pswd_auth_crap.new_lm_pswd_len =
+ params->new_password.response.lm_length;
+ memcpy(request.data.chng_pswd_auth_crap.new_lm_pswd,
+ params->new_password.response.lm_data,
+ request.data.chng_pswd_auth_crap.new_lm_pswd_len);
+ }
+
+ if (params->old_password.response.old_nt_hash_enc_data) {
+ request.data.chng_pswd_auth_crap.old_nt_hash_enc_len =
+ params->old_password.response.old_nt_hash_enc_length;
+ memcpy(request.data.chng_pswd_auth_crap.old_nt_hash_enc,
+ params->old_password.response.old_nt_hash_enc_data,
+ request.data.chng_pswd_auth_crap.old_nt_hash_enc_len);
+ }
+
+ if (params->old_password.response.old_lm_hash_enc_data) {
+ request.data.chng_pswd_auth_crap.old_lm_hash_enc_len =
+ params->old_password.response.old_lm_hash_enc_length;
+ memcpy(request.data.chng_pswd_auth_crap.old_lm_hash_enc,
+ params->old_password.response.old_lm_hash_enc_data,
+ request.data.chng_pswd_auth_crap.old_lm_hash_enc_len);
+ }
+
+ break;
+ default:
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ goto done;
+ break;
+ }
+
+ /* Send request */
+
+ wbc_status = wbcRequestResponse(ctx, cmd,
+ &request,
+ &response);
+ if (WBC_ERROR_IS_OK(wbc_status)) {
+ goto done;
+ }
+
+ /* Take the response above and return it to the caller */
+
+ if (response.data.auth.nt_status != 0) {
+ if (error) {
+ wbc_status = wbc_create_error_info(&response,
+ error);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ }
+
+ if (policy) {
+ wbc_status = wbc_create_password_policy_info(&response,
+ policy);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (reject_reason) {
+ *reject_reason = response.data.auth.reject_reason;
+ }
+
+ wbc_status = WBC_ERR_PWD_CHANGE_FAILED;
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ done:
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcChangeUserPasswordEx(const struct wbcChangePasswordParams *params,
+ struct wbcAuthErrorInfo **error,
+ enum wbcPasswordChangeRejectReason *reject_reason,
+ struct wbcUserPasswordPolicyInfo **policy)
+{
+ return wbcCtxChangeUserPasswordEx(NULL, params, error,
+ reject_reason, policy);
+}
+
+/* Change a password for a user */
+_PUBLIC_
+wbcErr wbcCtxChangeUserPassword(struct wbcContext *ctx,
+ const char *username,
+ const char *old_password,
+ const char *new_password)
+{
+ wbcErr wbc_status = WBC_ERR_SUCCESS;
+ struct wbcChangePasswordParams params;
+
+ ZERO_STRUCT(params);
+
+ params.account_name = username;
+ params.level = WBC_CHANGE_PASSWORD_LEVEL_PLAIN;
+ params.old_password.plaintext = old_password;
+ params.new_password.plaintext = new_password;
+
+ wbc_status = wbcCtxChangeUserPasswordEx(ctx, &params,
+ NULL,
+ NULL,
+ NULL);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+done:
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcChangeUserPassword(const char *username,
+ const char *old_password,
+ const char *new_password)
+{
+ return wbcCtxChangeUserPassword(NULL, username,
+ old_password, new_password);
+}
+
+/* Logon a User */
+_PUBLIC_
+wbcErr wbcCtxLogonUser(struct wbcContext *ctx,
+ const struct wbcLogonUserParams *params,
+ struct wbcLogonUserInfo **info,
+ struct wbcAuthErrorInfo **error,
+ struct wbcUserPasswordPolicyInfo **policy)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ uint32_t i;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (info) {
+ *info = NULL;
+ }
+ if (error) {
+ *error = NULL;
+ }
+ if (policy) {
+ *policy = NULL;
+ }
+
+ if (!params) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (!params->username) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if ((params->num_blobs > 0) && (params->blobs == NULL)) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ if ((params->num_blobs == 0) && (params->blobs != NULL)) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* Initialize request */
+
+ request.flags = WBFLAG_PAM_INFO3_TEXT |
+ WBFLAG_PAM_USER_SESSION_KEY |
+ WBFLAG_PAM_LMKEY;
+
+ if (!params->password) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ strncpy(request.data.auth.user,
+ params->username,
+ sizeof(request.data.auth.user)-1);
+
+ strncpy(request.data.auth.pass,
+ params->password,
+ sizeof(request.data.auth.pass)-1);
+
+ for (i=0; i<params->num_blobs; i++) {
+
+ if (strcasecmp(params->blobs[i].name, "krb5_cc_type") == 0) {
+ if (params->blobs[i].blob.data) {
+ strncpy(request.data.auth.krb5_cc_type,
+ (const char *)params->blobs[i].blob.data,
+ sizeof(request.data.auth.krb5_cc_type) - 1);
+ }
+ continue;
+ }
+
+ if (strcasecmp(params->blobs[i].name, "user_uid") == 0) {
+ if (params->blobs[i].blob.data) {
+ memcpy(&request.data.auth.uid,
+ params->blobs[i].blob.data,
+ MIN(sizeof(request.data.auth.uid),
+ params->blobs[i].blob.length));
+ }
+ continue;
+ }
+
+ if (strcasecmp(params->blobs[i].name, "flags") == 0) {
+ if (params->blobs[i].blob.data) {
+ uint32_t flags;
+ memcpy(&flags,
+ params->blobs[i].blob.data,
+ MIN(sizeof(flags),
+ params->blobs[i].blob.length));
+ request.flags |= flags;
+ }
+ continue;
+ }
+
+ if (strcasecmp(params->blobs[i].name, "membership_of") == 0) {
+ if (params->blobs[i].blob.data &&
+ params->blobs[i].blob.data[0] > 0) {
+ strncpy(request.data.auth.require_membership_of_sid,
+ (const char *)params->blobs[i].blob.data,
+ sizeof(request.data.auth.require_membership_of_sid) - 1);
+ }
+ continue;
+ }
+ }
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_PAM_AUTH,
+ &request,
+ &response);
+
+ if (response.data.auth.nt_status != 0) {
+ if (error) {
+ wbc_status = wbc_create_error_info(&response,
+ error);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ wbc_status = WBC_ERR_AUTH_ERROR;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ if (info) {
+ wbc_status = wbc_create_logon_info(&response,
+ info);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (policy) {
+ wbc_status = wbc_create_password_policy_info(&response,
+ policy);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+done:
+ winbindd_free_response(&response);
+
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcLogonUser(const struct wbcLogonUserParams *params,
+ struct wbcLogonUserInfo **info,
+ struct wbcAuthErrorInfo **error,
+ struct wbcUserPasswordPolicyInfo **policy)
+{
+ return wbcCtxLogonUser(NULL, params, info, error, policy);
+}
+
+static void wbcCredentialCacheInfoDestructor(void *ptr)
+{
+ struct wbcCredentialCacheInfo *i =
+ (struct wbcCredentialCacheInfo *)ptr;
+ wbcFreeMemory(i->blobs);
+}
+
+/* Authenticate a user with cached credentials */
+_PUBLIC_
+wbcErr wbcCtxCredentialCache(struct wbcContext *ctx,
+ struct wbcCredentialCacheParams *params,
+ struct wbcCredentialCacheInfo **info,
+ struct wbcAuthErrorInfo **error)
+{
+ wbcErr status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcCredentialCacheInfo *result = NULL;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ struct wbcNamedBlob *initial_blob = NULL;
+ struct wbcNamedBlob *challenge_blob = NULL;
+ size_t i;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ *info = NULL;
+
+ if (error != NULL) {
+ *error = NULL;
+ }
+ if ((params == NULL)
+ || (params->account_name == NULL)
+ || (params->level != WBC_CREDENTIAL_CACHE_LEVEL_NTLMSSP)) {
+ status = WBC_ERR_INVALID_PARAM;
+ goto fail;
+ }
+
+ for (i=0; i<params->num_blobs; i++) {
+ /*
+ * Older callers may used to provide the NEGOTIATE request
+ * as "initial_blob", but it was completely ignored by winbindd.
+ *
+ * So we keep ignoring it.
+ *
+ * A new callers that is capable to support "new_spnego",
+ * will provide the NEGOTIATE request as "negotiate_blob"
+ * instead.
+ */
+ if (strcasecmp(params->blobs[i].name, "negotiate_blob") == 0) {
+ if (initial_blob != NULL) {
+ status = WBC_ERR_INVALID_PARAM;
+ goto fail;
+ }
+ initial_blob = &params->blobs[i];
+ continue;
+ }
+ if (strcasecmp(params->blobs[i].name, "challenge_blob") == 0) {
+ if (challenge_blob != NULL) {
+ status = WBC_ERR_INVALID_PARAM;
+ goto fail;
+ }
+ challenge_blob = &params->blobs[i];
+ continue;
+ }
+ }
+
+ if (params->domain_name != NULL) {
+ status = wbcRequestResponse(ctx, WINBINDD_INFO,
+ NULL, &response);
+ if (!WBC_ERROR_IS_OK(status)) {
+ goto fail;
+ }
+ snprintf(request.data.ccache_ntlm_auth.user,
+ sizeof(request.data.ccache_ntlm_auth.user)-1,
+ "%s%c%s", params->domain_name,
+ response.data.info.winbind_separator,
+ params->account_name);
+ } else {
+ strncpy(request.data.ccache_ntlm_auth.user,
+ params->account_name,
+ sizeof(request.data.ccache_ntlm_auth.user)-1);
+ }
+ request.data.ccache_ntlm_auth.uid = getuid();
+
+ request.data.ccache_ntlm_auth.initial_blob_len = 0;
+ request.data.ccache_ntlm_auth.challenge_blob_len = 0;
+ request.extra_len = 0;
+
+ if (initial_blob != NULL) {
+ request.data.ccache_ntlm_auth.initial_blob_len =
+ initial_blob->blob.length;
+ request.extra_len += initial_blob->blob.length;
+ }
+ if (challenge_blob != NULL) {
+ request.data.ccache_ntlm_auth.challenge_blob_len =
+ challenge_blob->blob.length;
+ request.extra_len += challenge_blob->blob.length;
+ }
+
+ if (request.extra_len != 0) {
+ request.extra_data.data = (char *)malloc(request.extra_len);
+ if (request.extra_data.data == NULL) {
+ status = WBC_ERR_NO_MEMORY;
+ goto fail;
+ }
+ }
+ if (initial_blob != NULL) {
+ memcpy(request.extra_data.data,
+ initial_blob->blob.data, initial_blob->blob.length);
+ }
+ if (challenge_blob != NULL) {
+ memcpy(request.extra_data.data
+ + request.data.ccache_ntlm_auth.initial_blob_len,
+ challenge_blob->blob.data,
+ challenge_blob->blob.length);
+ }
+
+ status = wbcRequestResponse(ctx, WINBINDD_CCACHE_NTLMAUTH,
+ &request, &response);
+ if (!WBC_ERROR_IS_OK(status)) {
+ goto fail;
+ }
+
+ result = (struct wbcCredentialCacheInfo *)wbcAllocateMemory(
+ 1, sizeof(struct wbcCredentialCacheInfo),
+ wbcCredentialCacheInfoDestructor);
+ if (result == NULL) {
+ status = WBC_ERR_NO_MEMORY;
+ goto fail;
+ }
+ result->num_blobs = 0;
+ result->blobs = NULL;
+ status = wbcAddNamedBlob(&result->num_blobs, &result->blobs,
+ "auth_blob", 0,
+ (uint8_t *)response.extra_data.data,
+ response.data.ccache_ntlm_auth.auth_blob_len);
+ if (!WBC_ERROR_IS_OK(status)) {
+ goto fail;
+ }
+ status = wbcAddNamedBlob(
+ &result->num_blobs, &result->blobs, "session_key", 0,
+ response.data.ccache_ntlm_auth.session_key,
+ sizeof(response.data.ccache_ntlm_auth.session_key));
+ if (!WBC_ERROR_IS_OK(status)) {
+ goto fail;
+ }
+ if (response.data.ccache_ntlm_auth.new_spnego) {
+ status = wbcAddNamedBlob(
+ &result->num_blobs, &result->blobs, "new_spnego", 0,
+ &response.data.ccache_ntlm_auth.new_spnego,
+ sizeof(response.data.ccache_ntlm_auth.new_spnego));
+ if (!WBC_ERROR_IS_OK(status)) {
+ goto fail;
+ }
+ }
+
+ *info = result;
+ result = NULL;
+ status = WBC_ERR_SUCCESS;
+fail:
+ free(request.extra_data.data);
+ winbindd_free_response(&response);
+ wbcFreeMemory(result);
+ return status;
+}
+
+_PUBLIC_
+wbcErr wbcCredentialCache(struct wbcCredentialCacheParams *params,
+ struct wbcCredentialCacheInfo **info,
+ struct wbcAuthErrorInfo **error)
+{
+ return wbcCtxCredentialCache(NULL, params, info, error);
+}
+
+/* Authenticate a user with cached credentials */
+_PUBLIC_
+wbcErr wbcCtxCredentialSave(struct wbcContext *ctx,
+ const char *user, const char *password)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ strncpy(request.data.ccache_save.user, user,
+ sizeof(request.data.ccache_save.user)-1);
+ strncpy(request.data.ccache_save.pass, password,
+ sizeof(request.data.ccache_save.pass)-1);
+ request.data.ccache_save.uid = getuid();
+
+ return wbcRequestResponse(ctx, WINBINDD_CCACHE_SAVE, &request, &response);
+}
+
+_PUBLIC_
+wbcErr wbcCredentialSave(const char *user, const char *password)
+{
+ return wbcCtxCredentialSave(NULL, user, password);
+}
diff --git a/nsswitch/libwbclient/wbc_pwd.c b/nsswitch/libwbclient/wbc_pwd.c
new file mode 100644
index 0000000..4e83fbf
--- /dev/null
+++ b/nsswitch/libwbclient/wbc_pwd.c
@@ -0,0 +1,722 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind client API
+
+ Copyright (C) Gerald (Jerry) Carter 2007
+ Copyright (C) Matthew Newton 2015
+
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Required Headers */
+
+#include "replace.h"
+#include "libwbclient.h"
+#include "../winbind_client.h"
+
+/** @brief The maximum number of pwent structs to get from winbindd
+ *
+ */
+#define MAX_GETPWENT_USERS 500
+
+/** @brief The maximum number of grent structs to get from winbindd
+ *
+ */
+#define MAX_GETGRENT_GROUPS 500
+
+/**
+ *
+ **/
+
+static void wbcPasswdDestructor(void *ptr)
+{
+ struct passwd *pw = (struct passwd *)ptr;
+ free(pw->pw_name);
+ free(pw->pw_passwd);
+ free(pw->pw_gecos);
+ free(pw->pw_shell);
+ free(pw->pw_dir);
+}
+
+static struct passwd *copy_passwd_entry(struct winbindd_pw *p)
+{
+ struct passwd *pw = NULL;
+
+ pw = (struct passwd *)wbcAllocateMemory(1, sizeof(struct passwd),
+ wbcPasswdDestructor);
+ if (pw == NULL) {
+ return NULL;
+ }
+ pw->pw_name = strdup(p->pw_name);
+ if (pw->pw_name == NULL) {
+ goto fail;
+ }
+ pw->pw_passwd = strdup(p->pw_passwd);
+ if (pw->pw_passwd == NULL) {
+ goto fail;
+ }
+ pw->pw_gecos = strdup(p->pw_gecos);
+ if (pw->pw_gecos == NULL) {
+ goto fail;
+ }
+ pw->pw_shell = strdup(p->pw_shell);
+ if (pw->pw_shell == NULL) {
+ goto fail;
+ }
+ pw->pw_dir = strdup(p->pw_dir);
+ if (pw->pw_dir == NULL) {
+ goto fail;
+ }
+ pw->pw_uid = p->pw_uid;
+ pw->pw_gid = p->pw_gid;
+ return pw;
+
+fail:
+ wbcFreeMemory(pw);
+ return NULL;
+}
+
+/**
+ *
+ **/
+
+static void wbcGroupDestructor(void *ptr)
+{
+ struct group *gr = (struct group *)ptr;
+ int i;
+
+ free(gr->gr_name);
+ free(gr->gr_passwd);
+
+ /* if the array was partly created this can be NULL */
+ if (gr->gr_mem == NULL) {
+ return;
+ }
+
+ for (i=0; gr->gr_mem[i] != NULL; i++) {
+ free(gr->gr_mem[i]);
+ }
+ free(gr->gr_mem);
+}
+
+static struct group *copy_group_entry(struct winbindd_gr *g,
+ char *mem_buf)
+{
+ struct group *gr = NULL;
+ int i;
+ char *mem_p, *mem_q;
+
+ gr = (struct group *)wbcAllocateMemory(
+ 1, sizeof(struct group), wbcGroupDestructor);
+ if (gr == NULL) {
+ return NULL;
+ }
+
+ gr->gr_name = strdup(g->gr_name);
+ if (gr->gr_name == NULL) {
+ goto fail;
+ }
+ gr->gr_passwd = strdup(g->gr_passwd);
+ if (gr->gr_passwd == NULL) {
+ goto fail;
+ }
+ gr->gr_gid = g->gr_gid;
+
+ gr->gr_mem = (char **)calloc(g->num_gr_mem+1, sizeof(char *));
+ if (gr->gr_mem == NULL) {
+ goto fail;
+ }
+
+ mem_p = mem_q = mem_buf;
+ for (i=0; i<g->num_gr_mem && mem_p; i++) {
+ mem_q = strchr(mem_p, ',');
+ if (mem_q != NULL) {
+ *mem_q = '\0';
+ }
+
+ gr->gr_mem[i] = strdup(mem_p);
+ if (gr->gr_mem[i] == NULL) {
+ goto fail;
+ }
+
+ if (mem_q == NULL) {
+ i += 1;
+ break;
+ }
+ mem_p = mem_q + 1;
+ }
+ gr->gr_mem[i] = NULL;
+
+ return gr;
+
+fail:
+ wbcFreeMemory(gr);
+ return NULL;
+}
+
+/* Fill in a struct passwd* for a domain user based on username */
+_PUBLIC_
+wbcErr wbcCtxGetpwnam(struct wbcContext *ctx,
+ const char *name, struct passwd **pwd)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ if (!name || !pwd) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* Initialize request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* dst is already null terminated from the memset above */
+
+ strncpy(request.data.username, name, sizeof(request.data.username)-1);
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_GETPWNAM,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ *pwd = copy_passwd_entry(&response.data.pw);
+ BAIL_ON_PTR_ERROR(*pwd, wbc_status);
+
+ done:
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcGetpwnam(const char *name, struct passwd **pwd)
+{
+ return wbcCtxGetpwnam(NULL, name, pwd);
+}
+
+/* Fill in a struct passwd* for a domain user based on uid */
+_PUBLIC_
+wbcErr wbcCtxGetpwuid(struct wbcContext *ctx, uid_t uid, struct passwd **pwd)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ if (!pwd) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* Initialize request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.data.uid = uid;
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_GETPWUID,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ *pwd = copy_passwd_entry(&response.data.pw);
+ BAIL_ON_PTR_ERROR(*pwd, wbc_status);
+
+ done:
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcGetpwuid(uid_t uid, struct passwd **pwd)
+{
+ return wbcCtxGetpwuid(NULL, uid, pwd);
+}
+
+/* Fill in a struct passwd* for a domain user based on sid */
+_PUBLIC_
+wbcErr wbcCtxGetpwsid(struct wbcContext *ctx,
+ struct wbcDomainSid *sid, struct passwd **pwd)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ if (!pwd) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* Initialize request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ wbcSidToStringBuf(sid, request.data.sid, sizeof(request.data.sid));
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_GETPWSID,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ *pwd = copy_passwd_entry(&response.data.pw);
+ BAIL_ON_PTR_ERROR(*pwd, wbc_status);
+
+ done:
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcGetpwsid(struct wbcDomainSid *sid, struct passwd **pwd)
+{
+ return wbcCtxGetpwsid(NULL, sid, pwd);
+}
+
+/* Fill in a struct passwd* for a domain user based on username */
+_PUBLIC_
+wbcErr wbcCtxGetgrnam(struct wbcContext *ctx,
+ const char *name, struct group **grp)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ /* Initialize request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (!name || !grp) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* dst is already null terminated from the memset above */
+
+ strncpy(request.data.groupname, name, sizeof(request.data.groupname)-1);
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_GETGRNAM,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ *grp = copy_group_entry(&response.data.gr,
+ (char*)response.extra_data.data);
+ BAIL_ON_PTR_ERROR(*grp, wbc_status);
+
+ done:
+ winbindd_free_response(&response);
+
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcGetgrnam(const char *name, struct group **grp)
+{
+ return wbcCtxGetgrnam(NULL, name, grp);
+}
+
+/* Fill in a struct passwd* for a domain user based on uid */
+_PUBLIC_
+wbcErr wbcCtxGetgrgid(struct wbcContext *ctx, gid_t gid, struct group **grp)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ /* Initialize request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (!grp) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ request.data.gid = gid;
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_GETGRGID,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ *grp = copy_group_entry(&response.data.gr,
+ (char*)response.extra_data.data);
+ BAIL_ON_PTR_ERROR(*grp, wbc_status);
+
+ done:
+ winbindd_free_response(&response);
+
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcGetgrgid(gid_t gid, struct group **grp)
+{
+ return wbcCtxGetgrgid(NULL, gid, grp);
+}
+
+/** @brief Winbindd response containing the passwd structs
+ *
+ */
+static struct winbindd_response pw_response;
+
+/* Reset the passwd iterator */
+_PUBLIC_
+wbcErr wbcCtxSetpwent(struct wbcContext *ctx)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ if (!ctx) {
+ ctx = wbcGetGlobalCtx();
+ }
+
+ if (ctx->pw_cache_size > 0) {
+ ctx->pw_cache_idx = ctx->pw_cache_size = 0;
+ winbindd_free_response(&pw_response);
+ }
+
+ ZERO_STRUCT(pw_response);
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_SETPWENT,
+ NULL, NULL);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ done:
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcSetpwent(void)
+{
+ return wbcCtxSetpwent(NULL);
+}
+
+/* Close the passwd iterator */
+_PUBLIC_
+wbcErr wbcCtxEndpwent(struct wbcContext *ctx)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ if (!ctx) {
+ ctx = wbcGetGlobalCtx();
+ }
+
+ if (ctx->pw_cache_size > 0) {
+ ctx->pw_cache_idx = ctx->pw_cache_size = 0;
+ winbindd_free_response(&pw_response);
+ }
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_ENDPWENT,
+ NULL, NULL);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ done:
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcEndpwent(void)
+{
+ return wbcCtxEndpwent(NULL);
+}
+
+/* Return the next struct passwd* entry from the pwent iterator */
+_PUBLIC_
+wbcErr wbcCtxGetpwent(struct wbcContext *ctx, struct passwd **pwd)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct winbindd_request request;
+ struct winbindd_pw *wb_pw;
+
+ if (!ctx) {
+ ctx = wbcGetGlobalCtx();
+ }
+
+ /* If there's a cached result, return that. */
+ if (ctx->pw_cache_idx < ctx->pw_cache_size) {
+ goto return_result;
+ }
+
+ /* Otherwise, query winbindd for some entries. */
+
+ ctx->pw_cache_idx = 0;
+
+ winbindd_free_response(&pw_response);
+
+ ZERO_STRUCT(request);
+ request.data.num_entries = MAX_GETPWENT_USERS;
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_GETPWENT, &request,
+ &pw_response);
+
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ ctx->pw_cache_size = pw_response.data.num_entries;
+
+return_result:
+
+ wb_pw = (struct winbindd_pw *) pw_response.extra_data.data;
+
+ *pwd = copy_passwd_entry(&wb_pw[ctx->pw_cache_idx]);
+
+ BAIL_ON_PTR_ERROR(*pwd, wbc_status);
+
+ ctx->pw_cache_idx++;
+
+done:
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcGetpwent(struct passwd **pwd)
+{
+ return wbcCtxGetpwent(NULL, pwd);
+}
+
+/** @brief Winbindd response containing the group structs
+ *
+ */
+static struct winbindd_response gr_response;
+
+/* Reset the group iterator */
+_PUBLIC_
+wbcErr wbcCtxSetgrent(struct wbcContext *ctx)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ if (!ctx) {
+ ctx = wbcGetGlobalCtx();
+ }
+
+ if (ctx->gr_cache_size > 0) {
+ ctx->gr_cache_idx = ctx->gr_cache_size = 0;
+ winbindd_free_response(&gr_response);
+ }
+
+ ZERO_STRUCT(gr_response);
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_SETGRENT,
+ NULL, NULL);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ done:
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcSetgrent(void)
+{
+ return wbcCtxSetgrent(NULL);
+}
+
+/* Close the group iterator */
+_PUBLIC_
+wbcErr wbcCtxEndgrent(struct wbcContext *ctx)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ if (!ctx) {
+ ctx = wbcGetGlobalCtx();
+ }
+
+ if (ctx->gr_cache_size > 0) {
+ ctx->gr_cache_idx = ctx->gr_cache_size = 0;
+ winbindd_free_response(&gr_response);
+ }
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_ENDGRENT,
+ NULL, NULL);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ done:
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcEndgrent(void)
+{
+ return wbcCtxEndgrent(NULL);
+}
+
+/* Return the next struct group* entry from the pwent iterator */
+_PUBLIC_
+wbcErr wbcCtxGetgrent(struct wbcContext *ctx, struct group **grp)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct winbindd_request request;
+ struct winbindd_gr *wb_gr;
+ uint32_t mem_ofs;
+
+ if (!ctx) {
+ ctx = wbcGetGlobalCtx();
+ }
+
+ /* If there's a cached result, return that. */
+ if (ctx->gr_cache_idx < ctx->gr_cache_size) {
+ goto return_result;
+ }
+
+ /* Otherwise, query winbindd for some entries. */
+
+ ctx->gr_cache_idx = 0;
+
+ winbindd_free_response(&gr_response);
+
+ ZERO_STRUCT(request);
+ request.data.num_entries = MAX_GETGRENT_GROUPS;
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_GETGRENT,
+ &request, &gr_response);
+
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ ctx->gr_cache_size = gr_response.data.num_entries;
+
+return_result:
+
+ wb_gr = (struct winbindd_gr *) gr_response.extra_data.data;
+
+ mem_ofs = wb_gr[ctx->gr_cache_idx].gr_mem_ofs +
+ ctx->gr_cache_size * sizeof(struct winbindd_gr);
+
+ *grp = copy_group_entry(&wb_gr[ctx->gr_cache_idx],
+ ((char *)gr_response.extra_data.data)+mem_ofs);
+
+ BAIL_ON_PTR_ERROR(*grp, wbc_status);
+
+ ctx->gr_cache_idx++;
+
+done:
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcGetgrent(struct group **grp)
+{
+ return wbcCtxGetgrent(NULL, grp);
+}
+
+/* Return the next struct group* entry from the pwent iterator */
+_PUBLIC_
+wbcErr wbcCtxGetgrlist(struct wbcContext *ctx, struct group **grp)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct winbindd_request request;
+ struct winbindd_gr *wb_gr;
+
+ if (!ctx) {
+ ctx = wbcGetGlobalCtx();
+ }
+
+ /* If there's a cached result, return that. */
+ if (ctx->gr_cache_idx < ctx->gr_cache_size) {
+ goto return_result;
+ }
+
+ /* Otherwise, query winbindd for some entries. */
+
+ ctx->gr_cache_idx = 0;
+
+ winbindd_free_response(&gr_response);
+ ZERO_STRUCT(gr_response);
+
+ ZERO_STRUCT(request);
+ request.data.num_entries = MAX_GETGRENT_GROUPS;
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_GETGRLST,
+ &request, &gr_response);
+
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ ctx->gr_cache_size = gr_response.data.num_entries;
+
+return_result:
+
+ wb_gr = (struct winbindd_gr *) gr_response.extra_data.data;
+
+ *grp = copy_group_entry(&wb_gr[ctx->gr_cache_idx], NULL);
+
+ BAIL_ON_PTR_ERROR(*grp, wbc_status);
+
+ ctx->gr_cache_idx++;
+
+done:
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcGetgrlist(struct group **grp)
+{
+ return wbcCtxGetgrlist(NULL, grp);
+}
+
+/* Return the unix group array belonging to the given user */
+_PUBLIC_
+wbcErr wbcCtxGetGroups(struct wbcContext *ctx, const char *account,
+ uint32_t *num_groups, gid_t **_groups)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ uint32_t i;
+ gid_t *groups = NULL;
+
+ /* Initialize request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (!account) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* Send request */
+
+ strncpy(request.data.username, account, sizeof(request.data.username)-1);
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_GETGROUPS,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ groups = (gid_t *)wbcAllocateMemory(
+ response.data.num_entries, sizeof(gid_t), NULL);
+ BAIL_ON_PTR_ERROR(groups, wbc_status);
+
+ for (i = 0; i < response.data.num_entries; i++) {
+ groups[i] = ((gid_t *)response.extra_data.data)[i];
+ }
+
+ *num_groups = response.data.num_entries;
+ *_groups = groups;
+ groups = NULL;
+
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ winbindd_free_response(&response);
+ wbcFreeMemory(groups);
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcGetGroups(const char *account, uint32_t *num_groups, gid_t **_groups)
+{
+ return wbcCtxGetGroups(NULL, account, num_groups, _groups);
+}
diff --git a/nsswitch/libwbclient/wbc_sid.c b/nsswitch/libwbclient/wbc_sid.c
new file mode 100644
index 0000000..e4b6376
--- /dev/null
+++ b/nsswitch/libwbclient/wbc_sid.c
@@ -0,0 +1,1123 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind client API
+
+ Copyright (C) Gerald (Jerry) Carter 2007
+ Copyright (C) Volker Lendecke 2010
+
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Required Headers */
+
+#include "replace.h"
+#include "libwbclient.h"
+#include "../winbind_client.h"
+#include "lib/util/smb_strtox.h"
+
+/* Convert a sid to a string into a buffer. Return the string
+ * length. If buflen is too small, return the string length that would
+ * result if it was long enough. */
+_PUBLIC_
+int wbcSidToStringBuf(const struct wbcDomainSid *sid, char *buf, int buflen)
+{
+ uint64_t id_auth;
+ int i, ofs;
+
+ if (!sid) {
+ strlcpy(buf, "(NULL SID)", buflen);
+ return 10; /* strlen("(NULL SID)") */
+ }
+
+ id_auth = (uint64_t)sid->id_auth[5] +
+ ((uint64_t)sid->id_auth[4] << 8) +
+ ((uint64_t)sid->id_auth[3] << 16) +
+ ((uint64_t)sid->id_auth[2] << 24) +
+ ((uint64_t)sid->id_auth[1] << 32) +
+ ((uint64_t)sid->id_auth[0] << 40);
+
+ ofs = snprintf(buf, buflen, "S-%hhu-", (unsigned char)sid->sid_rev_num);
+ if (id_auth >= UINT32_MAX) {
+ ofs += snprintf(buf + ofs, MAX(buflen - ofs, 0), "0x%llx",
+ (unsigned long long)id_auth);
+ } else {
+ ofs += snprintf(buf + ofs, MAX(buflen - ofs, 0), "%llu",
+ (unsigned long long)id_auth);
+ }
+
+ for (i = 0; i < sid->num_auths; i++) {
+ ofs += snprintf(buf + ofs, MAX(buflen - ofs, 0), "-%u",
+ (unsigned int)sid->sub_auths[i]);
+ }
+ return ofs;
+}
+
+/* Convert a binary SID to a character string */
+_PUBLIC_
+wbcErr wbcSidToString(const struct wbcDomainSid *sid,
+ char **sid_string)
+{
+ char buf[WBC_SID_STRING_BUFLEN];
+ char *result;
+ int len;
+
+ if (!sid) {
+ return WBC_ERR_INVALID_SID;
+ }
+
+ len = wbcSidToStringBuf(sid, buf, sizeof(buf));
+
+ if (len >= WBC_SID_STRING_BUFLEN) {
+ return WBC_ERR_INVALID_SID;
+ }
+
+ result = (char *)wbcAllocateMemory(len+1, 1, NULL);
+ if (result == NULL) {
+ return WBC_ERR_NO_MEMORY;
+ }
+ memcpy(result, buf, len+1);
+
+ *sid_string = result;
+ return WBC_ERR_SUCCESS;
+}
+
+#define AUTHORITY_MASK (~(0xffffffffffffULL))
+
+/* Convert a character string to a binary SID */
+_PUBLIC_
+wbcErr wbcStringToSid(const char *str,
+ struct wbcDomainSid *sid)
+{
+ const char *p;
+ char *q;
+ int error = 0;
+ uint64_t x;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ if (!sid) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* Sanity check for either "S-" or "s-" */
+
+ if (!str
+ || (str[0]!='S' && str[0]!='s')
+ || (str[1]!='-'))
+ {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* Get the SID revision number */
+
+ p = str+2;
+ x = (uint64_t)smb_strtoul(p, &q, 10, &error, SMB_STR_STANDARD);
+ if (x == 0 || x > UINT8_MAX || !q || *q != '-' || error != 0) {
+ wbc_status = WBC_ERR_INVALID_SID;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ sid->sid_rev_num = (uint8_t)x;
+
+ /*
+ * Next the Identifier Authority. This is stored big-endian in a
+ * 6 byte array. If the authority value is >= UINT_MAX, then it should
+ * be expressed as a hex value, according to MS-DTYP.
+ */
+ p = q+1;
+ x = smb_strtoull(p, &q, 0, &error, SMB_STR_STANDARD);
+ if (!q || *q != '-' || (x & AUTHORITY_MASK) || error != 0) {
+ wbc_status = WBC_ERR_INVALID_SID;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ sid->id_auth[5] = (x & 0x0000000000ffULL);
+ sid->id_auth[4] = (x & 0x00000000ff00ULL) >> 8;
+ sid->id_auth[3] = (x & 0x000000ff0000ULL) >> 16;
+ sid->id_auth[2] = (x & 0x0000ff000000ULL) >> 24;
+ sid->id_auth[1] = (x & 0x00ff00000000ULL) >> 32;
+ sid->id_auth[0] = (x & 0xff0000000000ULL) >> 40;
+
+ /* now read the the subauthorities */
+ p = q +1;
+ sid->num_auths = 0;
+ while (sid->num_auths < WBC_MAXSUBAUTHS) {
+ x = smb_strtoull(p, &q, 10, &error, SMB_STR_ALLOW_NO_CONVERSION);
+ if (p == q)
+ break;
+ if (x > UINT32_MAX || error != 0) {
+ wbc_status = WBC_ERR_INVALID_SID;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ sid->sub_auths[sid->num_auths++] = x;
+
+ if (*q != '-') {
+ break;
+ }
+ p = q + 1;
+ }
+
+ /* IF we ended early, then the SID could not be converted */
+
+ if (q && *q!='\0') {
+ wbc_status = WBC_ERR_INVALID_SID;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ wbc_status = WBC_ERR_SUCCESS;
+
+done:
+ return wbc_status;
+
+}
+
+
+/* Convert a domain and name to SID */
+_PUBLIC_
+wbcErr wbcCtxLookupName(struct wbcContext *ctx,
+ const char *domain,
+ const char *name,
+ struct wbcDomainSid *sid,
+ enum wbcSidType *name_type)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ if (!sid || !name_type) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* Initialize request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* dst is already null terminated from the memset above */
+
+ strncpy(request.data.name.dom_name, domain,
+ sizeof(request.data.name.dom_name)-1);
+ strncpy(request.data.name.name, name,
+ sizeof(request.data.name.name)-1);
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_LOOKUPNAME,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ wbc_status = wbcStringToSid(response.data.sid.sid, sid);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ *name_type = (enum wbcSidType)response.data.sid.type;
+
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcLookupName(const char *domain,
+ const char *name,
+ struct wbcDomainSid *sid,
+ enum wbcSidType *name_type)
+{
+ return wbcCtxLookupName(NULL, domain, name, sid, name_type);
+}
+
+
+/* Convert a SID to a domain and name */
+_PUBLIC_
+wbcErr wbcCtxLookupSid(struct wbcContext *ctx,
+ const struct wbcDomainSid *sid,
+ char **pdomain,
+ char **pname,
+ enum wbcSidType *pname_type)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ char *domain, *name;
+
+ if (!sid) {
+ return WBC_ERR_INVALID_PARAM;
+ }
+
+ /* Initialize request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ wbcSidToStringBuf(sid, request.data.sid, sizeof(request.data.sid));
+
+ /* Make request */
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_LOOKUPSID,
+ &request,
+ &response);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return wbc_status;
+ }
+
+ /* Copy out result */
+
+ wbc_status = WBC_ERR_NO_MEMORY;
+ domain = NULL;
+ name = NULL;
+
+ domain = wbcStrDup(response.data.name.dom_name);
+ if (domain == NULL) {
+ goto done;
+ }
+ name = wbcStrDup(response.data.name.name);
+ if (name == NULL) {
+ goto done;
+ }
+ if (pdomain != NULL) {
+ *pdomain = domain;
+ domain = NULL;
+ }
+ if (pname != NULL) {
+ *pname = name;
+ name = NULL;
+ }
+ if (pname_type != NULL) {
+ *pname_type = (enum wbcSidType)response.data.name.type;
+ }
+ wbc_status = WBC_ERR_SUCCESS;
+done:
+ wbcFreeMemory(name);
+ wbcFreeMemory(domain);
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcLookupSid(const struct wbcDomainSid *sid,
+ char **pdomain,
+ char **pname,
+ enum wbcSidType *pname_type)
+{
+ return wbcCtxLookupSid(NULL, sid, pdomain, pname, pname_type);
+}
+
+static void wbcDomainInfosDestructor(void *ptr)
+{
+ struct wbcDomainInfo *i = (struct wbcDomainInfo *)ptr;
+
+ while (i->short_name != NULL) {
+ wbcFreeMemory(i->short_name);
+ wbcFreeMemory(i->dns_name);
+ i += 1;
+ }
+}
+
+static void wbcTranslatedNamesDestructor(void *ptr)
+{
+ struct wbcTranslatedName *n = (struct wbcTranslatedName *)ptr;
+
+ while (n->name != NULL) {
+ wbcFreeMemory(n->name);
+ n += 1;
+ }
+}
+
+_PUBLIC_
+wbcErr wbcCtxLookupSids(struct wbcContext *ctx,
+ const struct wbcDomainSid *sids, int num_sids,
+ struct wbcDomainInfo **pdomains, int *pnum_domains,
+ struct wbcTranslatedName **pnames)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ int buflen, i, extra_len, num_domains, num_names;
+ char *sidlist, *p, *q, *extra_data;
+ struct wbcDomainInfo *domains = NULL;
+ struct wbcTranslatedName *names = NULL;
+ int error = 0;
+
+ buflen = num_sids * (WBC_SID_STRING_BUFLEN + 1) + 1;
+
+ sidlist = (char *)malloc(buflen);
+ if (sidlist == NULL) {
+ return WBC_ERR_NO_MEMORY;
+ }
+
+ p = sidlist;
+
+ for (i=0; i<num_sids; i++) {
+ int remaining;
+ int len;
+
+ remaining = buflen - (p - sidlist);
+
+ len = wbcSidToStringBuf(&sids[i], p, remaining);
+ if (len > remaining) {
+ free(sidlist);
+ return WBC_ERR_UNKNOWN_FAILURE;
+ }
+
+ p += len;
+ *p++ = '\n';
+ }
+ *p++ = '\0';
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.extra_data.data = sidlist;
+ request.extra_len = p - sidlist;
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_LOOKUPSIDS,
+ &request, &response);
+ free(sidlist);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return wbc_status;
+ }
+
+ extra_len = response.length - sizeof(struct winbindd_response);
+ extra_data = (char *)response.extra_data.data;
+
+ if ((extra_len <= 0) || (extra_data[extra_len-1] != '\0')) {
+ goto wbc_err_invalid;
+ }
+
+ p = extra_data;
+
+ num_domains = smb_strtoul(p, &q, 10, &error, SMB_STR_STANDARD);
+ if (*q != '\n' || error != 0) {
+ goto wbc_err_invalid;
+ }
+ p = q+1;
+
+ domains = (struct wbcDomainInfo *)wbcAllocateMemory(
+ num_domains+1, sizeof(struct wbcDomainInfo),
+ wbcDomainInfosDestructor);
+ if (domains == NULL) {
+ wbc_status = WBC_ERR_NO_MEMORY;
+ goto fail;
+ }
+
+ for (i=0; i<num_domains; i++) {
+
+ q = strchr(p, ' ');
+ if (q == NULL) {
+ goto wbc_err_invalid;
+ }
+ *q = '\0';
+ wbc_status = wbcStringToSid(p, &domains[i].sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ goto fail;
+ }
+ p = q+1;
+
+ q = strchr(p, '\n');
+ if (q == NULL) {
+ goto wbc_err_invalid;
+ }
+ *q = '\0';
+ domains[i].short_name = wbcStrDup(p);
+ if (domains[i].short_name == NULL) {
+ wbc_status = WBC_ERR_NO_MEMORY;
+ goto fail;
+ }
+ p = q+1;
+ }
+
+ num_names = smb_strtoul(p, &q, 10, &error, SMB_STR_STANDARD);
+ if (*q != '\n' || error != 0) {
+ goto wbc_err_invalid;
+ }
+ p = q+1;
+
+ if (num_names != num_sids) {
+ goto wbc_err_invalid;
+ }
+
+ names = (struct wbcTranslatedName *)wbcAllocateMemory(
+ num_names+1, sizeof(struct wbcTranslatedName),
+ wbcTranslatedNamesDestructor);
+ if (names == NULL) {
+ wbc_status = WBC_ERR_NO_MEMORY;
+ goto fail;
+ }
+
+ for (i=0; i<num_names; i++) {
+
+ names[i].domain_index = smb_strtoul(p,
+ &q,
+ 10,
+ &error,
+ SMB_STR_STANDARD);
+ if (names[i].domain_index < 0 || error != 0) {
+ goto wbc_err_invalid;
+ }
+ if (names[i].domain_index >= num_domains) {
+ goto wbc_err_invalid;
+ }
+
+ if (*q != ' ') {
+ goto wbc_err_invalid;
+ }
+ p = q+1;
+
+ names[i].type = smb_strtoul(p, &q, 10, &error, SMB_STR_STANDARD);
+ if (*q != ' ' || error != 0) {
+ goto wbc_err_invalid;
+ }
+ p = q+1;
+
+ q = strchr(p, '\n');
+ if (q == NULL) {
+ goto wbc_err_invalid;
+ }
+ *q = '\0';
+ names[i].name = wbcStrDup(p);
+ if (names[i].name == NULL) {
+ wbc_status = WBC_ERR_NO_MEMORY;
+ goto fail;
+ }
+ p = q+1;
+ }
+ if (*p != '\0') {
+ goto wbc_err_invalid;
+ }
+
+ *pdomains = domains;
+ *pnames = names;
+ winbindd_free_response(&response);
+ return WBC_ERR_SUCCESS;
+
+wbc_err_invalid:
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+fail:
+ winbindd_free_response(&response);
+ wbcFreeMemory(domains);
+ wbcFreeMemory(names);
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcLookupSids(const struct wbcDomainSid *sids, int num_sids,
+ struct wbcDomainInfo **pdomains, int *pnum_domains,
+ struct wbcTranslatedName **pnames)
+{
+ return wbcCtxLookupSids(NULL, sids, num_sids, pdomains,
+ pnum_domains, pnames);
+}
+
+/* Translate a collection of RIDs within a domain to names */
+
+_PUBLIC_
+wbcErr wbcCtxLookupRids(struct wbcContext *ctx, struct wbcDomainSid *dom_sid,
+ int num_rids,
+ uint32_t *rids,
+ const char **pp_domain_name,
+ const char ***pnames,
+ enum wbcSidType **ptypes)
+{
+ size_t i, len, ridbuf_size;
+ char *ridlist;
+ char *p;
+ int error = 0;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ char *domain_name = NULL;
+ const char **names = NULL;
+ enum wbcSidType *types = NULL;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (!dom_sid || (num_rids == 0)) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ wbcSidToStringBuf(dom_sid, request.data.sid, sizeof(request.data.sid));
+
+ /* Even if all the Rids were of maximum 32bit values,
+ we would only have 11 bytes per rid in the final array
+ ("4294967296" + \n). Add one more byte for the
+ terminating '\0' */
+
+ ridbuf_size = (sizeof(char)*11) * num_rids + 1;
+
+ ridlist = (char *)malloc(ridbuf_size);
+ BAIL_ON_PTR_ERROR(ridlist, wbc_status);
+
+ len = 0;
+ for (i=0; i<num_rids; i++) {
+ len += snprintf(ridlist + len, ridbuf_size - len, "%u\n",
+ rids[i]);
+ }
+ ridlist[len] = '\0';
+ len += 1;
+
+ request.extra_data.data = ridlist;
+ request.extra_len = len;
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_LOOKUPRIDS,
+ &request,
+ &response);
+ free(ridlist);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ domain_name = wbcStrDup(response.data.domain_name);
+ BAIL_ON_PTR_ERROR(domain_name, wbc_status);
+
+ names = wbcAllocateStringArray(num_rids);
+ BAIL_ON_PTR_ERROR(names, wbc_status);
+
+ types = (enum wbcSidType *)wbcAllocateMemory(
+ num_rids, sizeof(enum wbcSidType), NULL);
+ BAIL_ON_PTR_ERROR(types, wbc_status);
+
+ p = (char *)response.extra_data.data;
+
+ for (i=0; i<num_rids; i++) {
+ char *q;
+
+ if (*p == '\0') {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ goto done;
+ }
+
+ types[i] = (enum wbcSidType)smb_strtoul(p,
+ &q,
+ 10,
+ &error,
+ SMB_STR_STANDARD);
+
+ if (*q != ' ' || error != 0) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ goto done;
+ }
+
+ p = q+1;
+
+ if ((q = strchr(p, '\n')) == NULL) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ goto done;
+ }
+
+ *q = '\0';
+
+ names[i] = strdup(p);
+ BAIL_ON_PTR_ERROR(names[i], wbc_status);
+
+ p = q+1;
+ }
+
+ if (*p != '\0') {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ goto done;
+ }
+
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ winbindd_free_response(&response);
+
+ if (WBC_ERROR_IS_OK(wbc_status)) {
+ *pp_domain_name = domain_name;
+ *pnames = names;
+ *ptypes = types;
+ }
+ else {
+ wbcFreeMemory(domain_name);
+ wbcFreeMemory(names);
+ wbcFreeMemory(types);
+ }
+
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcLookupRids(struct wbcDomainSid *dom_sid,
+ int num_rids,
+ uint32_t *rids,
+ const char **pp_domain_name,
+ const char ***pnames,
+ enum wbcSidType **ptypes)
+{
+ return wbcCtxLookupRids(NULL, dom_sid, num_rids, rids,
+ pp_domain_name, pnames, ptypes);
+}
+
+/* Get the groups a user belongs to */
+_PUBLIC_
+wbcErr wbcCtxLookupUserSids(struct wbcContext *ctx,
+ const struct wbcDomainSid *user_sid,
+ bool domain_groups_only,
+ uint32_t *num_sids,
+ struct wbcDomainSid **_sids)
+{
+ uint32_t i;
+ const char *s;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ struct wbcDomainSid *sids = NULL;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ int cmd;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (!user_sid) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ wbcSidToStringBuf(user_sid, request.data.sid, sizeof(request.data.sid));
+
+ if (domain_groups_only) {
+ cmd = WINBINDD_GETUSERDOMGROUPS;
+ } else {
+ cmd = WINBINDD_GETUSERSIDS;
+ }
+
+ wbc_status = wbcRequestResponse(ctx, cmd,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ if (response.data.num_entries &&
+ !response.extra_data.data) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ sids = (struct wbcDomainSid *)wbcAllocateMemory(
+ response.data.num_entries, sizeof(struct wbcDomainSid),
+ NULL);
+ BAIL_ON_PTR_ERROR(sids, wbc_status);
+
+ s = (const char *)response.extra_data.data;
+ for (i = 0; i < response.data.num_entries; i++) {
+ char *n = strchr(s, '\n');
+ if (n) {
+ *n = '\0';
+ }
+ wbc_status = wbcStringToSid(s, &sids[i]);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ s += strlen(s) + 1;
+ }
+
+ *num_sids = response.data.num_entries;
+ *_sids = sids;
+ sids = NULL;
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ winbindd_free_response(&response);
+ if (sids) {
+ wbcFreeMemory(sids);
+ }
+
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcLookupUserSids(const struct wbcDomainSid *user_sid,
+ bool domain_groups_only,
+ uint32_t *num_sids,
+ struct wbcDomainSid **_sids)
+{
+ return wbcCtxLookupUserSids(NULL, user_sid, domain_groups_only,
+ num_sids, _sids);
+}
+
+static inline
+wbcErr _sid_to_rid(struct wbcDomainSid *sid, uint32_t *rid)
+{
+ if (sid->num_auths < 1) {
+ return WBC_ERR_INVALID_RESPONSE;
+ }
+ *rid = sid->sub_auths[sid->num_auths - 1];
+
+ return WBC_ERR_SUCCESS;
+}
+
+/* Get alias membership for sids */
+_PUBLIC_
+wbcErr wbcCtxGetSidAliases(struct wbcContext *ctx,
+ const struct wbcDomainSid *dom_sid,
+ struct wbcDomainSid *sids,
+ uint32_t num_sids,
+ uint32_t **alias_rids,
+ uint32_t *num_alias_rids)
+{
+ uint32_t i;
+ const char *s;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ ssize_t extra_data_len = 0;
+ char * extra_data = NULL;
+ ssize_t buflen = 0;
+ struct wbcDomainSid sid;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ uint32_t * rids = NULL;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (!dom_sid) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ goto done;
+ }
+
+ wbcSidToStringBuf(dom_sid, request.data.sid, sizeof(request.data.sid));
+
+ /* Lets assume each sid is around 57 characters
+ * S-1-5-21-AAAAAAAAAAA-BBBBBBBBBBB-CCCCCCCCCCC-DDDDDDDDDDD\n */
+ buflen = 57 * num_sids;
+ extra_data = (char *)malloc(buflen);
+ if (!extra_data) {
+ wbc_status = WBC_ERR_NO_MEMORY;
+ goto done;
+ }
+
+ /* Build the sid list */
+ for (i=0; i<num_sids; i++) {
+ char sid_str[WBC_SID_STRING_BUFLEN];
+ size_t sid_len;
+
+ sid_len = wbcSidToStringBuf(&sids[i], sid_str, sizeof(sid_str));
+
+ if (buflen < extra_data_len + sid_len + 2) {
+ char * tmp_data = NULL;
+ buflen *= 2;
+ tmp_data = (char *)realloc(extra_data, buflen);
+ if (!tmp_data) {
+ wbc_status = WBC_ERR_NO_MEMORY;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ extra_data = tmp_data;
+ }
+
+ strncpy(&extra_data[extra_data_len], sid_str,
+ buflen - extra_data_len);
+ extra_data_len += sid_len;
+ extra_data[extra_data_len++] = '\n';
+ extra_data[extra_data_len] = '\0';
+ }
+ extra_data_len += 1;
+
+ request.extra_data.data = extra_data;
+ request.extra_len = extra_data_len;
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_GETSIDALIASES,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ if (response.data.num_entries &&
+ !response.extra_data.data) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ goto done;
+ }
+
+ rids = (uint32_t *)wbcAllocateMemory(response.data.num_entries,
+ sizeof(uint32_t), NULL);
+ BAIL_ON_PTR_ERROR(rids, wbc_status);
+
+ s = (const char *)response.extra_data.data;
+ for (i = 0; i < response.data.num_entries; i++) {
+ char *n = strchr(s, '\n');
+ if (n) {
+ *n = '\0';
+ }
+ wbc_status = wbcStringToSid(s, &sid);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ wbc_status = _sid_to_rid(&sid, &rids[i]);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ s += strlen(s) + 1;
+ }
+
+ *num_alias_rids = response.data.num_entries;
+ *alias_rids = rids;
+ rids = NULL;
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ free(extra_data);
+ winbindd_free_response(&response);
+ wbcFreeMemory(rids);
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcGetSidAliases(const struct wbcDomainSid *dom_sid,
+ struct wbcDomainSid *sids,
+ uint32_t num_sids,
+ uint32_t **alias_rids,
+ uint32_t *num_alias_rids)
+{
+ return wbcCtxGetSidAliases(NULL, dom_sid, sids, num_sids,
+ alias_rids, num_alias_rids);
+}
+
+
+/* Lists Users */
+_PUBLIC_
+wbcErr wbcCtxListUsers(struct wbcContext *ctx,
+ const char *domain_name,
+ uint32_t *_num_users,
+ const char ***_users)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ uint32_t num_users = 0;
+ const char **users = NULL;
+ const char *next;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (domain_name) {
+ strncpy(request.domain_name, domain_name,
+ sizeof(request.domain_name)-1);
+ }
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_LIST_USERS,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ users = wbcAllocateStringArray(response.data.num_entries);
+ if (users == NULL) {
+ return WBC_ERR_NO_MEMORY;
+ }
+
+ /* Look through extra data */
+
+ next = (const char *)response.extra_data.data;
+ while (next) {
+ const char *current;
+ char *k;
+
+ if (num_users >= response.data.num_entries) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ goto done;
+ }
+
+ current = next;
+ k = strchr(next, ',');
+
+ if (k) {
+ k[0] = '\0';
+ next = k+1;
+ } else {
+ next = NULL;
+ }
+
+ users[num_users] = strdup(current);
+ BAIL_ON_PTR_ERROR(users[num_users], wbc_status);
+ num_users += 1;
+ }
+ if (num_users != response.data.num_entries) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ goto done;
+ }
+
+ *_num_users = response.data.num_entries;
+ *_users = users;
+ users = NULL;
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ winbindd_free_response(&response);
+ wbcFreeMemory(users);
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcListUsers(const char *domain_name,
+ uint32_t *_num_users,
+ const char ***_users)
+{
+ return wbcCtxListUsers(NULL, domain_name, _num_users, _users);
+}
+
+/* Lists Groups */
+_PUBLIC_
+wbcErr wbcCtxListGroups(struct wbcContext *ctx,
+ const char *domain_name,
+ uint32_t *_num_groups,
+ const char ***_groups)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ uint32_t num_groups = 0;
+ const char **groups = NULL;
+ const char *next;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (domain_name) {
+ strncpy(request.domain_name, domain_name,
+ sizeof(request.domain_name)-1);
+ }
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_LIST_GROUPS,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ groups = wbcAllocateStringArray(response.data.num_entries);
+ if (groups == NULL) {
+ return WBC_ERR_NO_MEMORY;
+ }
+
+ /* Look through extra data */
+
+ next = (const char *)response.extra_data.data;
+ while (next) {
+ const char *current;
+ char *k;
+
+ if (num_groups >= response.data.num_entries) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ goto done;
+ }
+
+ current = next;
+ k = strchr(next, ',');
+
+ if (k) {
+ k[0] = '\0';
+ next = k+1;
+ } else {
+ next = NULL;
+ }
+
+ groups[num_groups] = strdup(current);
+ BAIL_ON_PTR_ERROR(groups[num_groups], wbc_status);
+ num_groups += 1;
+ }
+ if (num_groups != response.data.num_entries) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ goto done;
+ }
+
+ *_num_groups = response.data.num_entries;
+ *_groups = groups;
+ groups = NULL;
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ winbindd_free_response(&response);
+ wbcFreeMemory(groups);
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcListGroups(const char *domain_name,
+ uint32_t *_num_groups,
+ const char ***_groups)
+{
+ return wbcCtxListGroups(NULL, domain_name, _num_groups, _groups);
+}
+
+_PUBLIC_
+wbcErr wbcCtxGetDisplayName(struct wbcContext *ctx,
+ const struct wbcDomainSid *sid,
+ char **pdomain,
+ char **pfullname,
+ enum wbcSidType *pname_type)
+{
+ wbcErr wbc_status;
+ char *domain = NULL;
+ char *name = NULL;
+ enum wbcSidType name_type;
+
+ wbc_status = wbcCtxLookupSid(ctx, sid, &domain, &name, &name_type);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ if (name_type == WBC_SID_NAME_USER) {
+ uid_t uid;
+ struct passwd *pwd;
+
+ wbc_status = wbcCtxSidToUid(ctx, sid, &uid);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ wbc_status = wbcCtxGetpwuid(ctx, uid, &pwd);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ wbcFreeMemory(name);
+
+ name = wbcStrDup(pwd->pw_gecos);
+ wbcFreeMemory(pwd);
+ BAIL_ON_PTR_ERROR(name, wbc_status);
+ }
+
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ if (WBC_ERROR_IS_OK(wbc_status)) {
+ *pdomain = domain;
+ *pfullname = name;
+ *pname_type = name_type;
+ } else {
+ wbcFreeMemory(domain);
+ wbcFreeMemory(name);
+ }
+
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcGetDisplayName(const struct wbcDomainSid *sid,
+ char **pdomain,
+ char **pfullname,
+ enum wbcSidType *pname_type)
+{
+ return wbcCtxGetDisplayName(NULL, sid, pdomain, pfullname, pname_type);
+}
+
+_PUBLIC_
+const char* wbcSidTypeString(enum wbcSidType type)
+{
+ switch (type) {
+ case WBC_SID_NAME_USE_NONE: return "SID_NONE";
+ case WBC_SID_NAME_USER: return "SID_USER";
+ case WBC_SID_NAME_DOM_GRP: return "SID_DOM_GROUP";
+ case WBC_SID_NAME_DOMAIN: return "SID_DOMAIN";
+ case WBC_SID_NAME_ALIAS: return "SID_ALIAS";
+ case WBC_SID_NAME_WKN_GRP: return "SID_WKN_GROUP";
+ case WBC_SID_NAME_DELETED: return "SID_DELETED";
+ case WBC_SID_NAME_INVALID: return "SID_INVALID";
+ case WBC_SID_NAME_UNKNOWN: return "SID_UNKNOWN";
+ case WBC_SID_NAME_COMPUTER: return "SID_COMPUTER";
+ case WBC_SID_NAME_LABEL: return "SID_LABEL";
+ default: return "Unknown type";
+ }
+}
diff --git a/nsswitch/libwbclient/wbc_util.c b/nsswitch/libwbclient/wbc_util.c
new file mode 100644
index 0000000..9e54baf
--- /dev/null
+++ b/nsswitch/libwbclient/wbc_util.c
@@ -0,0 +1,920 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind client asynchronous API, utility functions
+
+ Copyright (C) Gerald (Jerry) Carter 2007-2008
+
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Required Headers */
+
+#include "replace.h"
+#include "libwbclient.h"
+#include "../winbind_client.h"
+
+/** @brief Ping winbindd to see if the daemon is running
+ *
+ * @param *ctx wbclient Context
+ *
+ * @return #wbcErr
+ **/
+_PUBLIC_
+wbcErr wbcCtxPing(struct wbcContext *ctx)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ /* Initialize request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ return wbcRequestResponse(ctx, WINBINDD_PING, &request, &response);
+}
+
+_PUBLIC_
+wbcErr wbcPing(void)
+{
+ return wbcCtxPing(NULL);
+}
+
+static void wbcInterfaceDetailsDestructor(void *ptr)
+{
+ struct wbcInterfaceDetails *i = (struct wbcInterfaceDetails *)ptr;
+ free(i->winbind_version);
+ free(i->netbios_name);
+ free(i->netbios_domain);
+ free(i->dns_domain);
+}
+
+/**
+ * @brief Query useful information about the winbind service
+ *
+ * @param *_details pointer to hold the struct wbcInterfaceDetails
+ *
+ * @return #wbcErr
+ */
+
+_PUBLIC_
+wbcErr wbcCtxInterfaceDetails(struct wbcContext *ctx,
+ struct wbcInterfaceDetails **_details)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcInterfaceDetails *info;
+ struct wbcDomainInfo *domain = NULL;
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ /* Initialize request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ info = (struct wbcInterfaceDetails *)wbcAllocateMemory(
+ 1, sizeof(struct wbcInterfaceDetails),
+ wbcInterfaceDetailsDestructor);
+ BAIL_ON_PTR_ERROR(info, wbc_status);
+
+ /* first the interface version */
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_INTERFACE_VERSION,
+ NULL, &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ info->interface_version = response.data.interface_version;
+
+ /* then the samba version and the winbind separator */
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_INFO, NULL, &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ info->winbind_version = strdup(response.data.info.samba_version);
+ BAIL_ON_PTR_ERROR(info->winbind_version, wbc_status);
+ info->winbind_separator = response.data.info.winbind_separator;
+
+ /* then the local netbios name */
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_NETBIOS_NAME,
+ NULL, &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ info->netbios_name = strdup(response.data.netbios_name);
+ BAIL_ON_PTR_ERROR(info->netbios_name, wbc_status);
+
+ /* then the local workgroup name */
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_DOMAIN_NAME,
+ NULL, &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ info->netbios_domain = strdup(response.data.domain_name);
+ BAIL_ON_PTR_ERROR(info->netbios_domain, wbc_status);
+
+ wbc_status = wbcCtxDomainInfo(ctx, info->netbios_domain, &domain);
+ if (wbc_status == WBC_ERR_DOMAIN_NOT_FOUND) {
+ /* maybe it's a standalone server */
+ domain = NULL;
+ } else {
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ if (domain) {
+ info->dns_domain = strdup(domain->dns_name);
+ wbcFreeMemory(domain);
+ BAIL_ON_PTR_ERROR(info->dns_domain, wbc_status);
+ } else {
+ info->dns_domain = NULL;
+ }
+
+ *_details = info;
+ info = NULL;
+
+ wbc_status = WBC_ERR_SUCCESS;
+
+done:
+ wbcFreeMemory(info);
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcInterfaceDetails(struct wbcInterfaceDetails **_details)
+{
+ return wbcCtxInterfaceDetails(NULL, _details);
+}
+
+static void wbcDomainInfoDestructor(void *ptr)
+{
+ struct wbcDomainInfo *i = (struct wbcDomainInfo *)ptr;
+ free(i->short_name);
+ free(i->dns_name);
+}
+
+/** @brief Lookup the current status of a trusted domain, sync wrapper
+ *
+ * @param domain Domain to query
+ * @param *dinfo Pointer to returned struct wbcDomainInfo
+ *
+ * @return #wbcErr
+ */
+
+_PUBLIC_
+wbcErr wbcCtxDomainInfo(struct wbcContext *ctx,
+ const char *domain,
+ struct wbcDomainInfo **dinfo)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainInfo *info = NULL;
+
+ if (!domain || !dinfo) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* Initialize request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ strncpy(request.domain_name, domain,
+ sizeof(request.domain_name)-1);
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_DOMAIN_INFO,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ info = (struct wbcDomainInfo *)wbcAllocateMemory(
+ 1, sizeof(struct wbcDomainInfo), wbcDomainInfoDestructor);
+ BAIL_ON_PTR_ERROR(info, wbc_status);
+
+ info->short_name = strdup(response.data.domain_info.name);
+ BAIL_ON_PTR_ERROR(info->short_name, wbc_status);
+
+ info->dns_name = strdup(response.data.domain_info.alt_name);
+ BAIL_ON_PTR_ERROR(info->dns_name, wbc_status);
+
+ wbc_status = wbcStringToSid(response.data.domain_info.sid,
+ &info->sid);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ if (response.data.domain_info.native_mode)
+ info->domain_flags |= WBC_DOMINFO_DOMAIN_NATIVE;
+ if (response.data.domain_info.active_directory)
+ info->domain_flags |= WBC_DOMINFO_DOMAIN_AD;
+ if (response.data.domain_info.primary)
+ info->domain_flags |= WBC_DOMINFO_DOMAIN_PRIMARY;
+
+ *dinfo = info;
+ info = NULL;
+
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ wbcFreeMemory(info);
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcDomainInfo(const char *domain, struct wbcDomainInfo **dinfo)
+{
+ return wbcCtxDomainInfo(NULL, domain, dinfo);
+}
+
+/* Get the list of current DCs */
+_PUBLIC_
+wbcErr wbcCtxDcInfo(struct wbcContext *ctx,
+ const char *domain, size_t *num_dcs,
+ const char ***dc_names, const char ***dc_ips)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ const char **names = NULL;
+ const char **ips = NULL;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ size_t extra_len;
+ int i;
+ char *p;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (domain != NULL) {
+ strncpy(request.domain_name, domain,
+ sizeof(request.domain_name) - 1);
+ }
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_DC_INFO,
+ &request, &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ names = wbcAllocateStringArray(response.data.num_entries);
+ BAIL_ON_PTR_ERROR(names, wbc_status);
+
+ ips = wbcAllocateStringArray(response.data.num_entries);
+ BAIL_ON_PTR_ERROR(ips, wbc_status);
+
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+
+ p = (char *)response.extra_data.data;
+
+ if (response.length < (sizeof(struct winbindd_response)+1)) {
+ goto done;
+ }
+
+ extra_len = response.length - sizeof(struct winbindd_response);
+
+ if (p[extra_len-1] != '\0') {
+ goto done;
+ }
+
+ for (i=0; i<response.data.num_entries; i++) {
+ char *q;
+
+ q = strchr(p, '\n');
+ if (q == NULL) {
+ goto done;
+ }
+ names[i] = strndup(p, q-p);
+ BAIL_ON_PTR_ERROR(names[i], wbc_status);
+ p = q+1;
+
+ q = strchr(p, '\n');
+ if (q == NULL) {
+ goto done;
+ }
+ ips[i] = strndup(p, q-p);
+ BAIL_ON_PTR_ERROR(ips[i], wbc_status);
+ p = q+1;
+ }
+ if (p[0] != '\0') {
+ goto done;
+ }
+
+ wbc_status = WBC_ERR_SUCCESS;
+done:
+ if (response.extra_data.data)
+ free(response.extra_data.data);
+
+ if (WBC_ERROR_IS_OK(wbc_status)) {
+ *num_dcs = response.data.num_entries;
+ *dc_names = names;
+ names = NULL;
+ *dc_ips = ips;
+ ips = NULL;
+ }
+ wbcFreeMemory(names);
+ wbcFreeMemory(ips);
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcDcInfo(const char *domain, size_t *num_dcs,
+ const char ***dc_names, const char ***dc_ips)
+{
+ return wbcCtxDcInfo(NULL, domain, num_dcs, dc_names, dc_ips);
+}
+
+/* Resolve a NetbiosName via WINS */
+_PUBLIC_
+wbcErr wbcCtxResolveWinsByName(struct wbcContext *ctx,
+ const char *name, char **ip)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ char *ipaddr;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ strncpy(request.data.winsreq, name,
+ sizeof(request.data.winsreq)-1);
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_WINS_BYNAME,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ /* Display response */
+
+ ipaddr = wbcStrDup(response.data.winsresp);
+ BAIL_ON_PTR_ERROR(ipaddr, wbc_status);
+
+ *ip = ipaddr;
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcResolveWinsByName(const char *name, char **ip)
+{
+ return wbcCtxResolveWinsByName(NULL, name, ip);
+}
+
+/* Resolve an IP address via WINS into a NetbiosName */
+_PUBLIC_
+wbcErr wbcCtxResolveWinsByIP(struct wbcContext *ctx,
+ const char *ip, char **name)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ char *name_str;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ strncpy(request.data.winsreq, ip,
+ sizeof(request.data.winsreq)-1);
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_WINS_BYIP,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ /* Display response */
+
+ name_str = wbcStrDup(response.data.winsresp);
+ BAIL_ON_PTR_ERROR(name_str, wbc_status);
+
+ *name = name_str;
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcResolveWinsByIP(const char *ip, char **name)
+{
+ return wbcCtxResolveWinsByIP(NULL, ip, name);
+}
+
+/**
+ */
+
+static wbcErr process_domain_info_string(struct wbcDomainInfo *info,
+ char *info_string)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ char *r = NULL;
+ char *s = NULL;
+
+ r = info_string;
+
+ /* Short Name */
+ if ((s = strchr(r, '\\')) == NULL) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ *s = '\0';
+ s++;
+
+ info->short_name = strdup(r);
+ BAIL_ON_PTR_ERROR(info->short_name, wbc_status);
+
+
+ /* DNS Name */
+ r = s;
+ if ((s = strchr(r, '\\')) == NULL) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ *s = '\0';
+ s++;
+
+ info->dns_name = strdup(r);
+ BAIL_ON_PTR_ERROR(info->dns_name, wbc_status);
+
+ /* SID */
+ r = s;
+ if ((s = strchr(r, '\\')) == NULL) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ *s = '\0';
+ s++;
+
+ wbc_status = wbcStringToSid(r, &info->sid);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ /* Trust type */
+ r = s;
+ if ((s = strchr(r, '\\')) == NULL) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ *s = '\0';
+ s++;
+
+ if (strncmp(r, "Routed", strlen("Routed")) == 0) {
+ info->trust_type = WBC_DOMINFO_TRUSTTYPE_NONE;
+ info->trust_routing = strdup(r);
+ BAIL_ON_PTR_ERROR(info->trust_routing, wbc_status);
+ } else if (strcmp(r, "Local") == 0) {
+ info->trust_type = WBC_DOMINFO_TRUSTTYPE_LOCAL;
+ } else if (strcmp(r, "Workstation") == 0) {
+ info->trust_type = WBC_DOMINFO_TRUSTTYPE_WKSTA;
+ } else if (strcmp(r, "RWDC") == 0) {
+ info->trust_type = WBC_DOMINFO_TRUSTTYPE_RWDC;
+ } else if (strcmp(r, "RODC") == 0) {
+ info->trust_type = WBC_DOMINFO_TRUSTTYPE_RODC;
+ } else if (strcmp(r, "PDC") == 0) {
+ info->trust_type = WBC_DOMINFO_TRUSTTYPE_PDC;
+ } else if (strcmp(r, "External") == 0) {
+ info->trust_type = WBC_DOMINFO_TRUSTTYPE_EXTERNAL;
+ } else if (strcmp(r, "Forest") == 0) {
+ info->trust_type = WBC_DOMINFO_TRUSTTYPE_FOREST;
+ } else if (strcmp(r, "In Forest") == 0) {
+ info->trust_type = WBC_DOMINFO_TRUSTTYPE_IN_FOREST;
+ } else {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ /* Transitive */
+ r = s;
+ if ((s = strchr(r, '\\')) == NULL) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ *s = '\0';
+ s++;
+
+ if (strcmp(r, "Yes") == 0) {
+ info->trust_flags |= WBC_DOMINFO_TRUST_TRANSITIVE;
+ }
+
+ /* Incoming */
+ r = s;
+ if ((s = strchr(r, '\\')) == NULL) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ *s = '\0';
+ s++;
+
+ if (strcmp(r, "Yes") == 0) {
+ info->trust_flags |= WBC_DOMINFO_TRUST_INCOMING;
+ }
+
+ /* Outgoing */
+ r = s;
+ if ((s = strchr(r, '\\')) == NULL) {
+ wbc_status = WBC_ERR_INVALID_RESPONSE;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+ *s = '\0';
+ s++;
+
+ if (strcmp(r, "Yes") == 0) {
+ info->trust_flags |= WBC_DOMINFO_TRUST_OUTGOING;
+ }
+
+ /* Online/Offline status */
+ r = s;
+ if ( strcmp(r, "Offline") == 0) {
+ info->domain_flags |= WBC_DOMINFO_DOMAIN_OFFLINE;
+ }
+
+ wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+ return wbc_status;
+}
+
+static void wbcDomainInfoListDestructor(void *ptr)
+{
+ struct wbcDomainInfo *i = (struct wbcDomainInfo *)ptr;
+
+ while (i->short_name != NULL) {
+ free(i->short_name);
+ free(i->dns_name);
+ i += 1;
+ }
+}
+
+/* Enumerate the domain trusts known by Winbind */
+_PUBLIC_
+wbcErr wbcCtxListTrusts(struct wbcContext *ctx,
+ struct wbcDomainInfo **domains, size_t *num_domains)
+{
+ struct winbindd_response response;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ char *p = NULL;
+ char *extra_data = NULL;
+ struct wbcDomainInfo *d_list = NULL;
+ int i = 0;
+
+ *domains = NULL;
+ *num_domains = 0;
+
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_LIST_TRUSTDOM,
+ NULL,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ /* Decode the response */
+
+ p = (char *)response.extra_data.data;
+
+ if ((p == NULL) || (strlen(p) == 0)) {
+ /* We should always at least get back our
+ own SAM domain */
+
+ wbc_status = WBC_ERR_DOMAIN_NOT_FOUND;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ d_list = (struct wbcDomainInfo *)wbcAllocateMemory(
+ response.data.num_entries + 1,sizeof(struct wbcDomainInfo),
+ wbcDomainInfoListDestructor);
+ BAIL_ON_PTR_ERROR(d_list, wbc_status);
+
+ extra_data = strdup((char*)response.extra_data.data);
+ BAIL_ON_PTR_ERROR(extra_data, wbc_status);
+
+ p = extra_data;
+
+ /* Outer loop processes the list of domain information */
+
+ for (i=0; i<response.data.num_entries && p; i++) {
+ char *next = strchr(p, '\n');
+
+ if (next) {
+ *next = '\0';
+ next++;
+ }
+
+ wbc_status = process_domain_info_string(&d_list[i], p);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ p = next;
+ }
+
+ *domains = d_list;
+ d_list = NULL;
+ *num_domains = i;
+
+ done:
+ winbindd_free_response(&response);
+ wbcFreeMemory(d_list);
+ free(extra_data);
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcListTrusts(struct wbcDomainInfo **domains, size_t *num_domains)
+{
+ return wbcCtxListTrusts(NULL, domains, num_domains);
+}
+
+static void wbcDomainControllerInfoDestructor(void *ptr)
+{
+ struct wbcDomainControllerInfo *i =
+ (struct wbcDomainControllerInfo *)ptr;
+ free(i->dc_name);
+}
+
+/* Enumerate the domain trusts known by Winbind */
+_PUBLIC_
+wbcErr wbcCtxLookupDomainController(struct wbcContext *ctx,
+ const char *domain, uint32_t flags,
+ struct wbcDomainControllerInfo **dc_info)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ struct wbcDomainControllerInfo *dc = NULL;
+
+ /* validate input params */
+
+ if (!domain || !dc_info) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ strncpy(request.data.dsgetdcname.domain_name, domain,
+ sizeof(request.data.dsgetdcname.domain_name)-1);
+
+ request.flags = flags;
+
+ dc = (struct wbcDomainControllerInfo *)wbcAllocateMemory(
+ 1, sizeof(struct wbcDomainControllerInfo),
+ wbcDomainControllerInfoDestructor);
+ BAIL_ON_PTR_ERROR(dc, wbc_status);
+
+ /* Send request */
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_DSGETDCNAME,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ dc->dc_name = strdup(response.data.dsgetdcname.dc_unc);
+ BAIL_ON_PTR_ERROR(dc->dc_name, wbc_status);
+
+ *dc_info = dc;
+ dc = NULL;
+
+done:
+ wbcFreeMemory(dc);
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcLookupDomainController(const char *domain, uint32_t flags,
+ struct wbcDomainControllerInfo **dc_info)
+{
+ return wbcCtxLookupDomainController(NULL, domain, flags, dc_info);
+}
+
+static void wbcDomainControllerInfoExDestructor(void *ptr)
+{
+ struct wbcDomainControllerInfoEx *i =
+ (struct wbcDomainControllerInfoEx *)ptr;
+ free(discard_const_p(char, i->dc_unc));
+ free(discard_const_p(char, i->dc_address));
+ free(discard_const_p(char, i->domain_guid));
+ free(discard_const_p(char, i->domain_name));
+ free(discard_const_p(char, i->forest_name));
+ free(discard_const_p(char, i->dc_site_name));
+ free(discard_const_p(char, i->client_site_name));
+}
+
+static wbcErr wbc_create_domain_controller_info_ex(const struct winbindd_response *resp,
+ struct wbcDomainControllerInfoEx **_i)
+{
+ wbcErr wbc_status = WBC_ERR_SUCCESS;
+ struct wbcDomainControllerInfoEx *i;
+ struct wbcGuid guid;
+
+ i = (struct wbcDomainControllerInfoEx *)wbcAllocateMemory(
+ 1, sizeof(struct wbcDomainControllerInfoEx),
+ wbcDomainControllerInfoExDestructor);
+ BAIL_ON_PTR_ERROR(i, wbc_status);
+
+ i->dc_unc = strdup(resp->data.dsgetdcname.dc_unc);
+ BAIL_ON_PTR_ERROR(i->dc_unc, wbc_status);
+
+ i->dc_address = strdup(resp->data.dsgetdcname.dc_address);
+ BAIL_ON_PTR_ERROR(i->dc_address, wbc_status);
+
+ i->dc_address_type = resp->data.dsgetdcname.dc_address_type;
+
+ wbc_status = wbcStringToGuid(resp->data.dsgetdcname.domain_guid, &guid);
+ if (WBC_ERROR_IS_OK(wbc_status)) {
+ i->domain_guid = (struct wbcGuid *)malloc(
+ sizeof(struct wbcGuid));
+ BAIL_ON_PTR_ERROR(i->domain_guid, wbc_status);
+
+ *i->domain_guid = guid;
+ }
+
+ i->domain_name = strdup(resp->data.dsgetdcname.domain_name);
+ BAIL_ON_PTR_ERROR(i->domain_name, wbc_status);
+
+ if (resp->data.dsgetdcname.forest_name[0] != '\0') {
+ i->forest_name = strdup(resp->data.dsgetdcname.forest_name);
+ BAIL_ON_PTR_ERROR(i->forest_name, wbc_status);
+ }
+
+ i->dc_flags = resp->data.dsgetdcname.dc_flags;
+
+ if (resp->data.dsgetdcname.dc_site_name[0] != '\0') {
+ i->dc_site_name = strdup(resp->data.dsgetdcname.dc_site_name);
+ BAIL_ON_PTR_ERROR(i->dc_site_name, wbc_status);
+ }
+
+ if (resp->data.dsgetdcname.client_site_name[0] != '\0') {
+ i->client_site_name = strdup(
+ resp->data.dsgetdcname.client_site_name);
+ BAIL_ON_PTR_ERROR(i->client_site_name, wbc_status);
+ }
+
+ *_i = i;
+ i = NULL;
+
+done:
+ if (i != NULL) {
+ wbcFreeMemory(i);
+ }
+ return wbc_status;
+}
+
+/* Get extended domain controller information */
+_PUBLIC_
+wbcErr wbcCtxLookupDomainControllerEx(struct wbcContext *ctx,
+ const char *domain,
+ struct wbcGuid *guid,
+ const char *site,
+ uint32_t flags,
+ struct wbcDomainControllerInfoEx **dc_info)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ /* validate input params */
+
+ if (!domain || !dc_info) {
+ wbc_status = WBC_ERR_INVALID_PARAM;
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.data.dsgetdcname.flags = flags;
+
+ strncpy(request.data.dsgetdcname.domain_name, domain,
+ sizeof(request.data.dsgetdcname.domain_name)-1);
+
+ if (site) {
+ strncpy(request.data.dsgetdcname.site_name, site,
+ sizeof(request.data.dsgetdcname.site_name)-1);
+ }
+
+ if (guid) {
+ char *str = NULL;
+
+ wbc_status = wbcGuidToString(guid, &str);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ strncpy(request.data.dsgetdcname.domain_guid, str,
+ sizeof(request.data.dsgetdcname.domain_guid)-1);
+
+ wbcFreeMemory(str);
+ }
+
+ /* Send request */
+
+ wbc_status = wbcRequestResponse(ctx, WINBINDD_DSGETDCNAME,
+ &request,
+ &response);
+ BAIL_ON_WBC_ERROR(wbc_status);
+
+ if (dc_info) {
+ wbc_status = wbc_create_domain_controller_info_ex(&response,
+ dc_info);
+ BAIL_ON_WBC_ERROR(wbc_status);
+ }
+
+ wbc_status = WBC_ERR_SUCCESS;
+done:
+ return wbc_status;
+}
+
+_PUBLIC_
+wbcErr wbcLookupDomainControllerEx(const char *domain,
+ struct wbcGuid *guid,
+ const char *site,
+ uint32_t flags,
+ struct wbcDomainControllerInfoEx **dc_info)
+{
+ return wbcCtxLookupDomainControllerEx(NULL, domain, guid, site,
+ flags, dc_info);
+}
+
+static void wbcNamedBlobDestructor(void *ptr)
+{
+ struct wbcNamedBlob *b = (struct wbcNamedBlob *)ptr;
+
+ while (b->name != NULL) {
+ free(discard_const_p(char, b->name));
+ free(b->blob.data);
+ b += 1;
+ }
+}
+
+/* Initialize a named blob and add to list of blobs */
+_PUBLIC_
+wbcErr wbcAddNamedBlob(size_t *num_blobs,
+ struct wbcNamedBlob **pblobs,
+ const char *name,
+ uint32_t flags,
+ uint8_t *data,
+ size_t length)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcNamedBlob *blobs, *blob;
+
+ if (name == NULL) {
+ return WBC_ERR_INVALID_PARAM;
+ }
+
+ /*
+ * Overallocate the b->name==NULL terminator for
+ * wbcNamedBlobDestructor
+ */
+ blobs = (struct wbcNamedBlob *)wbcAllocateMemory(
+ *num_blobs + 2, sizeof(struct wbcNamedBlob),
+ wbcNamedBlobDestructor);
+
+ if (blobs == NULL) {
+ return WBC_ERR_NO_MEMORY;
+ }
+
+ if (*pblobs != NULL) {
+ struct wbcNamedBlob *old = *pblobs;
+ memcpy(blobs, old, sizeof(struct wbcNamedBlob) * (*num_blobs));
+ if (*num_blobs != 0) {
+ /* end indicator for wbcNamedBlobDestructor */
+ old[0].name = NULL;
+ }
+ wbcFreeMemory(old);
+ }
+ *pblobs = blobs;
+
+ blob = &blobs[*num_blobs];
+
+ blob->name = strdup(name);
+ BAIL_ON_PTR_ERROR(blob->name, wbc_status);
+ blob->flags = flags;
+
+ blob->blob.length = length;
+ blob->blob.data = (uint8_t *)malloc(length);
+ BAIL_ON_PTR_ERROR(blob->blob.data, wbc_status);
+ memcpy(blob->blob.data, data, length);
+
+ *num_blobs += 1;
+ *pblobs = blobs;
+ blobs = NULL;
+
+ wbc_status = WBC_ERR_SUCCESS;
+done:
+ wbcFreeMemory(blobs);
+ return wbc_status;
+}
+
+_PUBLIC_
+void wbcSetClientProcessName(const char *name)
+{
+ winbind_set_client_name(name);
+}
diff --git a/nsswitch/libwbclient/wbclient.c b/nsswitch/libwbclient/wbclient.c
new file mode 100644
index 0000000..4562046
--- /dev/null
+++ b/nsswitch/libwbclient/wbclient.c
@@ -0,0 +1,344 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind client API
+
+ Copyright (C) Gerald (Jerry) Carter 2007
+ Copyright (C) Matthew Newton 2015
+
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Required Headers */
+
+#include "replace.h"
+#include "libwbclient.h"
+
+/* From wb_common.c */
+
+struct winbindd_context;
+
+NSS_STATUS winbindd_request_response(struct winbindd_context *wbctx,
+ int req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response);
+NSS_STATUS winbindd_priv_request_response(struct winbindd_context *wbctx,
+ int req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response);
+struct winbindd_context *winbindd_ctx_create(void);
+void winbindd_ctx_free(struct winbindd_context *ctx);
+
+/* Global context used for non-Ctx functions */
+
+static struct wbcContext wbcGlobalCtx = {
+ .winbindd_ctx = NULL,
+ .pw_cache_size = 0,
+ .pw_cache_idx = 0,
+ .gr_cache_size = 0,
+ .gr_cache_idx = 0
+};
+
+/*
+ result == NSS_STATUS_UNAVAIL: winbind not around
+ result == NSS_STATUS_NOTFOUND: winbind around, but domain missing
+
+ Due to a bad API NSS_STATUS_NOTFOUND is returned both when winbind_off
+ and when winbind return WINBINDD_ERROR. So the semantics of this
+ routine depends on winbind_on. Grepping for winbind_off I just
+ found 3 places where winbind is turned off, and this does not conflict
+ (as far as I have seen) with the callers of is_trusted_domains.
+
+ --Volker
+*/
+
+static wbcErr wbcRequestResponseInt(
+ struct winbindd_context *wbctx,
+ int cmd,
+ struct winbindd_request *request,
+ struct winbindd_response *response,
+ NSS_STATUS (*fn)(struct winbindd_context *wbctx, int req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response))
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ NSS_STATUS nss_status;
+
+ /* for some calls the request and/or response can be NULL */
+
+ nss_status = fn(wbctx, cmd, request, response);
+
+ switch (nss_status) {
+ case NSS_STATUS_SUCCESS:
+ wbc_status = WBC_ERR_SUCCESS;
+ break;
+ case NSS_STATUS_UNAVAIL:
+ wbc_status = WBC_ERR_WINBIND_NOT_AVAILABLE;
+ break;
+ case NSS_STATUS_NOTFOUND:
+ wbc_status = WBC_ERR_DOMAIN_NOT_FOUND;
+ break;
+ default:
+ wbc_status = WBC_ERR_NSS_ERROR;
+ break;
+ }
+
+ return wbc_status;
+}
+
+/**
+ * @brief Wrapper around Winbind's send/receive API call
+ *
+ * @param ctx Context
+ * @param cmd Winbind command operation to perform
+ * @param request Send structure
+ * @param response Receive structure
+ *
+ * @return #wbcErr
+ */
+_PUBLIC_ /* this is internal to wbclient_internal.h, but part of the ABI */
+wbcErr wbcRequestResponse(struct wbcContext *ctx, int cmd,
+ struct winbindd_request *request,
+ struct winbindd_response *response)
+{
+ struct winbindd_context *wbctx = NULL;
+
+ if (ctx) {
+ wbctx = ctx->winbindd_ctx;
+ }
+
+ return wbcRequestResponseInt(wbctx, cmd, request, response,
+ winbindd_request_response);
+}
+
+_PUBLIC_ /* this is internal to wbclient_internal.h, but part of the ABI */
+wbcErr wbcRequestResponsePriv(struct wbcContext *ctx, int cmd,
+ struct winbindd_request *request,
+ struct winbindd_response *response)
+{
+ struct winbindd_context *wbctx = NULL;
+
+ if (ctx) {
+ wbctx = ctx->winbindd_ctx;
+ }
+
+ return wbcRequestResponseInt(wbctx, cmd, request, response,
+ winbindd_priv_request_response);
+}
+
+/** @brief Translate an error value into a string
+ *
+ * @param error
+ *
+ * @return a pointer to a static string
+ **/
+_PUBLIC_
+const char *wbcErrorString(wbcErr error)
+{
+ switch (error) {
+ case WBC_ERR_SUCCESS:
+ return "WBC_ERR_SUCCESS";
+ case WBC_ERR_NOT_IMPLEMENTED:
+ return "WBC_ERR_NOT_IMPLEMENTED";
+ case WBC_ERR_UNKNOWN_FAILURE:
+ return "WBC_ERR_UNKNOWN_FAILURE";
+ case WBC_ERR_NO_MEMORY:
+ return "WBC_ERR_NO_MEMORY";
+ case WBC_ERR_INVALID_SID:
+ return "WBC_ERR_INVALID_SID";
+ case WBC_ERR_INVALID_PARAM:
+ return "WBC_ERR_INVALID_PARAM";
+ case WBC_ERR_WINBIND_NOT_AVAILABLE:
+ return "WBC_ERR_WINBIND_NOT_AVAILABLE";
+ case WBC_ERR_DOMAIN_NOT_FOUND:
+ return "WBC_ERR_DOMAIN_NOT_FOUND";
+ case WBC_ERR_INVALID_RESPONSE:
+ return "WBC_ERR_INVALID_RESPONSE";
+ case WBC_ERR_NSS_ERROR:
+ return "WBC_ERR_NSS_ERROR";
+ case WBC_ERR_UNKNOWN_USER:
+ return "WBC_ERR_UNKNOWN_USER";
+ case WBC_ERR_UNKNOWN_GROUP:
+ return "WBC_ERR_UNKNOWN_GROUP";
+ case WBC_ERR_AUTH_ERROR:
+ return "WBC_ERR_AUTH_ERROR";
+ case WBC_ERR_PWD_CHANGE_FAILED:
+ return "WBC_ERR_PWD_CHANGE_FAILED";
+ }
+
+ return "unknown wbcErr value";
+}
+
+#define WBC_MAGIC (0x7a2b0e1e)
+#define WBC_MAGIC_FREE (0x875634fe)
+
+struct wbcMemPrefix {
+ uint32_t magic;
+ void (*destructor)(void *ptr);
+};
+
+static size_t wbcPrefixLen(void)
+{
+ size_t result = sizeof(struct wbcMemPrefix);
+ return (result + 15) & ~15;
+}
+
+static struct wbcMemPrefix *wbcMemToPrefix(void *ptr)
+{
+ return (struct wbcMemPrefix *)(((char *)ptr) - wbcPrefixLen());
+}
+
+_PUBLIC_ /* this is internal to wbclient_internal.h, but part of the ABI */
+void *wbcAllocateMemory(size_t nelem, size_t elsize,
+ void (*destructor)(void *ptr))
+{
+ struct wbcMemPrefix *result;
+
+ if (nelem >= (2<<24)/elsize) {
+ /* basic protection against integer wrap */
+ return NULL;
+ }
+
+ result = (struct wbcMemPrefix *)calloc(
+ 1, nelem*elsize + wbcPrefixLen());
+ if (result == NULL) {
+ return NULL;
+ }
+ result->magic = WBC_MAGIC;
+ result->destructor = destructor;
+ return ((char *)result) + wbcPrefixLen();
+}
+
+/* Free library allocated memory */
+_PUBLIC_
+void wbcFreeMemory(void *p)
+{
+ struct wbcMemPrefix *wbcMem;
+
+ if (p == NULL) {
+ return;
+ }
+ wbcMem = wbcMemToPrefix(p);
+ if (wbcMem->magic != WBC_MAGIC) {
+ return;
+ }
+
+ /* paranoid check to ensure we don't double free */
+ wbcMem->magic = WBC_MAGIC_FREE;
+
+ if (wbcMem->destructor != NULL) {
+ wbcMem->destructor(p);
+ }
+ free(wbcMem);
+ return;
+}
+
+_PUBLIC_ /* this is internal to wbclient_internal.h, but part of the ABI */
+char *wbcStrDup(const char *str)
+{
+ char *result;
+ size_t len;
+
+ len = strlen(str);
+ result = (char *)wbcAllocateMemory(len+1, sizeof(char), NULL);
+ if (result == NULL) {
+ return NULL;
+ }
+ memcpy(result, str, len+1);
+ return result;
+}
+
+static void wbcStringArrayDestructor(void *ptr)
+{
+ char **p = (char **)ptr;
+ while (*p != NULL) {
+ free(*p);
+ p += 1;
+ }
+}
+
+_PUBLIC_ /* this is internal to wbclient_internal.h, but part of the ABI */
+const char **wbcAllocateStringArray(int num_strings)
+{
+ return (const char **)wbcAllocateMemory(
+ num_strings + 1, sizeof(const char *),
+ wbcStringArrayDestructor);
+}
+
+_PUBLIC_
+wbcErr wbcLibraryDetails(struct wbcLibraryDetails **_details)
+{
+ struct wbcLibraryDetails *info;
+
+ info = (struct wbcLibraryDetails *)wbcAllocateMemory(
+ 1, sizeof(struct wbcLibraryDetails), NULL);
+
+ if (info == NULL) {
+ return WBC_ERR_NO_MEMORY;
+ }
+
+ info->major_version = WBCLIENT_MAJOR_VERSION;
+ info->minor_version = WBCLIENT_MINOR_VERSION;
+ info->vendor_version = WBCLIENT_VENDOR_VERSION;
+
+ *_details = info;
+ return WBC_ERR_SUCCESS;
+}
+
+/* Context handling functions */
+
+static void wbcContextDestructor(void *ptr)
+{
+ struct wbcContext *ctx = (struct wbcContext *)ptr;
+
+ winbindd_ctx_free(ctx->winbindd_ctx);
+}
+
+_PUBLIC_
+struct wbcContext *wbcCtxCreate(void)
+{
+ struct wbcContext *ctx;
+ struct winbindd_context *wbctx;
+
+ ctx = (struct wbcContext *)wbcAllocateMemory(
+ 1, sizeof(struct wbcContext), wbcContextDestructor);
+
+ if (!ctx) {
+ return NULL;
+ }
+
+ wbctx = winbindd_ctx_create();
+
+ if (!wbctx) {
+ wbcFreeMemory(ctx);
+ return NULL;
+ }
+
+ ctx->winbindd_ctx = wbctx;
+
+ return ctx;
+}
+
+_PUBLIC_
+void wbcCtxFree(struct wbcContext *ctx)
+{
+ wbcFreeMemory(ctx);
+}
+
+_PUBLIC_ /* this is internal to wbclient_internal.h, but part of the ABI */
+struct wbcContext *wbcGetGlobalCtx(void)
+{
+ return &wbcGlobalCtx;
+}
diff --git a/nsswitch/libwbclient/wbclient.h b/nsswitch/libwbclient/wbclient.h
new file mode 100644
index 0000000..05cf8a1
--- /dev/null
+++ b/nsswitch/libwbclient/wbclient.h
@@ -0,0 +1,2068 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind client API
+
+ Copyright (C) Gerald (Jerry) Carter 2007
+ Copyright (C) Volker Lendecke 2009
+ Copyright (C) Matthew Newton 2015
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _WBCLIENT_H
+#define _WBCLIENT_H
+
+#include <pwd.h>
+#include <grp.h>
+
+/* Define error types */
+
+/**
+ * @brief Status codes returned from wbc functions
+ **/
+
+enum _wbcErrType {
+ WBC_ERR_SUCCESS = 0, /**< Successful completion **/
+ WBC_ERR_NOT_IMPLEMENTED,/**< Function not implemented **/
+ WBC_ERR_UNKNOWN_FAILURE,/**< General failure **/
+ WBC_ERR_NO_MEMORY, /**< Memory allocation error **/
+ WBC_ERR_INVALID_SID, /**< Invalid SID format **/
+ WBC_ERR_INVALID_PARAM, /**< An Invalid parameter was supplied **/
+ WBC_ERR_WINBIND_NOT_AVAILABLE, /**< Winbind daemon is not available **/
+ WBC_ERR_DOMAIN_NOT_FOUND, /**< Domain is not trusted or cannot be found **/
+ WBC_ERR_INVALID_RESPONSE, /**< Winbind returned an invalid response **/
+ WBC_ERR_NSS_ERROR, /**< NSS_STATUS error **/
+ WBC_ERR_AUTH_ERROR, /**< Authentication failed **/
+ WBC_ERR_UNKNOWN_USER, /**< User account cannot be found */
+ WBC_ERR_UNKNOWN_GROUP, /**< Group account cannot be found */
+ WBC_ERR_PWD_CHANGE_FAILED /**< Password Change has failed */
+};
+
+typedef enum _wbcErrType wbcErr;
+
+#define WBC_ERROR_IS_OK(x) ((x) == WBC_ERR_SUCCESS)
+
+const char *wbcErrorString(wbcErr error);
+
+/**
+ * @brief Some useful details about the wbclient library
+ *
+ * 0.1: Initial version
+ * 0.2: Added wbcRemoveUidMapping()
+ * Added wbcRemoveGidMapping()
+ * 0.3: Added wbcGetpwsid()
+ * Added wbcGetSidAliases()
+ * 0.4: Added wbcSidTypeString()
+ * 0.5: Added wbcChangeTrustCredentials()
+ * 0.6: Made struct wbcInterfaceDetails char* members non-const
+ * 0.7: Added wbcSidToStringBuf()
+ * 0.8: Added wbcSidsToUnixIds() and wbcLookupSids()
+ * 0.9: Added support for WBC_ID_TYPE_BOTH
+ * 0.10: Added wbcPingDc2()
+ * 0.11: Extended wbcAuthenticateUserEx to provide PAC parsing
+ * 0.12: Added wbcCtxCreate and friends
+ * 0.13: Added wbcCtxUnixIdsToSids and wbcUnixIdsToSids
+ * 0.14: Added "authoritative" to wbcAuthErrorInfo
+ * Added WBC_SID_NAME_LABEL
+ * 0.15: Added wbcSetClientProcessName()
+ **/
+#define WBCLIENT_MAJOR_VERSION 0
+#define WBCLIENT_MINOR_VERSION 15
+#define WBCLIENT_VENDOR_VERSION "Samba libwbclient"
+struct wbcLibraryDetails {
+ uint16_t major_version;
+ uint16_t minor_version;
+ const char *vendor_version;
+};
+
+/**
+ * @brief Some useful details about the running winbindd
+ *
+ **/
+struct wbcInterfaceDetails {
+ uint32_t interface_version;
+ char *winbind_version;
+ char winbind_separator;
+ char *netbios_name;
+ char *netbios_domain;
+ char *dns_domain;
+};
+
+/**
+ * @brief Library context data
+ *
+ **/
+
+struct wbcContext;
+
+/*
+ * Data types used by the Winbind Client API
+ */
+
+#ifndef WBC_MAXSUBAUTHS
+#define WBC_MAXSUBAUTHS 15 /* max sub authorities in a SID */
+#endif
+
+/**
+ * @brief Windows Security Identifier
+ *
+ **/
+
+struct wbcDomainSid {
+ uint8_t sid_rev_num;
+ uint8_t num_auths;
+ uint8_t id_auth[6];
+ uint32_t sub_auths[WBC_MAXSUBAUTHS];
+};
+
+/**
+ * @brief Security Identifier type
+ **/
+
+enum wbcSidType {
+ WBC_SID_NAME_USE_NONE=0,
+ WBC_SID_NAME_USER=1,
+ WBC_SID_NAME_DOM_GRP=2,
+ WBC_SID_NAME_DOMAIN=3,
+ WBC_SID_NAME_ALIAS=4,
+ WBC_SID_NAME_WKN_GRP=5,
+ WBC_SID_NAME_DELETED=6,
+ WBC_SID_NAME_INVALID=7,
+ WBC_SID_NAME_UNKNOWN=8,
+ WBC_SID_NAME_COMPUTER=9,
+ WBC_SID_NAME_LABEL=10
+};
+
+/**
+ * @brief Security Identifier with attributes
+ **/
+
+struct wbcSidWithAttr {
+ struct wbcDomainSid sid;
+ uint32_t attributes;
+};
+
+/* wbcSidWithAttr->attributes */
+
+#define WBC_SID_ATTR_GROUP_MANDATORY 0x00000001
+#define WBC_SID_ATTR_GROUP_ENABLED_BY_DEFAULT 0x00000002
+#define WBC_SID_ATTR_GROUP_ENABLED 0x00000004
+#define WBC_SID_ATTR_GROUP_OWNER 0x00000008
+#define WBC_SID_ATTR_GROUP_USEFOR_DENY_ONLY 0x00000010
+#define WBC_SID_ATTR_GROUP_RESOURCE 0x20000000
+#define WBC_SID_ATTR_GROUP_LOGON_ID 0xC0000000
+
+/**
+ * @brief Windows GUID
+ *
+ **/
+
+struct wbcGuid {
+ uint32_t time_low;
+ uint16_t time_mid;
+ uint16_t time_hi_and_version;
+ uint8_t clock_seq[2];
+ uint8_t node[6];
+};
+
+/**
+ * @brief Domain Information
+ **/
+
+struct wbcDomainInfo {
+ char *short_name;
+ char *dns_name;
+ struct wbcDomainSid sid;
+ uint32_t domain_flags;
+ uint32_t trust_flags;
+ uint32_t trust_type;
+ char *trust_routing;
+};
+
+/* wbcDomainInfo->domain_flags */
+
+#define WBC_DOMINFO_DOMAIN_UNKNOWN 0x00000000
+#define WBC_DOMINFO_DOMAIN_NATIVE 0x00000001
+#define WBC_DOMINFO_DOMAIN_AD 0x00000002
+#define WBC_DOMINFO_DOMAIN_PRIMARY 0x00000004
+#define WBC_DOMINFO_DOMAIN_OFFLINE 0x00000008
+
+/* wbcDomainInfo->trust_flags */
+
+#define WBC_DOMINFO_TRUST_TRANSITIVE 0x00000001
+#define WBC_DOMINFO_TRUST_INCOMING 0x00000002
+#define WBC_DOMINFO_TRUST_OUTGOING 0x00000004
+
+/* wbcDomainInfo->trust_type */
+
+#define WBC_DOMINFO_TRUSTTYPE_NONE 0x00000000
+#define WBC_DOMINFO_TRUSTTYPE_FOREST 0x00000001
+#define WBC_DOMINFO_TRUSTTYPE_IN_FOREST 0x00000002
+#define WBC_DOMINFO_TRUSTTYPE_EXTERNAL 0x00000003
+#define WBC_DOMINFO_TRUSTTYPE_LOCAL 0x00000004
+#define WBC_DOMINFO_TRUSTTYPE_WKSTA 0x00000005
+#define WBC_DOMINFO_TRUSTTYPE_RWDC 0x00000006
+#define WBC_DOMINFO_TRUSTTYPE_RODC 0x00000007
+#define WBC_DOMINFO_TRUSTTYPE_PDC 0x00000008
+
+
+/**
+ * @brief Generic Blob
+ **/
+
+struct wbcBlob {
+ uint8_t *data;
+ size_t length;
+};
+
+/**
+ * @brief Named Blob
+ **/
+
+struct wbcNamedBlob {
+ const char *name;
+ uint32_t flags;
+ struct wbcBlob blob;
+};
+
+/**
+ * @brief Auth User Parameters
+ **/
+
+struct wbcAuthUserParams {
+ const char *account_name;
+ const char *domain_name;
+ const char *workstation_name;
+
+ uint32_t flags;
+
+ uint32_t parameter_control;
+
+ enum wbcAuthUserLevel {
+ WBC_AUTH_USER_LEVEL_PLAIN = 1,
+ WBC_AUTH_USER_LEVEL_HASH = 2,
+ WBC_AUTH_USER_LEVEL_RESPONSE = 3,
+ WBC_AUTH_USER_LEVEL_PAC = 4
+ } level;
+ union {
+ const char *plaintext;
+ struct {
+ uint8_t nt_hash[16];
+ uint8_t lm_hash[16];
+ } hash;
+ struct {
+ uint8_t challenge[8];
+ uint32_t nt_length;
+ uint8_t *nt_data;
+ uint32_t lm_length;
+ uint8_t *lm_data;
+ } response;
+ struct wbcBlob pac;
+ } password;
+};
+
+/**
+ * @brief Logon User Parameters
+ **/
+
+struct wbcLogonUserParams {
+ const char *username;
+ const char *password;
+ size_t num_blobs;
+ struct wbcNamedBlob *blobs;
+};
+
+/**
+ * @brief ChangePassword Parameters
+ **/
+
+struct wbcChangePasswordParams {
+ const char *account_name;
+ const char *domain_name;
+
+ uint32_t flags;
+
+ enum wbcChangePasswordLevel {
+ WBC_CHANGE_PASSWORD_LEVEL_PLAIN = 1,
+ WBC_CHANGE_PASSWORD_LEVEL_RESPONSE = 2
+ } level;
+
+ union {
+ const char *plaintext;
+ struct {
+ uint32_t old_nt_hash_enc_length;
+ uint8_t *old_nt_hash_enc_data;
+ uint32_t old_lm_hash_enc_length;
+ uint8_t *old_lm_hash_enc_data;
+ } response;
+ } old_password;
+ union {
+ const char *plaintext;
+ struct {
+ uint32_t nt_length;
+ uint8_t *nt_data;
+ uint32_t lm_length;
+ uint8_t *lm_data;
+ } response;
+ } new_password;
+};
+
+/* wbcAuthUserParams->parameter_control */
+
+#define WBC_MSV1_0_CLEARTEXT_PASSWORD_ALLOWED 0x00000002
+#define WBC_MSV1_0_UPDATE_LOGON_STATISTICS 0x00000004
+#define WBC_MSV1_0_RETURN_USER_PARAMETERS 0x00000008
+#define WBC_MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT 0x00000020
+#define WBC_MSV1_0_RETURN_PROFILE_PATH 0x00000200
+#define WBC_MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT 0x00000800
+#define WBC_MSV1_0_ALLOW_MSVCHAPV2 0x00010000
+
+/* wbcAuthUserParams->flags */
+
+#define WBC_AUTH_PARAM_FLAGS_INTERACTIVE_LOGON 0x00000001
+
+/**
+ * @brief Auth User Information
+ *
+ * Some of the strings are maybe NULL
+ **/
+
+struct wbcAuthUserInfo {
+ uint32_t user_flags;
+
+ char *account_name;
+ char *user_principal;
+ char *full_name;
+ char *domain_name;
+ char *dns_domain_name;
+
+ uint32_t acct_flags;
+ uint8_t user_session_key[16];
+ uint8_t lm_session_key[8];
+
+ uint16_t logon_count;
+ uint16_t bad_password_count;
+
+ uint64_t logon_time;
+ uint64_t logoff_time;
+ uint64_t kickoff_time;
+ uint64_t pass_last_set_time;
+ uint64_t pass_can_change_time;
+ uint64_t pass_must_change_time;
+
+ char *logon_server;
+ char *logon_script;
+ char *profile_path;
+ char *home_directory;
+ char *home_drive;
+
+ /*
+ * the 1st one is the account sid
+ * the 2nd one is the primary_group sid
+ * followed by the rest of the groups
+ */
+ uint32_t num_sids;
+ struct wbcSidWithAttr *sids;
+};
+
+/**
+ * @brief Logon User Information
+ *
+ * Some of the strings are maybe NULL
+ **/
+
+struct wbcLogonUserInfo {
+ struct wbcAuthUserInfo *info;
+ size_t num_blobs;
+ struct wbcNamedBlob *blobs;
+};
+
+/* wbcAuthUserInfo->user_flags */
+
+#define WBC_AUTH_USER_INFO_GUEST 0x00000001
+#define WBC_AUTH_USER_INFO_NOENCRYPTION 0x00000002
+#define WBC_AUTH_USER_INFO_CACHED_ACCOUNT 0x00000004
+#define WBC_AUTH_USER_INFO_USED_LM_PASSWORD 0x00000008
+#define WBC_AUTH_USER_INFO_EXTRA_SIDS 0x00000020
+#define WBC_AUTH_USER_INFO_SUBAUTH_SESSION_KEY 0x00000040
+#define WBC_AUTH_USER_INFO_SERVER_TRUST_ACCOUNT 0x00000080
+#define WBC_AUTH_USER_INFO_NTLMV2_ENABLED 0x00000100
+#define WBC_AUTH_USER_INFO_RESOURCE_GROUPS 0x00000200
+#define WBC_AUTH_USER_INFO_PROFILE_PATH_RETURNED 0x00000400
+#define WBC_AUTH_USER_INFO_GRACE_LOGON 0x01000000
+
+/* wbcAuthUserInfo->acct_flags */
+
+#define WBC_ACB_DISABLED 0x00000001 /* 1 User account disabled */
+#define WBC_ACB_HOMDIRREQ 0x00000002 /* 1 Home directory required */
+#define WBC_ACB_PWNOTREQ 0x00000004 /* 1 User password not required */
+#define WBC_ACB_TEMPDUP 0x00000008 /* 1 Temporary duplicate account */
+#define WBC_ACB_NORMAL 0x00000010 /* 1 Normal user account */
+#define WBC_ACB_MNS 0x00000020 /* 1 MNS logon user account */
+#define WBC_ACB_DOMTRUST 0x00000040 /* 1 Interdomain trust account */
+#define WBC_ACB_WSTRUST 0x00000080 /* 1 Workstation trust account */
+#define WBC_ACB_SVRTRUST 0x00000100 /* 1 Server trust account */
+#define WBC_ACB_PWNOEXP 0x00000200 /* 1 User password does not expire */
+#define WBC_ACB_AUTOLOCK 0x00000400 /* 1 Account auto locked */
+#define WBC_ACB_ENC_TXT_PWD_ALLOWED 0x00000800 /* 1 Encryped text password is allowed */
+#define WBC_ACB_SMARTCARD_REQUIRED 0x00001000 /* 1 Smart Card required */
+#define WBC_ACB_TRUSTED_FOR_DELEGATION 0x00002000 /* 1 Trusted for Delegation */
+#define WBC_ACB_NOT_DELEGATED 0x00004000 /* 1 Not delegated */
+#define WBC_ACB_USE_DES_KEY_ONLY 0x00008000 /* 1 Use DES key only */
+#define WBC_ACB_DONT_REQUIRE_PREAUTH 0x00010000 /* 1 Preauth not required */
+#define WBC_ACB_PW_EXPIRED 0x00020000 /* 1 Password Expired */
+#define WBC_ACB_NO_AUTH_DATA_REQD 0x00080000 /* 1 = No authorization data required */
+
+struct wbcAuthErrorInfo {
+ uint32_t nt_status;
+ char *nt_string;
+ int32_t pam_error;
+ char *display_string;
+ uint8_t authoritative;
+};
+
+/**
+ * @brief User Password Policy Information
+ **/
+
+/* wbcUserPasswordPolicyInfo->password_properties */
+
+#define WBC_DOMAIN_PASSWORD_COMPLEX 0x00000001
+#define WBC_DOMAIN_PASSWORD_NO_ANON_CHANGE 0x00000002
+#define WBC_DOMAIN_PASSWORD_NO_CLEAR_CHANGE 0x00000004
+#define WBC_DOMAIN_PASSWORD_LOCKOUT_ADMINS 0x00000008
+#define WBC_DOMAIN_PASSWORD_STORE_CLEARTEXT 0x00000010
+#define WBC_DOMAIN_REFUSE_PASSWORD_CHANGE 0x00000020
+
+struct wbcUserPasswordPolicyInfo {
+ uint32_t min_length_password;
+ uint32_t password_history;
+ uint32_t password_properties;
+ uint64_t expire;
+ uint64_t min_passwordage;
+};
+
+/**
+ * @brief Change Password Reject Reason
+ **/
+
+enum wbcPasswordChangeRejectReason {
+ WBC_PWD_CHANGE_NO_ERROR=0,
+ WBC_PWD_CHANGE_PASSWORD_TOO_SHORT=1,
+ WBC_PWD_CHANGE_PWD_IN_HISTORY=2,
+ WBC_PWD_CHANGE_USERNAME_IN_PASSWORD=3,
+ WBC_PWD_CHANGE_FULLNAME_IN_PASSWORD=4,
+ WBC_PWD_CHANGE_NOT_COMPLEX=5,
+ WBC_PWD_CHANGE_MACHINE_NOT_DEFAULT=6,
+ WBC_PWD_CHANGE_FAILED_BY_FILTER=7,
+ WBC_PWD_CHANGE_PASSWORD_TOO_LONG=8
+};
+
+/* Note: this defines exist for compatibility reasons with existing code */
+#define WBC_PWD_CHANGE_REJECT_OTHER WBC_PWD_CHANGE_NO_ERROR
+#define WBC_PWD_CHANGE_REJECT_TOO_SHORT WBC_PWD_CHANGE_PASSWORD_TOO_SHORT
+#define WBC_PWD_CHANGE_REJECT_IN_HISTORY WBC_PWD_CHANGE_PWD_IN_HISTORY
+#define WBC_PWD_CHANGE_REJECT_COMPLEXITY WBC_PWD_CHANGE_NOT_COMPLEX
+
+/**
+ * @brief Logoff User Parameters
+ **/
+
+struct wbcLogoffUserParams {
+ const char *username;
+ size_t num_blobs;
+ struct wbcNamedBlob *blobs;
+};
+
+/** @brief Credential cache log-on parameters
+ *
+ */
+
+struct wbcCredentialCacheParams {
+ const char *account_name;
+ const char *domain_name;
+ enum wbcCredentialCacheLevel {
+ WBC_CREDENTIAL_CACHE_LEVEL_NTLMSSP = 1
+ } level;
+ size_t num_blobs;
+ struct wbcNamedBlob *blobs;
+};
+
+
+/** @brief Info returned by credential cache auth
+ *
+ */
+
+struct wbcCredentialCacheInfo {
+ size_t num_blobs;
+ struct wbcNamedBlob *blobs;
+};
+
+/*
+ * DomainControllerInfo struct
+ */
+struct wbcDomainControllerInfo {
+ char *dc_name;
+};
+
+/*
+ * DomainControllerInfoEx struct
+ */
+struct wbcDomainControllerInfoEx {
+ const char *dc_unc;
+ const char *dc_address;
+ uint16_t dc_address_type;
+ struct wbcGuid *domain_guid;
+ const char *domain_name;
+ const char *forest_name;
+ uint32_t dc_flags;
+ const char *dc_site_name;
+ const char *client_site_name;
+};
+
+/**********************************************************
+ * Memory Management
+ **********************************************************/
+
+/**
+ * @brief Free library allocated memory
+ *
+ * @param * Pointer to free
+ *
+ * @return void
+ **/
+void wbcFreeMemory(void*);
+
+
+/**********************************************************
+ * Context Management
+ **********************************************************/
+
+/**
+ * @brief Create a new wbcContext context
+ *
+ * @return wbcContext
+ **/
+struct wbcContext *wbcCtxCreate(void);
+
+/**
+ * @brief Free a library context
+ *
+ * @param ctx wbcContext to free
+ *
+ * @return void
+ **/
+void wbcCtxFree(struct wbcContext *ctx);
+
+
+
+/*
+ * Utility functions for dealing with SIDs
+ */
+
+/**
+ * @brief Get a string representation of the SID type
+ *
+ * @param type type of the SID
+ *
+ * @return string representation of the SID type
+ */
+const char* wbcSidTypeString(enum wbcSidType type);
+
+#define WBC_SID_STRING_BUFLEN (15*11+25)
+
+/*
+ * @brief Print a sid into a buffer
+ *
+ * @param sid Binary Security Identifier
+ * @param buf Target buffer
+ * @param buflen Target buffer length
+ *
+ * @return Resulting string length.
+ */
+int wbcSidToStringBuf(const struct wbcDomainSid *sid, char *buf, int buflen);
+
+/**
+ * @brief Convert a binary SID to a character string
+ *
+ * @param sid Binary Security Identifier
+ * @param **sid_string Resulting character string
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcSidToString(const struct wbcDomainSid *sid,
+ char **sid_string);
+
+/**
+ * @brief Convert a character string to a binary SID
+ *
+ * @param *sid_string Character string in the form of S-...
+ * @param sid Resulting binary SID
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcStringToSid(const char *sid_string,
+ struct wbcDomainSid *sid);
+
+/*
+ * Utility functions for dealing with GUIDs
+ */
+
+/**
+ * @brief Convert a binary GUID to a character string
+ *
+ * @param guid Binary Guid
+ * @param **guid_string Resulting character string
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcGuidToString(const struct wbcGuid *guid,
+ char **guid_string);
+
+/**
+ * @brief Convert a character string to a binary GUID
+ *
+ * @param *guid_string Character string
+ * @param guid Resulting binary GUID
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcStringToGuid(const char *guid_string,
+ struct wbcGuid *guid);
+
+/**
+ * @brief Ping winbindd to see if the daemon is running
+ *
+ * @param *ctx wbclient Context
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxPing(struct wbcContext *ctx);
+
+/**
+ * @brief Ping winbindd to see if the daemon is running
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcPing(void);
+
+wbcErr wbcLibraryDetails(struct wbcLibraryDetails **details);
+
+wbcErr wbcCtxInterfaceDetails(struct wbcContext *ctx,
+ struct wbcInterfaceDetails **details);
+wbcErr wbcInterfaceDetails(struct wbcInterfaceDetails **details);
+
+/**********************************************************
+ * Name/SID conversion
+ **********************************************************/
+
+/**
+ * @brief Convert a domain and name to SID
+ *
+ * @param *ctx wbclient Context
+ * @param dom_name Domain name (possibly "")
+ * @param name User or group name
+ * @param *sid Pointer to the resolved domain SID
+ * @param *name_type Pointer to the SID type
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxLookupName(struct wbcContext *ctx,
+ const char *dom_name,
+ const char *name,
+ struct wbcDomainSid *sid,
+ enum wbcSidType *name_type);
+
+/**
+ * @brief Convert a domain and name to SID
+ *
+ * @param dom_name Domain name (possibly "")
+ * @param name User or group name
+ * @param *sid Pointer to the resolved domain SID
+ * @param *name_type Pointer to the SID type
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcLookupName(const char *dom_name,
+ const char *name,
+ struct wbcDomainSid *sid,
+ enum wbcSidType *name_type);
+
+/**
+ * @brief Convert a SID to a domain and name
+ *
+ * @param *ctx wbclient Context
+ * @param *sid Pointer to the domain SID to be resolved
+ * @param domain Resolved Domain name (possibly "")
+ * @param name Resolved User or group name
+ * @param *name_type Pointer to the resolved SID type
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxLookupSid(struct wbcContext *ctx,
+ const struct wbcDomainSid *sid,
+ char **domain,
+ char **name,
+ enum wbcSidType *name_type);
+
+/**
+ * @brief Convert a SID to a domain and name
+ *
+ * @param *sid Pointer to the domain SID to be resolved
+ * @param domain Resolved Domain name (possibly "")
+ * @param name Resolved User or group name
+ * @param *name_type Pointer to the resolved SID type
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcLookupSid(const struct wbcDomainSid *sid,
+ char **domain,
+ char **name,
+ enum wbcSidType *name_type);
+
+struct wbcTranslatedName {
+ enum wbcSidType type;
+ char *name;
+ int domain_index;
+};
+
+wbcErr wbcCtxLookupSids(struct wbcContext *ctx,
+ const struct wbcDomainSid *sids, int num_sids,
+ struct wbcDomainInfo **domains, int *num_domains,
+ struct wbcTranslatedName **names);
+
+wbcErr wbcLookupSids(const struct wbcDomainSid *sids, int num_sids,
+ struct wbcDomainInfo **domains, int *num_domains,
+ struct wbcTranslatedName **names);
+
+/**
+ * @brief Translate a collection of RIDs within a domain to names
+ */
+wbcErr wbcCtxLookupRids(struct wbcContext *ctx,
+ struct wbcDomainSid *dom_sid,
+ int num_rids,
+ uint32_t *rids,
+ const char **domain_name,
+ const char ***names,
+ enum wbcSidType **types);
+
+/**
+ * @brief Translate a collection of RIDs within a domain to names
+ */
+wbcErr wbcLookupRids(struct wbcDomainSid *dom_sid,
+ int num_rids,
+ uint32_t *rids,
+ const char **domain_name,
+ const char ***names,
+ enum wbcSidType **types);
+
+/*
+ * @brief Get the groups a user belongs to
+ **/
+wbcErr wbcCtxLookupUserSids(struct wbcContext *ctx,
+ const struct wbcDomainSid *user_sid,
+ bool domain_groups_only,
+ uint32_t *num_sids,
+ struct wbcDomainSid **sids);
+
+/*
+ * @brief Get the groups a user belongs to
+ **/
+wbcErr wbcLookupUserSids(const struct wbcDomainSid *user_sid,
+ bool domain_groups_only,
+ uint32_t *num_sids,
+ struct wbcDomainSid **sids);
+
+/*
+ * @brief Get alias membership for sids
+ **/
+wbcErr wbcCtxGetSidAliases(struct wbcContext *ctx,
+ const struct wbcDomainSid *dom_sid,
+ struct wbcDomainSid *sids,
+ uint32_t num_sids,
+ uint32_t **alias_rids,
+ uint32_t *num_alias_rids);
+
+/*
+ * @brief Get alias membership for sids
+ **/
+wbcErr wbcGetSidAliases(const struct wbcDomainSid *dom_sid,
+ struct wbcDomainSid *sids,
+ uint32_t num_sids,
+ uint32_t **alias_rids,
+ uint32_t *num_alias_rids);
+
+/**
+ * @brief Lists Users
+ **/
+wbcErr wbcCtxListUsers(struct wbcContext *ctx,
+ const char *domain_name,
+ uint32_t *num_users,
+ const char ***users);
+
+/**
+ * @brief Lists Users
+ **/
+wbcErr wbcListUsers(const char *domain_name,
+ uint32_t *num_users,
+ const char ***users);
+
+/**
+ * @brief Lists Groups
+ **/
+wbcErr wbcCtxListGroups(struct wbcContext *ctx,
+ const char *domain_name,
+ uint32_t *num_groups,
+ const char ***groups);
+
+/**
+ * @brief Lists Groups
+ **/
+wbcErr wbcListGroups(const char *domain_name,
+ uint32_t *num_groups,
+ const char ***groups);
+
+wbcErr wbcCtxGetDisplayName(struct wbcContext *ctx,
+ const struct wbcDomainSid *sid,
+ char **pdomain,
+ char **pfullname,
+ enum wbcSidType *pname_type);
+
+wbcErr wbcGetDisplayName(const struct wbcDomainSid *sid,
+ char **pdomain,
+ char **pfullname,
+ enum wbcSidType *pname_type);
+
+/**********************************************************
+ * SID/uid/gid Mappings
+ **********************************************************/
+
+/**
+ * @brief Convert a Windows SID to a Unix uid, allocating an uid if needed
+ *
+ * @param *ctx wbclient Context
+ * @param *sid Pointer to the domain SID to be resolved
+ * @param *puid Pointer to the resolved uid_t value
+ *
+ * @return #wbcErr
+ *
+ **/
+wbcErr wbcCtxSidToUid(struct wbcContext *ctx,
+ const struct wbcDomainSid *sid,
+ uid_t *puid);
+
+/**
+ * @brief Convert a Windows SID to a Unix uid, allocating an uid if needed
+ *
+ * @param *sid Pointer to the domain SID to be resolved
+ * @param *puid Pointer to the resolved uid_t value
+ *
+ * @return #wbcErr
+ *
+ **/
+wbcErr wbcSidToUid(const struct wbcDomainSid *sid,
+ uid_t *puid);
+
+/**
+ * @brief Convert a Windows SID to a Unix uid if there already is a mapping
+ *
+ * @param *sid Pointer to the domain SID to be resolved
+ * @param *puid Pointer to the resolved uid_t value
+ *
+ * @return #wbcErr
+ *
+ **/
+wbcErr wbcQuerySidToUid(const struct wbcDomainSid *sid,
+ uid_t *puid);
+
+/**
+ * @brief Convert a Unix uid to a Windows SID, allocating a SID if needed
+ *
+ * @param *ctx wbclient Context
+ * @param uid Unix uid to be resolved
+ * @param *sid Pointer to the resolved domain SID
+ *
+ * @return #wbcErr
+ *
+ **/
+wbcErr wbcCtxUidToSid(struct wbcContext *ctx, uid_t uid,
+ struct wbcDomainSid *sid);
+
+/**
+ * @brief Convert a Unix uid to a Windows SID, allocating a SID if needed
+ *
+ * @param uid Unix uid to be resolved
+ * @param *sid Pointer to the resolved domain SID
+ *
+ * @return #wbcErr
+ *
+ **/
+wbcErr wbcUidToSid(uid_t uid,
+ struct wbcDomainSid *sid);
+
+/**
+ * @brief Convert a Unix uid to a Windows SID if there already is a mapping
+ *
+ * @param uid Unix uid to be resolved
+ * @param *sid Pointer to the resolved domain SID
+ *
+ * @return #wbcErr
+ *
+ **/
+wbcErr wbcQueryUidToSid(uid_t uid,
+ struct wbcDomainSid *sid);
+
+/**
+ * @brief Convert a Windows SID to a Unix gid, allocating a gid if needed
+ *
+ * @param *ctx wbclient Context
+ * @param *sid Pointer to the domain SID to be resolved
+ * @param *pgid Pointer to the resolved gid_t value
+ *
+ * @return #wbcErr
+ *
+ **/
+wbcErr wbcCtxSidToGid(struct wbcContext *ctx,
+ const struct wbcDomainSid *sid,
+ gid_t *pgid);
+
+/**
+ * @brief Convert a Windows SID to a Unix gid, allocating a gid if needed
+ *
+ * @param *sid Pointer to the domain SID to be resolved
+ * @param *pgid Pointer to the resolved gid_t value
+ *
+ * @return #wbcErr
+ *
+ **/
+wbcErr wbcSidToGid(const struct wbcDomainSid *sid,
+ gid_t *pgid);
+
+/**
+ * @brief Convert a Windows SID to a Unix gid if there already is a mapping
+ *
+ * @param *sid Pointer to the domain SID to be resolved
+ * @param *pgid Pointer to the resolved gid_t value
+ *
+ * @return #wbcErr
+ *
+ **/
+wbcErr wbcQuerySidToGid(const struct wbcDomainSid *sid,
+ gid_t *pgid);
+
+/**
+ * @brief Convert a Unix gid to a Windows SID, allocating a SID if needed
+ *
+ * @param *ctx wbclient Context
+ * @param gid Unix gid to be resolved
+ * @param *sid Pointer to the resolved domain SID
+ *
+ * @return #wbcErr
+ *
+ **/
+wbcErr wbcCtxGidToSid(struct wbcContext *ctx, gid_t gid,
+ struct wbcDomainSid *sid);
+
+/**
+ * @brief Convert a Unix gid to a Windows SID, allocating a SID if needed
+ *
+ * @param gid Unix gid to be resolved
+ * @param *sid Pointer to the resolved domain SID
+ *
+ * @return #wbcErr
+ *
+ **/
+wbcErr wbcGidToSid(gid_t gid,
+ struct wbcDomainSid *sid);
+
+/**
+ * @brief Convert a Unix gid to a Windows SID if there already is a mapping
+ *
+ * @param gid Unix gid to be resolved
+ * @param *sid Pointer to the resolved domain SID
+ *
+ * @return #wbcErr
+ *
+ **/
+wbcErr wbcQueryGidToSid(gid_t gid,
+ struct wbcDomainSid *sid);
+
+enum wbcIdType {
+ WBC_ID_TYPE_NOT_SPECIFIED,
+ WBC_ID_TYPE_UID,
+ WBC_ID_TYPE_GID,
+ WBC_ID_TYPE_BOTH
+};
+
+union wbcUnixIdContainer {
+ uid_t uid;
+ gid_t gid;
+};
+
+struct wbcUnixId {
+ enum wbcIdType type;
+ union wbcUnixIdContainer id;
+};
+
+/**
+ * @brief Convert a list of sids to unix ids
+ *
+ * @param *ctx wbclient Context
+ * @param sids Pointer to an array of SIDs to convert
+ * @param num_sids Number of SIDs
+ * @param ids Preallocated output array for translated IDs
+ *
+ * @return #wbcErr
+ *
+ **/
+wbcErr wbcCtxSidsToUnixIds(struct wbcContext *ctx,
+ const struct wbcDomainSid *sids, uint32_t num_sids,
+ struct wbcUnixId *ids);
+
+/**
+ * @brief Convert a list of sids to unix ids
+ *
+ * @param sids Pointer to an array of SIDs to convert
+ * @param num_sids Number of SIDs
+ * @param ids Preallocated output array for translated IDs
+ *
+ * @return #wbcErr
+ *
+ **/
+wbcErr wbcSidsToUnixIds(const struct wbcDomainSid *sids, uint32_t num_sids,
+ struct wbcUnixId *ids);
+
+wbcErr wbcCtxUnixIdsToSids(struct wbcContext *ctx,
+ const struct wbcUnixId *ids, uint32_t num_ids,
+ struct wbcDomainSid *sids);
+wbcErr wbcUnixIdsToSids(const struct wbcUnixId *ids, uint32_t num_ids,
+ struct wbcDomainSid *sids);
+
+/**
+ * @brief Obtain a new uid from Winbind
+ *
+ * @param *ctx wbclient Context
+ * @param *puid Pointer to the allocated uid
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxAllocateUid(struct wbcContext *ctx, uid_t *puid);
+
+/**
+ * @brief Obtain a new uid from Winbind
+ *
+ * @param *puid Pointer to the allocated uid
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcAllocateUid(uid_t *puid);
+
+/**
+ * @brief Obtain a new gid from Winbind
+ *
+ * @param *ctx wbclient Context
+ * @param *pgid Pointer to the allocated gid
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxAllocateGid(struct wbcContext *ctx, gid_t *pgid);
+
+/**
+ * @brief Obtain a new gid from Winbind
+ *
+ * @param *pgid Pointer to the allocated gid
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcAllocateGid(gid_t *pgid);
+
+/**
+ * @brief Set an user id mapping
+ *
+ * @param uid Uid of the desired mapping.
+ * @param *sid Pointer to the sid of the desired mapping.
+ *
+ * @return #wbcErr
+ *
+ * @deprecated This method is not impemented any more and should
+ * be removed in the next major version change.
+ **/
+wbcErr wbcSetUidMapping(uid_t uid, const struct wbcDomainSid *sid);
+
+/**
+ * @brief Set a group id mapping
+ *
+ * @param gid Gid of the desired mapping.
+ * @param *sid Pointer to the sid of the desired mapping.
+ *
+ * @return #wbcErr
+ *
+ * @deprecated This method is not impemented any more and should
+ * be removed in the next major version change.
+ **/
+wbcErr wbcSetGidMapping(gid_t gid, const struct wbcDomainSid *sid);
+
+/**
+ * @brief Remove a user id mapping
+ *
+ * @param uid Uid of the mapping to remove.
+ * @param *sid Pointer to the sid of the mapping to remove.
+ *
+ * @return #wbcErr
+ *
+ * @deprecated This method is not impemented any more and should
+ * be removed in the next major version change.
+ **/
+wbcErr wbcRemoveUidMapping(uid_t uid, const struct wbcDomainSid *sid);
+
+/**
+ * @brief Remove a group id mapping
+ *
+ * @param gid Gid of the mapping to remove.
+ * @param *sid Pointer to the sid of the mapping to remove.
+ *
+ * @return #wbcErr
+ *
+ * @deprecated This method is not impemented any more and should
+ * be removed in the next major version change.
+ **/
+wbcErr wbcRemoveGidMapping(gid_t gid, const struct wbcDomainSid *sid);
+
+/**
+ * @brief Set the highwater mark for allocated uids.
+ *
+ * @param uid_hwm The new uid highwater mark value
+ *
+ * @return #wbcErr
+ *
+ * @deprecated This method is not impemented any more and should
+ * be removed in the next major version change.
+ **/
+wbcErr wbcSetUidHwm(uid_t uid_hwm);
+
+/**
+ * @brief Set the highwater mark for allocated gids.
+ *
+ * @param gid_hwm The new gid highwater mark value
+ *
+ * @return #wbcErr
+ *
+ * @deprecated This method is not impemented any more and should
+ * be removed in the next major version change.
+ **/
+wbcErr wbcSetGidHwm(gid_t gid_hwm);
+
+/**********************************************************
+ * NSS Lookup User/Group details
+ **********************************************************/
+
+/**
+ * @brief Fill in a struct passwd* for a domain user based
+ * on username
+ *
+ * @param *ctx wbclient Context
+ * @param *name Username to lookup
+ * @param **pwd Pointer to resulting struct passwd* from the query.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxGetpwnam(struct wbcContext *ctx,
+ const char *name, struct passwd **pwd);
+
+/**
+ * @brief Fill in a struct passwd* for a domain user based
+ * on username
+ *
+ * @param *name Username to lookup
+ * @param **pwd Pointer to resulting struct passwd* from the query.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcGetpwnam(const char *name, struct passwd **pwd);
+
+/**
+ * @brief Fill in a struct passwd* for a domain user based
+ * on uid
+ *
+ * @param *ctx wbclient Context
+ * @param uid Uid to lookup
+ * @param **pwd Pointer to resulting struct passwd* from the query.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxGetpwuid(struct wbcContext *ctx,
+ uid_t uid, struct passwd **pwd);
+
+/**
+ * @brief Fill in a struct passwd* for a domain user based
+ * on uid
+ *
+ * @param uid Uid to lookup
+ * @param **pwd Pointer to resulting struct passwd* from the query.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcGetpwuid(uid_t uid, struct passwd **pwd);
+
+/**
+ * @brief Fill in a struct passwd* for a domain user based
+ * on sid
+ *
+ * @param *ctx wbclient Context
+ * @param sid Sid to lookup
+ * @param **pwd Pointer to resulting struct passwd* from the query.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxGetpwsid(struct wbcContext *ctx,
+ struct wbcDomainSid * sid, struct passwd **pwd);
+
+/**
+ * @brief Fill in a struct passwd* for a domain user based
+ * on sid
+ *
+ * @param sid Sid to lookup
+ * @param **pwd Pointer to resulting struct passwd* from the query.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcGetpwsid(struct wbcDomainSid * sid, struct passwd **pwd);
+
+/**
+ * @brief Fill in a struct passwd* for a domain user based
+ * on username
+ *
+ * @param *ctx wbclient Context
+ * @param *name Username to lookup
+ * @param **grp Pointer to resulting struct group* from the query.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxGetgrnam(struct wbcContext *ctx,
+ const char *name, struct group **grp);
+
+/**
+ * @brief Fill in a struct passwd* for a domain user based
+ * on username
+ *
+ * @param *name Username to lookup
+ * @param **grp Pointer to resulting struct group* from the query.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcGetgrnam(const char *name, struct group **grp);
+
+/**
+ * @brief Fill in a struct passwd* for a domain user based
+ * on uid
+ *
+ * @param *ctx wbclient Context
+ * @param gid Uid to lookup
+ * @param **grp Pointer to resulting struct group* from the query.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxGetgrgid(struct wbcContext *ctx,
+ gid_t gid, struct group **grp);
+
+/**
+ * @brief Fill in a struct passwd* for a domain user based
+ * on uid
+ *
+ * @param gid Uid to lookup
+ * @param **grp Pointer to resulting struct group* from the query.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcGetgrgid(gid_t gid, struct group **grp);
+
+/**
+ * @brief Reset the passwd iterator
+ *
+ * @param *ctx wbclient Context
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxSetpwent(struct wbcContext *ctx);
+
+/**
+ * @brief Reset the passwd iterator
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcSetpwent(void);
+
+/**
+ * @brief Close the passwd iterator
+ *
+ * @param *ctx wbclient Context
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxEndpwent(struct wbcContext *ctx);
+
+/**
+ * @brief Close the passwd iterator
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcEndpwent(void);
+
+/**
+ * @brief Return the next struct passwd* entry from the pwent iterator
+ *
+ * @param *ctx wbclient Context
+ * @param **pwd Pointer to resulting struct passwd* from the query.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxGetpwent(struct wbcContext *ctx, struct passwd **pwd);
+
+/**
+ * @brief Return the next struct passwd* entry from the pwent iterator
+ *
+ * @param **pwd Pointer to resulting struct passwd* from the query.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcGetpwent(struct passwd **pwd);
+
+/**
+ * @brief Reset the group iterator
+ *
+ * @param *ctx wbclient Context
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxSetgrent(struct wbcContext *ctx);
+
+/**
+ * @brief Reset the group iterator
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcSetgrent(void);
+
+/**
+ * @brief Close the group iterator
+ *
+ * @param *ctx wbclient Context
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxEndgrent(struct wbcContext *ctx);
+
+/**
+ * @brief Close the group iterator
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcEndgrent(void);
+
+/**
+ * @brief Return the next struct group* entry from the pwent iterator
+ *
+ * @param *ctx wbclient Context
+ * @param **grp Pointer to resulting struct group* from the query.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxGetgrent(struct wbcContext *ctx, struct group **grp);
+
+/**
+ * @brief Return the next struct group* entry from the pwent iterator
+ *
+ * @param **grp Pointer to resulting struct group* from the query.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcGetgrent(struct group **grp);
+
+/**
+ * @brief Return the next struct group* entry from the pwent iterator
+ *
+ * This is similar to #wbcGetgrent, just that the member list is empty
+ *
+ * @param *ctx wbclient Context
+ * @param **grp Pointer to resulting struct group* from the query.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxGetgrlist(struct wbcContext *ctx, struct group **grp);
+
+/**
+ * @brief Return the next struct group* entry from the pwent iterator
+ *
+ * This is similar to #wbcGetgrent, just that the member list is empty
+ *
+ * @param **grp Pointer to resulting struct group* from the query.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcGetgrlist(struct group **grp);
+
+/**
+ * @brief Return the unix group array belonging to the given user
+ *
+ * @param *ctx wbclient Context
+ * @param *account The given user name
+ * @param *num_groups Number of elements returned in the groups array
+ * @param **_groups Pointer to resulting gid_t array.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxGetGroups(struct wbcContext *ctx,
+ const char *account,
+ uint32_t *num_groups,
+ gid_t **_groups);
+
+/**
+ * @brief Return the unix group array belonging to the given user
+ *
+ * @param *account The given user name
+ * @param *num_groups Number of elements returned in the groups array
+ * @param **_groups Pointer to resulting gid_t array.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcGetGroups(const char *account,
+ uint32_t *num_groups,
+ gid_t **_groups);
+
+
+/**********************************************************
+ * Lookup Domain information
+ **********************************************************/
+
+/**
+ * @brief Lookup the current status of a trusted domain
+ *
+ * @param *ctx wbclient Context
+ * @param domain The domain to query
+ *
+ * @param dinfo A pointer to store the returned domain_info struct.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxDomainInfo(struct wbcContext *ctx,
+ const char *domain,
+ struct wbcDomainInfo **dinfo);
+
+/**
+ * @brief Lookup the current status of a trusted domain
+ *
+ * @param domain The domain to query
+ *
+ * @param dinfo A pointer to store the returned domain_info struct.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcDomainInfo(const char *domain,
+ struct wbcDomainInfo **dinfo);
+
+/**
+ * @brief Lookup the currently contacted DCs
+ *
+ * @param *ctx wbclient Context
+ * @param domain The domain to query
+ *
+ * @param num_dcs Number of DCs currently known
+ * @param dc_names Names of the currently known DCs
+ * @param dc_ips IP addresses of the currently known DCs
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxDcInfo(struct wbcContext *ctx,
+ const char *domain, size_t *num_dcs,
+ const char ***dc_names, const char ***dc_ips);
+
+/**
+ * @brief Lookup the currently contacted DCs
+ *
+ * @param domain The domain to query
+ *
+ * @param num_dcs Number of DCs currently known
+ * @param dc_names Names of the currently known DCs
+ * @param dc_ips IP addresses of the currently known DCs
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcDcInfo(const char *domain, size_t *num_dcs,
+ const char ***dc_names, const char ***dc_ips);
+
+/**
+ * @brief Enumerate the domain trusts known by Winbind
+ *
+ * @param *ctx wbclient Context
+ * @param **domains Pointer to the allocated domain list array
+ * @param *num_domains Pointer to number of domains returned
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxListTrusts(struct wbcContext *ctx,
+ struct wbcDomainInfo **domains,
+ size_t *num_domains);
+
+/**
+ * @brief Enumerate the domain trusts known by Winbind
+ *
+ * @param **domains Pointer to the allocated domain list array
+ * @param *num_domains Pointer to number of domains returned
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcListTrusts(struct wbcDomainInfo **domains,
+ size_t *num_domains);
+
+/* Flags for wbcLookupDomainController */
+
+#define WBC_LOOKUP_DC_FORCE_REDISCOVERY 0x00000001
+#define WBC_LOOKUP_DC_DS_REQUIRED 0x00000010
+#define WBC_LOOKUP_DC_DS_PREFERRED 0x00000020
+#define WBC_LOOKUP_DC_GC_SERVER_REQUIRED 0x00000040
+#define WBC_LOOKUP_DC_PDC_REQUIRED 0x00000080
+#define WBC_LOOKUP_DC_BACKGROUND_ONLY 0x00000100
+#define WBC_LOOKUP_DC_IP_REQUIRED 0x00000200
+#define WBC_LOOKUP_DC_KDC_REQUIRED 0x00000400
+#define WBC_LOOKUP_DC_TIMESERV_REQUIRED 0x00000800
+#define WBC_LOOKUP_DC_WRITABLE_REQUIRED 0x00001000
+#define WBC_LOOKUP_DC_GOOD_TIMESERV_PREFERRED 0x00002000
+#define WBC_LOOKUP_DC_AVOID_SELF 0x00004000
+#define WBC_LOOKUP_DC_ONLY_LDAP_NEEDED 0x00008000
+#define WBC_LOOKUP_DC_IS_FLAT_NAME 0x00010000
+#define WBC_LOOKUP_DC_IS_DNS_NAME 0x00020000
+#define WBC_LOOKUP_DC_TRY_NEXTCLOSEST_SITE 0x00040000
+#define WBC_LOOKUP_DC_DS_6_REQUIRED 0x00080000
+#define WBC_LOOKUP_DC_RETURN_DNS_NAME 0x40000000
+#define WBC_LOOKUP_DC_RETURN_FLAT_NAME 0x80000000
+
+/**
+ * @brief Enumerate the domain trusts known by Winbind
+ *
+ * @param *ctx wbclient Context
+ * @param domain Name of the domain to query for a DC
+ * @param flags Bit flags used to control the domain location query
+ * @param *dc_info Pointer to the returned domain controller information
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxLookupDomainController(struct wbcContext *ctx,
+ const char *domain,
+ uint32_t flags,
+ struct wbcDomainControllerInfo **dc_info);
+
+/**
+ * @brief Enumerate the domain trusts known by Winbind
+ *
+ * @param domain Name of the domain to query for a DC
+ * @param flags Bit flags used to control the domain location query
+ * @param *dc_info Pointer to the returned domain controller information
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcLookupDomainController(const char *domain,
+ uint32_t flags,
+ struct wbcDomainControllerInfo **dc_info);
+
+/**
+ * @brief Get extended domain controller information
+ *
+ * @param *ctx wbclient Context
+ * @param domain Name of the domain to query for a DC
+ * @param guid Guid of the domain to query for a DC
+ * @param site Site of the domain to query for a DC
+ * @param flags Bit flags used to control the domain location query
+ * @param *dc_info Pointer to the returned extended domain controller information
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxLookupDomainControllerEx(struct wbcContext *ctx,
+ const char *domain,
+ struct wbcGuid *guid,
+ const char *site,
+ uint32_t flags,
+ struct wbcDomainControllerInfoEx **dc_info);
+
+/**
+ * @brief Get extended domain controller information
+ *
+ * @param domain Name of the domain to query for a DC
+ * @param guid Guid of the domain to query for a DC
+ * @param site Site of the domain to query for a DC
+ * @param flags Bit flags used to control the domain location query
+ * @param *dc_info Pointer to the returned extended domain controller information
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcLookupDomainControllerEx(const char *domain,
+ struct wbcGuid *guid,
+ const char *site,
+ uint32_t flags,
+ struct wbcDomainControllerInfoEx **dc_info);
+
+/**********************************************************
+ * Athenticate functions
+ **********************************************************/
+
+/**
+ * @brief Authenticate a username/password pair
+ *
+ * @param *ctx wbclient Context
+ * @param username Name of user to authenticate
+ * @param password Clear text password os user
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxAuthenticateUser(struct wbcContext *ctx,
+ const char *username,
+ const char *password);
+
+/**
+ * @brief Authenticate a username/password pair
+ *
+ * @param username Name of user to authenticate
+ * @param password Clear text password os user
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcAuthenticateUser(const char *username,
+ const char *password);
+
+/**
+ * @brief Authenticate with more detailed information
+ *
+ * @param *ctx wbclient Context
+ * @param params Input parameters, WBC_AUTH_USER_LEVEL_HASH
+ * is not supported yet
+ * @param info Output details on WBC_ERR_SUCCESS
+ * @param error Output details on WBC_ERR_AUTH_ERROR
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxAuthenticateUserEx(struct wbcContext *ctx,
+ const struct wbcAuthUserParams *params,
+ struct wbcAuthUserInfo **info,
+ struct wbcAuthErrorInfo **error);
+
+/**
+ * @brief Authenticate with more detailed information
+ *
+ * @param params Input parameters, WBC_AUTH_USER_LEVEL_HASH
+ * is not supported yet
+ * @param info Output details on WBC_ERR_SUCCESS
+ * @param error Output details on WBC_ERR_AUTH_ERROR
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcAuthenticateUserEx(const struct wbcAuthUserParams *params,
+ struct wbcAuthUserInfo **info,
+ struct wbcAuthErrorInfo **error);
+
+/**
+ * @brief Logon a User
+ *
+ * @param[in] *ctx wbclient Context
+ * @param[in] params Pointer to a wbcLogonUserParams structure
+ * @param[out] info Pointer to a pointer to a wbcLogonUserInfo structure
+ * @param[out] error Pointer to a pointer to a wbcAuthErrorInfo structure
+ * @param[out] policy Pointer to a pointer to a wbcUserPasswordPolicyInfo structure
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxLogonUser(struct wbcContext *ctx,
+ const struct wbcLogonUserParams *params,
+ struct wbcLogonUserInfo **info,
+ struct wbcAuthErrorInfo **error,
+ struct wbcUserPasswordPolicyInfo **policy);
+
+/**
+ * @brief Logon a User
+ *
+ * @param[in] params Pointer to a wbcLogonUserParams structure
+ * @param[out] info Pointer to a pointer to a wbcLogonUserInfo structure
+ * @param[out] error Pointer to a pointer to a wbcAuthErrorInfo structure
+ * @param[out] policy Pointer to a pointer to a wbcUserPasswordPolicyInfo structure
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcLogonUser(const struct wbcLogonUserParams *params,
+ struct wbcLogonUserInfo **info,
+ struct wbcAuthErrorInfo **error,
+ struct wbcUserPasswordPolicyInfo **policy);
+
+/**
+ * @brief Trigger a logoff notification to Winbind for a specific user
+ *
+ * @param *ctx wbclient Context
+ * @param username Name of user to remove from Winbind's list of
+ * logged on users.
+ * @param uid Uid assigned to the username
+ * @param ccfilename Absolute path to the Krb5 credentials cache to
+ * be removed
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxLogoffUser(struct wbcContext *ctx,
+ const char *username, uid_t uid,
+ const char *ccfilename);
+
+/**
+ * @brief Trigger a logoff notification to Winbind for a specific user
+ *
+ * @param username Name of user to remove from Winbind's list of
+ * logged on users.
+ * @param uid Uid assigned to the username
+ * @param ccfilename Absolute path to the Krb5 credentials cache to
+ * be removed
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcLogoffUser(const char *username,
+ uid_t uid,
+ const char *ccfilename);
+
+/**
+ * @brief Trigger an extended logoff notification to Winbind for a specific user
+ *
+ * @param *ctx wbclient Context
+ * @param params A wbcLogoffUserParams structure
+ * @param error User output details on error
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxLogoffUserEx(struct wbcContext *ctx,
+ const struct wbcLogoffUserParams *params,
+ struct wbcAuthErrorInfo **error);
+
+/**
+ * @brief Trigger an extended logoff notification to Winbind for a specific user
+ *
+ * @param params A wbcLogoffUserParams structure
+ * @param error User output details on error
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcLogoffUserEx(const struct wbcLogoffUserParams *params,
+ struct wbcAuthErrorInfo **error);
+
+/**
+ * @brief Change a password for a user
+ *
+ * @param *ctx wbclient Context
+ * @param username Name of user to authenticate
+ * @param old_password Old clear text password of user
+ * @param new_password New clear text password of user
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxChangeUserPassword(struct wbcContext *ctx,
+ const char *username,
+ const char *old_password,
+ const char *new_password);
+
+/**
+ * @brief Change a password for a user
+ *
+ * @param username Name of user to authenticate
+ * @param old_password Old clear text password of user
+ * @param new_password New clear text password of user
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcChangeUserPassword(const char *username,
+ const char *old_password,
+ const char *new_password);
+
+/**
+ * @brief Change a password for a user with more detailed information upon
+ * failure
+ *
+ * @param *ctx wbclient Context
+ * @param params Input parameters
+ * @param error User output details on WBC_ERR_PWD_CHANGE_FAILED
+ * @param reject_reason New password reject reason on WBC_ERR_PWD_CHANGE_FAILED
+ * @param policy Password policy output details on WBC_ERR_PWD_CHANGE_FAILED
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxChangeUserPasswordEx(struct wbcContext *ctx,
+ const struct wbcChangePasswordParams *params,
+ struct wbcAuthErrorInfo **error,
+ enum wbcPasswordChangeRejectReason *reject_reason,
+ struct wbcUserPasswordPolicyInfo **policy);
+
+/**
+ * @brief Change a password for a user with more detailed information upon
+ * failure
+ *
+ * @param params Input parameters
+ * @param error User output details on WBC_ERR_PWD_CHANGE_FAILED
+ * @param reject_reason New password reject reason on WBC_ERR_PWD_CHANGE_FAILED
+ * @param policy Password policy output details on WBC_ERR_PWD_CHANGE_FAILED
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcChangeUserPasswordEx(const struct wbcChangePasswordParams *params,
+ struct wbcAuthErrorInfo **error,
+ enum wbcPasswordChangeRejectReason *reject_reason,
+ struct wbcUserPasswordPolicyInfo **policy);
+
+/**
+ * @brief Authenticate a user with cached credentials
+ *
+ * @param *ctx wbclient Context
+ * @param *params Pointer to a wbcCredentialCacheParams structure
+ * @param **info Pointer to a pointer to a wbcCredentialCacheInfo structure
+ * @param **error Pointer to a pointer to a wbcAuthErrorInfo structure
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxCredentialCache(struct wbcContext *ctx,
+ struct wbcCredentialCacheParams *params,
+ struct wbcCredentialCacheInfo **info,
+ struct wbcAuthErrorInfo **error);
+
+/**
+ * @brief Authenticate a user with cached credentials
+ *
+ * @param *params Pointer to a wbcCredentialCacheParams structure
+ * @param **info Pointer to a pointer to a wbcCredentialCacheInfo structure
+ * @param **error Pointer to a pointer to a wbcAuthErrorInfo structure
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCredentialCache(struct wbcCredentialCacheParams *params,
+ struct wbcCredentialCacheInfo **info,
+ struct wbcAuthErrorInfo **error);
+
+/**
+ * @brief Save a password with winbind for doing wbcCredentialCache() later
+ *
+ * @param *ctx wbclient Context
+ * @param *user Username
+ * @param *password Password
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxCredentialSave(struct wbcContext *ctx,
+ const char *user, const char *password);
+
+/**
+ * @brief Save a password with winbind for doing wbcCredentialCache() later
+ *
+ * @param *user Username
+ * @param *password Password
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCredentialSave(const char *user, const char *password);
+
+/**********************************************************
+ * Resolve functions
+ **********************************************************/
+
+/**
+ * @brief Resolve a NetbiosName via WINS
+ *
+ * @param *ctx wbclient Context
+ * @param name Name to resolve
+ * @param *ip Pointer to the ip address string
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxResolveWinsByName(struct wbcContext *ctx,
+ const char *name, char **ip);
+
+/**
+ * @brief Resolve a NetbiosName via WINS
+ *
+ * @param name Name to resolve
+ * @param *ip Pointer to the ip address string
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcResolveWinsByName(const char *name, char **ip);
+
+/**
+ * @brief Resolve an IP address via WINS into a NetbiosName
+ *
+ * @param *ctx wbclient Context
+ * @param ip The ip address string
+ * @param *name Pointer to the name
+ *
+ * @return #wbcErr
+ *
+ **/
+wbcErr wbcCtxResolveWinsByIP(struct wbcContext *ctx,
+ const char *ip, char **name);
+
+/**
+ * @brief Resolve an IP address via WINS into a NetbiosName
+ *
+ * @param ip The ip address string
+ * @param *name Pointer to the name
+ *
+ * @return #wbcErr
+ *
+ **/
+wbcErr wbcResolveWinsByIP(const char *ip, char **name);
+
+/**********************************************************
+ * Trusted domain functions
+ **********************************************************/
+
+/**
+ * @brief Trigger a verification of the trust credentials of a specific domain
+ *
+ * @param *ctx wbclient Context
+ * @param *domain The name of the domain.
+ * @param error Output details on WBC_ERR_AUTH_ERROR
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxCheckTrustCredentials(struct wbcContext *ctx, const char *domain,
+ struct wbcAuthErrorInfo **error);
+
+/**
+ * @brief Trigger a verification of the trust credentials of a specific domain
+ *
+ * @param *domain The name of the domain.
+ * @param error Output details on WBC_ERR_AUTH_ERROR
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCheckTrustCredentials(const char *domain,
+ struct wbcAuthErrorInfo **error);
+
+/**
+ * @brief Trigger a change of the trust credentials for a specific domain
+ *
+ * @param *ctx wbclient Context
+ * @param *domain The name of the domain.
+ * @param error Output details on WBC_ERR_AUTH_ERROR
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxChangeTrustCredentials(struct wbcContext *ctx, const char *domain,
+ struct wbcAuthErrorInfo **error);
+
+/**
+ * @brief Trigger a change of the trust credentials for a specific domain
+ *
+ * @param *domain The name of the domain.
+ * @param error Output details on WBC_ERR_AUTH_ERROR
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcChangeTrustCredentials(const char *domain,
+ struct wbcAuthErrorInfo **error);
+
+/**
+ * @brief Trigger a no-op call through the NETLOGON pipe. Low-cost
+ * version of wbcCheckTrustCredentials
+ *
+ * @param *ctx wbclient Context
+ * @param *domain The name of the domain, only NULL for the default domain is
+ * supported yet. Other values than NULL will result in
+ * WBC_ERR_NOT_IMPLEMENTED.
+ * @param error Output details on WBC_ERR_AUTH_ERROR
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxPingDc(struct wbcContext *ctx, const char *domain,
+ struct wbcAuthErrorInfo **error);
+
+/**
+ * @brief Trigger a no-op call through the NETLOGON pipe. Low-cost
+ * version of wbcCheckTrustCredentials
+ *
+ * @param *domain The name of the domain, only NULL for the default domain is
+ * supported yet. Other values than NULL will result in
+ * WBC_ERR_NOT_IMPLEMENTED.
+ * @param error Output details on WBC_ERR_AUTH_ERROR
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcPingDc(const char *domain, struct wbcAuthErrorInfo **error);
+
+/**
+ * @brief Trigger a no-op call through the NETLOGON pipe. Low-cost
+ * version of wbcCheckTrustCredentials
+ *
+ * @param *ctx wbclient Context
+ * @param *domain The name of the domain, only NULL for the default domain is
+ * supported yet. Other values than NULL will result in
+ * WBC_ERR_NOT_IMPLEMENTED.
+ * @param error Output details on WBC_ERR_AUTH_ERROR
+ * @param dcname DC that was attempted to ping
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCtxPingDc2(struct wbcContext *ctx, const char *domain,
+ struct wbcAuthErrorInfo **error,
+ char **dcname);
+
+/**
+ * @brief Trigger a no-op call through the NETLOGON pipe. Low-cost
+ * version of wbcCheckTrustCredentials
+ *
+ * @param *domain The name of the domain, only NULL for the default domain is
+ * supported yet. Other values than NULL will result in
+ * WBC_ERR_NOT_IMPLEMENTED.
+ * @param error Output details on WBC_ERR_AUTH_ERROR
+ * @param dcname DC that was attempted to ping
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcPingDc2(const char *domain, struct wbcAuthErrorInfo **error,
+ char **dcname);
+
+/**********************************************************
+ * Helper functions
+ **********************************************************/
+
+/**
+ * @brief Initialize a named blob and add to list of blobs
+ *
+ * @param[in,out] num_blobs Pointer to the number of blobs
+ * @param[in,out] blobs Pointer to an array of blobs
+ * @param[in] name Name of the new named blob
+ * @param[in] flags Flags of the new named blob
+ * @param[in] data Blob data of new blob
+ * @param[in] length Blob data length of new blob
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcAddNamedBlob(size_t *num_blobs,
+ struct wbcNamedBlob **blobs,
+ const char *name,
+ uint32_t flags,
+ uint8_t *data,
+ size_t length);
+
+/**
+ * @brief Set the name of the process which call wbclient.
+ *
+ * By default wbclient will figure out the process name. This should just be
+ * used in special cases like pam modules or similar. Only alpha numeric
+ * chars in ASCII are allowed.
+ *
+ * This function should only be called once!
+ *
+ * @param[in] name The process name to set.
+ */
+void wbcSetClientProcessName(const char *name);
+
+#endif /* _WBCLIENT_H */
diff --git a/nsswitch/libwbclient/wbclient.pc.in b/nsswitch/libwbclient/wbclient.pc.in
new file mode 100644
index 0000000..c7b199b
--- /dev/null
+++ b/nsswitch/libwbclient/wbclient.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+modulesdir=${prefix}/modules/gensec
+
+Name: wbclient
+Description: Winbind client
+Version: @PACKAGE_VERSION@
+Libs: @LIB_RPATH@ -L${libdir} -lwbclient
+Cflags: -I${includedir} -DHAVE_IMMEDIATE_STRUCTURES=1
diff --git a/nsswitch/libwbclient/wbclient_internal.h b/nsswitch/libwbclient/wbclient_internal.h
new file mode 100644
index 0000000..6d815c0
--- /dev/null
+++ b/nsswitch/libwbclient/wbclient_internal.h
@@ -0,0 +1,50 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind client API
+
+ Copyright (C) Gerald (Jerry) Carter 2007
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _WBCLIENT_INTERNAL_H
+#define _WBCLIENT_INTERNAL_H
+
+struct wbcContext {
+ struct winbindd_context *winbindd_ctx;
+ uint32_t pw_cache_size; /* Number of cached passwd structs */
+ uint32_t pw_cache_idx; /* Position of the pwent context */
+ uint32_t gr_cache_size; /* Number of cached group structs */
+ uint32_t gr_cache_idx; /* Position of the grent context */
+};
+
+/* Private functions */
+
+wbcErr wbcRequestResponse(struct wbcContext *ctx, int cmd,
+ struct winbindd_request *request,
+ struct winbindd_response *response);
+
+wbcErr wbcRequestResponsePriv(struct wbcContext *ctx, int cmd,
+ struct winbindd_request *request,
+ struct winbindd_response *response);
+
+void *wbcAllocateMemory(size_t nelem, size_t elsize,
+ void (*destructor)(void *ptr));
+
+char *wbcStrDup(const char *str);
+const char **wbcAllocateStringArray(int num_strings);
+struct wbcContext *wbcGetGlobalCtx(void);
+
+#endif /* _WBCLIENT_INTERNAL_H */
diff --git a/nsswitch/libwbclient/wscript b/nsswitch/libwbclient/wscript
new file mode 100644
index 0000000..ad1d321
--- /dev/null
+++ b/nsswitch/libwbclient/wscript
@@ -0,0 +1,57 @@
+#!/usr/bin/env python
+
+from waflib import Options, Logs
+
+# Remember to also update wbclient.h
+VERSION="0.15"
+
+# It may be useful at some point to allow Samba to build against a
+# system libwbclient, such as the one provided by Likewise. To to
+# this, not only must the check below be activated but this must only
+# be activated with an off-by-default option to disable the internal
+# build of both winbindd implementations, and all the internal
+# references to libwbclient.h will need to be fixed to point at the
+# system libwbclient. Finally, as a system libwbclient would probably
+# not use the same version scheme as Samba, so this would need to
+# reference Likewise version numbers instead.
+#
+#def configure(conf):
+# if conf.CHECK_BUNDLED_SYSTEM_PKG('wbclient', minversion=VERSION):
+# conf.define('USING_SYSTEM_LIBWBCLIENT', 1)
+#
+
+def build(bld):
+# if bld.CONFIG_SET('USING_SYSTEM_LIBWBCLIENT'):
+# Logs.info("\tSelected system libwbclient build")
+# return
+#
+# Logs.info("\tSelected embedded libwbclient build")
+
+ bld.SAMBA_SUBSYSTEM('wbclient-internal',
+ source='../wb_common.c',
+ deps='replace',
+ cflags='-DWINBINDD_SOCKET_DIR=\"%s\"' % bld.env.WINBINDD_SOCKET_DIR,
+ hide_symbols=True,
+ provide_builtin_linking=True,
+ builtin_cflags='-DWINBINDD_SOCKET_DIR=\"%s\"' % bld.env.WINBINDD_SOCKET_DIR,
+ )
+
+ abi_match = 'wbc*'
+ bld.SAMBA_LIBRARY('wbclient',
+ source='''
+ wbc_guid.c
+ wbc_idmap.c
+ wbclient.c
+ wbc_pam.c
+ wbc_pwd.c
+ wbc_sid.c
+ wbc_util.c''',
+ hide_symbols=True,
+ deps='wbclient-internal smb_strtox',
+ require_builtin_deps=True,
+ provide_builtin_linking=True,
+ pc_files='wbclient.pc',
+ public_headers='wbclient.h',
+ abi_directory='ABI',
+ abi_match=abi_match,
+ vnum=VERSION)
diff --git a/nsswitch/nsstest.c b/nsswitch/nsstest.c
new file mode 100644
index 0000000..45270cd
--- /dev/null
+++ b/nsswitch/nsstest.c
@@ -0,0 +1,500 @@
+/*
+ Unix SMB/CIFS implementation.
+ nss tester for winbindd
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Tim Potter 2003
+
+ 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 "replace.h"
+#include "nsswitch/nsstest.h"
+
+#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); (x)=NULL;} } while(0)
+
+static const char *so_path = "/lib/libnss_winbind.so";
+static const char *nss_name = "winbind";
+static int nss_errno;
+static NSS_STATUS last_error;
+static int total_errors;
+
+static void *find_fn(const char *name)
+{
+ char *s;
+ static void *h;
+ void *res;
+
+ if (asprintf(&s, "_nss_%s_%s", nss_name, name) < 0) {
+ exit(1);
+ }
+
+ if (!h) {
+ h = dlopen(so_path, RTLD_LAZY);
+ }
+ if (!h) {
+ printf("Can't open shared library %s\n", so_path);
+ exit(1);
+ }
+ res = dlsym(h, s);
+ if (!res) {
+ printf("Can't find function %s\n", s);
+ total_errors++;
+ SAFE_FREE(s);
+ return NULL;
+ }
+ SAFE_FREE(s);
+ return res;
+}
+
+static void report_nss_error(const char *who, NSS_STATUS status)
+{
+ last_error = status;
+ total_errors++;
+ printf("ERROR %s: NSS_STATUS=%d %d (nss_errno=%d)\n",
+ who, status, NSS_STATUS_SUCCESS, nss_errno);
+}
+
+static struct passwd *nss_getpwent(void)
+{
+ NSS_STATUS (*_nss_getpwent_r)(struct passwd *, char *,
+ size_t , int *) =
+ (NSS_STATUS (*)(struct passwd *, char *,
+ size_t, int *))find_fn("getpwent_r");
+ static struct passwd pwd;
+ static char buf[1000];
+ NSS_STATUS status;
+
+ if (!_nss_getpwent_r)
+ return NULL;
+
+ status = _nss_getpwent_r(&pwd, buf, sizeof(buf), &nss_errno);
+ if (status == NSS_STATUS_NOTFOUND) {
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getpwent", status);
+ return NULL;
+ }
+ return &pwd;
+}
+
+static struct passwd *nss_getpwnam(const char *name)
+{
+ NSS_STATUS (*_nss_getpwnam_r)(const char *, struct passwd *, char *,
+ size_t , int *) =
+ (NSS_STATUS (*)(const char *, struct passwd *, char *,
+ size_t, int *))find_fn("getpwnam_r");
+ static struct passwd pwd;
+ static char buf[1000];
+ NSS_STATUS status;
+
+ if (!_nss_getpwnam_r)
+ return NULL;
+
+ status = _nss_getpwnam_r(name, &pwd, buf, sizeof(buf), &nss_errno);
+ if (status == NSS_STATUS_NOTFOUND) {
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getpwnam", status);
+ return NULL;
+ }
+ return &pwd;
+}
+
+static struct passwd *nss_getpwuid(uid_t uid)
+{
+ NSS_STATUS (*_nss_getpwuid_r)(uid_t , struct passwd *, char *,
+ size_t , int *) =
+ (NSS_STATUS (*)(uid_t, struct passwd *, char *,
+ size_t, int *))find_fn("getpwuid_r");
+ static struct passwd pwd;
+ static char buf[1000];
+ NSS_STATUS status;
+
+ if (!_nss_getpwuid_r)
+ return NULL;
+
+ status = _nss_getpwuid_r(uid, &pwd, buf, sizeof(buf), &nss_errno);
+ if (status == NSS_STATUS_NOTFOUND) {
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getpwuid", status);
+ return NULL;
+ }
+ return &pwd;
+}
+
+static void samba_nss_setpwent(void)
+{
+ NSS_STATUS (*_nss_setpwent)(void) =
+ (NSS_STATUS(*)(void))find_fn("setpwent");
+ NSS_STATUS status;
+
+ if (!_nss_setpwent)
+ return;
+
+ status = _nss_setpwent();
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("setpwent", status);
+ }
+}
+
+static void samba_nss_endpwent(void)
+{
+ NSS_STATUS (*_nss_endpwent)(void) =
+ (NSS_STATUS (*)(void))find_fn("endpwent");
+ NSS_STATUS status;
+
+ if (!_nss_endpwent)
+ return;
+
+ status = _nss_endpwent();
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("endpwent", status);
+ }
+}
+
+
+static struct group *nss_getgrent(void)
+{
+ NSS_STATUS (*_nss_getgrent_r)(struct group *, char *,
+ size_t , int *) =
+ (NSS_STATUS (*)(struct group *, char *,
+ size_t, int *))find_fn("getgrent_r");
+ static struct group grp;
+ static char *buf;
+ static int buflen = 1024;
+ NSS_STATUS status;
+
+ if (!_nss_getgrent_r)
+ return NULL;
+
+ if (!buf)
+ buf = (char *)malloc(buflen);
+
+again:
+ status = _nss_getgrent_r(&grp, buf, buflen, &nss_errno);
+ if (status == NSS_STATUS_TRYAGAIN) {
+ char *oldbuf = buf;
+ buflen *= 2;
+ buf = (char *)realloc(buf, buflen);
+ if (!buf) {
+ SAFE_FREE(oldbuf);
+ return NULL;
+ }
+ goto again;
+ }
+ if (status == NSS_STATUS_NOTFOUND) {
+ SAFE_FREE(buf);
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getgrent", status);
+ SAFE_FREE(buf);
+ return NULL;
+ }
+ return &grp;
+}
+
+static struct group *nss_getgrnam(const char *name)
+{
+ NSS_STATUS (*_nss_getgrnam_r)(const char *, struct group *, char *,
+ size_t , int *) =
+ (NSS_STATUS (*)(const char *, struct group *, char *,
+ size_t, int *))find_fn("getgrnam_r");
+ static struct group grp;
+ static char *buf;
+ static int buflen = 1000;
+ NSS_STATUS status;
+
+ if (!_nss_getgrnam_r)
+ return NULL;
+
+ if (!buf)
+ buf = (char *)malloc(buflen);
+again:
+ status = _nss_getgrnam_r(name, &grp, buf, buflen, &nss_errno);
+ if (status == NSS_STATUS_TRYAGAIN) {
+ char *oldbuf = buf;
+ buflen *= 2;
+ buf = (char *)realloc(buf, buflen);
+ if (!buf) {
+ SAFE_FREE(oldbuf);
+ return NULL;
+ }
+ goto again;
+ }
+ if (status == NSS_STATUS_NOTFOUND) {
+ SAFE_FREE(buf);
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getgrnam", status);
+ SAFE_FREE(buf);
+ return NULL;
+ }
+ return &grp;
+}
+
+static struct group *nss_getgrgid(gid_t gid)
+{
+ NSS_STATUS (*_nss_getgrgid_r)(gid_t , struct group *, char *,
+ size_t , int *) =
+ (NSS_STATUS (*)(gid_t, struct group *, char *,
+ size_t, int *))find_fn("getgrgid_r");
+ static struct group grp;
+ static char *buf;
+ static int buflen = 1000;
+ NSS_STATUS status;
+
+ if (!_nss_getgrgid_r)
+ return NULL;
+
+ if (!buf)
+ buf = (char *)malloc(buflen);
+
+again:
+ status = _nss_getgrgid_r(gid, &grp, buf, buflen, &nss_errno);
+ if (status == NSS_STATUS_TRYAGAIN) {
+ char *oldbuf = buf;
+ buflen *= 2;
+ buf = (char *)realloc(buf, buflen);
+ if (!buf) {
+ SAFE_FREE(oldbuf);
+ return NULL;
+ }
+ goto again;
+ }
+ if (status == NSS_STATUS_NOTFOUND) {
+ SAFE_FREE(buf);
+ return NULL;
+ }
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("getgrgid", status);
+ SAFE_FREE(buf);
+ return NULL;
+ }
+ return &grp;
+}
+
+static void samba_nss_setgrent(void)
+{
+ NSS_STATUS (*_nss_setgrent)(void) =
+ (NSS_STATUS (*)(void))find_fn("setgrent");
+ NSS_STATUS status;
+
+ if (!_nss_setgrent)
+ return;
+
+ status = _nss_setgrent();
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("setgrent", status);
+ }
+}
+
+static void samba_nss_endgrent(void)
+{
+ NSS_STATUS (*_nss_endgrent)(void) =
+ (NSS_STATUS (*)(void))find_fn("endgrent");
+ NSS_STATUS status;
+
+ if (!_nss_endgrent)
+ return;
+
+ status = _nss_endgrent();
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("endgrent", status);
+ }
+}
+
+static int nss_initgroups(char *user, gid_t group, gid_t **groups, long int *start, long int *size)
+{
+ NSS_STATUS (*_nss_initgroups)(char *, gid_t , long int *,
+ long int *, gid_t **, long int , int *) =
+ (NSS_STATUS (*)(char *, gid_t, long int *,
+ long int *, gid_t **,
+ long int, int *))find_fn("initgroups_dyn");
+ NSS_STATUS status;
+
+ if (!_nss_initgroups)
+ return NSS_STATUS_UNAVAIL;
+
+ status = _nss_initgroups(user, group, start, size, groups, 0, &nss_errno);
+ if (status != NSS_STATUS_SUCCESS) {
+ report_nss_error("initgroups", status);
+ }
+ return status;
+}
+
+static void print_passwd(struct passwd *pwd)
+{
+ printf("%s:%s:%lu:%lu:%s:%s:%s\n",
+ pwd->pw_name,
+ pwd->pw_passwd,
+ (unsigned long)pwd->pw_uid,
+ (unsigned long)pwd->pw_gid,
+ pwd->pw_gecos,
+ pwd->pw_dir,
+ pwd->pw_shell);
+}
+
+static void print_group(struct group *grp)
+{
+ int i;
+ printf("%s:%s:%lu:",
+ grp->gr_name,
+ grp->gr_passwd,
+ (unsigned long)grp->gr_gid);
+
+ if (!grp->gr_mem[0]) {
+ printf("\n");
+ return;
+ }
+
+ for (i=0; grp->gr_mem[i+1]; i++) {
+ printf("%s,", grp->gr_mem[i]);
+ }
+ printf("%s\n", grp->gr_mem[i]);
+}
+
+static void nss_test_initgroups(char *name, gid_t gid)
+{
+ long int size = 16;
+ long int start = 1;
+ gid_t *groups = NULL;
+ int i;
+ NSS_STATUS status;
+
+ groups = (gid_t *)malloc(sizeof(gid_t) * size);
+ if (groups == NULL) {
+ printf("Unable to allocate memory for groups\n");
+ return;
+ }
+ groups[0] = gid;
+
+ status = nss_initgroups(name, gid, &groups, &start, &size);
+ if (status == NSS_STATUS_UNAVAIL) {
+ printf("No initgroups fn\n");
+ return;
+ }
+
+ for (i=0; i<start-1; i++) {
+ printf("%lu, ", (unsigned long)groups[i]);
+ }
+ printf("%lu\n", (unsigned long)groups[i]);
+}
+
+
+static void nss_test_users(void)
+{
+ struct passwd *pwd;
+
+ samba_nss_setpwent();
+ /* loop over all users */
+ while ((pwd = nss_getpwent())) {
+ printf("Testing user %s\n", pwd->pw_name);
+ printf("getpwent: "); print_passwd(pwd);
+ pwd = nss_getpwuid(pwd->pw_uid);
+ if (!pwd) {
+ total_errors++;
+ printf("ERROR: can't getpwuid\n");
+ continue;
+ }
+ printf("getpwuid: "); print_passwd(pwd);
+ pwd = nss_getpwnam(pwd->pw_name);
+ if (!pwd) {
+ total_errors++;
+ printf("ERROR: can't getpwnam\n");
+ continue;
+ }
+ printf("getpwnam: "); print_passwd(pwd);
+ printf("initgroups: "); nss_test_initgroups(pwd->pw_name, pwd->pw_gid);
+ printf("\n");
+ }
+ samba_nss_endpwent();
+}
+
+static void nss_test_groups(void)
+{
+ struct group *grp;
+
+ samba_nss_setgrent();
+ /* loop over all groups */
+ while ((grp = nss_getgrent())) {
+ printf("Testing group %s\n", grp->gr_name);
+ printf("getgrent: "); print_group(grp);
+ grp = nss_getgrnam(grp->gr_name);
+ if (!grp) {
+ total_errors++;
+ printf("ERROR: can't getgrnam\n");
+ continue;
+ }
+ printf("getgrnam: "); print_group(grp);
+ grp = nss_getgrgid(grp->gr_gid);
+ if (!grp) {
+ total_errors++;
+ printf("ERROR: can't getgrgid\n");
+ continue;
+ }
+ printf("getgrgid: "); print_group(grp);
+ printf("\n");
+ }
+ samba_nss_endgrent();
+}
+
+static void nss_test_errors(void)
+{
+ struct passwd *pwd;
+ struct group *grp;
+
+ pwd = getpwnam("nosuchname");
+ if (pwd || last_error != NSS_STATUS_NOTFOUND) {
+ total_errors++;
+ printf("ERROR Non existent user gave error %d\n", last_error);
+ }
+
+ pwd = getpwuid(0xFF00);
+ if (pwd || last_error != NSS_STATUS_NOTFOUND) {
+ total_errors++;
+ printf("ERROR Non existent uid gave error %d\n", last_error);
+ }
+
+ grp = getgrnam("nosuchgroup");
+ if (grp || last_error != NSS_STATUS_NOTFOUND) {
+ total_errors++;
+ printf("ERROR Non existent group gave error %d\n", last_error);
+ }
+
+ grp = getgrgid(0xFFF0);
+ if (grp || last_error != NSS_STATUS_NOTFOUND) {
+ total_errors++;
+ printf("ERROR Non existent gid gave error %d\n", last_error);
+ }
+}
+
+ int main(int argc, char *argv[])
+{
+ if (argc > 1) so_path = argv[1];
+ if (argc > 2) nss_name = argv[2];
+
+ nss_test_users();
+ nss_test_groups();
+ nss_test_errors();
+
+ printf("total_errors=%d\n", total_errors);
+
+ return total_errors;
+}
diff --git a/nsswitch/nsstest.h b/nsswitch/nsstest.h
new file mode 100644
index 0000000..8112b57
--- /dev/null
+++ b/nsswitch/nsstest.h
@@ -0,0 +1,119 @@
+/*
+ Unix SMB/CIFS implementation.
+ nss includes for the nss tester
+ Copyright (C) Kai Blin 2007
+
+ ** NOTE! The following LGPL license applies to the nsstest
+ ** header. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _NSSTEST_H
+#define _NSSTEST_H
+
+#include <pwd.h>
+#include <grp.h>
+
+#ifdef HAVE_NSS_COMMON_H
+
+/*
+ * Sun Solaris
+ */
+
+#include <nss_common.h>
+#include <nss_dbdefs.h>
+#include <nsswitch.h>
+
+typedef nss_status_t NSS_STATUS;
+
+#define NSS_STATUS_SUCCESS NSS_SUCCESS
+#define NSS_STATUS_NOTFOUND NSS_NOTFOUND
+#define NSS_STATUS_UNAVAIL NSS_UNAVAIL
+#define NSS_STATUS_TRYAGAIN NSS_TRYAGAIN
+
+#elif defined(HAVE_NSS_H)
+
+/*
+ * Linux (glibc)
+ */
+
+#include <nss.h>
+typedef enum nss_status NSS_STATUS;
+
+#elif defined(HAVE_NS_API_H)
+
+/*
+ * SGI IRIX
+ */
+
+#ifdef DATUM
+#define _DATUM_DEFINED
+#endif
+
+#include <ns_api.h>
+
+typedef enum
+{
+ NSS_STATUS_SUCCESS=NS_SUCCESS,
+ NSS_STATUS_NOTFOUND=NS_NOTFOUND,
+ NSS_STATUS_UNAVAIL=NS_UNAVAIL,
+ NSS_STATUS_TRYAGAIN=NS_TRYAGAIN
+} NSS_STATUS;
+
+#define NSD_MEM_STATIC 0
+#define NSD_MEM_VOLATILE 1
+#define NSD_MEM_DYNAMIC 2
+
+#elif defined(HPUX) && defined(HAVE_NSSWITCH_H)
+
+/* HP-UX 11 */
+
+#include <nsswitch.h>
+
+#define NSS_STATUS_SUCCESS NSS_SUCCESS
+#define NSS_STATUS_NOTFOUND NSS_NOTFOUND
+#define NSS_STATUS_UNAVAIL NSS_UNAVAIL
+#define NSS_STATUS_TRYAGAIN NSS_TRYAGAIN
+
+#ifdef HAVE_SYNCH_H
+#include <synch.h>
+#endif
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+
+typedef enum {
+ NSS_SUCCESS,
+ NSS_NOTFOUND,
+ NSS_UNAVAIL,
+ NSS_TRYAGAIN
+} nss_status_t;
+
+typedef nss_status_t NSS_STATUS;
+
+#else /* Nothing's defined. Neither solaris nor gnu nor sun nor hp */
+
+typedef enum
+{
+ NSS_STATUS_SUCCESS=0,
+ NSS_STATUS_NOTFOUND=1,
+ NSS_STATUS_UNAVAIL=2,
+ NSS_STATUS_TRYAGAIN=3
+} NSS_STATUS;
+
+#endif
+
+#endif /* _NSSTEST_H */
diff --git a/nsswitch/pam_winbind.c b/nsswitch/pam_winbind.c
new file mode 100644
index 0000000..06a8db2
--- /dev/null
+++ b/nsswitch/pam_winbind.c
@@ -0,0 +1,3527 @@
+/* pam_winbind module
+
+ Copyright Andrew Tridgell <tridge@samba.org> 2000
+ Copyright Tim Potter <tpot@samba.org> 2000
+ Copyright Andrew Bartlett <abartlet@samba.org> 2002
+ Copyright Guenther Deschner <gd@samba.org> 2005-2008
+
+ largely based on pam_userdb by Cristian Gafton <gafton@redhat.com> also
+ contains large slabs of code from pam_unix by Elliot Lee
+ <sopwith@redhat.com> (see copyright below for full details)
+*/
+
+#include "pam_winbind.h"
+
+enum pam_winbind_request_type
+{
+ PAM_WINBIND_AUTHENTICATE,
+ PAM_WINBIND_SETCRED,
+ PAM_WINBIND_ACCT_MGMT,
+ PAM_WINBIND_OPEN_SESSION,
+ PAM_WINBIND_CLOSE_SESSION,
+ PAM_WINBIND_CHAUTHTOK,
+ PAM_WINBIND_CLEANUP
+};
+
+static int wbc_error_to_pam_error(wbcErr status)
+{
+ switch (status) {
+ case WBC_ERR_SUCCESS:
+ return PAM_SUCCESS;
+ case WBC_ERR_NOT_IMPLEMENTED:
+ return PAM_SERVICE_ERR;
+ case WBC_ERR_UNKNOWN_FAILURE:
+ break;
+ case WBC_ERR_NO_MEMORY:
+ return PAM_BUF_ERR;
+ case WBC_ERR_INVALID_SID:
+ case WBC_ERR_INVALID_PARAM:
+ break;
+ case WBC_ERR_WINBIND_NOT_AVAILABLE:
+ return PAM_AUTHINFO_UNAVAIL;
+ case WBC_ERR_DOMAIN_NOT_FOUND:
+ return PAM_AUTHINFO_UNAVAIL;
+ case WBC_ERR_INVALID_RESPONSE:
+ return PAM_BUF_ERR;
+ case WBC_ERR_NSS_ERROR:
+ return PAM_USER_UNKNOWN;
+ case WBC_ERR_AUTH_ERROR:
+ return PAM_AUTH_ERR;
+ case WBC_ERR_UNKNOWN_USER:
+ return PAM_USER_UNKNOWN;
+ case WBC_ERR_UNKNOWN_GROUP:
+ return PAM_USER_UNKNOWN;
+ case WBC_ERR_PWD_CHANGE_FAILED:
+ break;
+ }
+
+ /* be paranoid */
+ return PAM_AUTH_ERR;
+}
+
+static const char *_pam_error_code_str(int err)
+{
+ switch (err) {
+ case PAM_SUCCESS:
+ return "PAM_SUCCESS";
+ case PAM_OPEN_ERR:
+ return "PAM_OPEN_ERR";
+ case PAM_SYMBOL_ERR:
+ return "PAM_SYMBOL_ERR";
+ case PAM_SERVICE_ERR:
+ return "PAM_SERVICE_ERR";
+ case PAM_SYSTEM_ERR:
+ return "PAM_SYSTEM_ERR";
+ case PAM_BUF_ERR:
+ return "PAM_BUF_ERR";
+ case PAM_PERM_DENIED:
+ return "PAM_PERM_DENIED";
+ case PAM_AUTH_ERR:
+ return "PAM_AUTH_ERR";
+ case PAM_CRED_INSUFFICIENT:
+ return "PAM_CRED_INSUFFICIENT";
+ case PAM_AUTHINFO_UNAVAIL:
+ return "PAM_AUTHINFO_UNAVAIL";
+ case PAM_USER_UNKNOWN:
+ return "PAM_USER_UNKNOWN";
+ case PAM_MAXTRIES:
+ return "PAM_MAXTRIES";
+ case PAM_NEW_AUTHTOK_REQD:
+ return "PAM_NEW_AUTHTOK_REQD";
+ case PAM_ACCT_EXPIRED:
+ return "PAM_ACCT_EXPIRED";
+ case PAM_SESSION_ERR:
+ return "PAM_SESSION_ERR";
+ case PAM_CRED_UNAVAIL:
+ return "PAM_CRED_UNAVAIL";
+ case PAM_CRED_EXPIRED:
+ return "PAM_CRED_EXPIRED";
+ case PAM_CRED_ERR:
+ return "PAM_CRED_ERR";
+ case PAM_NO_MODULE_DATA:
+ return "PAM_NO_MODULE_DATA";
+ case PAM_CONV_ERR:
+ return "PAM_CONV_ERR";
+ case PAM_AUTHTOK_ERR:
+ return "PAM_AUTHTOK_ERR";
+ case PAM_AUTHTOK_RECOVER_ERR:
+ return "PAM_AUTHTOK_RECOVER_ERR";
+ case PAM_AUTHTOK_LOCK_BUSY:
+ return "PAM_AUTHTOK_LOCK_BUSY";
+ case PAM_AUTHTOK_DISABLE_AGING:
+ return "PAM_AUTHTOK_DISABLE_AGING";
+ case PAM_TRY_AGAIN:
+ return "PAM_TRY_AGAIN";
+ case PAM_IGNORE:
+ return "PAM_IGNORE";
+ case PAM_ABORT:
+ return "PAM_ABORT";
+ case PAM_AUTHTOK_EXPIRED:
+ return "PAM_AUTHTOK_EXPIRED";
+#ifdef PAM_MODULE_UNKNOWN
+ case PAM_MODULE_UNKNOWN:
+ return "PAM_MODULE_UNKNOWN";
+#endif
+#ifdef PAM_BAD_ITEM
+ case PAM_BAD_ITEM:
+ return "PAM_BAD_ITEM";
+#endif
+#ifdef PAM_CONV_AGAIN
+ case PAM_CONV_AGAIN:
+ return "PAM_CONV_AGAIN";
+#endif
+#ifdef PAM_INCOMPLETE
+ case PAM_INCOMPLETE:
+ return "PAM_INCOMPLETE";
+#endif
+ default:
+ return NULL;
+ }
+}
+
+#define _PAM_LOG_FUNCTION_ENTER(function, ctx) \
+ do { \
+ _pam_log_debug(ctx, LOG_DEBUG, "[pamh: %p] ENTER: " \
+ function " (flags: 0x%04x)", ctx->pamh, ctx->flags); \
+ _pam_log_state(ctx); \
+ } while (0)
+
+#define _PAM_LOG_FUNCTION_LEAVE(function, ctx, retval) \
+ do { \
+ _pam_log_debug(ctx, LOG_DEBUG, "[pamh: %p] LEAVE: " \
+ function " returning %d (%s)", ctx ? ctx->pamh : NULL, retval, \
+ _pam_error_code_str(retval)); \
+ _pam_log_state(ctx); \
+ } while (0)
+
+/* data tokens */
+
+#define MAX_PASSWD_TRIES 3
+
+#ifdef HAVE_GETTEXT
+static char initialized = 0;
+
+static inline void textdomain_init(void);
+static inline void textdomain_init(void)
+{
+ if (!initialized) {
+ bindtextdomain(MODULE_NAME, LOCALEDIR);
+ initialized = 1;
+ }
+ return;
+}
+#endif
+
+
+/* some syslogging */
+static void _pam_log_int(const pam_handle_t *pamh,
+ int err,
+ const char *format,
+ va_list args) PRINTF_ATTRIBUTE(3, 0);
+
+#ifdef HAVE_PAM_VSYSLOG
+static void _pam_log_int(const pam_handle_t *pamh,
+ int err,
+ const char *format,
+ va_list args)
+{
+ pam_vsyslog(pamh, err, format, args);
+}
+#else
+static void _pam_log_int(const pam_handle_t *pamh,
+ int err,
+ const char *format,
+ va_list args)
+{
+ char *base = NULL;
+ va_list args2;
+ const char *service;
+ int ret;
+
+ va_copy(args2, args);
+
+ pam_get_item(pamh, PAM_SERVICE, (const void **) &service);
+
+ ret = vasprintf(&base, format, args);
+ if (ret == -1) {
+ /* what else todo ? */
+ vsyslog(err, format, args2);
+ va_end(args2);
+ return;
+ }
+
+ syslog(err, "%s(%s): %s", MODULE_NAME, service, base);
+ SAFE_FREE(base);
+ va_end(args2);
+}
+#endif /* HAVE_PAM_VSYSLOG */
+
+static bool _pam_log_is_silent(int ctrl)
+{
+ return on(ctrl, WINBIND_SILENT);
+}
+
+static void _pam_log(struct pwb_context *r, int err, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
+static void _pam_log(struct pwb_context *r, int err, const char *format, ...)
+{
+ va_list args;
+
+ if (_pam_log_is_silent(r->ctrl)) {
+ return;
+ }
+
+ va_start(args, format);
+ _pam_log_int(r->pamh, err, format, args);
+ va_end(args);
+}
+static void __pam_log(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...) PRINTF_ATTRIBUTE(4,5);
+static void __pam_log(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...)
+{
+ va_list args;
+
+ if (_pam_log_is_silent(ctrl)) {
+ return;
+ }
+
+ va_start(args, format);
+ _pam_log_int(pamh, err, format, args);
+ va_end(args);
+}
+
+static bool _pam_log_is_debug_enabled(int ctrl)
+{
+ if (ctrl == -1) {
+ return false;
+ }
+
+ if (_pam_log_is_silent(ctrl)) {
+ return false;
+ }
+
+ if (!(ctrl & WINBIND_DEBUG_ARG)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool _pam_log_is_debug_state_enabled(int ctrl)
+{
+ if (!(ctrl & WINBIND_DEBUG_STATE)) {
+ return false;
+ }
+
+ return _pam_log_is_debug_enabled(ctrl);
+}
+
+static void _pam_log_debug(struct pwb_context *r, int err, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
+static void _pam_log_debug(struct pwb_context *r, int err, const char *format, ...)
+{
+ va_list args;
+
+ if (!r || !_pam_log_is_debug_enabled(r->ctrl)) {
+ return;
+ }
+
+ va_start(args, format);
+ _pam_log_int(r->pamh, err, format, args);
+ va_end(args);
+}
+static void __pam_log_debug(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...) PRINTF_ATTRIBUTE(4,5);
+static void __pam_log_debug(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...)
+{
+ va_list args;
+
+ if (!_pam_log_is_debug_enabled(ctrl)) {
+ return;
+ }
+
+ va_start(args, format);
+ _pam_log_int(pamh, err, format, args);
+ va_end(args);
+}
+
+static void _pam_log_state_datum(struct pwb_context *ctx,
+ int item_type,
+ const char *key,
+ int is_string)
+{
+ const void *data = NULL;
+ if (item_type != 0) {
+ pam_get_item(ctx->pamh, item_type, &data);
+ } else {
+ pam_get_data(ctx->pamh, key, &data);
+ }
+ if (data != NULL) {
+ const char *type = (item_type != 0) ? "ITEM" : "DATA";
+ if (is_string != 0) {
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "[pamh: %p] STATE: %s(%s) = \"%s\" (%p)",
+ ctx->pamh, type, key, (const char *)data,
+ data);
+ } else {
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "[pamh: %p] STATE: %s(%s) = %p",
+ ctx->pamh, type, key, data);
+ }
+ }
+}
+
+#define _PAM_LOG_STATE_DATA_POINTER(ctx, module_data_name) \
+ _pam_log_state_datum(ctx, 0, module_data_name, 0)
+
+#define _PAM_LOG_STATE_DATA_STRING(ctx, module_data_name) \
+ _pam_log_state_datum(ctx, 0, module_data_name, 1)
+
+#define _PAM_LOG_STATE_ITEM_POINTER(ctx, item_type) \
+ _pam_log_state_datum(ctx, item_type, #item_type, 0)
+
+#define _PAM_LOG_STATE_ITEM_STRING(ctx, item_type) \
+ _pam_log_state_datum(ctx, item_type, #item_type, 1)
+
+#ifdef DEBUG_PASSWORD
+#define _LOG_PASSWORD_AS_STRING 1
+#else
+#define _LOG_PASSWORD_AS_STRING 0
+#endif
+
+#define _PAM_LOG_STATE_ITEM_PASSWORD(ctx, item_type) \
+ _pam_log_state_datum(ctx, item_type, #item_type, \
+ _LOG_PASSWORD_AS_STRING)
+/*
+ * wrapper to preserve old behaviour of iniparser which ignored
+ * key values that had no value assigned like
+ * key =
+ * for a key like above newer iniparser will return a zero-length
+ * string, previously iniparser would return NULL
+ *
+ * JRA: For compatibility, tiniparser behaves like iniparser.
+ */
+static const char *tiniparser_getstring_nonempty(struct tiniparser_dictionary *d,
+ const char *key,
+ const char *def)
+{
+ const char *ret = tiniparser_getstring(d, key, def);
+ if (ret && strlen(ret) == 0) {
+ ret = NULL;
+ }
+ return ret;
+}
+
+static void _pam_log_state(struct pwb_context *ctx)
+{
+ if (!ctx || !_pam_log_is_debug_state_enabled(ctx->ctrl)) {
+ return;
+ }
+
+ _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_SERVICE);
+ _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_USER);
+ _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_TTY);
+ _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_RHOST);
+ _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_RUSER);
+ _PAM_LOG_STATE_ITEM_PASSWORD(ctx, PAM_OLDAUTHTOK);
+ _PAM_LOG_STATE_ITEM_PASSWORD(ctx, PAM_AUTHTOK);
+ _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_USER_PROMPT);
+ _PAM_LOG_STATE_ITEM_POINTER(ctx, PAM_CONV);
+#ifdef PAM_FAIL_DELAY
+ _PAM_LOG_STATE_ITEM_POINTER(ctx, PAM_FAIL_DELAY);
+#endif
+#ifdef PAM_REPOSITORY
+ _PAM_LOG_STATE_ITEM_POINTER(ctx, PAM_REPOSITORY);
+#endif
+
+ _PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_HOMEDIR);
+ _PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_LOGONSCRIPT);
+ _PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_LOGONSERVER);
+ _PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_PROFILEPATH);
+ _PAM_LOG_STATE_DATA_STRING(ctx,
+ PAM_WINBIND_NEW_AUTHTOK_REQD);
+ /* Use atoi to get PAM result code */
+ _PAM_LOG_STATE_DATA_STRING(ctx,
+ PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH);
+ _PAM_LOG_STATE_DATA_POINTER(ctx, PAM_WINBIND_PWD_LAST_SET);
+}
+
+static int _pam_parse(const pam_handle_t *pamh,
+ int flags,
+ int argc,
+ const char **argv,
+ enum pam_winbind_request_type type,
+ struct tiniparser_dictionary **result_d)
+{
+ int ctrl = 0;
+ const char *config_file = NULL;
+ int i;
+ const char **v;
+ struct tiniparser_dictionary *d = NULL;
+
+ if (flags & PAM_SILENT) {
+ ctrl |= WINBIND_SILENT;
+ }
+
+ for (i=argc,v=argv; i-- > 0; ++v) {
+ if (!strncasecmp(*v, "config", strlen("config"))) {
+ ctrl |= WINBIND_CONFIG_FILE;
+ config_file = v[i];
+ break;
+ }
+ }
+
+ if (config_file == NULL) {
+ config_file = PAM_WINBIND_CONFIG_FILE;
+ }
+
+ d = tiniparser_load(config_file);
+ if (d == NULL) {
+ goto config_from_pam;
+ }
+
+ if (tiniparser_getboolean(d, "global:debug", false)) {
+ ctrl |= WINBIND_DEBUG_ARG;
+ }
+
+ if (tiniparser_getboolean(d, "global:debug_state", false)) {
+ ctrl |= WINBIND_DEBUG_STATE;
+ }
+
+ if (tiniparser_getboolean(d, "global:cached_login", false)) {
+ ctrl |= WINBIND_CACHED_LOGIN;
+ }
+
+ if (tiniparser_getboolean(d, "global:krb5_auth", false)) {
+ ctrl |= WINBIND_KRB5_AUTH;
+ }
+
+ if (tiniparser_getboolean(d, "global:silent", false)) {
+ ctrl |= WINBIND_SILENT;
+ }
+
+ if (tiniparser_getstring_nonempty(d, "global:krb5_ccache_type", NULL) != NULL) {
+ ctrl |= WINBIND_KRB5_CCACHE_TYPE;
+ }
+
+ if ((tiniparser_getstring_nonempty(d, "global:require-membership-of", NULL)
+ != NULL) ||
+ (tiniparser_getstring_nonempty(d, "global:require_membership_of", NULL)
+ != NULL)) {
+ ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
+ }
+
+ if (tiniparser_getboolean(d, "global:try_first_pass", false)) {
+ ctrl |= WINBIND_TRY_FIRST_PASS_ARG;
+ }
+
+ if (tiniparser_getint(d, "global:warn_pwd_expire", 0)) {
+ ctrl |= WINBIND_WARN_PWD_EXPIRE;
+ }
+
+ if (tiniparser_getboolean(d, "global:mkhomedir", false)) {
+ ctrl |= WINBIND_MKHOMEDIR;
+ }
+
+ if (tiniparser_getboolean(d, "global:pwd_change_prompt", false)) {
+ ctrl |= WINBIND_PWD_CHANGE_PROMPT;
+ }
+
+config_from_pam:
+ /* step through arguments */
+ for (i=argc,v=argv; i-- > 0; ++v) {
+
+ /* generic options */
+ if (!strcmp(*v,"debug"))
+ ctrl |= WINBIND_DEBUG_ARG;
+ else if (!strcasecmp(*v, "debug_state"))
+ ctrl |= WINBIND_DEBUG_STATE;
+ else if (!strcasecmp(*v, "silent"))
+ ctrl |= WINBIND_SILENT;
+ else if (!strcasecmp(*v, "use_authtok"))
+ ctrl |= WINBIND_USE_AUTHTOK_ARG;
+ else if (!strcasecmp(*v, "try_authtok"))
+ ctrl |= WINBIND_TRY_AUTHTOK_ARG;
+ else if (!strcasecmp(*v, "use_first_pass"))
+ ctrl |= WINBIND_USE_FIRST_PASS_ARG;
+ else if (!strcasecmp(*v, "try_first_pass"))
+ ctrl |= WINBIND_TRY_FIRST_PASS_ARG;
+ else if (!strcasecmp(*v, "unknown_ok"))
+ ctrl |= WINBIND_UNKNOWN_OK_ARG;
+ else if ((type == PAM_WINBIND_AUTHENTICATE
+ || type == PAM_WINBIND_SETCRED)
+ && !strncasecmp(*v, "require_membership_of",
+ strlen("require_membership_of")))
+ ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
+ else if ((type == PAM_WINBIND_AUTHENTICATE
+ || type == PAM_WINBIND_SETCRED)
+ && !strncasecmp(*v, "require-membership-of",
+ strlen("require-membership-of")))
+ ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
+ else if (!strcasecmp(*v, "krb5_auth"))
+ ctrl |= WINBIND_KRB5_AUTH;
+ else if (!strncasecmp(*v, "krb5_ccache_type",
+ strlen("krb5_ccache_type")))
+ ctrl |= WINBIND_KRB5_CCACHE_TYPE;
+ else if (!strcasecmp(*v, "cached_login"))
+ ctrl |= WINBIND_CACHED_LOGIN;
+ else if (!strcasecmp(*v, "mkhomedir"))
+ ctrl |= WINBIND_MKHOMEDIR;
+ else if (!strncasecmp(*v, "warn_pwd_expire",
+ strlen("warn_pwd_expire")))
+ ctrl |= WINBIND_WARN_PWD_EXPIRE;
+ else if (!strcasecmp(*v, "pwd_change_prompt"))
+ ctrl |= WINBIND_PWD_CHANGE_PROMPT;
+ else if (type != PAM_WINBIND_CLEANUP) {
+ __pam_log(pamh, ctrl, LOG_ERR,
+ "pam_parse: unknown option: %s", *v);
+ return -1;
+ }
+
+ }
+
+ if (result_d) {
+ *result_d = d;
+ } else {
+ if (d) {
+ tiniparser_freedict(d);
+ }
+ }
+
+ return ctrl;
+};
+
+static int _pam_winbind_free_context(struct pwb_context *ctx)
+{
+ if (!ctx) {
+ return 0;
+ }
+
+ if (ctx->dict) {
+ tiniparser_freedict(ctx->dict);
+ }
+
+ wbcCtxFree(ctx->wbc_ctx);
+
+ return 0;
+}
+
+static int _pam_winbind_init_context(pam_handle_t *pamh,
+ int flags,
+ int argc,
+ const char **argv,
+ enum pam_winbind_request_type type,
+ struct pwb_context **ctx_p)
+{
+ struct pwb_context *r = NULL;
+ const char *service = NULL;
+ char service_name[32] = {0};
+ int ctrl_code;
+
+#ifdef HAVE_GETTEXT
+ textdomain_init();
+#endif
+
+ r = talloc_zero(NULL, struct pwb_context);
+ if (!r) {
+ return PAM_BUF_ERR;
+ }
+
+ talloc_set_destructor(r, _pam_winbind_free_context);
+
+ r->pamh = pamh;
+ r->flags = flags;
+ r->argc = argc;
+ r->argv = argv;
+ ctrl_code = _pam_parse(pamh, flags, argc, argv, type, &r->dict);
+ if (ctrl_code == -1) {
+ TALLOC_FREE(r);
+ return PAM_SYSTEM_ERR;
+ }
+ r->ctrl = ctrl_code;
+
+ r->wbc_ctx = wbcCtxCreate();
+ if (r->wbc_ctx == NULL) {
+ TALLOC_FREE(r);
+ return PAM_SYSTEM_ERR;
+ }
+
+ pam_get_item(pamh, PAM_SERVICE, (const void **)&service);
+
+ snprintf(service_name, sizeof(service_name), "PAM_WINBIND[%s]", service);
+
+ wbcSetClientProcessName(service_name);
+
+ *ctx_p = r;
+
+ return PAM_SUCCESS;
+}
+
+static void _pam_winbind_cleanup_func(pam_handle_t *pamh,
+ void *data,
+ int error_status)
+{
+ int ctrl = _pam_parse(pamh, 0, 0, NULL, PAM_WINBIND_CLEANUP, NULL);
+ if (_pam_log_is_debug_state_enabled(ctrl)) {
+ __pam_log_debug(pamh, ctrl, LOG_DEBUG,
+ "[pamh: %p] CLEAN: cleaning up PAM data %p "
+ "(error_status = %d)", pamh, data,
+ error_status);
+ }
+ TALLOC_FREE(data);
+}
+
+
+static const struct ntstatus_errors {
+ const char *ntstatus_string;
+ const char *error_string;
+} ntstatus_errors[] = {
+ {"NT_STATUS_OK",
+ N_("Success")},
+ {"NT_STATUS_BACKUP_CONTROLLER",
+ N_("No primary Domain Controller available")},
+ {"NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND",
+ N_("No domain controllers found")},
+ {"NT_STATUS_NO_LOGON_SERVERS",
+ N_("No logon servers")},
+ {"NT_STATUS_PWD_TOO_SHORT",
+ N_("Password too short")},
+ {"NT_STATUS_PWD_TOO_RECENT",
+ N_("The password was recently changed and cannot be changed again before %s")},
+ {"NT_STATUS_PWD_HISTORY_CONFLICT",
+ N_("Password is already in password history")},
+ {"NT_STATUS_PASSWORD_EXPIRED",
+ N_("Your password has expired")},
+ {"NT_STATUS_PASSWORD_MUST_CHANGE",
+ N_("You need to change your password now")},
+ {"NT_STATUS_INVALID_WORKSTATION",
+ N_("You are not allowed to logon from this workstation")},
+ {"NT_STATUS_INVALID_LOGON_HOURS",
+ N_("You are not allowed to logon at this time")},
+ {"NT_STATUS_ACCOUNT_EXPIRED",
+ N_("Your account has expired. "
+ "Please contact your System administrator")}, /* SCNR */
+ {"NT_STATUS_ACCOUNT_DISABLED",
+ N_("Your account is disabled. "
+ "Please contact your System administrator")}, /* SCNR */
+ {"NT_STATUS_ACCOUNT_LOCKED_OUT",
+ N_("Your account has been locked. "
+ "Please contact your System administrator")}, /* SCNR */
+ {"NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT",
+ N_("Invalid Trust Account")},
+ {"NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT",
+ N_("Invalid Trust Account")},
+ {"NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT",
+ N_("Invalid Trust Account")},
+ {"NT_STATUS_ACCESS_DENIED",
+ N_("Access is denied")},
+ {NULL, NULL}
+};
+
+static const char *_get_ntstatus_error_string(const char *nt_status_string)
+{
+ int i;
+ for (i=0; ntstatus_errors[i].ntstatus_string != NULL; i++) {
+ if (!strcasecmp(ntstatus_errors[i].ntstatus_string,
+ nt_status_string)) {
+ return _(ntstatus_errors[i].error_string);
+ }
+ }
+ return NULL;
+}
+
+/* --- authentication management functions --- */
+
+/* Attempt a conversation */
+
+static int converse(const pam_handle_t *pamh,
+ int nargs,
+ const struct pam_message **message,
+ struct pam_response **response)
+{
+ int retval;
+ const struct pam_conv *conv;
+
+ retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
+ if (retval == PAM_SUCCESS) {
+ retval = conv->conv(nargs,
+ discard_const_p(const struct pam_message *, message),
+ response, conv->appdata_ptr);
+ }
+
+ return retval; /* propagate error status */
+}
+
+
+static int _make_remark(struct pwb_context *ctx,
+ int type,
+ const char *text)
+{
+ int retval = PAM_SUCCESS;
+
+ const struct pam_message *pmsg[1];
+ struct pam_message msg[1];
+ struct pam_response *resp;
+
+ if (ctx->flags & WINBIND_SILENT) {
+ return PAM_SUCCESS;
+ }
+
+ pmsg[0] = &msg[0];
+ msg[0].msg = discard_const_p(char, text);
+ msg[0].msg_style = type;
+
+ resp = NULL;
+ retval = converse(ctx->pamh, 1, pmsg, &resp);
+
+ if (resp) {
+ _pam_drop_reply(resp, 1);
+ }
+ return retval;
+}
+
+static int _make_remark_v(struct pwb_context *ctx,
+ int type,
+ const char *format,
+ va_list args) PRINTF_ATTRIBUTE(3, 0);
+
+static int _make_remark_v(struct pwb_context *ctx,
+ int type,
+ const char *format,
+ va_list args)
+{
+ char *var;
+ int ret;
+
+ ret = vasprintf(&var, format, args);
+ if (ret < 0) {
+ _pam_log(ctx, LOG_ERR, "memory allocation failure");
+ return ret;
+ }
+
+ ret = _make_remark(ctx, type, var);
+ SAFE_FREE(var);
+ return ret;
+}
+
+static int _make_remark_format(struct pwb_context *ctx, int type, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
+static int _make_remark_format(struct pwb_context *ctx, int type, const char *format, ...)
+{
+ int ret;
+ va_list args;
+
+ va_start(args, format);
+ ret = _make_remark_v(ctx, type, format, args);
+ va_end(args);
+ return ret;
+}
+
+static int pam_winbind_request_log(struct pwb_context *ctx,
+ int retval,
+ const char *user,
+ const char *fn)
+{
+ switch (retval) {
+ case PAM_AUTH_ERR:
+ /* incorrect password */
+ _pam_log(ctx, LOG_WARNING, "user '%s' denied access "
+ "(incorrect password or invalid membership)", user);
+ return retval;
+ case PAM_ACCT_EXPIRED:
+ /* account expired */
+ _pam_log(ctx, LOG_WARNING, "user '%s' account expired",
+ user);
+ return retval;
+ case PAM_AUTHTOK_EXPIRED:
+ /* password expired */
+ _pam_log(ctx, LOG_WARNING, "user '%s' password expired",
+ user);
+ return retval;
+ case PAM_NEW_AUTHTOK_REQD:
+ /* new password required */
+ _pam_log(ctx, LOG_WARNING, "user '%s' new password "
+ "required", user);
+ return retval;
+ case PAM_USER_UNKNOWN:
+ /* the user does not exist */
+ _pam_log_debug(ctx, LOG_NOTICE, "user '%s' not found",
+ user);
+ if (ctx->ctrl & WINBIND_UNKNOWN_OK_ARG) {
+ return PAM_IGNORE;
+ }
+ return retval;
+ case PAM_AUTHTOK_ERR:
+ /* Authentication token manipulation error */
+ _pam_log(ctx, LOG_WARNING, "user `%s' authentication token change failed "
+ "(pwd complexity/history/min_age not met?)", user);
+ return retval;
+ case PAM_SUCCESS:
+ /* Otherwise, the authentication looked good */
+ if (strcmp(fn, "wbcLogonUser") == 0) {
+ _pam_log(ctx, LOG_NOTICE,
+ "user '%s' granted access", user);
+ } else {
+ _pam_log(ctx, LOG_NOTICE,
+ "user '%s' OK", user);
+ }
+ return retval;
+ default:
+ /* we don't know anything about this return value */
+ _pam_log(ctx, LOG_ERR,
+ "internal module error (retval = %s(%d), user = '%s')",
+ _pam_error_code_str(retval), retval, user);
+ return retval;
+ }
+}
+
+static int wbc_auth_error_to_pam_error(struct pwb_context *ctx,
+ struct wbcAuthErrorInfo *e,
+ wbcErr status,
+ const char *username,
+ const char *fn)
+{
+ int ret = PAM_AUTH_ERR;
+
+ if (WBC_ERROR_IS_OK(status)) {
+ _pam_log_debug(ctx, LOG_DEBUG, "request %s succeeded",
+ fn);
+ ret = PAM_SUCCESS;
+ return pam_winbind_request_log(ctx, ret, username, fn);
+ }
+
+ if (e) {
+ if (e->pam_error != PAM_SUCCESS) {
+ _pam_log(ctx, LOG_ERR,
+ "request %s failed: %s, "
+ "PAM error: %s (%d), NTSTATUS: %s, "
+ "Error message was: %s",
+ fn,
+ wbcErrorString(status),
+ _pam_error_code_str(e->pam_error),
+ e->pam_error,
+ e->nt_string,
+ e->display_string);
+ ret = e->pam_error;
+ return pam_winbind_request_log(ctx, ret, username, fn);
+ }
+
+ _pam_log(ctx, LOG_ERR, "request %s failed, but PAM error 0!", fn);
+
+ ret = PAM_SERVICE_ERR;
+ return pam_winbind_request_log(ctx, ret, username, fn);
+ }
+
+ ret = wbc_error_to_pam_error(status);
+ _pam_log(ctx, LOG_ERR,
+ "request %s failed: %s, PAM error: %s (%d)!",
+ fn, wbcErrorString(status),
+ _pam_error_code_str(ret), ret);
+ return pam_winbind_request_log(ctx, ret, username, fn);
+}
+
+#if defined(HAVE_PAM_RADIO_TYPE)
+static bool _pam_winbind_change_pwd(struct pwb_context *ctx)
+{
+ struct pam_message msg;
+ const struct pam_message *pmsg;
+ struct pam_response *resp = NULL;
+ int ret;
+ bool retval = false;
+ pmsg = &msg;
+ msg.msg_style = PAM_RADIO_TYPE;
+ msg.msg = _("Do you want to change your password now?");
+ ret = converse(ctx->pamh, 1, &pmsg, &resp);
+ if (resp == NULL) {
+ if (ret == PAM_SUCCESS) {
+ _pam_log(ctx, LOG_CRIT, "pam_winbind: system error!\n");
+ return false;
+ }
+ }
+ if (ret != PAM_SUCCESS) {
+ return false;
+ }
+ _pam_log(ctx, LOG_CRIT, "Received [%s] reply from application.\n", resp->resp);
+
+ if ((resp->resp != NULL) && (strcasecmp(resp->resp, "yes") == 0)) {
+ retval = true;
+ }
+
+ _pam_drop_reply(resp, 1);
+ return retval;
+}
+#else
+static bool _pam_winbind_change_pwd(struct pwb_context *ctx)
+{
+ return false;
+}
+#endif
+
+/**
+ * send a password expiry message if required
+ *
+ * @param ctx PAM winbind context.
+ * @param next_change expected (calculated) next expiry date.
+ * @param already_expired pointer to a boolean to indicate if the password is
+ * already expired.
+ *
+ * @return boolean Returns true if message has been sent, false if not.
+ */
+
+static bool _pam_send_password_expiry_message(struct pwb_context *ctx,
+ time_t next_change,
+ time_t now,
+ int warn_pwd_expire,
+ bool *already_expired,
+ bool *change_pwd)
+{
+ int days = 0;
+ struct tm tm_now, tm_next_change;
+ bool retval = false;
+ int ret;
+
+ if (already_expired) {
+ *already_expired = false;
+ }
+
+ if (change_pwd) {
+ *change_pwd = false;
+ }
+
+ if (next_change <= now) {
+ PAM_WB_REMARK_DIRECT(ctx, "NT_STATUS_PASSWORD_EXPIRED");
+ if (already_expired) {
+ *already_expired = true;
+ }
+ return true;
+ }
+
+ if ((next_change < 0) ||
+ (next_change > now + warn_pwd_expire * SECONDS_PER_DAY)) {
+ return false;
+ }
+
+ if ((localtime_r(&now, &tm_now) == NULL) ||
+ (localtime_r(&next_change, &tm_next_change) == NULL)) {
+ return false;
+ }
+
+ days = (tm_next_change.tm_yday+tm_next_change.tm_year*365) -
+ (tm_now.tm_yday+tm_now.tm_year*365);
+
+ if (days == 0) {
+ ret = _make_remark(ctx, PAM_TEXT_INFO,
+ _("Your password expires today.\n"));
+
+ /*
+ * If change_pwd and already_expired is null.
+ * We are just sending a notification message.
+ * We don't expect any response in this case.
+ */
+
+ if (!change_pwd && !already_expired) {
+ return true;
+ }
+
+ /*
+ * successfully sent the warning message.
+ * Give the user a chance to change pwd.
+ */
+ if (ret == PAM_SUCCESS &&
+ (ctx->ctrl & WINBIND_PWD_CHANGE_PROMPT)) {
+ if (change_pwd) {
+ retval = _pam_winbind_change_pwd(ctx);
+ if (retval) {
+ *change_pwd = true;
+ }
+ }
+ }
+ return true;
+ }
+
+ if (days > 0 && days < warn_pwd_expire) {
+
+ ret = _make_remark_format(ctx, PAM_TEXT_INFO,
+ _("Your password will expire in %d %s.\n"),
+ days, (days > 1) ? _("days"):_("day"));
+ /*
+ * If change_pwd and already_expired is null.
+ * We are just sending a notification message.
+ * We don't expect any response in this case.
+ */
+
+ if (!change_pwd && !already_expired) {
+ return true;
+ }
+
+ /*
+ * successfully sent the warning message.
+ * Give the user a chance to change pwd.
+ */
+ if (ret == PAM_SUCCESS &&
+ (ctx->ctrl & WINBIND_PWD_CHANGE_PROMPT)) {
+ if (change_pwd) {
+ retval = _pam_winbind_change_pwd(ctx);
+ if (retval) {
+ *change_pwd = true;
+ }
+ }
+ }
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Send a warning if the password expires in the near future
+ *
+ * @param ctx PAM winbind context.
+ * @param response The full authentication response structure.
+ * @param already_expired boolean, is the pwd already expired?
+ *
+ * @return void.
+ */
+
+static void _pam_warn_password_expiry(struct pwb_context *ctx,
+ const struct wbcAuthUserInfo *info,
+ int warn_pwd_expire,
+ bool *already_expired,
+ bool *change_pwd)
+{
+ time_t now = time(NULL);
+ time_t next_change = 0;
+
+ if (info == NULL) {
+ return;
+ }
+
+ if (already_expired) {
+ *already_expired = false;
+ }
+
+ if (change_pwd) {
+ *change_pwd = false;
+ }
+
+ /* accounts with WBC_ACB_PWNOEXP set never receive a warning */
+ if (info->acct_flags & WBC_ACB_PWNOEXP) {
+ return;
+ }
+
+ /* no point in sending a warning if this is a grace logon */
+ if (PAM_WB_GRACE_LOGON(info->user_flags)) {
+ return;
+ }
+
+ /* check if the info3 must change timestamp has been set */
+ next_change = info->pass_must_change_time;
+
+ if (_pam_send_password_expiry_message(ctx, next_change, now,
+ warn_pwd_expire,
+ already_expired,
+ change_pwd)) {
+ return;
+ }
+
+ /* no warning sent */
+}
+
+#define IS_SID_STRING(name) (strncmp("S-", name, 2) == 0)
+
+/**
+ * Append a string, making sure not to overflow and to always return a
+ * NULL-terminated string.
+ *
+ * @param dest Destination string buffer (must already be NULL-terminated).
+ * @param src Source string buffer.
+ * @param dest_buffer_size Size of dest buffer in bytes.
+ *
+ * @return false if dest buffer is not big enough (no bytes copied), true on
+ * success.
+ */
+
+static bool safe_append_string(char *dest,
+ const char *src,
+ int dest_buffer_size)
+{
+ size_t len;
+ len = strlcat(dest, src, dest_buffer_size);
+ return (len < dest_buffer_size);
+}
+
+/**
+ * Convert a names into a SID string, appending it to a buffer.
+ *
+ * @param ctx PAM winbind context.
+ * @param user User in PAM request.
+ * @param name Name to convert.
+ * @param sid_list_buffer Where to append the string sid.
+ * @param sid_list_buffer Size of sid_list_buffer (in bytes).
+ *
+ * @return false on failure, true on success.
+ */
+static bool winbind_name_to_sid_string(struct pwb_context *ctx,
+ const char *user,
+ const char *name,
+ char *sid_list_buffer,
+ int sid_list_buffer_size)
+{
+ char sid_string[WBC_SID_STRING_BUFLEN];
+
+ /* lookup name? */
+ if (IS_SID_STRING(name)) {
+ strlcpy(sid_string, name, sizeof(sid_string));
+ } else {
+ wbcErr wbc_status;
+ struct wbcDomainSid sid;
+ enum wbcSidType type;
+
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "no sid given, looking up: %s\n", name);
+
+ wbc_status = wbcCtxLookupName(ctx->wbc_ctx,
+ "",
+ name,
+ &sid,
+ &type);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ _pam_log(ctx, LOG_INFO,
+ "could not lookup name: %s\n", name);
+ return false;
+ }
+
+ wbcSidToStringBuf(&sid, sid_string, sizeof(sid_string));
+ }
+
+ if (!safe_append_string(sid_list_buffer, sid_string,
+ sid_list_buffer_size)) {
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Convert a list of names into a list of sids.
+ *
+ * @param ctx PAM winbind context.
+ * @param user User in PAM request.
+ * @param name_list List of names or string sids, separated by commas.
+ * @param sid_list_buffer Where to put the list of string sids.
+ * @param sid_list_buffer Size of sid_list_buffer (in bytes).
+ *
+ * @return false on failure, true on success.
+ */
+static bool winbind_name_list_to_sid_string_list(struct pwb_context *ctx,
+ const char *user,
+ const char *name_list,
+ char *sid_list_buffer,
+ int sid_list_buffer_size)
+{
+ bool result = false;
+ char *current_name = NULL;
+ const char *search_location;
+ const char *comma;
+ int len;
+
+ if (sid_list_buffer_size > 0) {
+ sid_list_buffer[0] = 0;
+ }
+
+ search_location = name_list;
+ while ((comma = strchr(search_location, ',')) != NULL) {
+ current_name = strndup(search_location,
+ comma - search_location);
+ if (NULL == current_name) {
+ goto out;
+ }
+
+ if (!winbind_name_to_sid_string(ctx, user,
+ current_name,
+ sid_list_buffer,
+ sid_list_buffer_size)) {
+ /*
+ * If one group name failed, we must not fail
+ * the authentication totally, continue with
+ * the following group names. If user belongs to
+ * one of the valid groups, we must allow it
+ * login. -- BoYang
+ */
+
+ _pam_log(ctx, LOG_INFO, "cannot convert group %s to sid, "
+ "check if group %s is valid group.", current_name,
+ current_name);
+ _make_remark_format(ctx, PAM_TEXT_INFO, _("Cannot convert group %s "
+ "to sid, please contact your administrator to see "
+ "if group %s is valid."), current_name, current_name);
+ SAFE_FREE(current_name);
+ search_location = comma + 1;
+ continue;
+ }
+
+ SAFE_FREE(current_name);
+
+ if (!safe_append_string(sid_list_buffer, ",",
+ sid_list_buffer_size)) {
+ goto out;
+ }
+
+ search_location = comma + 1;
+ }
+
+ if (!winbind_name_to_sid_string(ctx, user, search_location,
+ sid_list_buffer,
+ sid_list_buffer_size)) {
+ _pam_log(ctx, LOG_INFO, "cannot convert group %s to sid, "
+ "check if group %s is valid group.", search_location,
+ search_location);
+ _make_remark_format(ctx, PAM_TEXT_INFO, _("Cannot convert group %s "
+ "to sid, please contact your administrator to see "
+ "if group %s is valid."), search_location, search_location);
+
+ /* If no valid groups were converted we should fail outright */
+ if (name_list != NULL && strlen(sid_list_buffer) == 0) {
+ result = false;
+ goto out;
+ }
+ /*
+ * The lookup of the last name failed..
+ * It results in require_member_of_sid ends with ','
+ * It is malformatted parameter here, overwrite the last ','.
+ */
+ len = strlen(sid_list_buffer);
+ if ((len != 0) && (sid_list_buffer[len - 1] == ',')) {
+ sid_list_buffer[len - 1] = '\0';
+ }
+ }
+
+ result = true;
+
+out:
+ SAFE_FREE(current_name);
+ return result;
+}
+
+/**
+ * put krb5ccname variable into environment
+ *
+ * @param ctx PAM winbind context.
+ * @param krb5ccname env variable retrieved from winbindd.
+ *
+ * @return void.
+ */
+
+static void _pam_setup_krb5_env(struct pwb_context *ctx,
+ struct wbcLogonUserInfo *info)
+{
+ char *var = NULL;
+ int ret;
+ uint32_t i;
+ const char *krb5ccname = NULL;
+
+ if (off(ctx->ctrl, WINBIND_KRB5_AUTH)) {
+ return;
+ }
+
+ if (!info) {
+ return;
+ }
+
+ for (i=0; i < info->num_blobs; i++) {
+ if (strcasecmp(info->blobs[i].name, "krb5ccname") == 0) {
+ krb5ccname = (const char *)info->blobs[i].blob.data;
+ break;
+ }
+ }
+
+ if (!krb5ccname || (strlen(krb5ccname) == 0)) {
+ return;
+ }
+
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "request returned KRB5CCNAME: %s", krb5ccname);
+
+ if (asprintf(&var, "KRB5CCNAME=%s", krb5ccname) == -1) {
+ return;
+ }
+
+ ret = pam_putenv(ctx->pamh, var);
+ if (ret != PAM_SUCCESS) {
+ _pam_log(ctx, LOG_ERR,
+ "failed to set KRB5CCNAME to %s: %s",
+ var, pam_strerror(ctx->pamh, ret));
+ }
+ free(var);
+}
+
+/**
+ * Copy unix username if available (further processed in PAM).
+ *
+ * @param ctx PAM winbind context
+ * @param user_ret A pointer that holds a pointer to a string
+ * @param unix_username A username
+ *
+ * @return void.
+ */
+
+static void _pam_setup_unix_username(struct pwb_context *ctx,
+ char **user_ret,
+ struct wbcLogonUserInfo *info)
+{
+ const char *unix_username = NULL;
+ uint32_t i;
+
+ if (!user_ret || !info) {
+ return;
+ }
+
+ for (i=0; i < info->num_blobs; i++) {
+ if (strcasecmp(info->blobs[i].name, "unix_username") == 0) {
+ unix_username = (const char *)info->blobs[i].blob.data;
+ break;
+ }
+ }
+
+ if (!unix_username || !unix_username[0]) {
+ return;
+ }
+
+ *user_ret = strdup(unix_username);
+}
+
+/**
+ * Set string into the PAM stack.
+ *
+ * @param ctx PAM winbind context.
+ * @param data_name Key name for pam_set_data.
+ * @param value String value.
+ *
+ * @return void.
+ */
+
+static void _pam_set_data_string(struct pwb_context *ctx,
+ const char *data_name,
+ const char *value)
+{
+ int ret;
+
+ if (!data_name || !value || (strlen(data_name) == 0) ||
+ (strlen(value) == 0)) {
+ return;
+ }
+
+ ret = pam_set_data(ctx->pamh, data_name, talloc_strdup(NULL, value),
+ _pam_winbind_cleanup_func);
+ if (ret != PAM_SUCCESS) {
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "Could not set data %s: %s\n",
+ data_name, pam_strerror(ctx->pamh, ret));
+ }
+}
+
+/**
+ * Set info3 strings into the PAM stack.
+ *
+ * @param ctx PAM winbind context.
+ * @param data_name Key name for pam_set_data.
+ * @param value String value.
+ *
+ * @return void.
+ */
+
+static void _pam_set_data_info3(struct pwb_context *ctx,
+ const struct wbcAuthUserInfo *info)
+{
+ if (info != NULL) {
+ _pam_set_data_string(ctx, PAM_WINBIND_HOMEDIR,
+ info->home_directory);
+ _pam_set_data_string(ctx, PAM_WINBIND_LOGONSCRIPT,
+ info->logon_script);
+ _pam_set_data_string(ctx, PAM_WINBIND_LOGONSERVER,
+ info->logon_server);
+ _pam_set_data_string(ctx, PAM_WINBIND_PROFILEPATH,
+ info->profile_path);
+ }
+}
+
+/**
+ * Free info3 strings in the PAM stack.
+ *
+ * @param pamh PAM handle
+ *
+ * @return void.
+ */
+
+static void _pam_free_data_info3(pam_handle_t *pamh)
+{
+ pam_set_data(pamh, PAM_WINBIND_HOMEDIR, NULL, NULL);
+ pam_set_data(pamh, PAM_WINBIND_LOGONSCRIPT, NULL, NULL);
+ pam_set_data(pamh, PAM_WINBIND_LOGONSERVER, NULL, NULL);
+ pam_set_data(pamh, PAM_WINBIND_PROFILEPATH, NULL, NULL);
+}
+
+/**
+ * Send PAM_ERROR_MSG for cached or grace logons.
+ *
+ * @param ctx PAM winbind context.
+ * @param username User in PAM request.
+ * @param info3_user_flgs Info3 flags containing logon type bits.
+ *
+ * @return void.
+ */
+
+static void _pam_warn_logon_type(struct pwb_context *ctx,
+ const char *username,
+ uint32_t info3_user_flgs)
+{
+ /* inform about logon type */
+ if (PAM_WB_GRACE_LOGON(info3_user_flgs)) {
+
+ _make_remark(ctx, PAM_ERROR_MSG,
+ _("Grace login. "
+ "Please change your password as soon you're "
+ "online again"));
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "User %s logged on using grace logon\n",
+ username);
+
+ } else if (PAM_WB_CACHED_LOGON(info3_user_flgs)) {
+
+ _make_remark(ctx, PAM_ERROR_MSG,
+ _("Domain Controller unreachable, "
+ "using cached credentials instead. "
+ "Network resources may be unavailable"));
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "User %s logged on using cached credentials\n",
+ username);
+ }
+}
+
+/**
+ * Send PAM_ERROR_MSG for krb5 errors.
+ *
+ * @param ctx PAM winbind context.
+ * @param username User in PAM request.
+ * @param info3_user_flgs Info3 flags containing logon type bits.
+ *
+ * @return void.
+ */
+
+static void _pam_warn_krb5_failure(struct pwb_context *ctx,
+ const char *username,
+ uint32_t info3_user_flgs)
+{
+ if (PAM_WB_KRB5_CLOCK_SKEW(info3_user_flgs)) {
+ _make_remark(ctx, PAM_ERROR_MSG,
+ _("Failed to establish your Kerberos Ticket cache "
+ "due time differences\n"
+ "with the domain controller. "
+ "Please verify the system time.\n"));
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "User %s: Clock skew when getting Krb5 TGT\n",
+ username);
+ }
+}
+
+static bool _pam_check_remark_auth_err(struct pwb_context *ctx,
+ const struct wbcAuthErrorInfo *e,
+ const char *nt_status_string,
+ int *pam_err)
+{
+ const char *ntstatus = NULL;
+ const char *error_string = NULL;
+
+ if (!e || !pam_err) {
+ return false;
+ }
+
+ ntstatus = e->nt_string;
+ if (!ntstatus) {
+ return false;
+ }
+
+ if (strcasecmp(ntstatus, nt_status_string) == 0) {
+
+ error_string = _get_ntstatus_error_string(nt_status_string);
+ if (error_string) {
+ _make_remark(ctx, PAM_ERROR_MSG, error_string);
+ *pam_err = e->pam_error;
+ return true;
+ }
+
+ if (e->display_string) {
+ _make_remark(ctx, PAM_ERROR_MSG, _(e->display_string));
+ *pam_err = e->pam_error;
+ return true;
+ }
+
+ _make_remark(ctx, PAM_ERROR_MSG, nt_status_string);
+ *pam_err = e->pam_error;
+
+ return true;
+ }
+
+ return false;
+};
+
+/**
+ * Compose Password Restriction String for a PAM_ERROR_MSG conversation.
+ *
+ * @param i The wbcUserPasswordPolicyInfo struct.
+ *
+ * @return string (caller needs to talloc_free).
+ */
+
+static char *_pam_compose_pwd_restriction_string(struct pwb_context *ctx,
+ struct wbcUserPasswordPolicyInfo *i)
+{
+ char *str = NULL;
+
+ if (!i) {
+ goto failed;
+ }
+
+ str = talloc_asprintf(ctx, _("Your password "));
+ if (!str) {
+ goto failed;
+ }
+
+ if (i->min_length_password > 0) {
+ str = talloc_asprintf_append(str,
+ _("must be at least %d characters; "),
+ i->min_length_password);
+ if (!str) {
+ goto failed;
+ }
+ }
+
+ if (i->password_history > 0) {
+ str = talloc_asprintf_append(str,
+ _("cannot repeat any of your previous %d "
+ "passwords; "),
+ i->password_history);
+ if (!str) {
+ goto failed;
+ }
+ }
+
+ if (i->password_properties & WBC_DOMAIN_PASSWORD_COMPLEX) {
+ str = talloc_asprintf_append(str,
+ _("must contain capitals, numerals "
+ "or punctuation; "
+ "and cannot contain your account "
+ "or full name; "));
+ if (!str) {
+ goto failed;
+ }
+ }
+
+ str = talloc_asprintf_append(str,
+ _("Please type a different password. "
+ "Type a password which meets these requirements in "
+ "both text boxes."));
+ if (!str) {
+ goto failed;
+ }
+
+ return str;
+
+ failed:
+ TALLOC_FREE(str);
+ return NULL;
+}
+
+static int _pam_create_homedir(struct pwb_context *ctx,
+ const char *dirname,
+ mode_t mode)
+{
+ int ret;
+
+ ret = mkdir(dirname, mode);
+ if (ret != 0 && errno == EEXIST) {
+ struct stat sbuf;
+
+ ret = stat(dirname, &sbuf);
+ if (ret != 0) {
+ return PAM_PERM_DENIED;
+ }
+
+ if (!S_ISDIR(sbuf.st_mode)) {
+ return PAM_PERM_DENIED;
+ }
+ }
+
+ if (ret != 0) {
+ _make_remark_format(ctx, PAM_TEXT_INFO,
+ _("Creating directory: %s failed: %s"),
+ dirname, strerror(errno));
+ _pam_log(ctx, LOG_ERR, "could not create dir: %s (%s)",
+ dirname, strerror(errno));
+ return PAM_PERM_DENIED;
+ }
+
+ return PAM_SUCCESS;
+}
+
+static int _pam_chown_homedir(struct pwb_context *ctx,
+ const char *dirname,
+ uid_t uid,
+ gid_t gid)
+{
+ if (chown(dirname, uid, gid) != 0) {
+ _pam_log(ctx, LOG_ERR, "failed to chown user homedir: %s (%s)",
+ dirname, strerror(errno));
+ return PAM_PERM_DENIED;
+ }
+
+ return PAM_SUCCESS;
+}
+
+static int _pam_mkhomedir(struct pwb_context *ctx)
+{
+ struct passwd *pwd = NULL;
+ char *token = NULL;
+ char *create_dir = NULL;
+ char *user_dir = NULL;
+ int ret;
+ const char *username;
+ mode_t mode = 0700;
+ char *safe_ptr = NULL;
+ char *p = NULL;
+
+ /* Get the username */
+ ret = pam_get_user(ctx->pamh, &username, NULL);
+ if ((ret != PAM_SUCCESS) || (!username)) {
+ _pam_log_debug(ctx, LOG_DEBUG, "can not get the username");
+ return PAM_SERVICE_ERR;
+ }
+
+ pwd = getpwnam(username);
+ if (pwd == NULL) {
+ _pam_log_debug(ctx, LOG_DEBUG, "can not get the username");
+ return PAM_USER_UNKNOWN;
+ }
+ _pam_log_debug(ctx, LOG_DEBUG, "homedir is: %s", pwd->pw_dir);
+
+ ret = _pam_create_homedir(ctx, pwd->pw_dir, 0700);
+ if (ret == PAM_SUCCESS) {
+ ret = _pam_chown_homedir(ctx, pwd->pw_dir,
+ pwd->pw_uid,
+ pwd->pw_gid);
+ }
+
+ if (ret == PAM_SUCCESS) {
+ return ret;
+ }
+
+ /* maybe we need to create parent dirs */
+ create_dir = talloc_strdup(ctx, "/");
+ if (!create_dir) {
+ return PAM_BUF_ERR;
+ }
+
+ /* find final directory */
+ user_dir = strrchr(pwd->pw_dir, '/');
+ if (!user_dir) {
+ return PAM_BUF_ERR;
+ }
+ user_dir++;
+
+ _pam_log(ctx, LOG_DEBUG, "final directory: %s", user_dir);
+
+ p = pwd->pw_dir;
+
+ while ((token = strtok_r(p, "/", &safe_ptr)) != NULL) {
+
+ mode = 0755;
+
+ p = NULL;
+
+ _pam_log_debug(ctx, LOG_DEBUG, "token is %s", token);
+
+ create_dir = talloc_asprintf_append(create_dir, "%s/", token);
+ if (!create_dir) {
+ return PAM_BUF_ERR;
+ }
+ _pam_log_debug(ctx, LOG_DEBUG, "current_dir is %s", create_dir);
+
+ if (strcmp(token, user_dir) == 0) {
+ _pam_log_debug(ctx, LOG_DEBUG, "assuming last directory: %s", token);
+ mode = 0700;
+ }
+
+ ret = _pam_create_homedir(ctx, create_dir, mode);
+ if (ret != PAM_SUCCESS) {
+ return ret;
+ }
+ }
+
+ return _pam_chown_homedir(ctx, create_dir,
+ pwd->pw_uid,
+ pwd->pw_gid);
+}
+
+/* talk to winbindd */
+static int winbind_auth_request(struct pwb_context *ctx,
+ const char *user,
+ const char *pass,
+ const char *member,
+ const char *cctype,
+ const int warn_pwd_expire,
+ struct wbcAuthErrorInfo **p_error,
+ struct wbcLogonUserInfo **p_info,
+ time_t *pwd_last_set,
+ char **user_ret)
+{
+ wbcErr wbc_status;
+ struct wbcLogonUserParams logon;
+ char membership_of[1024];
+ uid_t user_uid = -1;
+ uint32_t flags = WBFLAG_PAM_INFO3_TEXT;
+ struct wbcLogonUserInfo *info = NULL;
+ struct wbcAuthUserInfo *user_info = NULL;
+ struct wbcAuthErrorInfo *error = NULL;
+ int ret = PAM_AUTH_ERR;
+ int i;
+ const char *codes[] = {
+ "NT_STATUS_PASSWORD_EXPIRED",
+ "NT_STATUS_PASSWORD_MUST_CHANGE",
+ "NT_STATUS_INVALID_WORKSTATION",
+ "NT_STATUS_INVALID_LOGON_HOURS",
+ "NT_STATUS_ACCOUNT_EXPIRED",
+ "NT_STATUS_ACCOUNT_DISABLED",
+ "NT_STATUS_ACCOUNT_LOCKED_OUT",
+ "NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT",
+ "NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT",
+ "NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT",
+ "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND",
+ "NT_STATUS_NO_LOGON_SERVERS",
+ "NT_STATUS_WRONG_PASSWORD",
+ "NT_STATUS_ACCESS_DENIED"
+ };
+
+ if (pwd_last_set) {
+ *pwd_last_set = 0;
+ }
+
+ /* Krb5 auth always has to go against the KDC of the user's realm */
+
+ if (ctx->ctrl & WINBIND_KRB5_AUTH) {
+ flags |= WBFLAG_PAM_CONTACT_TRUSTDOM;
+ }
+
+ if (ctx->ctrl & (WINBIND_KRB5_AUTH|WINBIND_CACHED_LOGIN)) {
+ struct passwd *pwd = NULL;
+
+ pwd = getpwnam(user);
+ if (pwd == NULL) {
+ return PAM_USER_UNKNOWN;
+ }
+ user_uid = pwd->pw_uid;
+ }
+
+ if (ctx->ctrl & WINBIND_KRB5_AUTH) {
+
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "enabling krb5 login flag\n");
+
+ flags |= WBFLAG_PAM_KRB5 |
+ WBFLAG_PAM_FALLBACK_AFTER_KRB5;
+ }
+
+ if (ctx->ctrl & WINBIND_CACHED_LOGIN) {
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "enabling cached login flag\n");
+ flags |= WBFLAG_PAM_CACHED_LOGIN;
+ }
+
+ if (user_ret) {
+ *user_ret = NULL;
+ flags |= WBFLAG_PAM_UNIX_NAME;
+ }
+
+ if (cctype != NULL) {
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "enabling request for a %s krb5 ccache\n",
+ cctype);
+ }
+
+ if (member != NULL) {
+
+ ZERO_STRUCT(membership_of);
+
+ if (!winbind_name_list_to_sid_string_list(ctx, user, member,
+ membership_of,
+ sizeof(membership_of))) {
+ _pam_log_debug(ctx, LOG_ERR,
+ "failed to serialize membership of sid "
+ "\"%s\"\n", member);
+ return PAM_AUTH_ERR;
+ }
+ }
+
+ ZERO_STRUCT(logon);
+
+ logon.username = user;
+ logon.password = pass;
+
+ if (cctype) {
+ wbc_status = wbcAddNamedBlob(&logon.num_blobs,
+ &logon.blobs,
+ "krb5_cc_type",
+ 0,
+ discard_const_p(uint8_t, cctype),
+ strlen(cctype)+1);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ goto done;
+ }
+ }
+
+ wbc_status = wbcAddNamedBlob(&logon.num_blobs,
+ &logon.blobs,
+ "flags",
+ 0,
+ (uint8_t *)&flags,
+ sizeof(flags));
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ goto done;
+ }
+
+ wbc_status = wbcAddNamedBlob(&logon.num_blobs,
+ &logon.blobs,
+ "user_uid",
+ 0,
+ (uint8_t *)&user_uid,
+ sizeof(user_uid));
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ goto done;
+ }
+
+ if (member) {
+ wbc_status = wbcAddNamedBlob(&logon.num_blobs,
+ &logon.blobs,
+ "membership_of",
+ 0,
+ (uint8_t *)membership_of,
+ sizeof(membership_of));
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ goto done;
+ }
+ }
+
+ wbc_status = wbcCtxLogonUser(ctx->wbc_ctx,
+ &logon,
+ &info,
+ &error,
+ NULL);
+ ret = wbc_auth_error_to_pam_error(ctx, error, wbc_status,
+ user, "wbcLogonUser");
+ wbcFreeMemory(logon.blobs);
+ logon.blobs = NULL;
+
+ if (info && info->info) {
+ user_info = info->info;
+ }
+
+ if (pwd_last_set && user_info) {
+ *pwd_last_set = user_info->pass_last_set_time;
+ }
+
+ if (p_info && info) {
+ *p_info = info;
+ }
+
+ if (p_error && error) {
+ /* We want to process the error in the caller. */
+ *p_error = error;
+ return ret;
+ }
+
+ for (i=0; i<ARRAY_SIZE(codes); i++) {
+ int _ret = ret;
+ if (_pam_check_remark_auth_err(ctx, error, codes[i], &_ret)) {
+ ret = _ret;
+ goto done;
+ }
+ }
+
+ if ((ret == PAM_SUCCESS) && user_info && info) {
+
+ bool already_expired = false;
+ bool change_pwd = false;
+
+ /* warn a user if the password is about to expire soon */
+ _pam_warn_password_expiry(ctx, user_info,
+ warn_pwd_expire,
+ &already_expired,
+ &change_pwd);
+
+ if (already_expired == true) {
+
+ SMB_TIME_T last_set = user_info->pass_last_set_time;
+ SMB_TIME_T must_set = user_info->pass_must_change_time;
+
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "Password has expired "
+ "(Password was last set: %lld, "
+ "it must be changed here "
+ "%lld (now it's: %ld))\n",
+ (long long int)last_set,
+ (long long int)must_set,
+ (long)time(NULL));
+
+ return PAM_AUTHTOK_EXPIRED;
+ }
+
+ if (change_pwd) {
+ ret = PAM_NEW_AUTHTOK_REQD;
+ goto done;
+ }
+
+ /* inform about logon type */
+ _pam_warn_logon_type(ctx, user, user_info->user_flags);
+
+ /* inform about krb5 failures */
+ _pam_warn_krb5_failure(ctx, user, user_info->user_flags);
+
+ /* set some info3 info for other modules in the stack */
+ _pam_set_data_info3(ctx, user_info);
+
+ /* put krb5ccname into env */
+ _pam_setup_krb5_env(ctx, info);
+
+ /* If winbindd returned a username, return the pointer to it
+ * here. */
+ _pam_setup_unix_username(ctx, user_ret, info);
+ }
+
+ done:
+ wbcFreeMemory(logon.blobs);
+ if (info && info->blobs && !p_info) {
+ wbcFreeMemory(info->blobs);
+ /*
+ * We set blobs to NULL to prevent a use after free in the
+ * in the wbcLogonUserInfoDestructor
+ */
+ info->blobs = NULL;
+ }
+ if (error && !p_error) {
+ wbcFreeMemory(error);
+ }
+ if (info && !p_info) {
+ wbcFreeMemory(info);
+ }
+
+ return ret;
+}
+
+/* talk to winbindd */
+static int winbind_chauthtok_request(struct pwb_context *ctx,
+ const char *user,
+ const char *oldpass,
+ const char *newpass,
+ time_t pwd_last_set)
+{
+ wbcErr wbc_status;
+ struct wbcChangePasswordParams params;
+ struct wbcAuthErrorInfo *error = NULL;
+ struct wbcUserPasswordPolicyInfo *policy = NULL;
+ enum wbcPasswordChangeRejectReason reject_reason = -1;
+ uint32_t flags = 0;
+
+ int i;
+ const char *codes[] = {
+ "NT_STATUS_BACKUP_CONTROLLER",
+ "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND",
+ "NT_STATUS_NO_LOGON_SERVERS",
+ "NT_STATUS_ACCESS_DENIED",
+ "NT_STATUS_PWD_TOO_SHORT", /* TODO: tell the min pwd length ? */
+ "NT_STATUS_PWD_TOO_RECENT", /* TODO: tell the minage ? */
+ "NT_STATUS_PWD_HISTORY_CONFLICT" /* TODO: tell the history length ? */
+ };
+ int ret = PAM_AUTH_ERR;
+
+ ZERO_STRUCT(params);
+
+ if (ctx->ctrl & WINBIND_KRB5_AUTH) {
+ flags |= WBFLAG_PAM_KRB5 |
+ WBFLAG_PAM_CONTACT_TRUSTDOM;
+ }
+
+ if (ctx->ctrl & WINBIND_CACHED_LOGIN) {
+ flags |= WBFLAG_PAM_CACHED_LOGIN;
+ }
+
+ params.account_name = user;
+ params.level = WBC_CHANGE_PASSWORD_LEVEL_PLAIN;
+ params.old_password.plaintext = oldpass;
+ params.new_password.plaintext = newpass;
+ params.flags = flags;
+
+ wbc_status = wbcCtxChangeUserPasswordEx(ctx->wbc_ctx,
+ &params,
+ &error,
+ &reject_reason,
+ &policy);
+ ret = wbc_auth_error_to_pam_error(ctx, error, wbc_status,
+ user, "wbcChangeUserPasswordEx");
+
+ if (WBC_ERROR_IS_OK(wbc_status)) {
+ _pam_log(ctx, LOG_NOTICE,
+ "user '%s' password changed", user);
+ return PAM_SUCCESS;
+ }
+
+ if (!error) {
+ wbcFreeMemory(policy);
+ return ret;
+ }
+
+ for (i=0; i<ARRAY_SIZE(codes); i++) {
+ int _ret = ret;
+ if (_pam_check_remark_auth_err(ctx, error, codes[i], &_ret)) {
+ ret = _ret;
+ goto done;
+ }
+ }
+
+ if (!strcasecmp(error->nt_string,
+ "NT_STATUS_PASSWORD_RESTRICTION")) {
+
+ char *pwd_restriction_string = NULL;
+ SMB_TIME_T min_pwd_age = 0;
+
+ if (policy) {
+ min_pwd_age = policy->min_passwordage;
+ }
+
+ /* FIXME: avoid to send multiple PAM messages after another */
+ switch ((int)reject_reason) {
+ case -1:
+ break;
+ case WBC_PWD_CHANGE_NO_ERROR:
+ if ((min_pwd_age > 0) &&
+ (pwd_last_set + min_pwd_age > time(NULL))) {
+ time_t next_change = pwd_last_set + min_pwd_age;
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-nonliteral"
+#endif
+ _make_remark_format(ctx, PAM_ERROR_MSG,
+ _get_ntstatus_error_string("NT_STATUS_PWD_TOO_RECENT"),
+ ctime(&next_change));
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+ goto done;
+ }
+ break;
+ case WBC_PWD_CHANGE_PASSWORD_TOO_SHORT:
+ PAM_WB_REMARK_DIRECT(ctx,
+ "NT_STATUS_PWD_TOO_SHORT");
+ break;
+ case WBC_PWD_CHANGE_PWD_IN_HISTORY:
+ PAM_WB_REMARK_DIRECT(ctx,
+ "NT_STATUS_PWD_HISTORY_CONFLICT");
+ break;
+ case WBC_PWD_CHANGE_NOT_COMPLEX:
+ _make_remark(ctx, PAM_ERROR_MSG,
+ _("Password does not meet "
+ "complexity requirements"));
+ break;
+ default:
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "unknown password change "
+ "reject reason: %d",
+ reject_reason);
+ break;
+ }
+
+ pwd_restriction_string =
+ _pam_compose_pwd_restriction_string(ctx, policy);
+ if (pwd_restriction_string) {
+ _make_remark(ctx, PAM_ERROR_MSG,
+ pwd_restriction_string);
+ TALLOC_FREE(pwd_restriction_string);
+ }
+ }
+ done:
+ wbcFreeMemory(error);
+ wbcFreeMemory(policy);
+
+ return ret;
+}
+
+/*
+ * Checks if a user has an account
+ *
+ * return values:
+ * 1 = User not found
+ * 0 = OK
+ * -1 = System error
+ */
+static int valid_user(struct pwb_context *ctx,
+ const char *user)
+{
+ /* check not only if the user is available over NSS calls, also make
+ * sure it's really a winbind user, this is important when stacking PAM
+ * modules in the 'account' or 'password' facility. */
+
+ wbcErr wbc_status;
+ struct passwd *pwd = NULL;
+ struct passwd *wb_pwd = NULL;
+
+ pwd = getpwnam(user);
+ if (pwd == NULL) {
+ return 1;
+ }
+
+ wbc_status = wbcCtxGetpwnam(ctx->wbc_ctx, user, &wb_pwd);
+ wbcFreeMemory(wb_pwd);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ _pam_log(ctx, LOG_DEBUG, "valid_user: wbcGetpwnam gave %s\n",
+ wbcErrorString(wbc_status));
+ }
+
+ switch (wbc_status) {
+ case WBC_ERR_UNKNOWN_USER:
+ /* match other insane libwbclient return codes */
+ case WBC_ERR_WINBIND_NOT_AVAILABLE:
+ case WBC_ERR_DOMAIN_NOT_FOUND:
+ return 1;
+ case WBC_ERR_SUCCESS:
+ return 0;
+ default:
+ break;
+ }
+ return -1;
+}
+
+static char *_pam_delete(register char *xx)
+{
+ _pam_overwrite(xx);
+ _pam_drop(xx);
+ return NULL;
+}
+
+/*
+ * obtain a password from the user
+ */
+
+static int _winbind_read_password(struct pwb_context *ctx,
+ unsigned int ctrl,
+ const char *comment,
+ const char *prompt1,
+ const char *prompt2,
+ const char **pass)
+{
+ int authtok_flag;
+ int retval;
+ const char *item;
+ char *token;
+
+ _pam_log(ctx, LOG_DEBUG, "getting password (0x%08x)", ctrl);
+
+ /*
+ * make sure nothing inappropriate gets returned
+ */
+
+ *pass = token = NULL;
+
+ /*
+ * which authentication token are we getting?
+ */
+
+ if (on(WINBIND__OLD_PASSWORD, ctrl)) {
+ authtok_flag = PAM_OLDAUTHTOK;
+ } else {
+ authtok_flag = PAM_AUTHTOK;
+ }
+
+ /*
+ * should we obtain the password from a PAM item ?
+ */
+
+ if (on(WINBIND_TRY_FIRST_PASS_ARG, ctrl) ||
+ on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
+ retval = pam_get_item(ctx->pamh,
+ authtok_flag,
+ (const void **) &item);
+ if (retval != PAM_SUCCESS) {
+ /* very strange. */
+ _pam_log(ctx, LOG_ALERT,
+ "pam_get_item returned error "
+ "to unix-read-password");
+ return retval;
+ } else if (item != NULL) { /* we have a password! */
+ *pass = item;
+ item = NULL;
+ _pam_log(ctx, LOG_DEBUG,
+ "pam_get_item returned a password");
+ return PAM_SUCCESS;
+ } else if (on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
+ return PAM_AUTHTOK_RECOVER_ERR; /* didn't work */
+ } else if (on(WINBIND_USE_AUTHTOK_ARG, ctrl)
+ && off(WINBIND__OLD_PASSWORD, ctrl)) {
+ return PAM_AUTHTOK_RECOVER_ERR;
+ }
+ }
+ /*
+ * getting here implies we will have to get the password from the
+ * user directly.
+ */
+
+ {
+ struct pam_message msg[3];
+ const struct pam_message *pmsg[3];
+ struct pam_response *resp;
+ int i, replies;
+
+ /* prepare to converse */
+
+ if (comment != NULL && off(ctrl, WINBIND_SILENT)) {
+ pmsg[0] = &msg[0];
+ msg[0].msg_style = PAM_TEXT_INFO;
+ msg[0].msg = discard_const_p(char, comment);
+ i = 1;
+ } else {
+ i = 0;
+ }
+
+ pmsg[i] = &msg[i];
+ msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[i++].msg = discard_const_p(char, prompt1);
+ replies = 1;
+
+ if (prompt2 != NULL) {
+ pmsg[i] = &msg[i];
+ msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[i++].msg = discard_const_p(char, prompt2);
+ ++replies;
+ }
+ /* so call the conversation expecting i responses */
+ resp = NULL;
+ retval = converse(ctx->pamh, i, pmsg, &resp);
+ if (resp == NULL) {
+ if (retval == PAM_SUCCESS) {
+ retval = PAM_AUTHTOK_RECOVER_ERR;
+ }
+ goto done;
+ }
+ if (retval != PAM_SUCCESS) {
+ _pam_drop_reply(resp, i);
+ goto done;
+ }
+
+ /* interpret the response */
+
+ token = x_strdup(resp[i - replies].resp);
+ if (!token) {
+ _pam_log(ctx, LOG_NOTICE,
+ "could not recover "
+ "authentication token");
+ retval = PAM_AUTHTOK_RECOVER_ERR;
+ goto done;
+ }
+
+ if (replies == 2) {
+ /* verify that password entered correctly */
+ if (!resp[i - 1].resp ||
+ strcmp(token, resp[i - 1].resp)) {
+ _pam_delete(token); /* mistyped */
+ retval = PAM_AUTHTOK_RECOVER_ERR;
+ _make_remark(ctx, PAM_ERROR_MSG,
+ MISTYPED_PASS);
+ }
+ }
+
+ /*
+ * tidy up the conversation (resp_retcode) is ignored
+ * -- what is it for anyway? AGM
+ */
+ _pam_drop_reply(resp, i);
+ }
+
+ done:
+ if (retval != PAM_SUCCESS) {
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "unable to obtain a password");
+ return retval;
+ }
+ /* 'token' is the entered password */
+
+ /* we store this password as an item */
+
+ retval = pam_set_item(ctx->pamh, authtok_flag, token);
+ _pam_delete(token); /* clean it up */
+ if (retval != PAM_SUCCESS ||
+ (retval = pam_get_item(ctx->pamh, authtok_flag, (const void **) &item)) != PAM_SUCCESS) {
+
+ _pam_log(ctx, LOG_CRIT, "error manipulating password");
+ return retval;
+
+ }
+
+ *pass = item;
+ item = NULL; /* break link to password */
+
+ return PAM_SUCCESS;
+}
+
+static const char *get_conf_item_string(struct pwb_context *ctx,
+ const char *item,
+ int config_flag)
+{
+ int i = 0;
+ const char *parm_opt = NULL;
+
+ if (!(ctx->ctrl & config_flag)) {
+ goto out;
+ }
+
+ /* let the pam opt take precedence over the pam_winbind.conf option */
+ for (i=0; i<ctx->argc; i++) {
+
+ if ((strncmp(ctx->argv[i], item, strlen(item)) == 0)) {
+ char *p;
+
+ if ((p = strchr(ctx->argv[i], '=')) == NULL) {
+ _pam_log(ctx, LOG_INFO,
+ "no \"=\" delimiter for \"%s\" found\n",
+ item);
+ goto out;
+ }
+ _pam_log_debug(ctx, LOG_INFO,
+ "PAM config: %s '%s'\n", item, p+1);
+ return p + 1;
+ }
+ }
+
+ if (ctx->dict) {
+ char *key = NULL;
+
+ key = talloc_asprintf(ctx, "global:%s", item);
+ if (!key) {
+ goto out;
+ }
+
+ parm_opt = tiniparser_getstring_nonempty(ctx->dict, key, NULL);
+ TALLOC_FREE(key);
+
+ _pam_log_debug(ctx, LOG_INFO, "CONFIG file: %s '%s'\n",
+ item, parm_opt);
+ }
+out:
+ return parm_opt;
+}
+
+static int get_config_item_int(struct pwb_context *ctx,
+ const char *item,
+ int config_flag)
+{
+ int i, parm_opt = -1;
+
+ if (!(ctx->ctrl & config_flag)) {
+ goto out;
+ }
+
+ /* let the pam opt take precedence over the pam_winbind.conf option */
+ for (i = 0; i < ctx->argc; i++) {
+
+ if ((strncmp(ctx->argv[i], item, strlen(item)) == 0)) {
+ char *p;
+
+ if ((p = strchr(ctx->argv[i], '=')) == NULL) {
+ _pam_log(ctx, LOG_INFO,
+ "no \"=\" delimiter for \"%s\" found\n",
+ item);
+ goto out;
+ }
+ parm_opt = atoi(p + 1);
+ _pam_log_debug(ctx, LOG_INFO,
+ "PAM config: %s '%d'\n",
+ item, parm_opt);
+ return parm_opt;
+ }
+ }
+
+ if (ctx->dict) {
+ char *key = NULL;
+
+ key = talloc_asprintf(ctx, "global:%s", item);
+ if (!key) {
+ goto out;
+ }
+
+ parm_opt = tiniparser_getint(ctx->dict, key, -1);
+ TALLOC_FREE(key);
+
+ _pam_log_debug(ctx, LOG_INFO,
+ "CONFIG file: %s '%d'\n",
+ item, parm_opt);
+ }
+out:
+ return parm_opt;
+}
+
+static const char *get_krb5_cc_type_from_config(struct pwb_context *ctx)
+{
+ return get_conf_item_string(ctx, "krb5_ccache_type",
+ WINBIND_KRB5_CCACHE_TYPE);
+}
+
+static const char *get_member_from_config(struct pwb_context *ctx)
+{
+ const char *ret = NULL;
+ ret = get_conf_item_string(ctx, "require_membership_of",
+ WINBIND_REQUIRED_MEMBERSHIP);
+ if (ret != NULL) {
+ return ret;
+ }
+ return get_conf_item_string(ctx, "require-membership-of",
+ WINBIND_REQUIRED_MEMBERSHIP);
+}
+
+static int get_warn_pwd_expire_from_config(struct pwb_context *ctx)
+{
+ int ret;
+ ret = get_config_item_int(ctx, "warn_pwd_expire",
+ WINBIND_WARN_PWD_EXPIRE);
+ /* no or broken setting */
+ if (ret < 0) {
+ return DEFAULT_DAYS_TO_WARN_BEFORE_PWD_EXPIRES;
+ }
+ return ret;
+}
+
+/**
+ * Retrieve the winbind separator.
+ *
+ * @param ctx PAM winbind context.
+ *
+ * @return string separator character. NULL on failure.
+ */
+
+static char winbind_get_separator(struct pwb_context *ctx)
+{
+ wbcErr wbc_status;
+ static struct wbcInterfaceDetails *details = NULL;
+
+ wbc_status = wbcCtxInterfaceDetails(ctx->wbc_ctx, &details);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ _pam_log(ctx, LOG_ERR,
+ "Could not retrieve winbind interface details: %s",
+ wbcErrorString(wbc_status));
+ return '\0';
+ }
+
+ if (!details) {
+ return '\0';
+ }
+
+ return details->winbind_separator;
+}
+
+
+/**
+ * Convert a upn to a name.
+ *
+ * @param ctx PAM winbind context.
+ * @param upn User UPN to be translated.
+ *
+ * @return converted name. NULL pointer on failure. Caller needs to free.
+ */
+
+static char* winbind_upn_to_username(struct pwb_context *ctx,
+ const char *upn)
+{
+ char sep;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainSid sid;
+ enum wbcSidType type;
+ char *domain = NULL;
+ char *name;
+ char *p;
+ char *result;
+
+ /* This cannot work when the winbind separator = @ */
+
+ sep = winbind_get_separator(ctx);
+ if (!sep || sep == '@') {
+ return NULL;
+ }
+
+ name = talloc_strdup(ctx, upn);
+ if (!name) {
+ return NULL;
+ }
+
+ p = strchr(name, '@');
+ if (p == NULL) {
+ TALLOC_FREE(name);
+ return NULL;
+ }
+ *p = '\0';
+ domain = p + 1;
+
+ /* Convert the UPN to a SID */
+
+ wbc_status = wbcCtxLookupName(ctx->wbc_ctx, domain, name, &sid, &type);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return NULL;
+ }
+
+ /* Convert the the SID back to the sAMAccountName */
+
+ wbc_status = wbcCtxLookupSid(ctx->wbc_ctx, &sid, &domain, &name, &type);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return NULL;
+ }
+
+ result = talloc_asprintf(ctx, "%s%c%s", domain, sep, name);
+ wbcFreeMemory(domain);
+ wbcFreeMemory(name);
+ return result;
+}
+
+static int _pam_delete_cred(pam_handle_t *pamh, int flags,
+ int argc, enum pam_winbind_request_type type,
+ const char **argv)
+{
+ int retval = PAM_SUCCESS;
+ struct pwb_context *ctx = NULL;
+ struct wbcLogoffUserParams logoff;
+ struct wbcAuthErrorInfo *error = NULL;
+ const char *user;
+ wbcErr wbc_status = WBC_ERR_SUCCESS;
+
+ ZERO_STRUCT(logoff);
+
+ retval = _pam_winbind_init_context(pamh, flags, argc, argv, type, &ctx);
+ if (retval != PAM_SUCCESS) {
+ return retval;
+ }
+
+ _PAM_LOG_FUNCTION_ENTER("_pam_delete_cred", ctx);
+
+ if (ctx->ctrl & WINBIND_KRB5_AUTH) {
+
+ /* destroy the ccache here */
+
+ uint32_t wbc_flags = 0;
+ const char *ccname = NULL;
+ struct passwd *pwd = NULL;
+
+ retval = pam_get_user(pamh, &user, _("Username: "));
+ if (retval != PAM_SUCCESS) {
+ _pam_log(ctx, LOG_ERR,
+ "could not identify user");
+ goto out;
+ }
+
+ if (user == NULL) {
+ _pam_log(ctx, LOG_ERR,
+ "username was NULL!");
+ retval = PAM_USER_UNKNOWN;
+ goto out;
+ }
+
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "username [%s] obtained", user);
+
+ ccname = pam_getenv(pamh, "KRB5CCNAME");
+ if (ccname == NULL) {
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "user has no KRB5CCNAME environment");
+ }
+
+ pwd = getpwnam(user);
+ if (pwd == NULL) {
+ retval = PAM_USER_UNKNOWN;
+ goto out;
+ }
+
+ wbc_flags = WBFLAG_PAM_KRB5 |
+ WBFLAG_PAM_CONTACT_TRUSTDOM;
+
+ logoff.username = user;
+
+ if (ccname) {
+ wbc_status = wbcAddNamedBlob(&logoff.num_blobs,
+ &logoff.blobs,
+ "ccfilename",
+ 0,
+ discard_const_p(uint8_t, ccname),
+ strlen(ccname)+1);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ goto out;
+ }
+ }
+
+ wbc_status = wbcAddNamedBlob(&logoff.num_blobs,
+ &logoff.blobs,
+ "flags",
+ 0,
+ (uint8_t *)&wbc_flags,
+ sizeof(wbc_flags));
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ goto out;
+ }
+
+ wbc_status = wbcAddNamedBlob(&logoff.num_blobs,
+ &logoff.blobs,
+ "user_uid",
+ 0,
+ (uint8_t *)&pwd->pw_uid,
+ sizeof(pwd->pw_uid));
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ goto out;
+ }
+
+ wbc_status = wbcCtxLogoffUserEx(ctx->wbc_ctx, &logoff, &error);
+ retval = wbc_auth_error_to_pam_error(ctx, error, wbc_status,
+ user, "wbcLogoffUser");
+ wbcFreeMemory(logoff.blobs);
+ logoff.blobs = NULL;
+
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ _pam_log(ctx, LOG_INFO,
+ "failed to logoff user %s: %s\n",
+ user, wbcErrorString(wbc_status));
+ }
+ }
+
+out:
+ if (logoff.blobs) {
+ wbcFreeMemory(logoff.blobs);
+ }
+
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ retval = wbc_auth_error_to_pam_error(ctx, error, wbc_status,
+ user, "wbcLogoffUser");
+ }
+ wbcFreeMemory(error);
+
+ /*
+ * Delete the krb5 ccname variable from the PAM environment
+ * if it was set by winbind.
+ */
+ if ((ctx->ctrl & WINBIND_KRB5_AUTH) && pam_getenv(pamh, "KRB5CCNAME")) {
+ pam_putenv(pamh, "KRB5CCNAME");
+ }
+
+ _PAM_LOG_FUNCTION_LEAVE("_pam_delete_cred", ctx, retval);
+
+ TALLOC_FREE(ctx);
+
+ return retval;
+}
+
+#ifdef SECURITY_OPENPAM_H_INCLUDED
+/*
+ * Logic below is copied from openpam_check_error_code() in
+ *./contrib/openpam/lib/libpam/openpam_dispatch.c on FreeBSD.
+ */
+static int openpam_convert_error_code(struct pwb_context *ctx,
+ enum pam_winbind_request_type req,
+ int r)
+{
+ if (r == PAM_SUCCESS ||
+ r == PAM_SYSTEM_ERR ||
+ r == PAM_SERVICE_ERR ||
+ r == PAM_BUF_ERR ||
+ r == PAM_CONV_ERR ||
+ r == PAM_PERM_DENIED ||
+ r == PAM_ABORT) {
+ return r;
+ }
+
+ /* specific winbind request types */
+ switch (req) {
+ case PAM_WINBIND_AUTHENTICATE:
+ if (r == PAM_AUTH_ERR ||
+ r == PAM_CRED_INSUFFICIENT ||
+ r == PAM_AUTHINFO_UNAVAIL ||
+ r == PAM_USER_UNKNOWN ||
+ r == PAM_MAXTRIES) {
+ return r;
+ }
+ break;
+ case PAM_WINBIND_SETCRED:
+ if (r == PAM_CRED_UNAVAIL ||
+ r == PAM_CRED_EXPIRED ||
+ r == PAM_USER_UNKNOWN ||
+ r == PAM_CRED_ERR) {
+ return r;
+ }
+ break;
+ case PAM_WINBIND_ACCT_MGMT:
+ if (r == PAM_USER_UNKNOWN ||
+ r == PAM_AUTH_ERR ||
+ r == PAM_NEW_AUTHTOK_REQD ||
+ r == PAM_ACCT_EXPIRED) {
+ return r;
+ }
+ break;
+ case PAM_WINBIND_OPEN_SESSION:
+ case PAM_WINBIND_CLOSE_SESSION:
+ if (r == PAM_SESSION_ERR) {
+ return r;
+ }
+ break;
+ case PAM_WINBIND_CHAUTHTOK:
+ if (r == PAM_PERM_DENIED ||
+ r == PAM_AUTHTOK_ERR ||
+ r == PAM_AUTHTOK_RECOVERY_ERR ||
+ r == PAM_AUTHTOK_LOCK_BUSY ||
+ r == PAM_AUTHTOK_DISABLE_AGING ||
+ r == PAM_TRY_AGAIN) {
+ return r;
+ }
+ break;
+ default:
+ break;
+ }
+ _pam_log(ctx, LOG_INFO,
+ "Converting PAM error [%d] to PAM_SERVICE_ERR.\n", r);
+ return PAM_SERVICE_ERR;
+};
+#define pam_error_code(a, b, c) openpam_convert_error_code(a, b, c)
+#else
+#define pam_error_code(a, b, c) (c)
+#endif
+
+_PUBLIC_ PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ const char *username;
+ const char *password;
+ const char *member = NULL;
+ const char *cctype = NULL;
+ int warn_pwd_expire;
+ int retval = PAM_AUTH_ERR;
+ char *username_ret = NULL;
+ char *new_authtok_required = NULL;
+ char *real_username = NULL;
+ struct pwb_context *ctx = NULL;
+
+ retval = _pam_winbind_init_context(pamh, flags, argc, argv,
+ PAM_WINBIND_AUTHENTICATE, &ctx);
+ if (retval != PAM_SUCCESS) {
+ return retval;
+ }
+
+ _PAM_LOG_FUNCTION_ENTER("pam_sm_authenticate", ctx);
+
+ /* Get the username */
+ retval = pam_get_user(pamh, &username, NULL);
+ if ((retval != PAM_SUCCESS) || (!username)) {
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "can not get the username");
+ retval = PAM_SERVICE_ERR;
+ goto out;
+ }
+
+
+#if defined(AIX)
+ /* Decode the user name since AIX does not support logn user
+ names by default. The name is encoded as _#uid. */
+
+ if (username[0] == '_') {
+ uid_t id = atoi(&username[1]);
+ struct passwd *pw = NULL;
+
+ if ((id!=0) && ((pw = getpwuid(id)) != NULL)) {
+ real_username = strdup(pw->pw_name);
+ }
+ }
+#endif
+
+ if (!real_username) {
+ /* Just making a copy of the username we got from PAM */
+ if ((real_username = strdup(username)) == NULL) {
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "memory allocation failure when copying "
+ "username");
+ retval = PAM_SERVICE_ERR;
+ goto out;
+ }
+ }
+
+ /* Maybe this was a UPN */
+
+ if (strchr(real_username, '@') != NULL) {
+ char *samaccountname = NULL;
+
+ samaccountname = winbind_upn_to_username(ctx,
+ real_username);
+ if (samaccountname) {
+ free(real_username);
+ real_username = strdup(samaccountname);
+ }
+ }
+
+ retval = _winbind_read_password(ctx, ctx->ctrl, NULL,
+ _("Password: "), NULL,
+ &password);
+
+ if (retval != PAM_SUCCESS) {
+ _pam_log(ctx, LOG_ERR,
+ "Could not retrieve user's password");
+ retval = PAM_AUTHTOK_ERR;
+ goto out;
+ }
+
+ /* Let's not give too much away in the log file */
+
+#ifdef DEBUG_PASSWORD
+ _pam_log_debug(ctx, LOG_INFO,
+ "Verify user '%s' with password '%s'",
+ real_username, password);
+#else
+ _pam_log_debug(ctx, LOG_INFO,
+ "Verify user '%s'", real_username);
+#endif
+
+ member = get_member_from_config(ctx);
+ cctype = get_krb5_cc_type_from_config(ctx);
+ warn_pwd_expire = get_warn_pwd_expire_from_config(ctx);
+
+ /* Now use the username to look up password */
+ retval = winbind_auth_request(ctx, real_username, password,
+ member, cctype, warn_pwd_expire,
+ NULL, NULL, NULL, &username_ret);
+
+ if (retval == PAM_NEW_AUTHTOK_REQD ||
+ retval == PAM_AUTHTOK_EXPIRED) {
+
+ char *new_authtok_required_during_auth = NULL;
+
+ new_authtok_required = talloc_asprintf(NULL, "%d", retval);
+ if (!new_authtok_required) {
+ retval = PAM_BUF_ERR;
+ goto out;
+ }
+
+ pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD,
+ new_authtok_required,
+ _pam_winbind_cleanup_func);
+
+ retval = PAM_SUCCESS;
+
+ new_authtok_required_during_auth = talloc_asprintf(NULL, "%d", true);
+ if (!new_authtok_required_during_auth) {
+ retval = PAM_BUF_ERR;
+ goto out;
+ }
+
+ pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH,
+ new_authtok_required_during_auth,
+ _pam_winbind_cleanup_func);
+
+ goto out;
+ }
+
+out:
+ if (username_ret) {
+ pam_set_item (pamh, PAM_USER, username_ret);
+ _pam_log_debug(ctx, LOG_INFO,
+ "Returned user was '%s'", username_ret);
+ free(username_ret);
+ }
+
+ if (real_username) {
+ free(real_username);
+ }
+
+ if (!new_authtok_required) {
+ pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD, NULL, NULL);
+ }
+
+ if (retval != PAM_SUCCESS) {
+ _pam_free_data_info3(pamh);
+ }
+
+ _PAM_LOG_FUNCTION_LEAVE("pam_sm_authenticate", ctx, retval);
+
+ TALLOC_FREE(ctx);
+
+ return retval;
+}
+
+_PUBLIC_ PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ int ret = PAM_SYSTEM_ERR;
+ struct pwb_context *ctx = NULL;
+
+ ret = _pam_winbind_init_context(pamh, flags, argc, argv,
+ PAM_WINBIND_SETCRED, &ctx);
+ if (ret != PAM_SUCCESS) {
+ return ret;
+ }
+
+ _PAM_LOG_FUNCTION_ENTER("pam_sm_setcred", ctx);
+
+ switch (flags & ~PAM_SILENT) {
+
+ case PAM_DELETE_CRED:
+ ret = _pam_delete_cred(pamh, flags, argc,
+ PAM_WINBIND_SETCRED, argv);
+ break;
+ case PAM_REFRESH_CRED:
+ _pam_log_debug(ctx, LOG_WARNING,
+ "PAM_REFRESH_CRED not implemented");
+ ret = PAM_SUCCESS;
+ break;
+ case PAM_REINITIALIZE_CRED:
+ _pam_log_debug(ctx, LOG_WARNING,
+ "PAM_REINITIALIZE_CRED not implemented");
+ ret = PAM_SUCCESS;
+ break;
+ case PAM_ESTABLISH_CRED:
+ _pam_log_debug(ctx, LOG_WARNING,
+ "PAM_ESTABLISH_CRED not implemented");
+ ret = PAM_SUCCESS;
+ break;
+ default:
+ ret = PAM_SYSTEM_ERR;
+ break;
+ }
+
+ _PAM_LOG_FUNCTION_LEAVE("pam_sm_setcred", ctx, ret);
+
+ TALLOC_FREE(ctx);
+
+ return pam_error_code(ctx, PAM_WINBIND_SETCRED, ret);
+}
+
+/*
+ * Account management. We want to verify that the account exists
+ * before returning PAM_SUCCESS
+ */
+_PUBLIC_ PAM_EXTERN
+int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ const char *username;
+ int ret = PAM_USER_UNKNOWN;
+ const char *tmp = NULL;
+ struct pwb_context *ctx = NULL;
+
+ ret = _pam_winbind_init_context(pamh, flags, argc, argv,
+ PAM_WINBIND_ACCT_MGMT, &ctx);
+ if (ret != PAM_SUCCESS) {
+ return ret;
+ }
+
+ _PAM_LOG_FUNCTION_ENTER("pam_sm_acct_mgmt", ctx);
+
+
+ /* Get the username */
+ ret = pam_get_user(pamh, &username, NULL);
+ if ((ret != PAM_SUCCESS) || (!username)) {
+ _pam_log_debug(ctx, LOG_DEBUG,
+ "can not get the username");
+ ret = PAM_SERVICE_ERR;
+ goto out;
+ }
+
+ /* Verify the username */
+ ret = valid_user(ctx, username);
+ switch (ret) {
+ case -1:
+ /* some sort of system error. The log was already printed */
+ ret = PAM_SERVICE_ERR;
+ goto out;
+ case 1:
+ /* the user does not exist */
+ _pam_log_debug(ctx, LOG_NOTICE, "user '%s' not found",
+ username);
+ if (ctx->ctrl & WINBIND_UNKNOWN_OK_ARG) {
+ ret = PAM_IGNORE;
+ goto out;
+ }
+ ret = PAM_USER_UNKNOWN;
+ goto out;
+ case 0:
+ pam_get_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD,
+ (const void **)&tmp);
+ if (tmp != NULL) {
+ ret = atoi(tmp);
+ switch (ret) {
+ case PAM_AUTHTOK_EXPIRED:
+ /* Since new token is required in this case */
+ FALL_THROUGH;
+ case PAM_NEW_AUTHTOK_REQD:
+ _pam_log(ctx, LOG_WARNING,
+ "pam_sm_acct_mgmt success but %s is set",
+ PAM_WINBIND_NEW_AUTHTOK_REQD);
+ _pam_log(ctx, LOG_NOTICE,
+ "user '%s' needs new password",
+ username);
+ /* PAM_AUTHTOKEN_REQD does not exist, but is documented in the manpage */
+ ret = PAM_NEW_AUTHTOK_REQD;
+ goto out;
+ default:
+ _pam_log(ctx, LOG_WARNING,
+ "pam_sm_acct_mgmt success");
+ _pam_log(ctx, LOG_NOTICE,
+ "user '%s' granted access", username);
+ ret = PAM_SUCCESS;
+ goto out;
+ }
+ }
+
+ /* Otherwise, the authentication looked good */
+ _pam_log(ctx, LOG_NOTICE,
+ "user '%s' granted access", username);
+ ret = PAM_SUCCESS;
+ goto out;
+ default:
+ /* we don't know anything about this return value */
+ _pam_log(ctx, LOG_ERR,
+ "internal module error (ret = %d, user = '%s')",
+ ret, username);
+ ret = PAM_SERVICE_ERR;
+ goto out;
+ }
+
+ /* should not be reached */
+ ret = PAM_IGNORE;
+
+ out:
+
+ _PAM_LOG_FUNCTION_LEAVE("pam_sm_acct_mgmt", ctx, ret);
+
+ TALLOC_FREE(ctx);
+
+ return pam_error_code(ctx, PAM_WINBIND_ACCT_MGMT, ret);
+}
+
+_PUBLIC_ PAM_EXTERN
+int pam_sm_open_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ int ret = PAM_SUCCESS;
+ struct pwb_context *ctx = NULL;
+
+ ret = _pam_winbind_init_context(pamh, flags, argc, argv,
+ PAM_WINBIND_OPEN_SESSION, &ctx);
+ if (ret != PAM_SUCCESS) {
+ return ret;
+ }
+
+ _PAM_LOG_FUNCTION_ENTER("pam_sm_open_session", ctx);
+
+ if (ctx->ctrl & WINBIND_MKHOMEDIR) {
+ /* check and create homedir */
+ ret = _pam_mkhomedir(ctx);
+ }
+
+ _PAM_LOG_FUNCTION_LEAVE("pam_sm_open_session", ctx, ret);
+
+ TALLOC_FREE(ctx);
+
+ return pam_error_code(ctx, PAM_WINBIND_OPEN_SESSION, ret);
+}
+
+_PUBLIC_ PAM_EXTERN
+int pam_sm_close_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ int ret = PAM_SUCCESS;
+ struct pwb_context *ctx = NULL;
+
+ ret = _pam_winbind_init_context(pamh, flags, argc, argv,
+ PAM_WINBIND_CLOSE_SESSION, &ctx);
+ if (ret != PAM_SUCCESS) {
+ return ret;
+ }
+
+ _PAM_LOG_FUNCTION_ENTER("pam_sm_close_session", ctx);
+
+ _PAM_LOG_FUNCTION_LEAVE("pam_sm_close_session", ctx, ret);
+
+ TALLOC_FREE(ctx);
+
+ return pam_error_code(ctx, PAM_WINBIND_CLOSE_SESSION, ret);
+}
+
+/**
+ * evaluate whether we need to re-authenticate with kerberos after a
+ * password change
+ *
+ * @param ctx PAM winbind context.
+ * @param user The username
+ *
+ * @return boolean Returns true if required, false if not.
+ */
+
+static bool _pam_require_krb5_auth_after_chauthtok(struct pwb_context *ctx,
+ const char *user)
+{
+
+ /* Make sure that we only do this if a) the chauthtok got initiated
+ * during a logon attempt (authenticate->acct_mgmt->chauthtok) b) any
+ * later password change via the "passwd" command if done by the user
+ * itself
+ * NB. If we login from gdm or xdm and the password expires,
+ * we change the password, but there is no memory cache.
+ * Thus, even for passthrough login, we should do the
+ * authentication again to update memory cache.
+ * --- BoYang
+ * */
+
+ const char *new_authtok_reqd_during_auth = NULL;
+ struct passwd *pwd = NULL;
+
+ pam_get_data(ctx->pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH,
+ (const void **) &new_authtok_reqd_during_auth);
+ pam_set_data(ctx->pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH,
+ NULL, NULL);
+
+ if (new_authtok_reqd_during_auth) {
+ return true;
+ }
+
+ pwd = getpwnam(user);
+ if (!pwd) {
+ return false;
+ }
+
+ if (getuid() == pwd->pw_uid) {
+ return true;
+ }
+
+ return false;
+}
+
+
+_PUBLIC_ PAM_EXTERN
+int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
+ int argc, const char **argv)
+{
+ unsigned int lctrl;
+ int ret;
+ bool cached_login = false;
+
+ /* <DO NOT free() THESE> */
+ const char *user;
+ const char *pass_old;
+ const char *pass_new;
+ /* </DO NOT free() THESE> */
+
+ char *Announce;
+
+ int retry = 0;
+ char *username_ret = NULL;
+ struct wbcAuthErrorInfo *error = NULL;
+ struct pwb_context *ctx = NULL;
+
+ ret = _pam_winbind_init_context(pamh, flags, argc, argv,
+ PAM_WINBIND_CHAUTHTOK, &ctx);
+ if (ret != PAM_SUCCESS) {
+ return ret;
+ }
+
+ _PAM_LOG_FUNCTION_ENTER("pam_sm_chauthtok", ctx);
+
+ cached_login = (ctx->ctrl & WINBIND_CACHED_LOGIN);
+
+ /* clearing offline bit for auth */
+ ctx->ctrl &= ~WINBIND_CACHED_LOGIN;
+
+ /*
+ * First get the name of a user
+ */
+ ret = pam_get_user(pamh, &user, _("Username: "));
+ if (ret != PAM_SUCCESS) {
+ _pam_log(ctx, LOG_ERR,
+ "password - could not identify user");
+ goto out;
+ }
+
+ if (user == NULL) {
+ _pam_log(ctx, LOG_ERR, "username was NULL!");
+ ret = PAM_USER_UNKNOWN;
+ goto out;
+ }
+
+ _pam_log_debug(ctx, LOG_DEBUG, "username [%s] obtained", user);
+
+ /* check if this is really a user in winbindd, not only in NSS */
+ ret = valid_user(ctx, user);
+ switch (ret) {
+ case 1:
+ ret = PAM_USER_UNKNOWN;
+ goto out;
+ case -1:
+ ret = PAM_SYSTEM_ERR;
+ goto out;
+ default:
+ break;
+ }
+
+ /*
+ * obtain and verify the current password (OLDAUTHTOK) for
+ * the user.
+ */
+
+ if (flags & PAM_PRELIM_CHECK) {
+ time_t *pwdlastset_prelim = NULL;
+
+ pwdlastset_prelim = talloc_zero(NULL, time_t);
+ if (pwdlastset_prelim == NULL) {
+ _pam_log(ctx, LOG_CRIT,
+ "password - out of memory");
+ ret = PAM_BUF_ERR;
+ goto out;
+ }
+
+ /* instruct user what is happening */
+
+#define greeting _("Changing password for")
+ Announce = talloc_asprintf(ctx, "%s %s", greeting, user);
+ if (!Announce) {
+ _pam_log(ctx, LOG_CRIT,
+ "password - out of memory");
+ ret = PAM_BUF_ERR;
+ goto out;
+ }
+#undef greeting
+
+ lctrl = ctx->ctrl | WINBIND__OLD_PASSWORD;
+ ret = _winbind_read_password(ctx, lctrl,
+ Announce,
+ _("(current) NT password: "),
+ NULL,
+ (const char **) &pass_old);
+ TALLOC_FREE(Announce);
+ if (ret != PAM_SUCCESS) {
+ _pam_log(ctx, LOG_NOTICE,
+ "password - (old) token not obtained");
+ goto out;
+ }
+
+ /* verify that this is the password for this user */
+
+ ret = winbind_auth_request(ctx, user, pass_old,
+ NULL, NULL, 0,
+ &error, NULL,
+ pwdlastset_prelim, NULL);
+
+ if (ret != PAM_ACCT_EXPIRED &&
+ ret != PAM_AUTHTOK_EXPIRED &&
+ ret != PAM_NEW_AUTHTOK_REQD &&
+ ret != PAM_SUCCESS) {
+ pass_old = NULL;
+ goto out;
+ }
+
+ pam_set_data(pamh, PAM_WINBIND_PWD_LAST_SET,
+ pwdlastset_prelim,
+ _pam_winbind_cleanup_func);
+
+ ret = pam_set_item(pamh, PAM_OLDAUTHTOK,
+ (const void *) pass_old);
+ pass_old = NULL;
+ if (ret != PAM_SUCCESS) {
+ _pam_log(ctx, LOG_CRIT,
+ "failed to set PAM_OLDAUTHTOK");
+ }
+ } else if (flags & PAM_UPDATE_AUTHTOK) {
+
+ time_t *pwdlastset_update = NULL;
+
+ /*
+ * obtain the proposed password
+ */
+
+ /*
+ * get the old token back.
+ */
+
+ ret = pam_get_item(pamh, PAM_OLDAUTHTOK, (const void **) &pass_old);
+
+ if (ret != PAM_SUCCESS) {
+ _pam_log(ctx, LOG_NOTICE,
+ "user not authenticated");
+ goto out;
+ }
+
+ lctrl = ctx->ctrl & ~WINBIND_TRY_FIRST_PASS_ARG;
+
+ if (on(WINBIND_USE_AUTHTOK_ARG, lctrl)) {
+ lctrl |= WINBIND_USE_FIRST_PASS_ARG;
+ }
+ if (on(WINBIND_TRY_AUTHTOK_ARG, lctrl)) {
+ lctrl |= WINBIND_TRY_FIRST_PASS_ARG;
+ }
+ retry = 0;
+ ret = PAM_AUTHTOK_ERR;
+ while ((ret != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
+ /*
+ * use_authtok is to force the use of a previously entered
+ * password -- needed for pluggable password strength checking
+ */
+
+ ret = _winbind_read_password(ctx, lctrl,
+ NULL,
+ _("Enter new NT password: "),
+ _("Retype new NT password: "),
+ (const char **)&pass_new);
+
+ if (ret != PAM_SUCCESS) {
+ _pam_log_debug(ctx, LOG_ALERT,
+ "password - "
+ "new password not obtained");
+ pass_old = NULL;/* tidy up */
+ goto out;
+ }
+
+ /*
+ * At this point we know who the user is and what they
+ * propose as their new password. Verify that the new
+ * password is acceptable.
+ */
+
+ if (pass_new[0] == '\0') {/* "\0" password = NULL */
+ pass_new = NULL;
+ }
+ }
+
+ /*
+ * By reaching here we have approved the passwords and must now
+ * rebuild the password database file.
+ */
+ pam_get_data(pamh,
+ PAM_WINBIND_PWD_LAST_SET,
+ (const void **)&pwdlastset_update);
+
+ /*
+ * if cached creds were enabled, make sure to set the
+ * WINBIND_CACHED_LOGIN bit here in order to have winbindd
+ * update the cached creds storage - gd
+ */
+ if (cached_login) {
+ ctx->ctrl |= WINBIND_CACHED_LOGIN;
+ }
+
+ ret = winbind_chauthtok_request(ctx, user, pass_old,
+ pass_new, *pwdlastset_update);
+ if (ret != PAM_SUCCESS) {
+ pass_old = pass_new = NULL;
+ goto out;
+ }
+
+ if (_pam_require_krb5_auth_after_chauthtok(ctx, user)) {
+
+ const char *member = NULL;
+ const char *cctype = NULL;
+ int warn_pwd_expire;
+ struct wbcLogonUserInfo *info = NULL;
+
+ member = get_member_from_config(ctx);
+ cctype = get_krb5_cc_type_from_config(ctx);
+ warn_pwd_expire = get_warn_pwd_expire_from_config(ctx);
+
+ /* Keep WINBIND_CACHED_LOGIN bit for
+ * authentication after changing the password.
+ * This will update the cached credentials in case
+ * that winbindd_dual_pam_chauthtok() fails
+ * to update them.
+ * --- BoYang
+ * */
+
+ ret = winbind_auth_request(ctx, user, pass_new,
+ member, cctype, 0,
+ &error, &info,
+ NULL, &username_ret);
+ pass_old = pass_new = NULL;
+
+ if (ret == PAM_SUCCESS) {
+
+ struct wbcAuthUserInfo *user_info = NULL;
+
+ if (info && info->info) {
+ user_info = info->info;
+ }
+
+ /* warn a user if the password is about to
+ * expire soon */
+ _pam_warn_password_expiry(ctx, user_info,
+ warn_pwd_expire,
+ NULL, NULL);
+
+ /* set some info3 info for other modules in the
+ * stack */
+ _pam_set_data_info3(ctx, user_info);
+
+ /* put krb5ccname into env */
+ _pam_setup_krb5_env(ctx, info);
+
+ if (username_ret) {
+ pam_set_item(pamh, PAM_USER,
+ username_ret);
+ _pam_log_debug(ctx, LOG_INFO,
+ "Returned user was '%s'",
+ username_ret);
+ free(username_ret);
+ }
+
+ }
+
+ if (info && info->blobs) {
+ wbcFreeMemory(info->blobs);
+ }
+ wbcFreeMemory(info);
+
+ goto out;
+ }
+ } else {
+ ret = PAM_SERVICE_ERR;
+ }
+
+out:
+ {
+ /* Deal with offline errors. */
+ int i;
+ const char *codes[] = {
+ "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND",
+ "NT_STATUS_NO_LOGON_SERVERS",
+ "NT_STATUS_ACCESS_DENIED"
+ };
+
+ for (i=0; i<ARRAY_SIZE(codes); i++) {
+ int _ret;
+ if (_pam_check_remark_auth_err(ctx, error, codes[i], &_ret)) {
+ break;
+ }
+ }
+ }
+
+ wbcFreeMemory(error);
+
+ _PAM_LOG_FUNCTION_LEAVE("pam_sm_chauthtok", ctx, ret);
+
+ TALLOC_FREE(ctx);
+
+ return pam_error_code(ctx, PAM_WINBIND_CHAUTHTOK, ret);
+}
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_winbind_modstruct = {
+ MODULE_NAME,
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ pam_sm_acct_mgmt,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ pam_sm_chauthtok
+};
+
+#endif
+
+/*
+ * Copyright (c) Andrew Tridgell <tridge@samba.org> 2000
+ * Copyright (c) Tim Potter <tpot@samba.org> 2000
+ * Copyright (c) Andrew Bartlettt <abartlet@samba.org> 2002
+ * Copyright (c) Guenther Deschner <gd@samba.org> 2005-2008
+ * Copyright (c) Jan Rêkorajski 1999.
+ * Copyright (c) Andrew G. Morgan 1996-8.
+ * Copyright (c) Alex O. Yuriev, 1996.
+ * Copyright (c) Cristian Gafton 1996.
+ * Copyright (C) Elliot Lee <sopwith@redhat.com> 1996, Red Hat Software.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/nsswitch/pam_winbind.h b/nsswitch/pam_winbind.h
new file mode 100644
index 0000000..2f4a257
--- /dev/null
+++ b/nsswitch/pam_winbind.h
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) Andrew Tridgell <tridge@samba.org> 2000
+ * Copyright (c) Tim Potter <tpot@samba.org> 2000
+ * Copyright (c) Andrew Bartlettt <abartlet@samba.org> 2002
+ * Copyright (c) Guenther Deschner <gd@samba.org> 2005-2008
+ * Copyright (c) Jan Rêkorajski 1999.
+ * Copyright (c) Andrew G. Morgan 1996-8.
+ * Copyright (c) Alex O. Yuriev, 1996.
+ * Copyright (c) Cristian Gafton 1996.
+ * Copyright (C) Elliot Lee <sopwith@redhat.com> 1996, Red Hat Software.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* pam_winbind header file
+ (Solaris needs some macros from Linux for common PAM code)
+
+ Shirish Kalele 2000
+*/
+
+#ifndef _NSSWITCH_PAM_WINBIND_H_
+#define _NSSWITCH_PAM_WINBIND_H_
+
+#include "../lib/replace/replace.h"
+#include "system/syslog.h"
+#include "system/time.h"
+#include <talloc.h>
+#include "libwbclient/wbclient.h"
+#include "lib/util/tiniparser.h"
+
+#define MODULE_NAME "pam_winbind"
+#define PAM_SM_AUTH
+#define PAM_SM_ACCOUNT
+#define PAM_SM_PASSWORD
+#define PAM_SM_SESSION
+
+#ifndef PAM_WINBIND_CONFIG_FILE
+#define PAM_WINBIND_CONFIG_FILE "/etc/security/pam_winbind.conf"
+#endif
+
+#ifdef HAVE_LIBINTL_H
+#include <libintl.h>
+#endif
+
+#if defined(LINUX)
+
+/* newer versions of PAM have this in _pam_compat.h */
+#ifndef PAM_AUTHTOK_RECOVERY_ERR
+#define PAM_AUTHTOK_RECOVERY_ERR PAM_AUTHTOK_RECOVER_ERR
+#endif
+
+#else /* !LINUX */
+
+/* Solaris always uses dynamic pam modules */
+#define PAM_EXTERN extern
+#if defined(HAVE_SECURITY_PAM_APPL_H)
+#include <security/pam_appl.h>
+#elif defined(HAVE_PAM_PAM_APPL_H)
+#include <pam/pam_appl.h>
+#endif
+
+#ifndef PAM_AUTHTOK_RECOVER_ERR
+#define PAM_AUTHTOK_RECOVER_ERR PAM_AUTHTOK_RECOVERY_ERR
+#endif
+
+#endif /* (!)LINUX */
+
+#if defined(HAVE_SECURITY_PAM_MODULES_H)
+#include <security/pam_modules.h>
+#elif defined(HAVE_PAM_PAM_MODULES_H)
+#include <pam/pam_modules.h>
+#endif
+
+#if defined(HAVE_SECURITY__PAM_MACROS_H)
+#include <security/_pam_macros.h>
+#elif defined(HAVE_PAM__PAM_MACROS_H)
+#include <pam/_pam_macros.h>
+#else
+/* Define required macros from (Linux PAM 0.68) security/_pam_macros.h */
+#define _pam_drop_reply(/* struct pam_response * */ reply, /* int */ replies) \
+do { \
+ int reply_i; \
+ \
+ for (reply_i=0; reply_i<replies; ++reply_i) { \
+ if (reply[reply_i].resp) { \
+ _pam_overwrite(reply[reply_i].resp); \
+ free(reply[reply_i].resp); \
+ } \
+ } \
+ if (reply) \
+ free(reply); \
+} while (0)
+
+#define _pam_overwrite(x) \
+do { \
+ register char *__xx__; \
+ if ((__xx__=(x))) \
+ while (*__xx__) \
+ *__xx__++ = '\0'; \
+} while (0)
+
+/*
+ * Don't just free it, forget it too.
+ */
+
+#define _pam_drop(X) SAFE_FREE(X)
+
+#define x_strdup(s) ( (s) ? strdup(s):NULL )
+#endif /* HAVE_SECURITY__PAM_MACROS_H */
+
+#ifdef HAVE_SECURITY_PAM_EXT_H
+#include <security/pam_ext.h>
+#endif
+
+#define WINBIND_DEBUG_ARG 0x00000001
+#define WINBIND_USE_AUTHTOK_ARG 0x00000002
+#define WINBIND_UNKNOWN_OK_ARG 0x00000004
+#define WINBIND_TRY_FIRST_PASS_ARG 0x00000008
+#define WINBIND_USE_FIRST_PASS_ARG 0x00000010
+#define WINBIND__OLD_PASSWORD 0x00000020
+#define WINBIND_REQUIRED_MEMBERSHIP 0x00000040
+#define WINBIND_KRB5_AUTH 0x00000080
+#define WINBIND_KRB5_CCACHE_TYPE 0x00000100
+#define WINBIND_CACHED_LOGIN 0x00000200
+#define WINBIND_CONFIG_FILE 0x00000400
+#define WINBIND_SILENT 0x00000800
+#define WINBIND_DEBUG_STATE 0x00001000
+#define WINBIND_WARN_PWD_EXPIRE 0x00002000
+#define WINBIND_MKHOMEDIR 0x00004000
+#define WINBIND_TRY_AUTHTOK_ARG 0x00008000
+#define WINBIND_PWD_CHANGE_PROMPT 0x00010000
+
+#if defined(HAVE_GETTEXT) && !defined(__LCLINT__)
+#define _(string) dgettext(MODULE_NAME, string)
+#else
+#define _(string) string
+#endif
+
+#define N_(string) string
+
+/*
+ * here is the string to inform the user that the new passwords they
+ * typed were not the same.
+ */
+
+#define MISTYPED_PASS _("Sorry, passwords do not match")
+
+#define on(x, y) (x & y)
+#define off(x, y) (!(x & y))
+
+#define PAM_WINBIND_NEW_AUTHTOK_REQD "PAM_WINBIND_NEW_AUTHTOK_REQD"
+#define PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH "PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH"
+#define PAM_WINBIND_HOMEDIR "PAM_WINBIND_HOMEDIR"
+#define PAM_WINBIND_LOGONSCRIPT "PAM_WINBIND_LOGONSCRIPT"
+#define PAM_WINBIND_LOGONSERVER "PAM_WINBIND_LOGONSERVER"
+#define PAM_WINBIND_PROFILEPATH "PAM_WINBIND_PROFILEPATH"
+#define PAM_WINBIND_PWD_LAST_SET "PAM_WINBIND_PWD_LAST_SET"
+
+#define SECONDS_PER_DAY 86400
+
+#define DEFAULT_DAYS_TO_WARN_BEFORE_PWD_EXPIRES 14
+
+#include "winbind_client.h"
+
+#define PAM_WB_REMARK_DIRECT(c,x)\
+{\
+ const char *error_string = NULL; \
+ error_string = _get_ntstatus_error_string(x);\
+ if (error_string != NULL) {\
+ _make_remark(c, PAM_ERROR_MSG, error_string);\
+ } else {\
+ _make_remark(c, PAM_ERROR_MSG, x);\
+ };\
+};
+
+#define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
+
+#define PAM_WB_CACHED_LOGON(x) (x & WBC_AUTH_USER_INFO_CACHED_ACCOUNT)
+#define PAM_WB_KRB5_CLOCK_SKEW(x) (x & LOGON_KRB5_FAIL_CLOCK_SKEW)
+#define PAM_WB_GRACE_LOGON(x) ((WBC_AUTH_USER_INFO_CACHED_ACCOUNT|WBC_AUTH_USER_INFO_GRACE_LOGON) == ( x & (WBC_AUTH_USER_INFO_CACHED_ACCOUNT|WBC_AUTH_USER_INFO_GRACE_LOGON)))
+
+struct pwb_context {
+ pam_handle_t *pamh;
+ int flags;
+ int argc;
+ const char **argv;
+ struct tiniparser_dictionary *dict;
+ uint32_t ctrl;
+ struct wbcContext *wbc_ctx;
+};
+
+#endif /* _NSSWITCH_PAM_WINBIND_H_ */
diff --git a/nsswitch/stress-nss-libwbclient.c b/nsswitch/stress-nss-libwbclient.c
new file mode 100644
index 0000000..df1d85c
--- /dev/null
+++ b/nsswitch/stress-nss-libwbclient.c
@@ -0,0 +1,169 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Stress test for parallel NSS & libwbclient calls.
+
+ Copyright (C) Ralph Wuerthner 2018
+
+ 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 <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <pthread.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <wbclient.h>
+
+#define RUNTIME 10
+
+struct thread_state {
+ const char *username;
+ time_t timeout;
+ pthread_mutex_t lock;
+ bool fail;
+ int nss_loop_count;
+ int wbc_loop_count;
+};
+
+static void *query_nss_thread(void *ptr)
+{
+ struct thread_state *state = ptr;
+ char buf[1024];
+ int rc;
+ struct passwd pwd, *result;
+
+ while (time(NULL) < state->timeout) {
+ rc = getpwnam_r(state->username,
+ &pwd,
+ buf,
+ sizeof(buf),
+ &result);
+ if (rc != 0 || result == NULL) {
+ pthread_mutex_lock(&state->lock);
+ state->fail = true;
+ pthread_mutex_unlock(&state->lock);
+ fprintf(stderr,
+ "getpwnam_r failed with rc='%s' result=%p\n",
+ strerror(rc),
+ result);
+ break;
+ }
+ state->nss_loop_count++;
+ pthread_mutex_lock(&state->lock);
+ if (state->fail) {
+ pthread_mutex_unlock(&state->lock);
+ break;
+ }
+ pthread_mutex_unlock(&state->lock);
+ }
+ return NULL;
+}
+
+static void *query_wbc_thread(void *ptr)
+{
+ struct thread_state *state = ptr;
+ struct passwd *ppwd;
+ wbcErr wbc_status;
+
+ while (time(NULL) < state->timeout) {
+ wbc_status = wbcGetpwnam(state->username, &ppwd);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ pthread_mutex_lock(&state->lock);
+ state->fail = true;
+ pthread_mutex_unlock(&state->lock);
+ fprintf(stderr,
+ "wbcGetpwnam failed with %s\n",
+ wbcErrorString(wbc_status));
+ break;
+ }
+ wbcFreeMemory(ppwd);
+ state->wbc_loop_count++;
+ pthread_mutex_lock(&state->lock);
+ if (state->fail) {
+ pthread_mutex_unlock(&state->lock);
+ break;
+ }
+ pthread_mutex_unlock(&state->lock);
+ }
+ return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+ int rc, n;
+ struct thread_state state;
+ pthread_t threads[2];
+
+ if (argc < 2 ) {
+ fprintf(stderr,"%s: missing domain user\n", argv[0]);
+ return 1;
+ }
+
+ state.username = argv[1];
+ state.timeout = time(NULL) + RUNTIME;
+ rc = pthread_mutex_init(&state.lock, NULL);
+ if (rc != 0) {
+ fprintf(stderr,
+ "pthread_mutex_init failed: %s\n",
+ strerror(rc));
+ exit(1);
+ }
+ state.fail = false;
+ state.nss_loop_count = 0;
+ state.wbc_loop_count = 0;
+
+ printf("query domain user '%s'\n", state.username);
+
+ /* create query threads */
+ rc = pthread_create(&threads[0], NULL, query_nss_thread, &state);
+ if (rc != 0) {
+ fprintf(stderr,
+ "creating NSS thread failed: %s\n",
+ strerror(rc));
+ exit(1);
+ }
+ rc = pthread_create(&threads[1], NULL, query_wbc_thread, &state);
+ if (rc != 0) {
+ fprintf(stderr,
+ "creating libwbclient thread failed: %s\n",
+ strerror(rc));
+ exit(1);
+ }
+
+ /* wait for query threads to terminate */
+ for (n = 0; n < 2; n++) {
+ rc = pthread_join(threads[n], NULL);
+ if (rc != 0) {
+ fprintf(stderr,
+ "joining query thread %i failed: %s\n",
+ n,
+ strerror(rc));
+ exit(1);
+ }
+ }
+
+ fprintf(state.fail ? stderr: stdout,
+ "test %s with %i NSS and %i libwbclient calls\n",
+ state.fail ? "failed" : "passed",
+ state.nss_loop_count,
+ state.wbc_loop_count);
+
+ return state.fail;
+}
diff --git a/nsswitch/tests/test_idmap_ad.sh b/nsswitch/tests/test_idmap_ad.sh
new file mode 100755
index 0000000..323aa17
--- /dev/null
+++ b/nsswitch/tests/test_idmap_ad.sh
@@ -0,0 +1,248 @@
+#!/bin/sh
+#
+# Basic testing of id mapping with idmap_ad
+#
+
+if [ $# -ne 6 ]; then
+ echo Usage: $0 DOMAIN DC_SERVER DC_PASSWORD TRUST_DOMAIN TRUST_SERVER TRUST_PASSWORD
+ exit 1
+fi
+
+DOMAIN="$1"
+DC_SERVER="$2"
+DC_PASSWORD="$3"
+TRUST_DOMAIN="$4"
+TRUST_SERVER="$5"
+TRUST_PASSWORD="$6"
+
+wbinfo="$VALGRIND $BINDIR/wbinfo"
+ldbmodify="$VALGRIND $BINDIR/ldbmodify"
+ldbsearch="$VALGRIND $BINDIR/ldbsearch"
+
+failed=0
+
+. $(dirname $0)/../../testprogs/blackbox/subunit.sh
+
+DOMAIN_SID=$($wbinfo -n "$DOMAIN/" | cut -f 1 -d " ")
+if [ $? -ne 0 ]; then
+ echo "Could not find domain SID" | subunit_fail_test "test_idmap_ad"
+ exit 1
+fi
+
+TRUST_DOMAIN_SID=$($wbinfo -n "$TRUST_DOMAIN/" | cut -f 1 -d " ")
+if [ $? -ne 0 ]; then
+ echo "Could not find trusted domain SID" | subunit_fail_test "test_idmap_ad"
+ exit 1
+fi
+
+BASE_DN=$($ldbsearch -H ldap://$DC_SERVER -b "" --scope=base defaultNamingContext | awk '/^defaultNamingContext/ {print $2}')
+if [ $? -ne 0 ]; then
+ echo "Could not find base DN" | subunit_fail_test "test_idmap_ad"
+ exit 1
+fi
+
+TRUST_BASE_DN=$($ldbsearch -H ldap://$TRUST_SERVER -b "" --scope=base defaultNamingContext | awk '/^defaultNamingContext/ {print $2}')
+if [ $? -ne 0 ]; then
+ echo "Could not find trusted base DN" | subunit_fail_test "test_idmap_ad"
+ exit 1
+fi
+
+#
+# Add POSIX ids to AD
+#
+cat <<EOF | $ldbmodify -H ldap://$DC_SERVER -U "$DOMAIN\Administrator%$DC_PASSWORD"
+dn: CN=Administrator,CN=Users,$BASE_DN
+changetype: modify
+add: uidNumber
+uidNumber: 2000000
+add: gidNumber
+gidNumber: 2000100
+add: unixHomeDirectory
+unixHomeDirectory: /home/admin
+add: loginShell
+loginShell: /bin/tcsh
+add: gecos
+gecos: Administrator Full Name
+EOF
+
+cat <<EOF | $ldbmodify -H ldap://$DC_SERVER -U "$DOMAIN\Administrator%$DC_PASSWORD"
+dn: CN=Domain Users,CN=Users,$BASE_DN
+changetype: modify
+add: gidNumber
+gidNumber: 2000001
+EOF
+
+cat <<EOF | $ldbmodify -H ldap://$DC_SERVER -U "$DOMAIN\Administrator%$DC_PASSWORD"
+dn: CN=Domain Admins,CN=Users,$BASE_DN
+changetype: modify
+add: gidNumber
+gidNumber: 2000002
+EOF
+
+#
+# Add POSIX ids to trusted domain
+#
+cat <<EOF | $ldbmodify -H ldap://$TRUST_SERVER \
+ -U "$TRUST_DOMAIN\Administrator%$TRUST_PASSWORD"
+dn: CN=Administrator,CN=Users,$TRUST_BASE_DN
+changetype: modify
+add: uidNumber
+uidNumber: 2500000
+EOF
+
+cat <<EOF | $ldbmodify -H ldap://$TRUST_SERVER \
+ -U "$TRUST_DOMAIN\Administrator%$TRUST_PASSWORD"
+dn: CN=Domain Users,CN=Users,$TRUST_BASE_DN
+changetype: modify
+add: gidNumber
+gidNumber: 2500001
+EOF
+
+cat <<EOF | $ldbmodify -H ldap://$TRUST_SERVER \
+ -U "$TRUST_DOMAIN\Administrator%$TRUST_PASSWORD"
+dn: CN=Domain Admins,CN=Users,$TRUST_BASE_DN
+changetype: modify
+add: gidNumber
+gidNumber: 2500002
+EOF
+
+#
+# Test 1: Test uid of Administrator, should be 2000000
+#
+
+out="$($wbinfo -S $DOMAIN_SID-500)"
+echo "wbinfo returned: \"$out\", expecting \"2000000\""
+test "$out" = "2000000"
+ret=$?
+testit "Test uid of Administrator is 2000000" test $ret -eq 0 || failed=$(expr $failed + 1)
+
+#
+# Test 2: Test gid of Domain Users, should be 2000001
+#
+
+out="$($wbinfo -Y $DOMAIN_SID-513)"
+echo "wbinfo returned: \"$out\", expecting \"2000001\""
+test "$out" = "2000001"
+ret=$?
+testit "Test uid of Domain Users is 2000001" test $ret -eq 0 || failed=$(expr $failed + 1)
+
+#
+# Test 3: Test get userinfo for Administrator works
+#
+
+out="$($wbinfo -i $DOMAIN/Administrator)"
+echo "wbinfo returned: \"$out\", expecting \"$DOMAIN/administrator:*:2000000:2000100:Administrator Full Name:/home/admin:/bin/tcsh\""
+test "$out" = "$DOMAIN/administrator:*:2000000:2000100:Administrator Full Name:/home/admin:/bin/tcsh"
+ret=$?
+testit "Test get userinfo for Administrator works" test $ret -eq 0 || failed=$(expr $failed + 1)
+
+#
+# Test 4: Test lookup from gid to sid
+#
+
+out="$($wbinfo -G 2000002)"
+echo "wbinfo returned: \"$out\", expecting \"$DOMAIN_SID-512\""
+test "$out" = "$DOMAIN_SID-512"
+ret=$?
+testit "Test gid lookup of Domain Admins" test $ret -eq 0 || failed=$(expr $failed + 1)
+
+#
+# Trusted domain test 1: Test uid of Administrator, should be 2500000
+#
+
+out="$($wbinfo -S $TRUST_DOMAIN_SID-500)"
+echo "wbinfo returned: \"$out\", expecting \"2500000\""
+test "$out" = "2500000"
+ret=$?
+testit "Test uid of Administrator in trusted domain is 2500000" test $ret -eq 0 || failed=$(expr $failed + 1)
+
+#
+# Trusted domain test 2: Test gid of Domain Users, should be 2500001
+#
+
+out="$($wbinfo -Y $TRUST_DOMAIN_SID-513)"
+echo "wbinfo returned: \"$out\", expecting \"2500001\""
+test "$out" = "2500001"
+ret=$?
+testit "Test uid of Domain Users in trusted domain is 2500001" test $ret -eq 0 || failed=$(expr $failed + 1)
+
+#
+# Trusted domain test 3: Test get userinfo for Administrator works
+#
+
+out="$($wbinfo -i $TRUST_DOMAIN/Administrator)"
+echo "wbinfo returned: \"$out\", expecting \"$TRUST_DOMAIN/administrator:*:2500000:2500001::/home/$TRUST_DOMAIN/administrator:/bin/false\""
+test "$out" = "$TRUST_DOMAIN/administrator:*:2500000:2500001::/home/$TRUST_DOMAIN/administrator:/bin/false"
+ret=$?
+testit "Test get userinfo for Administrator works" test $ret -eq 0 || failed=$(expr $failed + 1)
+
+#
+# Trusted domain test 4: Test lookup from gid to sid
+#
+
+out="$($wbinfo -G 2500002)"
+echo "wbinfo returned: \"$out\", expecting \"$TRUST_DOMAIN_SID-512\""
+test "$out" = "$TRUST_DOMAIN_SID-512"
+ret=$?
+testit "Test gid lookup of Domain Admins in trusted domain." test $ret -eq 0 || failed=$(expr $failed + 1)
+
+#
+# Remove POSIX ids from AD
+#
+cat <<EOF | $ldbmodify -H ldap://$DC_SERVER -U "$DOMAIN\Administrator%$DC_PASSWORD"
+dn: CN=Administrator,CN=Users,$BASE_DN
+changetype: modify
+delete: uidNumber
+uidNumber: 2000000
+delete: gidNumber
+gidNumber: 2000100
+delete: unixHomeDirectory
+unixHomeDirectory: /home/admin
+delete: loginShell
+loginShell: /bin/tcsh
+delete: gecos
+gecos: Administrator Full Name
+EOF
+
+cat <<EOF | $ldbmodify -H ldap://$DC_SERVER -U "$DOMAIN\Administrator%$DC_PASSWORD"
+dn: CN=Domain Users,CN=Users,$BASE_DN
+changetype: modify
+delete: gidNumber
+gidNumber: 2000001
+EOF
+
+cat <<EOF | $ldbmodify -H ldap://$DC_SERVER -U "$DOMAIN\Administrator%$DC_PASSWORD"
+dn: CN=Domain Admins,CN=Users,$BASE_DN
+changetype: modify
+delete: gidNumber
+gidNumber: 2000002
+EOF
+
+#
+# Remove POSIX ids from trusted domain
+#
+cat <<EOF | $ldbmodify -H ldap://$TRUST_SERVER \
+ -U "$TRUST_DOMAIN\Administrator%$TRUST_PASSWORD"
+dn: CN=Administrator,CN=Users,$TRUST_BASE_DN
+changetype: modify
+delete: uidNumber
+uidNumber: 2500000
+EOF
+
+cat <<EOF | $ldbmodify -H ldap://$TRUST_SERVER \
+ -U "$TRUST_DOMAIN\Administrator%$TRUST_PASSWORD"
+dn: CN=Domain Users,CN=Users,$TRUST_BASE_DN
+changetype: modify
+delete: gidNumber
+gidNumber: 2500001
+EOF
+
+cat <<EOF | $ldbmodify -H ldap://$TRUST_SERVER \
+ -U "$TRUST_DOMAIN\Administrator%$TRUST_PASSWORD"
+dn: CN=Domain Admins,CN=Users,$TRUST_BASE_DN
+changetype: modify
+delete: gidNumber
+gidNumber: 2500002
+EOF
+
+exit $failed
diff --git a/nsswitch/tests/test_idmap_nss.sh b/nsswitch/tests/test_idmap_nss.sh
new file mode 100755
index 0000000..e7a57dc
--- /dev/null
+++ b/nsswitch/tests/test_idmap_nss.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+# Test id mapping with unknown SID and non-allocating idmap backend
+if [ $# -lt 1 ]; then
+ echo Usage: $0 DOMAIN
+ exit 1
+fi
+
+DOMAIN="$1"
+
+wbinfo="$VALGRIND $BINDIR/wbinfo"
+
+failed=0
+
+. $(dirname $0)/../../testprogs/blackbox/subunit.sh
+
+testit "wbinfo returns domain SID" $wbinfo -n "$DOMAIN/" || exit 1
+DOMAIN_SID=$($wbinfo -n "$DOMAIN/" | cut -f 1 -d " ")
+echo "Domain $DOMAIN has SID $DOMAIN_SID"
+
+# Find an unused uid and SID
+RID=66666
+while true; do
+ id $RID
+ if [ $? -ne 0 ]; then
+ $wbinfo -s $DOMAIN_SID-$RID
+ if [ $? -ne 0 ]; then
+ break
+ fi
+ fi
+ RID=$(expr $RID + 1)
+done
+
+echo "Using non-existing SID $DOMAIN_SID-$RID to check no id allocation is done by the backend"
+
+out="$($wbinfo --sids-to-unix-ids=$DOMAIN_SID-$RID)"
+echo "wbinfo returned: $out"
+test "$out" = "$DOMAIN_SID-$RID -> unmapped"
+ret=$?
+testit "wbinfo SID to xid returns unmapped for unknown SID" test $ret -eq 0 || failed=$(expr $failed + 1)
+
+exit $failed
diff --git a/nsswitch/tests/test_idmap_rfc2307.sh b/nsswitch/tests/test_idmap_rfc2307.sh
new file mode 100755
index 0000000..b24cf45
--- /dev/null
+++ b/nsswitch/tests/test_idmap_rfc2307.sh
@@ -0,0 +1,221 @@
+#!/bin/sh
+# Test id mapping through idmap_rfc2307 module
+if [ $# -lt 15 ]; then
+ echo Usage: $0 DOMAIN USERNAME UID USERNAME2 UID2 \
+ GROUPNAME GID GROUPNAME2 GID2 GID_START NUMGROUPS \
+ LDAPPREFIX DC_SERVER DC_USERNAME DC_PASSWORD
+ exit 1
+fi
+
+DOMAIN="$1"
+USERNAME="$2"
+USERUID="$3"
+USERNAME2="$4"
+USERUID2="$5"
+GROUPNAME="$6"
+GROUPGID="$7"
+GROUPNAME2="$8"
+GROUPGID2="$9"
+shift 9
+GID_START="$1"
+NUMGROUPS="$2"
+LDAPPREFIX="$3"
+DC_SERVER="$4"
+DC_USERNAME="$5"
+DC_PASSWORD="$6"
+
+wbinfo="$VALGRIND $BINDIR/wbinfo"
+net="$VALGRIND $BINDIR/net"
+
+ldbsearch="ldbsearch"
+if [ -x "$BINDIR/ldbsearch" ]; then
+ ldbsearch="$BINDIR/ldbsearch"
+fi
+
+ldbadd="ldbadd"
+if [ -x "$BINDIR/ldbadd" ]; then
+ ldbadd="$BINDIR/ldbadd"
+fi
+
+ldbdel="ldbdel"
+if [ -x "$BINDIR/ldbdel" ]; then
+ ldbdel="$BINDIR/ldbdel"
+fi
+
+failed=0
+
+. $(dirname $0)/../../testprogs/blackbox/subunit.sh
+
+# Delete LDAP records
+$VALGRIND $ldbdel -H ldap://$DC_SERVER -U$DOMAIN/$DC_USERNAME%$DC_PASSWORD "$LDAPPREFIX" --controls="tree_delete:1"
+
+# Add id mapping information to LDAP
+
+testit "add ldap prefix" $VALGRIND $ldbadd -H ldap://$DC_SERVER \
+ -U$DOMAIN/$DC_USERNAME%$DC_PASSWORD <<EOF
+dn: $LDAPPREFIX
+objectclass: organizationalUnit
+EOF
+
+testit "add ldap user mapping record" $VALGRIND $ldbadd -H ldap://$DC_SERVER \
+ -U$DOMAIN/$DC_USERNAME%$DC_PASSWORD <<EOF
+dn: cn=$USERNAME,$LDAPPREFIX
+objectClass: organizationalPerson
+objectClass: posixAccount
+ou: People
+cn: $USERNAME
+uid: $USERNAME
+uidNumber: $USERUID
+gidNumber: 1
+homeDirectory: /home/admin
+EOF
+
+testit "add second ldap user mapping record" $VALGRIND $ldbadd \
+ -H ldap://$DC_SERVER -U$DOMAIN/$DC_USERNAME%$DC_PASSWORD <<EOF
+dn: cn=$USERNAME2,$LDAPPREFIX
+objectClass: organizationalPerson
+objectClass: posixAccount
+ou: People
+cn: $USERNAME2
+uid: $USERNAME2
+uidNumber: $USERUID2
+gidNumber: 2
+homeDirectory: /home/admin
+EOF
+
+testit "add ldap group mapping record" $VALGRIND $ldbadd \
+ -H ldap://$DC_SERVER -U$DOMAIN/$DC_USERNAME%$DC_PASSWORD <<EOF
+dn: cn=$GROUPNAME,$LDAPPREFIX
+objectClass: posixGroup
+objectClass: groupOfNames
+cn: $GROUPNAME
+gidNumber: $GROUPGID
+member: cn=$USERNAME,$LDAPPREFIX
+EOF
+
+testit "add second ldap group mapping record" $VALGRIND $ldbadd \
+ -H ldap://$DC_SERVER -U$DOMAIN/$DC_USERNAME%$DC_PASSWORD <<EOF
+dn: cn=$GROUPNAME2,$LDAPPREFIX
+objectClass: posixGroup
+objectClass: groupOfNames
+cn: $GROUPNAME2
+gidNumber: $GROUPGID2
+member: cn=$USERNAME,$LDAPPREFIX
+EOF
+
+testit "wbinfo --name-to-sid" $wbinfo --name-to-sid "$DOMAIN/$USERNAME" || failed=$(expr $failed + 1)
+user_sid=$($wbinfo -n "$DOMAIN/$USERNAME" | cut -d " " -f1)
+echo "$DOMAIN/$USERNAME resolved to $user_sid"
+
+testit "wbinfo --sid-to-uid=$user_sid" $wbinfo --sid-to-uid=$user_sid || failed=$(expr $failed + 1)
+user_uid=$($wbinfo --sid-to-uid=$user_sid | cut -d " " -f1)
+echo "$DOMAIN/$USERNAME resolved to $user_uid"
+
+testit "test $user_uid -eq $USERUID" test $user_uid -eq $USERUID || failed=$(expr $failed + 1)
+
+# Not sure how to get group names with spaces to resolve through testit
+#testit "wbinfo --name-to-sid" $wbinfo --name-to-sid="$DOMAIN/$GROUPNAME" || failed=$(expr $failed + 1)
+group_sid=$($wbinfo --name-to-sid="$DOMAIN/$GROUPNAME" | cut -d " " -f1)
+echo "$DOMAIN/$GROUPNAME resolved to $group_sid"
+
+testit "wbinfo --sid-to-gid=$group_sid" $wbinfo --sid-to-gid=$group_sid || failed=$(expr $failed + 1)
+group_gid=$($wbinfo --sid-to-gid=$group_sid | cut -d " " -f1)
+echo "$DOMAIN/$GROUPNAME resolved to $group_gid"
+
+testit "test $group_gid -eq $GROUPGID" test $group_gid -eq $GROUPGID || failed=$(expr $failed + 1)
+
+# Use different user and group for reverse lookup to not read from cache
+
+testit "$wbinfo --uid-to-sid=$USERUID2" $wbinfo --uid-to-sid=$USERUID2 || failed=$(expr $failed + 1)
+user_sid2=$($wbinfo --uid-to-sid=$USERUID2 | cut -d " " -f1)
+echo "UID $USERUID2 resolved to SID $user_sid2"
+
+testit "$wbinfo --sid-to-name=$user_sid2" $wbinfo --sid-to-name=$user_sid2 || failed=$(expr $failed + 1)
+user_name2=$($wbinfo --sid-to-name=$user_sid2 | cut -d " " -f1)
+echo "SID $user_sid2 resolved to $user_name2"
+
+testit "test $user_name2 = $DOMAIN/$USERNAME2" test "$(echo $user_name2 | tr A-Z a-z)" = "$(echo $DOMAIN/$USERNAME2 | tr A-Z a-z)" || failed=$(expr $failed + 1)
+
+testit "$wbinfo --gid-to-sid=$GROUPGID2" $wbinfo --gid-to-sid=$GROUPGID2 || failed=$(expr $failed + 1)
+group_sid2=$($wbinfo --gid-to-sid=$GROUPGID2 | cut -d " " -f1)
+echo "GID $GROUPGID2 resolved to SID $group_sid2"
+
+testit "$wbinfo --sid-to-name=$group_sid2" $wbinfo --sid-to-name=$group_sid2 || failed=$(expr $failed + 1)
+group_name2=$($wbinfo --sid-to-name=$group_sid2 | cut -d " " -f1)
+echo "SID $group_sid2 resolved to $group_name2"
+
+testit "test $group_name2 = $DOMAIN/$GROUPNAME2" test "$(echo $group_name2 | tr A-Z a-z)" = "$(echo $DOMAIN/$GROUPNAME2 | tr A-Z a-z)" || failed=$(expr $failed + 1)
+
+i=0
+while [ ${i} -lt ${NUMGROUPS} ]; do
+ GRP=$(printf "test_rfc2307_group_%3.3d" "$i")
+ GRP_GID=$(expr "$GID_START" + "$i")
+ testit "Add group $GRP" $net rpc group add "$GRP" -S "$DC_SERVER" \
+ -U"${DOMAIN}\\${DC_USERNAME}"%"${DC_PASSWORD}" ||
+ failed=$(expr $failed + 1)
+ testit "Add groupmem $GRP $USERNAME" \
+ $net rpc group addmem "$GRP" "$USERNAME" \
+ -S "$DC_SERVER" \
+ -U"${DOMAIN}\\${DC_USERNAME}"%"${DC_PASSWORD}" ||
+ failed=$(expr $failed + 1)
+ testit "Add group object for $GRP $GRP_GID" \
+ $VALGRIND $ldbadd \
+ -H ldap://$DC_SERVER -U$DOMAIN/$DC_USERNAME%$DC_PASSWORD <<EOF
+dn: cn=$GRP,$LDAPPREFIX
+objectClass: posixGroup
+objectClass: groupOfNames
+cn: $GRP
+gidNumber: $GRP_GID
+member: cn=$USERNAME,$LDAPPREFIX
+EOF
+ i=$(expr "$i" + 1)
+done
+
+# Test whether wbinfo --xids-to-sids finds everything
+
+GIDS=""
+i=0
+while [ ${i} -lt ${NUMGROUPS} ]; do
+ GIDS="$GIDS g$(expr ${i} + ${GID_START})"
+ i=$(expr "$i" + 1)
+done
+NUM_VALID_SIDS=$($wbinfo --unix-ids-to-sids="$GIDS" | grep -v ^"NOT MAPPED" | wc -l)
+
+testit "Count number of valid sids found" \
+ test ${NUM_VALID_SIDS} = ${NUMGROUPS} ||
+ failed=$(expr $failed + 1)
+
+# Prime the cache so we test idmap, not the harder problem of
+# consistent group memberships for users without a login.
+
+testit "Authenticate the user to prime the netlogon cache" \
+ $wbinfo -a $DOMAIN/$DC_USERNAME%$DC_PASSWORD || failed=$(expr $failed + 1)
+
+# Test whether wbinfo -r shows all groups
+
+EXPECTED_USERGROUPS="1000000/1000001/2000002/"
+i=0
+while [ ${i} -lt ${NUMGROUPS} ]; do
+ EXPECTED_USERGROUPS="$EXPECTED_USERGROUPS$(expr ${i} + ${GID_START})/"
+ i=$(expr "$i" + 1)
+done
+
+USERGROUPS=$($wbinfo -r $DOMAIN/$USERNAME | sort -n | tr '\n' '/')
+
+testit "Testing for expected group memberships" \
+ test "$USERGROUPS" = "$EXPECTED_USERGROUPS" ||
+ failed=$(expr $failed + 1)
+
+i=0
+while [ ${i} -lt ${NUMGROUPS} ]; do
+ GRP=$(printf "test_rfc2307_group_%3.3d" ${i})
+ testit "Del group $GRP" $net rpc group delete "$GRP" -S "$DC_SERVER" \
+ -U"${DOMAIN}\\${DC_USERNAME}"%"${DC_PASSWORD}" ||
+ failed=$(expr $failed + 1)
+ i=$(expr "$i" + 1)
+done
+
+# Delete LDAP records
+$VALGRIND $ldbdel -H ldap://$DC_SERVER -U$DOMAIN/$DC_USERNAME%$DC_PASSWORD "$LDAPPREFIX" --controls="tree_delete:1"
+
+exit $failed
diff --git a/nsswitch/tests/test_idmap_rid.sh b/nsswitch/tests/test_idmap_rid.sh
new file mode 100755
index 0000000..1487aa2
--- /dev/null
+++ b/nsswitch/tests/test_idmap_rid.sh
@@ -0,0 +1,198 @@
+#!/bin/sh
+#
+# Test id mapping with various SIDs and idmap_rid
+#
+
+if [ $# -lt 1 ]; then
+ echo Usage: $0 DOMAIN RANGE_START
+ exit 1
+fi
+
+DOMAIN="$1"
+RANGE_START="$2"
+
+wbinfo="$VALGRIND $BINDIR/wbinfo"
+failed=0
+
+. $(dirname $0)/../../testprogs/blackbox/subunit.sh
+
+DOMAIN_SID=$($wbinfo -n "$DOMAIN/" | cut -f 1 -d " ")
+if [ $? -ne 0 ]; then
+ echo "Could not find domain SID" | subunit_fail_test "test_idmap_rid"
+ exit 1
+fi
+
+# Find an unused uid and SID
+RID=66666
+MAX_RID=77777
+while true; do
+ id $RID
+ if [ $? -ne 0 ]; then
+ SID="$DOMAIN_SID-$RID"
+ $wbinfo -s $SID
+ if [ $? -ne 0 ]; then
+ break
+ fi
+ fi
+ RID=$(expr $RID + 1)
+ if [ $RID -eq $MAX_RID ]; then
+ echo "Could not find free SID" | subunit_fail_test "test_idmap_rid"
+ exit 1
+ fi
+done
+
+#
+# Test 1: Using non-existing SID to check backend returns a mapping
+#
+
+EXPECTED_ID=$(expr $RID + $RANGE_START)
+out="$($wbinfo --sids-to-unix-ids=$SID)"
+echo "wbinfo returned: \"$out\", expecting \"$SID -> uid/gid $EXPECTED_ID\""
+test "$out" = "$SID -> uid/gid $EXPECTED_ID"
+ret=$?
+testit "Unknown RID from primary domain returns a mapping" test $ret -eq 0 || failed=$(expr $failed + 1)
+
+#
+# Test 2: Using bogus SID with bad domain part to check idmap backend does not generate a mapping
+#
+
+SID=S-1-5-21-1111-2222-3333-666
+out="$($wbinfo --sids-to-unix-ids=$SID)"
+echo "wbinfo returned: \"$out\", expecting \"$SID -> unmapped\""
+test "$out" = "$SID -> unmapped"
+ret=$?
+testit "Bogus SID returns unmapped" test $ret -eq 0 || failed=$(expr $failed + 1)
+
+#
+# Test 3: ID_TYPE_BOTH mappings for group
+#
+
+GROUP="$DOMAIN/Domain Users"
+GROUP_SID=$($wbinfo --name-to-sid="$GROUP" | sed -e 's/ .*//')
+
+uid=$($wbinfo --sid-to-uid=$GROUP_SID)
+ret=$?
+testit "ID_TYPE_BOTH group map to uid succeeds" test $ret -eq 0 ||
+ failed=$(expr $failed + 1)
+testit "ID_TYPE_BOTH group map to uid has result" test -n $uid ||
+ failed=$(expr $failed + 1)
+
+gid=$($wbinfo --sid-to-gid=$GROUP_SID)
+ret=$?
+testit "ID_TYPE_BOTH group map to gid succeeds" test $ret -eq 0 ||
+ failed=$(expr $failed + 1)
+testit "ID_TYPE_BOTH group map to gid has result" test -n $gid ||
+ failed=$(expr $failed + 1)
+
+testit "ID_TYPE_BOTH group uid equals gid" test $uid -eq $gid ||
+ failed=$(expr $failed + 1)
+
+group_pw="$DOMAIN/domain users:*:$uid:$gid::/home/$DOMAIN/domain users:/bin/false"
+
+out=$(getent passwd "$GROUP")
+ret=$?
+testit "getpwnam for ID_TYPE_BOTH group succeeds" test $ret -eq 0 ||
+ failed=$(expr $failed + 1)
+
+test "$out" = "$group_pw"
+ret=$?
+testit "getpwnam for ID_TYPE_BOTH group output" test $ret -eq 0 ||
+ failed=$(expr $failed + 1)
+
+out=$(getent passwd $uid)
+ret=$?
+testit "getpwuid for ID_TYPE_BOTH group succeeds" test $ret -eq 0 ||
+ failed=$(expr $failed + 1)
+test "$out" = "$group_pw"
+ret=$?
+testit "getpwuid for ID_TYPE_BOTH group output" test $ret -eq 0 ||
+ failed=$(expr $failed + 1)
+
+group_gr="$DOMAIN/domain users:x:$gid:"
+
+out=$(getent group "$GROUP")
+ret=$?
+testit "getgrnam for ID_TYPE_BOTH group succeeds" test $ret -eq 0 ||
+ failed=$(expr $failed + 1)
+test "$out" = "$group_gr"
+ret=$?
+testit "getgrnam for ID_TYPE_BOTH group output" test $ret -eq 0 ||
+ failed=$(expr $failed + 1)
+
+out=$(getent group "$gid")
+ret=$?
+testit "getgrgid for ID_TYPE_BOTH group succeeds" test $ret -eq 0 ||
+ failed=$(expr $failed + 1)
+test "$out" = "$group_gr"
+ret=$?
+testit "getgrgid for ID_TYPE_BOTH group output" test $ret -eq 0 ||
+ failed=$(expr $failed + 1)
+
+#
+# Test 4: ID_TYPE_BOTH mappings for user
+#
+
+dom_users_gid=$gid
+
+USER="$DOMAIN/Administrator"
+USER_SID=$($wbinfo --name-to-sid="$USER" | sed -e 's/ .*//')
+
+uid=$($wbinfo --sid-to-uid=$USER_SID)
+ret=$?
+testit "ID_TYPE_BOTH user map to uid succeeds" test $ret -eq 0 ||
+ failed=$(expr $failed + 1)
+testit "ID_TYPE_BOTH user map to uid has result" test -n $uid ||
+ failed=$(expr $failed + 1)
+
+gid=$($wbinfo --sid-to-gid=$USER_SID)
+ret=$?
+testit "ID_TYPE_BOTH user map to gid succeeds" test $ret -eq 0 ||
+ failed=$(expr $failed + 1)
+testit "ID_TYPE_BOTH user map to gid has result" test -n $gid ||
+ failed=$(expr $failed + 1)
+
+testit "ID_TYPE_BOTH user uid equals gid" test $uid -eq $gid ||
+ failed=$(expr $failed + 1)
+
+user_pw="$DOMAIN/administrator:*:$uid:$dom_users_gid::/home/$DOMAIN/administrator:/bin/false"
+
+out=$(getent passwd "$USER")
+ret=$?
+testit "getpwnam for ID_TYPE_BOTH user succeeds" test $ret -eq 0 ||
+ failed=$(expr $failed + 1)
+
+test "$out" = "$user_pw"
+ret=$?
+testit "getpwnam for ID_TYPE_BOTH user output" test $ret -eq 0 ||
+ failed=$(expr $failed + 1)
+
+out=$(getent passwd $uid)
+ret=$?
+testit "getpwuid for ID_TYPE_BOTH user succeeds" test $ret -eq 0 ||
+ failed=$(expr $failed + 1)
+test "$out" = "$user_pw"
+ret=$?
+testit "getpwuid for ID_TYPE_BOTH user output" test $ret -eq 0 ||
+ failed=$(expr $failed + 1)
+
+user_gr="$DOMAIN/administrator:x:$gid:$DOMAIN/administrator"
+
+out=$(getent group "$USER")
+ret=$?
+testit "getgrnam for ID_TYPE_BOTH user succeeds" test $ret -eq 0 ||
+ failed=$(expr $failed + 1)
+test "$out" = "$user_gr"
+ret=$?
+testit "getgrnam for ID_TYPE_BOTH user output" test $ret -eq 0 ||
+ failed=$(expr $failed + 1)
+
+out=$(getent group "$gid")
+ret=$?
+testit "getgrgid for ID_TYPE_BOTH user succeeds" test $ret -eq 0 ||
+ failed=$(expr $failed + 1)
+test "$out" = "$user_gr"
+ret=$?
+testit "getgrgid for ID_TYPE_BOTH user output" test $ret -eq 0 ||
+ failed=$(expr $failed + 1)
+
+exit $failed
diff --git a/nsswitch/tests/test_rfc2307_mapping.sh b/nsswitch/tests/test_rfc2307_mapping.sh
new file mode 100755
index 0000000..8cd2e5d
--- /dev/null
+++ b/nsswitch/tests/test_rfc2307_mapping.sh
@@ -0,0 +1,187 @@
+#!/bin/sh
+# Blackbox test for wbinfo and rfc2307 mappings
+if [ $# -lt 4 ]; then
+ cat <<EOF
+Usage: test_rfc2307_mapping.sh DOMAIN USERNAME PASSWORD SERVER UID_RFC2307TEST GID_RFC2307TEST
+EOF
+ exit 1
+fi
+
+DOMAIN=$1
+USERNAME=$2
+PASSWORD=$3
+SERVER=$4
+UID_RFC2307TEST=$5
+GID_RFC2307TEST=$6
+shift 6
+
+failed=0
+samba4bindir="$BINDIR"
+wbinfo="$VALGRIND $samba4bindir/wbinfo"
+samba_tool="$VALGRIND $samba4bindir/samba-tool"
+if [ -f "$samba4bindir/ldbmodify" ]; then
+ ldbmodify="$samba4bindir/ldbmodify"
+else
+ # Using system ldbmodify
+ ldbmodify="ldbmodify"
+fi
+
+. $(dirname $0)/../../testprogs/blackbox/subunit.sh
+
+testfail()
+{
+ name="$1"
+ shift
+ cmdline="$*"
+ echo "test: $name"
+ $cmdline
+ status=$?
+ if [ x$status = x0 ]; then
+ echo "failure: $name"
+ else
+ echo "success: $name"
+ fi
+ return $status
+}
+
+knownfail()
+{
+ name="$1"
+ shift
+ cmdline="$*"
+ echo "test: $name"
+ $cmdline
+ status=$?
+ if [ x$status = x0 ]; then
+ echo "failure: $name [unexpected success]"
+ status=1
+ else
+ echo "knownfail: $name"
+ status=0
+ fi
+ return $status
+}
+
+# Create new testing account
+testit "user add" $PYTHON $samba_tool user create --given-name="rfc2307" --surname="Tester" --initial="UT" rfc2307_test_user testp@ssw0Rd $@
+
+#test creation of six different groups
+testit "group add" $PYTHON $samba_tool group add $CONFIG --group-scope='Domain' --group-type='Security' rfc2307_test_group $@
+
+# Create new testing group
+
+# Convert name to SID
+testit "wbinfo -n against $TARGET" $wbinfo -n "$DOMAIN/rfc2307_test_user" || failed=$(expr $failed + 1)
+user_sid=$($wbinfo -n "$DOMAIN/rfc2307_test_user" | cut -d " " -f1)
+echo "$DOMAIN/rfc2307_test_user resolved to $user_sid"
+
+testit "wbinfo -s $user_sid against $TARGET" $wbinfo -s $user_sid || failed=$(expr $failed + 1)
+user_name=$($wbinfo -s $user_sid | cut -d " " -f1 | tr a-z A-Z)
+echo "$user_sid resolved to $user_name"
+
+tested_name=$(echo $DOMAIN/rfc2307_test_user | tr a-z A-Z)
+
+# Now check that wbinfo works correctly (sid <=> name)
+echo "test: wbinfo -s check for sane mapping"
+if test x$user_name != x$tested_name; then
+ echo "$user_name does not match $tested_name"
+ echo "failure: wbinfo -s check for sane mapping"
+ failed=$(expr $failed + 1)
+else
+ echo "success: wbinfo -s check for sane mapping"
+fi
+
+testit "wbinfo -n on the returned name against $TARGET" $wbinfo -n $user_name || failed=$(expr $failed + 1)
+test_sid=$($wbinfo -n $tested_name | cut -d " " -f1)
+
+echo "test: wbinfo -n check for sane mapping"
+if test x$user_sid != x$test_sid; then
+ echo "$user_sid does not match $test_sid"
+ echo "failure: wbinfo -n check for sane mapping"
+ failed=$(expr $failed + 1)
+else
+ echo "success: wbinfo -n check for sane mapping"
+fi
+
+testit "wbinfo -n against $TARGET" $wbinfo -n "$DOMAIN/rfc2307_test_group" || failed=$(expr $failed + 1)
+group_sid=$($wbinfo -n "$DOMAIN/rfc2307_test_group" | cut -d " " -f1)
+echo "$DOMAIN/rfc2307_test_group resolved to $group_sid"
+
+# Then add a uidNumber to the group record using ldbmodify
+cat >$PREFIX/tmpldbmodify <<EOF
+dn: <SID=$user_sid>
+changetype: modify
+add: uidNumber
+uidNumber: $UID_RFC2307TEST
+EOF
+
+testit "modify gidNumber on group" $VALGRIND $ldbmodify -H ldap://$SERVER $PREFIX/tmpldbmodify -U$DOMAIN/$USERNAME%$PASSWORD $@ || failed=$(expr $failed + 1)
+
+# Then add a gidNumber to the group record using ldbmodify
+cat >$PREFIX/tmpldbmodify <<EOF
+dn: <SID=$group_sid>
+changetype: modify
+add: gidNumber
+gidNumber: $GID_RFC2307TEST
+EOF
+
+testit "modify gidNumber on group" $VALGRIND $ldbmodify -H ldap://$SERVER $PREFIX/tmpldbmodify -U$DOMAIN/$USERNAME%$PASSWORD $@ || failed=$(expr $failed + 1)
+
+rm -f $PREFIX/tmpldbmodify
+
+# Now check we get a correct SID for the UID
+
+testit "wbinfo -U against $TARGET" $wbinfo -U $UID_RFC2307TEST || failed=$(expr $failed + 1)
+
+echo "test: wbinfo -U check for sane mapping"
+sid_for_user=$($wbinfo -U $UID_RFC2307TEST)
+if test x"$sid_for_user" != x"$user_sid"; then
+ echo "uid $UID_RFC2307TEST mapped to $sid_for_user, not $user_sid"
+ echo "failure: wbinfo -U check for sane mapping"
+ failed=$(expr $failed + 1)
+else
+ echo "success: wbinfo -U check for sane mapping"
+fi
+
+testit "wbinfo -G against $TARGET" $wbinfo -G $GID_RFC2307TEST || failed=$(expr $failed + 1)
+
+echo "test: wbinfo -G check for sane mapping"
+sid_for_group=$($wbinfo -G $GID_RFC2307TEST)
+if test x$sid_for_group != "x$group_sid"; then
+ echo "gid $GID_RFC2307TEST mapped to $sid_for_group, not $group_sid"
+ echo "failure: wbinfo -G check for sane mapping"
+ failed=$(expr $failed + 1)
+else
+ echo "success: wbinfo -G check for sane mapping"
+fi
+
+# Now check we get the right UID from the SID
+testit "wbinfo -S against $TARGET" $wbinfo -S "$user_sid" || failed=$(expr $failed + 1)
+
+echo "test: wbinfo -S check for sane mapping"
+uid_for_user_sid=$($wbinfo -S $user_sid)
+if test 0$uid_for_user_sid -ne $UID_RFC2307TEST; then
+ echo "$user_sid mapped to $uid_for_sid, not $UID_RFC2307TEST"
+ echo "failure: wbinfo -S check for sane mapping"
+ failed=$(expr $failed + 1)
+else
+ echo "success: wbinfo -S check for sane mapping"
+fi
+
+# Now check we get the right GID from the SID
+testit "wbinfo -Y" $wbinfo -Y "$group_sid" || failed=$(expr $failed + 1)
+
+echo "test: wbinfo -Y check for sane mapping"
+gid_for_user_sid=$($wbinfo -Y $group_sid)
+if test 0$gid_for_user_sid -ne $GID_RFC2307TEST; then
+ echo "$group_sid mapped to $gid_for_sid, not $GID_RFC2307TEST"
+ echo "failure: wbinfo -Y check for sane mapping"
+ failed=$(expr $failed + 1)
+else
+ echo "success: wbinfo -Y check for sane mapping"
+fi
+
+testit "group delete" $PYTHON $samba_tool group delete rfc2307_test_group $@
+testit "user delete" $PYTHON $samba_tool user delete rfc2307_test_user $@
+
+exit $failed
diff --git a/nsswitch/tests/test_ticket_expiry.sh b/nsswitch/tests/test_ticket_expiry.sh
new file mode 100755
index 0000000..f2fed55
--- /dev/null
+++ b/nsswitch/tests/test_ticket_expiry.sh
@@ -0,0 +1,74 @@
+#!/bin/sh
+# Test winbind ad backend behaviour when the kerberos ticket expires
+
+if [ $# -ne 1 ]; then
+ echo Usage: $0 DOMAIN
+ exit 1
+fi
+
+DOMAIN="$1"
+
+wbinfo="$VALGRIND $BINDIR/wbinfo"
+net="$VALGRIND $BINDIR/net"
+
+failed=0
+
+. $(dirname $0)/../../testprogs/blackbox/subunit.sh
+
+DOMAIN_SID=$($wbinfo -n "$DOMAIN/" | cut -f 1 -d " ")
+if [ $? -ne 0 ]; then
+ echo "Could not find domain SID" | subunit_fail_test "test_idmap_ad"
+ exit 1
+fi
+ADMINS_SID="$DOMAIN_SID-512"
+
+# Previous tests might have put in a mapping
+$net cache del IDMAP/SID2XID/"$ADMINS_SID"
+
+# Trigger a winbind ad connection with a 5-second ticket lifetime,
+# see the smb.conf for the ad_member_idmap_ad environment we're in
+#
+# We expect failure here because there are no mappings in AD. In this
+# test we are only interested in the winbind LDAP connection as such,
+# we don't really care whether idmap_ad works fine. This is done in
+# different tests. And a negative lookup also triggers the LDAP
+# connection.
+
+testit_expect_failure "Deleting0 IDMAP/SID2XID/$ADMINS_SID" $net cache del IDMAP/SID2XID/"$ADMINS_SID" ||
+ failed=$(expr $failed + 1)
+
+testit_expect_failure "Expecting failure1, no mapping in AD" $wbinfo --sid-to-gid "$ADMINS_SID" ||
+ failed=$(expr $failed + 1)
+
+testit "Deleting1 IDMAP/SID2XID/$ADMINS_SID" $net cache del IDMAP/SID2XID/"$ADMINS_SID" ||
+ failed=$(expr $failed + 1)
+
+# allow our kerberos ticket to expire
+testit "Sleeping for 6 seconds" sleep 6 || failed=$(expr $failed + 1)
+
+# Try again, check how long it took to recover from ticket expiry
+#
+# On the LDAP connection two things happen: First we get an
+# unsolicited exop response telling us the network session was
+# abandoned, and secondly the LDAP server will kill the TCP
+# connection. Our ldap server is configured to defer the TCP
+# disconnect by 10 seconds. We need to make sure that winbind already
+# reacts to the unsolicited exop reply, discarding the connection. The
+# only way is to make sure the following wbinfo does not take too
+# long.
+
+# We need to do the test command in this funny way as on gitlab we're
+# using the bash builtin
+
+START=$(date +%s)
+testit_expect_failure "Expecting failure2, no mapping in AD" $wbinfo --sid-to-gid "$ADMINS_SID" ||
+ failed=$(expr $failed + 1)
+END=$(date +%s)
+DURATION=$(expr $END - $START)
+testit "timeout DURATION[$DURATION] < 8" test "$DURATION" -le 8 ||
+ failed=$(expr $failed + 1)
+
+testit "Deleting2 IDMAP/SID2XID/$ADMINS_SID" $net cache del IDMAP/SID2XID/"$ADMINS_SID" ||
+ failed=$(expr $failed + 1)
+
+exit $failed
diff --git a/nsswitch/tests/test_wbinfo.sh b/nsswitch/tests/test_wbinfo.sh
new file mode 100755
index 0000000..8b48abc
--- /dev/null
+++ b/nsswitch/tests/test_wbinfo.sh
@@ -0,0 +1,321 @@
+#!/bin/sh
+# Blackbox test for wbinfo
+if [ $# -lt 4 ]; then
+ cat <<EOF
+Usage: test_wbinfo.sh DOMAIN USERNAME PASSWORD TARGET
+EOF
+ exit 1
+fi
+
+DOMAIN=$1
+USERNAME=$2
+PASSWORD=$3
+TARGET=$4
+shift 4
+
+failed=0
+samba4bindir="$BINDIR"
+wbinfo="$VALGRIND $samba4bindir/wbinfo"
+
+. $(dirname $0)/../../testprogs/blackbox/subunit.sh
+
+testfail()
+{
+ name="$1"
+ shift
+ cmdline="$*"
+ echo "test: $name"
+ $cmdline
+ status=$?
+ if [ x$status = x0 ]; then
+ echo "failure: $name"
+ else
+ echo "success: $name"
+ fi
+ return $status
+}
+
+knownfail()
+{
+ name="$1"
+ shift
+ cmdline="$*"
+ echo "test: $name"
+ $cmdline
+ status=$?
+ if [ x$status = x0 ]; then
+ echo "failure: $name [unexpected success]"
+ status=1
+ else
+ echo "knownfail: $name"
+ status=0
+ fi
+ return $status
+}
+
+KRB5CCNAME_PATH="$PREFIX/test_wbinfo_krb5ccache"
+rm -f $KRB5CCNAME_PATH
+
+KRB5CCNAME="FILE:$KRB5CCNAME_PATH"
+export KRB5CCNAME
+
+# List users
+testit "wbinfo -u against $TARGET" $wbinfo -u || failed=$(expr $failed + 1)
+# List groups
+testit "wbinfo -g against $TARGET" $wbinfo -g || failed=$(expr $failed + 1)
+# Convert netbios name to IP
+# Does not work yet
+testit "wbinfo -N against $TARGET" $wbinfo -N $NETBIOSNAME || failed=$(expr $failed + 1)
+# Convert IP to netbios name
+# Does not work yet
+testit "wbinfo -I against $TARGET" $wbinfo -I $SERVER_IP || failed=$(expr $failed + 1)
+
+# Convert name to SID
+testit "wbinfo -n against $TARGET" $wbinfo -n "$DOMAIN/$USERNAME" || failed=$(expr $failed + 1)
+admin_sid=$($wbinfo -n "$DOMAIN/$USERNAME" | cut -d " " -f1)
+echo "$DOMAIN/$USERNAME resolved to $admin_sid"
+
+testit "wbinfo -s $admin_sid against $TARGET" $wbinfo -s $admin_sid || failed=$(expr $failed + 1)
+admin_name=$($wbinfo -s $admin_sid | cut -d " " -f1 | tr a-z A-Z)
+echo "$admin_sid resolved to $admin_name"
+
+tested_name=$(echo $DOMAIN/$USERNAME | tr a-z A-Z)
+
+echo "test: wbinfo -s check for sane mapping"
+if test x$admin_name != x$tested_name; then
+ echo "$admin_name does not match $tested_name"
+ echo "failure: wbinfo -s check for sane mapping"
+ failed=$(expr $failed + 1)
+else
+ echo "success: wbinfo -s check for sane mapping"
+fi
+
+while read SID; do
+ read NAME
+
+ testit "wbinfo -s $SID against $TARGET" $wbinfo -s $SID || failed=$(expr $failed + 1)
+
+ RESOLVED_NAME=$($wbinfo -s $SID | tr a-z A-Z)
+ echo "$SID resolved to $RESOLVED_NAME"
+
+ echo "test: wbinfo -s $SID against $TARGET"
+ if test x"$RESOLVED_NAME" != x"$NAME"; then
+ echo "$RESOLVED_NAME does not match $NAME"
+ echo "failure: wbinfo -s $SID against $TARGET"
+ failed=$(expr $failed + 1)
+ else
+ echo "success: wbinfo -s $SID against $TARGET"
+ fi
+done <<EOF
+S-1-1-0
+/EVERYONE 5
+S-1-3-1
+/CREATOR GROUP 5
+S-1-5-1
+NT AUTHORITY/DIALUP 5
+EOF
+
+testit "wbinfo -n on the returned name against $TARGET" $wbinfo -n $admin_name || failed=$(expr $failed + 1)
+test_sid=$($wbinfo -n $tested_name | cut -d " " -f1)
+
+echo "test: wbinfo -n check for sane mapping"
+if test x$admin_sid != x$test_sid; then
+ echo "$admin_sid does not match $test_sid"
+ echo "failure: wbinfo -n check for sane mapping"
+ failed=$(expr $failed + 1)
+else
+ echo "success: wbinfo -n check for sane mapping"
+fi
+
+echo "test: wbinfo -n NT Authority/Authenticated Users"
+$wbinfo -n "NT Authority/Authenticated Users"
+if [ $? -ne 0 ]; then
+ echo "failure: wbinfo -n NT Authority/Authenticated Users"
+ failed=$(expr $failed + 1)
+else
+ echo "success: wbinfo -n NT Authority/Authenticated Users"
+fi
+
+echo "test: wbinfo --group-info NT Authority/Authenticated Users"
+$wbinfo --group-info "NT Authority/Authenticated Users"
+if [ $? -ne 0 ]; then
+ echo "failure: wbinfo --group-info NT Authority/Authenticated Users"
+ failed=$(expr $failed + 1)
+else
+ echo "success: wbinfo --group-info NT Authority/Authenticated Users"
+fi
+
+testit "wbinfo -U against $TARGET" $wbinfo -U 30000 || failed=$(expr $failed + 1)
+
+echo "test: wbinfo -U check for sane mapping"
+sid_for_30000=$($wbinfo -U 30000)
+if test x$sid_for_30000 != "xS-1-22-1-30000"; then
+ echo "uid 30000 mapped to $sid_for_30000, not S-1-22-1-30000"
+ echo "failure: wbinfo -U check for sane mapping"
+ failed=$(expr $failed + 1)
+else
+ echo "success: wbinfo -U check for sane mapping"
+fi
+
+admin_uid=$($wbinfo -S $admin_sid)
+
+testit "wbinfo -G against $TARGET" $wbinfo -G 30000 || failed=$(expr $failed + 1)
+
+echo "test: wbinfo -G check for sane mapping"
+sid_for_30000=$($wbinfo -G 30000)
+if test x$sid_for_30000 != "xS-1-22-2-30000"; then
+ echo "gid 30000 mapped to $sid_for_30000, not S-1-22-2-30000"
+ echo "failure: wbinfo -G check for sane mapping"
+ failed=$(expr $failed + 1)
+else
+ echo "success: wbinfo -G check for sane mapping"
+fi
+
+testit "wbinfo -S against $TARGET" $wbinfo -S "S-1-22-1-30000" || failed=$(expr $failed + 1)
+
+echo "test: wbinfo -S check for sane mapping"
+uid_for_sid=$($wbinfo -S S-1-22-1-30000)
+if test 0$uid_for_sid -ne 30000; then
+ echo "S-1-22-1-30000 mapped to $uid_for_sid, not 30000"
+ echo "failure: wbinfo -S check for sane mapping"
+ failed=$(expr $failed + 1)
+else
+ echo "success: wbinfo -S check for sane mapping"
+fi
+
+testfail "wbinfo -S against $TARGET using invalid SID" $wbinfo -S "S-1-22-2-30000" && failed=$(expr $failed + 1)
+
+testit "wbinfo -Y against $TARGET" $wbinfo -Y "S-1-22-2-30000" || failed=$(expr $failed + 1)
+
+echo "test: wbinfo -Y check for sane mapping"
+gid_for_sid=$($wbinfo -Y S-1-22-2-30000)
+if test 0$gid_for_sid -ne 30000; then
+ echo "S-1-22-2-30000 mapped to $gid_for_sid, not 30000"
+ echo "failure: wbinfo -Y check for sane mapping"
+ failed=$(expr $failed + 1)
+else
+ echo "success: wbinfo -Y check for sane mapping"
+fi
+
+testfail "wbinfo -Y against $TARGET using invalid SID" $wbinfo -Y "S-1-22-1-30000" && failed=$(expr $failed + 1)
+
+testit "wbinfo -t against $TARGET" $wbinfo -t || failed=$(expr $failed + 1)
+
+#didn't really work anyway
+testit "wbinfo --trusted-domains against $TARGET" $wbinfo --trusted-domains || failed=$(expr $failed + 1)
+testit "wbinfo --all-domains against $TARGET" $wbinfo --all-domains || failed=$(expr $failed + 1)
+
+testit "wbinfo --own-domain against $TARGET" $wbinfo --own-domain || failed=$(expr $failed + 1)
+
+echo "test: wbinfo --own-domain against $TARGET check output"
+own_domain=$($wbinfo --own-domain)
+if test x$own_domain = x$DOMAIN; then
+ echo "success: wbinfo --own-domain against $TARGET check output"
+else
+ echo "Own domain reported as $own_domain instead of $DOMAIN"
+ echo "failure: wbinfo --own-domain against $TARGET check output"
+ failed=$(expr $failed + 1)
+fi
+
+# this does not work
+knownfail "wbinfo --sequence against $TARGET" $wbinfo --sequence
+
+# this is stubbed out now
+testit "wbinfo -D against $TARGET" $wbinfo -D $DOMAIN || failed=$(expr $failed + 1)
+
+testit "wbinfo -i against $TARGET" $wbinfo -i "$DOMAIN/$USERNAME" || failed=$(expr $failed + 1)
+
+echo "test: wbinfo --group-info against $TARGET"
+gid=$($wbinfo --group-info "$DOMAIN/Domain users" | cut -d: -f3)
+if test x$? = x0; then
+ echo "success: wbinfo --group-info against $TARGET"
+else
+ echo "failure: wbinfo --group-info against $TARGET"
+ failed=$(expr $failed + 1)
+fi
+
+test_name="wbinfo -i against $TARGET"
+subunit_start_test "$test_name"
+passwd_line=$($wbinfo -i "$DOMAIN/$USERNAME")
+if test x$? = x0; then
+ subunit_pass_test "$test_name"
+else
+ subunit_fail_test "$test_name"
+ failed=$(expr $failed + 1)
+fi
+
+test_name="confirm output of wbinfo -i against $TARGET"
+subunit_start_test "$test_name"
+
+# The full name (GECOS) is based on name (the RDN, in this case CN)
+# and displayName in winbindd_ads, and is based only on displayName in
+# winbindd_msrpc and winbindd_rpc. Allow both versions.
+if test "$TARGET" = "ad_member"; then
+ expected1_line="$DOMAIN/administrator:*:$admin_uid:$gid:Administrator:/home/$DOMAIN/Domain Users/administrator:/bin/false"
+ expected2_line="$DOMAIN/administrator:*:$admin_uid:$gid::/home/$DOMAIN/Domain Users/administrator:/bin/false"
+else
+ expected1_line="$DOMAIN/administrator:*:$admin_uid:$gid:Administrator:/home/$DOMAIN/administrator:/bin/false"
+ expected2_line="$DOMAIN/administrator:*:$admin_uid:$gid::/home/$DOMAIN/administrator:/bin/false"
+fi
+
+if test "x$passwd_line" = "x$expected1_line" -o "x$passwd_line" = "x$expected2_line"; then
+ subunit_pass_test "$test_name"
+else
+ echo "expected '$expected1_line' or '$expected2_line' got '$passwd_line'" | subunit_fail_test "$test_name"
+ failed=$(expr $failed + 1)
+fi
+
+test_name="wbinfo --uid-info against $TARGET"
+subunit_start_test "$test_name"
+passwd_line=$($wbinfo --uid-info=$admin_uid)
+if test x$? = x0; then
+ subunit_pass_test "$test_name"
+else
+ subunit_fail_test "$test_name"
+ failed=$(expr $failed + 1)
+fi
+
+test_name="confirm output of wbinfo --uid-info against $TARGET"
+subunit_start_test "$test_name"
+if test "x$passwd_line" = "x$expected1_line" -o "x$passwd_line" = "x$expected2_line"; then
+ subunit_pass_test "$test_name"
+else
+ echo "expected '$expected1_line' or '$expected2_line' got '$passwd_line'" | subunit_fail_test "$test_name"
+ failed=$(expr $failed + 1)
+fi
+
+testfail "wbinfo --group-info against $TARGET with $USERNAME" $wbinfo --group-info $USERNAME && failed=$(expr $failed + 1)
+
+testit "wbinfo --gid-info against $TARGET" $wbinfo --gid-info $gid || failed=$(expr $failed + 1)
+
+testit "wbinfo -r against $TARGET" $wbinfo -r "$DOMAIN/$USERNAME" || failed=$(expr $failed + 1)
+
+testit "wbinfo --user-domgroups against $TARGET" $wbinfo --user-domgroups $admin_sid || failed=$(expr $failed + 1)
+
+testit "wbinfo --user-sids against $TARGET" $wbinfo --user-sids $admin_sid || failed=$(expr $failed + 1)
+
+testit "wbinfo -a against $TARGET with domain creds" $wbinfo -a "$DOMAIN/$USERNAME"%"$PASSWORD" || failed=$(expr $failed + 1)
+
+testit "wbinfo -a against $TARGET with domain upn creds" $wbinfo -a "$USERNAME@$DOMAIN"%"$PASSWORD" || failed=$(expr $failed + 1)
+
+testit "wbinfo --getdcname against $TARGET" $wbinfo --getdcname=$DOMAIN
+
+testit "wbinfo -p against $TARGET" $wbinfo -p || failed=$(expr $failed + 1)
+
+testit "wbinfo -K against $TARGET with domain creds" $wbinfo --krb5ccname=$KRB5CCNAME --krb5auth="$DOMAIN/$USERNAME"%"$PASSWORD" || failed=$(expr $failed + 1)
+
+testit "wbinfo --separator against $TARGET" $wbinfo --separator || failed=$(expr $failed + 1)
+
+if test "$TARGET" = "ad_member"; then
+ testit "wbinfo --domain-info=$DOMAIN" $wbinfo --domain-info=$DOMAIN || failed=$(expr $failed + 1)
+
+ testit "wbinfo --dc-info=$DOMAIN" $wbinfo --dc-info=$DOMAIN || failed=$(expr $failed + 1)
+fi
+
+testit_expect_failure "wbinfo -a against $TARGET with invalid password" $wbinfo -a "$DOMAIN/$USERNAME%InvalidPassword" && failed=$(expr $failed + 1)
+
+testit_expect_failure "wbinfo -K against $TARGET with invalid password" $wbinfo -K "$DOMAIN/$USERNAME%InvalidPassword" && failed=$(expr $failed + 1)
+
+rm -f $KRB5CCNAME_PATH
+
+exit $failed
diff --git a/nsswitch/tests/test_wbinfo_name_lookup.sh b/nsswitch/tests/test_wbinfo_name_lookup.sh
new file mode 100755
index 0000000..2b2b29a
--- /dev/null
+++ b/nsswitch/tests/test_wbinfo_name_lookup.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+# Blackbox test for wbinfo name lookup
+if [ $# -lt 3 ]; then
+ cat <<EOF
+Usage: test_wbinfo.sh DOMAIN REALM DC_USERNAME
+EOF
+ exit 1
+fi
+
+DOMAIN=$1
+REALM=$2
+DC_USERNAME=$3
+shift 3
+
+failed=0
+sambabindir="$BINDIR"
+wbinfo="$VALGRIND $sambabindir/wbinfo"
+
+. $(dirname $0)/../../testprogs/blackbox/subunit.sh
+
+# Correct query is expected to work
+testit "name-to-sid.single-separator" \
+ $wbinfo -n $DOMAIN/$DC_USERNAME ||
+ failed=$(expr $failed + 1)
+
+testit "name-to-sid.at_domain" \
+ $wbinfo -n $DOMAIN/ ||
+ failed=$(expr $failed + 1)
+
+testit "name-to-sid.upn" \
+ $wbinfo -n $DC_USERNAME@$REALM ||
+ failed=$(expr $failed + 1)
+
+testit "name-to-sid.realm-user" \
+ $wbinfo -n $REALM/$DC_USERNAME ||
+ failed=$(expr $failed + 1)
+
+# For the name-to-sid.realm-user query, ensure
+# that this does not change subsequent sid-to-name
+# queries.
+sid=$($wbinfo -n $REALM/$DC_USERNAME | sed -e 's/ .*//')
+out=$($wbinfo -s $sid | sed -e 's/ .//')
+# winbindd returns usernames in lowercase
+lcuser=$(echo $DC_USERNAME | tr A-Z a-z)
+testit "Verify DOMAIN/USER output" \
+ test "$out" = "$DOMAIN/$lcuser" ||
+ failed=$(expr $failed + 1)
+
+# Two separator characters should fail
+testit_expect_failure "name-to-sid.double-separator" \
+ $wbinfo -n $DOMAIN//$DC_USERNAME ||
+ failed=$(expr $failed + 1)
+
+# Invalid domain is expected to fail
+testit_expect_failure "name-to-sid.invalid-domain" \
+ $wbinfo -n INVALID/$DC_USERNAME ||
+ failed=$(expr $failed + 1)
+
+# Invalid domain with two separator characters is expected to fail
+testit_expect_failure "name-to-sid.double-separator-invalid-domain" \
+ $wbinfo -n INVALID//$DC_USERNAME ||
+ failed=$(expr $failed + 1)
+
+exit $failed
diff --git a/nsswitch/tests/test_wbinfo_sids_to_xids.sh b/nsswitch/tests/test_wbinfo_sids_to_xids.sh
new file mode 100755
index 0000000..c910d19
--- /dev/null
+++ b/nsswitch/tests/test_wbinfo_sids_to_xids.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+incdir=$(dirname $0)/../../testprogs/blackbox
+. $incdir/subunit.sh
+
+#
+# S-1-5-123456789 fails, but S-1-5-11 succeeds. Check that S-1-5-11 is
+# mapped successfully with a GID in the 1000x range
+#
+wbinfo_some_mapped()
+{
+ output=$($VALGRIND $BINDIR/wbinfo --sids-to-unix-ids=S-1-5-123456789,S-1-5-11)
+ test x"$?" = x"0" || {
+ return 1
+ }
+
+ printf '%s' "$output" | grep -q 'S-1-5-123456789 -> unmapped' || {
+ printf '%s' "$output"
+ return 1
+ }
+
+ printf '%s' "$output" | grep -q 'S-1-5-11 -> gid 10000' || {
+ printf '%s' "$output"
+ return 1
+ }
+
+ return 0
+}
+
+testit "wbinfo some mapped" wbinfo_some_mapped || failed=$(expr $failed + 1)
+
+testok $0 $failed
diff --git a/nsswitch/tests/test_wbinfo_simple.sh b/nsswitch/tests/test_wbinfo_simple.sh
new file mode 100755
index 0000000..226715a
--- /dev/null
+++ b/nsswitch/tests/test_wbinfo_simple.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+if [ $# -lt 1 ]; then
+ cat <<EOF
+Usage: test_wbinfo_simple.sh <wbinfo args>
+EOF
+ exit 1
+fi
+
+ADDARGS="$*"
+
+incdir=$(dirname $0)/../../testprogs/blackbox
+. $incdir/subunit.sh
+
+KRB5CCNAME_PATH="$PREFIX/test_wbinfo_simple_krb5ccname"
+rm -f $KRB5CCNAME_PATH
+
+KRB5CCNAME="FILE:$KRB5CCNAME_PATH"
+export KRB5CCNAME
+
+testit "wbinfo" $VALGRIND $BINDIR/wbinfo --krb5ccname="$KRB5CCNAME" $ADDARGS || failed=$(expr $failed + 1)
+
+rm -f $KRB5CCNAME_PATH
+
+testok $0 $failed
diff --git a/nsswitch/tests/test_wbinfo_user_info.sh b/nsswitch/tests/test_wbinfo_user_info.sh
new file mode 100755
index 0000000..b9a720d
--- /dev/null
+++ b/nsswitch/tests/test_wbinfo_user_info.sh
@@ -0,0 +1,140 @@
+#!/bin/sh
+# Blackbox test for wbinfo lookup for account name and upn
+# Copyright (c) 2018 Andreas Schneider <asn@samba.org>
+
+if [ $# -lt 6 ]; then
+ cat <<EOF
+Usage: $(basename $0) DOMAIN REALM OWN_DOMAIN USERNAME1 UPN_NAME1 USERNAME2 UPN_NAME2 ENVNAME
+EOF
+ exit 1
+fi
+
+DOMAIN=$1
+REALM=$2
+OWN_DOMAIN=$3
+USERNAME1=$4
+UPN_NAME1=$5
+USERNAME2=$6
+UPN_NAME2=$7
+ENVNAME=$8
+shift 7
+
+failed=0
+
+samba_bindir="$BINDIR"
+wbinfo_tool="$VALGRIND $samba_bindir/wbinfo"
+
+UPN1="$UPN_NAME1@$REALM"
+UPN2="$UPN_NAME2@$REALM"
+
+. $(dirname $0)/../../testprogs/blackbox/subunit.sh
+
+test_user_info()
+{
+ local cmd out ret user domain upn userinfo
+
+ local domain="$1"
+ local user="$2"
+ local upn="$3"
+
+ if [ $# -lt 3 ]; then
+ userinfo="$domain/$user"
+ else
+ userinfo="$upn"
+ fi
+
+ cmd='$wbinfo_tool --user-info $userinfo'
+ eval echo "$cmd"
+ out=$(eval $cmd)
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ echo "failed to lookup $userinfo"
+ echo "$out"
+ return 1
+ fi
+
+ echo "$out" | grep "$domain/$user:.*:.*:.*::/home/$domain/Domain Users/$user"
+ ret=$?
+ if [ $ret != 0 ]; then
+ echo "failed to lookup $userinfo"
+ echo "$out"
+ return 1
+ fi
+
+ return 0
+}
+
+test_getpwnam()
+{
+ local cmd out ret
+
+ local lookup_username=$1
+ local expected_return=$2
+ local expected_output=$3
+
+ cmd='getent passwd $lookup_username'
+ eval echo "$cmd"
+ out=$(eval $cmd)
+ ret=$?
+
+ if [ $ret -ne $expected_return ]; then
+ echo "return code: $ret, expected return code is: $expected_return"
+ echo "$out"
+ return 1
+ fi
+
+ if [ -n "$expected_output" ]; then
+ echo "$out" | grep "$expected_output"
+ ret=$?
+
+ if [ $ret -ne 0 ]; then
+ echo "Unable to find $expected_output in:"
+ echo "$out"
+ return 1
+ fi
+ fi
+
+ return 0
+}
+
+testit "name_to_sid.domain.$USERNAME1" $wbinfo_tool --name-to-sid $DOMAIN/$USERNAME1 || failed=$(expr $failed + 1)
+testit "name_to_sid.upn.$UPN_NAME1" $wbinfo_tool --name-to-sid $UPN1 || failed=$(expr $failed + 1)
+
+testit "user_info.domain.$USERNAME1" test_user_info $DOMAIN $USERNAME1 || failed=$(expr $failed + 1)
+testit "user_info.upn.$UPN_NAME1" test_user_info $DOMAIN $USERNAME1 $UPN1 || failed=$(expr $failed + 1)
+
+testit "name_to_sid.domain.$USERNAME2" $wbinfo_tool --name-to-sid $DOMAIN/$USERNAME2 || failed=$(expr $failed + 1)
+testit "name_to_sid.upn.$UPN_NAME2" $wbinfo_tool --name-to-sid $UPN2 || failed=$(expr $failed + 1)
+
+testit "user_info.domain.$USERNAME2" test_user_info $DOMAIN $USERNAME2 || failed=$(expr $failed + 1)
+testit "user_info.upn.$UPN_NAME2" test_user_info $DOMAIN $USERNAME2 $UPN2 || failed=$(expr $failed + 1)
+
+USERNAME3="testdenied"
+UPN_NAME3="testdenied_upn"
+UPN3="$UPN_NAME3@${REALM}.upn"
+testit "name_to_sid.upn.$UPN_NAME3" $wbinfo_tool --name-to-sid $UPN3 || failed=$(expr $failed + 1)
+testit "user_info.upn.$UPN_NAME3" test_user_info $DOMAIN $USERNAME3 $UPN3 || failed=$(expr $failed + 1)
+
+testit "getpwnam.domain.$DOMAIN.$USERNAME1" test_getpwnam "$DOMAIN/$USERNAME1" 0 "$DOMAIN/$USERNAME1" || failed=$(expr $failed + 1)
+
+testit "getpwnam.upn.$UPN_NAME1" test_getpwnam "$UPN1" 0 "$DOMAIN/$USERNAME1" || failed=$(expr $failed + 1)
+
+case ${ENVNAME} in
+ad_member*)
+ # We should not be able to lookup the user just by the name
+ test_ret=2
+ test_output=""
+ ;;
+fl2008r2dc*)
+ test_ret=0
+ test_output="$OWN_DOMAIN/$USERNAME1"
+ ;;
+*)
+ test_ret=0
+ test_output="$DOMAIN/$USERNAME1"
+ ;;
+esac
+
+testit "getpwnam.local.$USERNAME1" test_getpwnam "$USERNAME1" $test_ret $test_output || failed=$(expr $failed + 1)
+
+exit $failed
diff --git a/nsswitch/tests/test_wbinfo_user_info_cached.sh b/nsswitch/tests/test_wbinfo_user_info_cached.sh
new file mode 100755
index 0000000..a7ed471
--- /dev/null
+++ b/nsswitch/tests/test_wbinfo_user_info_cached.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+# Blackbox test for wbinfo primary groups and samlogon caching
+# Copyright (c) 2020 Andreas Schneider <asn@samba.org>
+
+if [ $# -lt 5 ]; then
+ cat <<EOF
+Usage: $(basename $0) DOMAIN REALM USERNAME PASSWORD PRIMARY_GROUP
+EOF
+ exit 1
+fi
+
+DOMAIN=$1
+REALM=$2
+USERNAME=$3
+PASSWORD=$4
+PRIMARY_GROUP=$5
+shift 5
+
+DEFAULT_GROUP="Domain Users"
+
+failed=0
+
+samba_bindir="$BINDIR"
+wbinfo_tool="$VALGRIND $samba_bindir/wbinfo"
+net_tool="$VALGRIND $samba_bindir/net --configfile=$SERVERCONFFILE"
+
+. $(dirname $0)/../../testprogs/blackbox/subunit.sh
+
+KRB5CCNAME_PATH="$PREFIX/test_wbinfo_user_info_cached_krb5ccache"
+rm -f $KRB5CCNAME_PATH
+
+KRB5CCNAME="FILE:$KRB5CCNAME_PATH"
+export KRB5CCNAME
+
+USER="$DOMAIN/$USERNAME"
+USER_SID=$($wbinfo_tool --name-to-sid="$USER" | sed -e 's/ .*//')
+
+testit_grep "user_info.no_cache" "$DEFAULT_GROUP" $wbinfo_tool --user-info=$USER || failed=$(expr $failed + 1)
+
+# Fill the samlogon cache
+testit "kerberos_login" $wbinfo_tool --krb5ccname=$KRB5CCNAME --krb5auth=$USER%$PASSWORD || failed=$(expr $failed + 1)
+
+testit_grep "user_info.samlogon_cache" "$PRIMARY_GROUP" $wbinfo_tool --user-info=$USER || failed=$(expr $failed + 1)
+
+# Cleanup
+$net_tool cache samlogon delete $USER_SID
+
+rm -f $KRB5CCNAME_PATH
+
+exit $failed
diff --git a/nsswitch/wb_common.c b/nsswitch/wb_common.c
new file mode 100644
index 0000000..1a3ed12
--- /dev/null
+++ b/nsswitch/wb_common.c
@@ -0,0 +1,852 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ winbind client common code
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Andrew Tridgell 2000
+ Copyright (C) Andrew Bartlett 2002
+ Copyright (C) Matthew Newton 2015
+
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "system/select.h"
+#include "winbind_client.h"
+
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+
+static char client_name[32];
+
+/* Global context */
+
+struct winbindd_context {
+ int winbindd_fd; /* winbind file descriptor */
+ bool is_privileged; /* using the privileged socket? */
+ pid_t our_pid; /* calling process pid */
+};
+
+#ifdef HAVE_PTHREAD
+static pthread_mutex_t wb_global_ctx_mutex = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+static struct winbindd_context *get_wb_global_ctx(void)
+{
+ static struct winbindd_context wb_global_ctx = {
+ .winbindd_fd = -1,
+ .is_privileged = false,
+ .our_pid = 0
+ };
+
+#ifdef HAVE_PTHREAD
+ pthread_mutex_lock(&wb_global_ctx_mutex);
+#endif
+ return &wb_global_ctx;
+}
+
+static void put_wb_global_ctx(void)
+{
+#ifdef HAVE_PTHREAD
+ pthread_mutex_unlock(&wb_global_ctx_mutex);
+#endif
+ return;
+}
+
+void winbind_set_client_name(const char *name)
+{
+ if (name == NULL || strlen(name) == 0) {
+ return;
+ }
+
+ (void)snprintf(client_name, sizeof(client_name), "%s", name);
+}
+
+static const char *winbind_get_client_name(void)
+{
+ if (client_name[0] == '\0') {
+ const char *progname = getprogname();
+ int len;
+
+ if (progname == NULL) {
+ progname = "<unknown>";
+ }
+
+ len = snprintf(client_name,
+ sizeof(client_name),
+ "%s",
+ progname);
+ if (len <= 0) {
+ return progname;
+ }
+ }
+
+ return client_name;
+}
+
+/* Initialise a request structure */
+
+static void winbindd_init_request(struct winbindd_request *request,
+ int request_type)
+{
+ request->length = sizeof(struct winbindd_request);
+
+ request->cmd = (enum winbindd_cmd)request_type;
+ request->pid = getpid();
+
+ (void)snprintf(request->client_name,
+ sizeof(request->client_name),
+ "%s",
+ winbind_get_client_name());
+}
+
+/* Initialise a response structure */
+
+static void init_response(struct winbindd_response *response)
+{
+ /* Initialise return value */
+
+ response->result = WINBINDD_ERROR;
+}
+
+/* Close established socket */
+
+static void winbind_close_sock(struct winbindd_context *ctx)
+{
+ if (!ctx) {
+ return;
+ }
+
+ if (ctx->winbindd_fd != -1) {
+ close(ctx->winbindd_fd);
+ ctx->winbindd_fd = -1;
+ }
+}
+
+/* Destructor for global context to ensure fd is closed */
+
+#ifdef HAVE_DESTRUCTOR_ATTRIBUTE
+__attribute__((destructor))
+#elif defined (HAVE_PRAGMA_FINI)
+#pragma fini (winbind_destructor)
+#endif
+static void winbind_destructor(void)
+{
+ struct winbindd_context *ctx;
+
+ ctx = get_wb_global_ctx();
+ winbind_close_sock(ctx);
+ put_wb_global_ctx();
+}
+
+#define CONNECT_TIMEOUT 30
+
+/* Make sure socket handle isn't stdin, stdout or stderr */
+#define RECURSION_LIMIT 3
+
+static int make_nonstd_fd_internals(int fd, int limit /* Recursion limiter */)
+{
+ int new_fd;
+ if (fd >= 0 && fd <= 2) {
+#ifdef F_DUPFD
+ if ((new_fd = fcntl(fd, F_DUPFD, 3)) == -1) {
+ return -1;
+ }
+ /* Paranoia */
+ if (new_fd < 3) {
+ close(new_fd);
+ return -1;
+ }
+ close(fd);
+ return new_fd;
+#else
+ if (limit <= 0)
+ return -1;
+
+ new_fd = dup(fd);
+ if (new_fd == -1)
+ return -1;
+
+ /* use the program stack to hold our list of FDs to close */
+ new_fd = make_nonstd_fd_internals(new_fd, limit - 1);
+ close(fd);
+ return new_fd;
+#endif
+ }
+ return fd;
+}
+
+/****************************************************************************
+ Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
+ else
+ if SYSV use O_NDELAY
+ if BSD use FNDELAY
+ Set close on exec also.
+****************************************************************************/
+
+static int make_safe_fd(int fd)
+{
+ int result, flags;
+ int new_fd = make_nonstd_fd_internals(fd, RECURSION_LIMIT);
+ if (new_fd == -1) {
+ close(fd);
+ return -1;
+ }
+
+ /* Socket should be nonblocking. */
+#ifdef O_NONBLOCK
+#define FLAG_TO_SET O_NONBLOCK
+#else
+#ifdef SYSV
+#define FLAG_TO_SET O_NDELAY
+#else /* BSD */
+#define FLAG_TO_SET FNDELAY
+#endif
+#endif
+
+ if ((flags = fcntl(new_fd, F_GETFL)) == -1) {
+ close(new_fd);
+ return -1;
+ }
+
+ flags |= FLAG_TO_SET;
+ if (fcntl(new_fd, F_SETFL, flags) == -1) {
+ close(new_fd);
+ return -1;
+ }
+
+#undef FLAG_TO_SET
+
+ /* Socket should be closed on exec() */
+#ifdef FD_CLOEXEC
+ result = flags = fcntl(new_fd, F_GETFD, 0);
+ if (flags >= 0) {
+ flags |= FD_CLOEXEC;
+ result = fcntl( new_fd, F_SETFD, flags );
+ }
+ if (result < 0) {
+ close(new_fd);
+ return -1;
+ }
+#endif
+ return new_fd;
+}
+
+/**
+ * @internal
+ *
+ * @brief Check if we talk to the privileged pipe which should be owned by root.
+ *
+ * This checks if we have uid_wrapper running and if this is the case it will
+ * allow one to connect to the winbind privileged pipe even it is not owned by root.
+ *
+ * @param[in] uid The uid to check if we can safely talk to the pipe.
+ *
+ * @return If we have access it returns true, else false.
+ */
+static bool winbind_privileged_pipe_is_root(uid_t uid)
+{
+ if (uid == 0) {
+ return true;
+ }
+
+ if (uid_wrapper_enabled()) {
+ return true;
+ }
+
+ return false;
+}
+
+/* Connect to winbindd socket */
+
+static int winbind_named_pipe_sock(const char *dir)
+{
+ struct sockaddr_un sunaddr;
+ struct stat st;
+ int fd;
+ int wait_time;
+ int slept;
+ int ret;
+
+ /* Check permissions on unix socket directory */
+
+ if (lstat(dir, &st) == -1) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ /*
+ * This tells us that the pipe is owned by a privileged
+ * process, as we will be sending passwords to it.
+ */
+ if (!S_ISDIR(st.st_mode) ||
+ !winbind_privileged_pipe_is_root(st.st_uid)) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ /* Connect to socket */
+
+ sunaddr = (struct sockaddr_un) { .sun_family = AF_UNIX };
+
+ ret = snprintf(sunaddr.sun_path, sizeof(sunaddr.sun_path),
+ "%s/%s", dir, WINBINDD_SOCKET_NAME);
+ if ((ret == -1) || (ret >= sizeof(sunaddr.sun_path))) {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+
+ /* If socket file doesn't exist, don't bother trying to connect
+ with retry. This is an attempt to make the system usable when
+ the winbindd daemon is not running. */
+
+ if (lstat(sunaddr.sun_path, &st) == -1) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ /* Check permissions on unix socket file */
+
+ /*
+ * This tells us that the pipe is owned by a privileged
+ * process, as we will be sending passwords to it.
+ */
+ if (!S_ISSOCK(st.st_mode) ||
+ !winbind_privileged_pipe_is_root(st.st_uid)) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ /* Connect to socket */
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+ return -1;
+ }
+
+ /* Set socket non-blocking and close on exec. */
+
+ if ((fd = make_safe_fd( fd)) == -1) {
+ return fd;
+ }
+
+ for (wait_time = 0; connect(fd, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) == -1;
+ wait_time += slept) {
+ struct pollfd pfd;
+ int connect_errno = 0;
+ socklen_t errnosize;
+
+ if (wait_time >= CONNECT_TIMEOUT)
+ goto error_out;
+
+ switch (errno) {
+ case EINPROGRESS:
+ pfd.fd = fd;
+ pfd.events = POLLOUT;
+
+ ret = poll(&pfd, 1, (CONNECT_TIMEOUT - wait_time) * 1000);
+
+ if (ret > 0) {
+ errnosize = sizeof(connect_errno);
+
+ ret = getsockopt(fd, SOL_SOCKET,
+ SO_ERROR, &connect_errno, &errnosize);
+
+ if (ret >= 0 && connect_errno == 0) {
+ /* Connect succeed */
+ goto out;
+ }
+ }
+
+ slept = CONNECT_TIMEOUT;
+ break;
+ case EAGAIN:
+ slept = rand() % 3 + 1;
+ sleep(slept);
+ break;
+ default:
+ goto error_out;
+ }
+
+ }
+
+ out:
+
+ return fd;
+
+ error_out:
+
+ close(fd);
+ return -1;
+}
+
+static const char *winbindd_socket_dir(void)
+{
+ if (nss_wrapper_enabled()) {
+ const char *env_dir;
+
+ env_dir = getenv("SELFTEST_WINBINDD_SOCKET_DIR");
+ if (env_dir != NULL) {
+ return env_dir;
+ }
+ }
+
+ return WINBINDD_SOCKET_DIR;
+}
+
+/* Connect to winbindd socket */
+
+static int winbind_open_pipe_sock(struct winbindd_context *ctx,
+ int recursing, int need_priv)
+{
+#ifdef HAVE_UNIXSOCKET
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (!ctx) {
+ return -1;
+ }
+
+ if (ctx->our_pid != getpid()) {
+ winbind_close_sock(ctx);
+ ctx->our_pid = getpid();
+ }
+
+ if ((need_priv != 0) && !ctx->is_privileged) {
+ winbind_close_sock(ctx);
+ }
+
+ if (ctx->winbindd_fd != -1) {
+ return ctx->winbindd_fd;
+ }
+
+ if (recursing) {
+ return -1;
+ }
+
+ ctx->winbindd_fd = winbind_named_pipe_sock(winbindd_socket_dir());
+
+ if (ctx->winbindd_fd == -1) {
+ return -1;
+ }
+
+ ctx->is_privileged = false;
+
+ /* version-check the socket */
+
+ request.wb_flags = WBFLAG_RECURSE;
+ if ((winbindd_request_response(ctx, WINBINDD_INTERFACE_VERSION, &request,
+ &response) != NSS_STATUS_SUCCESS) ||
+ (response.data.interface_version != WINBIND_INTERFACE_VERSION)) {
+ winbind_close_sock(ctx);
+ return -1;
+ }
+
+ if (need_priv == 0) {
+ return ctx->winbindd_fd;
+ }
+
+ /* try and get priv pipe */
+
+ request.wb_flags = WBFLAG_RECURSE;
+
+ /* Note that response needs to be initialized to avoid
+ * crashing on clean up after WINBINDD_PRIV_PIPE_DIR call failed
+ * as interface version (from the first request) returned as a fstring,
+ * thus response.extra_data.data will not be NULL even though
+ * winbindd response did not write over it due to a failure */
+ ZERO_STRUCT(response);
+ if (winbindd_request_response(ctx, WINBINDD_PRIV_PIPE_DIR, &request,
+ &response) == NSS_STATUS_SUCCESS) {
+ int fd;
+ fd = winbind_named_pipe_sock((char *)response.extra_data.data);
+ if (fd != -1) {
+ close(ctx->winbindd_fd);
+ ctx->winbindd_fd = fd;
+ ctx->is_privileged = true;
+ }
+
+ SAFE_FREE(response.extra_data.data);
+ }
+
+ if (!ctx->is_privileged) {
+ return -1;
+ }
+
+ return ctx->winbindd_fd;
+#else
+ return -1;
+#endif /* HAVE_UNIXSOCKET */
+}
+
+/* Write data to winbindd socket */
+
+static int winbind_write_sock(struct winbindd_context *ctx, void *buffer,
+ int count, int recursing, int need_priv)
+{
+ int fd, result, nwritten;
+
+ /* Open connection to winbind daemon */
+
+ restart:
+
+ fd = winbind_open_pipe_sock(ctx, recursing, need_priv);
+ if (fd == -1) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ /* Write data to socket */
+
+ nwritten = 0;
+
+ while(nwritten < count) {
+ struct pollfd pfd;
+ int ret;
+
+ /* Catch pipe close on other end by checking if a read()
+ call would not block by calling poll(). */
+
+ pfd.fd = fd;
+ pfd.events = POLLIN|POLLOUT|POLLHUP;
+
+ ret = poll(&pfd, 1, -1);
+ if (ret == -1) {
+ winbind_close_sock(ctx);
+ return -1; /* poll error */
+ }
+
+ /* Write should be OK if fd not available for reading */
+
+ if ((ret == 1) && (pfd.revents & (POLLIN|POLLHUP|POLLERR))) {
+
+ /* Pipe has closed on remote end */
+
+ winbind_close_sock(ctx);
+ goto restart;
+ }
+
+ /* Do the write */
+
+ result = write(fd, (char *)buffer + nwritten,
+ count - nwritten);
+
+ if ((result == -1) || (result == 0)) {
+
+ /* Write failed */
+
+ winbind_close_sock(ctx);
+ return -1;
+ }
+
+ nwritten += result;
+ }
+
+ return nwritten;
+}
+
+/* Read data from winbindd socket */
+
+static int winbind_read_sock(struct winbindd_context *ctx,
+ void *buffer, int count)
+{
+ int fd;
+ int nread = 0;
+ int total_time = 0;
+
+ fd = winbind_open_pipe_sock(ctx, false, false);
+ if (fd == -1) {
+ return -1;
+ }
+
+ /* Read data from socket */
+ while(nread < count) {
+ struct pollfd pfd;
+ int ret;
+
+ /* Catch pipe close on other end by checking if a read()
+ call would not block by calling poll(). */
+
+ pfd.fd = fd;
+ pfd.events = POLLIN|POLLHUP;
+
+ /* Wait for 5 seconds for a reply. May need to parameterise this... */
+
+ ret = poll(&pfd, 1, 5000);
+ if (ret == -1) {
+ winbind_close_sock(ctx);
+ return -1; /* poll error */
+ }
+
+ if (ret == 0) {
+ /* Not ready for read yet... */
+ if (total_time >= 300) {
+ /* Timeout */
+ winbind_close_sock(ctx);
+ return -1;
+ }
+ total_time += 5;
+ continue;
+ }
+
+ if ((ret == 1) && (pfd.revents & (POLLIN|POLLHUP|POLLERR))) {
+
+ /* Do the Read */
+
+ int result = read(fd, (char *)buffer + nread,
+ count - nread);
+
+ if ((result == -1) || (result == 0)) {
+
+ /* Read failed. I think the only useful thing we
+ can do here is just return -1 and fail since the
+ transaction has failed half way through. */
+
+ winbind_close_sock(ctx);
+ return -1;
+ }
+
+ nread += result;
+
+ }
+ }
+
+ return nread;
+}
+
+/* Read reply */
+
+static int winbindd_read_reply(struct winbindd_context *ctx,
+ struct winbindd_response *response)
+{
+ int result1, result2 = 0;
+
+ if (!response) {
+ return -1;
+ }
+
+ /* Read fixed length response */
+
+ result1 = winbind_read_sock(ctx, response,
+ sizeof(struct winbindd_response));
+
+ /* We actually send the pointer value of the extra_data field from
+ the server. This has no meaning in the client's address space
+ so we clear it out. */
+
+ response->extra_data.data = NULL;
+
+ if (result1 == -1) {
+ return -1;
+ }
+
+ if (response->length < sizeof(struct winbindd_response)) {
+ return -1;
+ }
+
+ /* Read variable length response */
+
+ if (response->length > sizeof(struct winbindd_response)) {
+ int extra_data_len = response->length -
+ sizeof(struct winbindd_response);
+
+ /* Mallocate memory for extra data */
+
+ if (!(response->extra_data.data = malloc(extra_data_len))) {
+ return -1;
+ }
+
+ result2 = winbind_read_sock(ctx, response->extra_data.data,
+ extra_data_len);
+ if (result2 == -1) {
+ winbindd_free_response(response);
+ return -1;
+ }
+ }
+
+ /* Return total amount of data read */
+
+ return result1 + result2;
+}
+
+/*
+ * send simple types of requests
+ */
+
+static NSS_STATUS winbindd_send_request(
+ struct winbindd_context *ctx,
+ int req_type,
+ int need_priv,
+ struct winbindd_request *request)
+{
+ struct winbindd_request lrequest;
+
+ /* Check for our tricky environment variable */
+
+ if (winbind_env_set()) {
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ if (!request) {
+ ZERO_STRUCT(lrequest);
+ request = &lrequest;
+ }
+
+ /* Fill in request and send down pipe */
+
+ winbindd_init_request(request, req_type);
+
+ if (winbind_write_sock(ctx, request, sizeof(*request),
+ request->wb_flags & WBFLAG_RECURSE,
+ need_priv) == -1)
+ {
+ /* Set ENOENT for consistency. Required by some apps */
+ errno = ENOENT;
+
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ if ((request->extra_len != 0) &&
+ (winbind_write_sock(ctx, request->extra_data.data,
+ request->extra_len,
+ request->wb_flags & WBFLAG_RECURSE,
+ need_priv) == -1))
+ {
+ /* Set ENOENT for consistency. Required by some apps */
+ errno = ENOENT;
+
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ return NSS_STATUS_SUCCESS;
+}
+
+/*
+ * Get results from winbindd request
+ */
+
+static NSS_STATUS winbindd_get_response(struct winbindd_context *ctx,
+ struct winbindd_response *response)
+{
+ struct winbindd_response lresponse;
+
+ if (!response) {
+ ZERO_STRUCT(lresponse);
+ response = &lresponse;
+ }
+
+ init_response(response);
+
+ /* Wait for reply */
+ if (winbindd_read_reply(ctx, response) == -1) {
+ /* Set ENOENT for consistency. Required by some apps */
+ errno = ENOENT;
+
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ /* Throw away extra data if client didn't request it */
+ if (response == &lresponse) {
+ winbindd_free_response(response);
+ }
+
+ /* Copy reply data from socket */
+ if (response->result != WINBINDD_OK) {
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ return NSS_STATUS_SUCCESS;
+}
+
+/* Handle simple types of requests */
+
+NSS_STATUS winbindd_request_response(struct winbindd_context *ctx,
+ int req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response)
+{
+ NSS_STATUS status = NSS_STATUS_UNAVAIL;
+ bool release_global_ctx = false;
+
+ if (ctx == NULL) {
+ ctx = get_wb_global_ctx();
+ release_global_ctx = true;
+ }
+
+ status = winbindd_send_request(ctx, req_type, 0, request);
+ if (status != NSS_STATUS_SUCCESS) {
+ goto out;
+ }
+ status = winbindd_get_response(ctx, response);
+
+out:
+ if (release_global_ctx) {
+ put_wb_global_ctx();
+ }
+ return status;
+}
+
+NSS_STATUS winbindd_priv_request_response(struct winbindd_context *ctx,
+ int req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response)
+{
+ NSS_STATUS status = NSS_STATUS_UNAVAIL;
+ bool release_global_ctx = false;
+
+ if (ctx == NULL) {
+ ctx = get_wb_global_ctx();
+ release_global_ctx = true;
+ }
+
+ status = winbindd_send_request(ctx, req_type, 1, request);
+ if (status != NSS_STATUS_SUCCESS) {
+ goto out;
+ }
+ status = winbindd_get_response(ctx, response);
+
+out:
+ if (release_global_ctx) {
+ put_wb_global_ctx();
+ }
+ return status;
+}
+
+/* Create and free winbindd context */
+
+struct winbindd_context *winbindd_ctx_create(void)
+{
+ struct winbindd_context *ctx;
+
+ ctx = calloc(1, sizeof(struct winbindd_context));
+
+ if (!ctx) {
+ return NULL;
+ }
+
+ ctx->winbindd_fd = -1;
+
+ return ctx;
+}
+
+void winbindd_ctx_free(struct winbindd_context *ctx)
+{
+ winbind_close_sock(ctx);
+ free(ctx);
+}
diff --git a/nsswitch/wb_reqtrans.c b/nsswitch/wb_reqtrans.c
new file mode 100644
index 0000000..779ef52
--- /dev/null
+++ b/nsswitch/wb_reqtrans.c
@@ -0,0 +1,446 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Async transfer of winbindd_request and _response structs
+
+ Copyright (C) Volker Lendecke 2008
+
+ ** NOTE! The following LGPL license applies to the wbclient
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "system/filesys.h"
+#include "system/network.h"
+#include <talloc.h>
+#include <tevent.h>
+#include "lib/async_req/async_sock.h"
+#include "lib/util/tevent_unix.h"
+#include "nsswitch/winbind_struct_protocol.h"
+#include "nsswitch/libwbclient/wbclient.h"
+#include "nsswitch/wb_reqtrans.h"
+
+/* can't use DEBUG here... */
+#define DEBUG(a,b)
+
+struct req_read_state {
+ struct winbindd_request *wb_req;
+ size_t max_extra_data;
+ ssize_t ret;
+};
+
+static ssize_t wb_req_more(uint8_t *buf, size_t buflen, void *private_data);
+static void wb_req_read_done(struct tevent_req *subreq);
+
+struct tevent_req *wb_req_read_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ int fd, size_t max_extra_data)
+{
+ struct tevent_req *req, *subreq;
+ struct req_read_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct req_read_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->max_extra_data = max_extra_data;
+
+ subreq = read_packet_send(state, ev, fd, 4, wb_req_more, state);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wb_req_read_done, req);
+ return req;
+}
+
+static ssize_t wb_req_more(uint8_t *buf, size_t buflen, void *private_data)
+{
+ struct req_read_state *state = talloc_get_type_abort(
+ private_data, struct req_read_state);
+ struct winbindd_request *req = (struct winbindd_request *)buf;
+
+ if (buflen == 4) {
+ if (req->length != sizeof(struct winbindd_request)) {
+ DEBUG(0, ("wb_req_read_len: Invalid request size "
+ "received: %d (expected %d)\n",
+ (int)req->length,
+ (int)sizeof(struct winbindd_request)));
+ return -1;
+ }
+ return sizeof(struct winbindd_request) - 4;
+ }
+
+ if (buflen > sizeof(struct winbindd_request)) {
+ /* We've been here, we're done */
+ return 0;
+ }
+
+ if ((state->max_extra_data != 0)
+ && (req->extra_len > state->max_extra_data)) {
+ DEBUG(3, ("Got request with %d bytes extra data on "
+ "unprivileged socket\n", (int)req->extra_len));
+ return -1;
+ }
+
+ return req->extra_len;
+}
+
+static void wb_req_read_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct req_read_state *state = tevent_req_data(
+ req, struct req_read_state);
+ int err;
+ uint8_t *buf;
+
+ state->ret = read_packet_recv(subreq, state, &buf, &err);
+ TALLOC_FREE(subreq);
+ if (state->ret == -1) {
+ tevent_req_error(req, err);
+ return;
+ }
+
+ state->wb_req = (struct winbindd_request *)buf;
+
+ if (state->wb_req->extra_len != 0) {
+ state->wb_req->extra_data.data =
+ (char *)buf + sizeof(struct winbindd_request);
+ } else {
+ state->wb_req->extra_data.data = NULL;
+ }
+ tevent_req_done(req);
+}
+
+ssize_t wb_req_read_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct winbindd_request **preq, int *err)
+{
+ struct req_read_state *state = tevent_req_data(
+ req, struct req_read_state);
+
+ if (tevent_req_is_unix_error(req, err)) {
+ return -1;
+ }
+ *preq = talloc_move(mem_ctx, &state->wb_req);
+ return state->ret;
+}
+
+struct req_write_state {
+ struct iovec iov[2];
+ ssize_t ret;
+};
+
+static void wb_req_write_done(struct tevent_req *subreq);
+
+struct tevent_req *wb_req_write_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tevent_queue *queue, int fd,
+ struct winbindd_request *wb_req)
+{
+ struct tevent_req *req, *subreq;
+ struct req_write_state *state;
+ int count = 1;
+
+ req = tevent_req_create(mem_ctx, &state, struct req_write_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->iov[0].iov_base = (void *)wb_req;
+ state->iov[0].iov_len = sizeof(struct winbindd_request);
+
+ if (wb_req->extra_len != 0) {
+ state->iov[1].iov_base = (void *)wb_req->extra_data.data;
+ state->iov[1].iov_len = wb_req->extra_len;
+ count = 2;
+ }
+
+ subreq = writev_send(state, ev, queue, fd, true, state->iov, count);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wb_req_write_done, req);
+ return req;
+}
+
+static void wb_req_write_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct req_write_state *state = tevent_req_data(
+ req, struct req_write_state);
+ int err;
+
+ state->ret = writev_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+ if (state->ret < 0) {
+ tevent_req_error(req, err);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+ssize_t wb_req_write_recv(struct tevent_req *req, int *err)
+{
+ struct req_write_state *state = tevent_req_data(
+ req, struct req_write_state);
+
+ if (tevent_req_is_unix_error(req, err)) {
+ return -1;
+ }
+ return state->ret;
+}
+
+struct resp_read_state {
+ struct winbindd_response *wb_resp;
+ ssize_t ret;
+};
+
+static ssize_t wb_resp_more(uint8_t *buf, size_t buflen, void *private_data);
+static void wb_resp_read_done(struct tevent_req *subreq);
+
+struct tevent_req *wb_resp_read_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev, int fd)
+{
+ struct tevent_req *req, *subreq;
+ struct resp_read_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct resp_read_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ subreq = read_packet_send(state, ev, fd, 4, wb_resp_more, state);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wb_resp_read_done, req);
+ return req;
+}
+
+static ssize_t wb_resp_more(uint8_t *buf, size_t buflen, void *private_data)
+{
+ struct winbindd_response *resp = (struct winbindd_response *)buf;
+
+ if (buflen == 4) {
+ if (resp->length < sizeof(struct winbindd_response)) {
+ DEBUG(0, ("wb_resp_read_len: Invalid response size "
+ "received: %d (expected at least%d)\n",
+ (int)resp->length,
+ (int)sizeof(struct winbindd_response)));
+ return -1;
+ }
+ }
+ return resp->length - buflen;
+}
+
+static void wb_resp_read_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct resp_read_state *state = tevent_req_data(
+ req, struct resp_read_state);
+ uint8_t *buf;
+ int err;
+
+ state->ret = read_packet_recv(subreq, state, &buf, &err);
+ TALLOC_FREE(subreq);
+ if (state->ret == -1) {
+ tevent_req_error(req, err);
+ return;
+ }
+
+ state->wb_resp = (struct winbindd_response *)buf;
+
+ if (state->wb_resp->length > sizeof(struct winbindd_response)) {
+ state->wb_resp->extra_data.data =
+ (char *)buf + sizeof(struct winbindd_response);
+ } else {
+ state->wb_resp->extra_data.data = NULL;
+ }
+ tevent_req_done(req);
+}
+
+ssize_t wb_resp_read_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct winbindd_response **presp, int *err)
+{
+ struct resp_read_state *state = tevent_req_data(
+ req, struct resp_read_state);
+
+ if (tevent_req_is_unix_error(req, err)) {
+ return -1;
+ }
+ *presp = talloc_move(mem_ctx, &state->wb_resp);
+ return state->ret;
+}
+
+struct resp_write_state {
+ struct iovec iov[2];
+ ssize_t ret;
+};
+
+static void wb_resp_write_done(struct tevent_req *subreq);
+
+struct tevent_req *wb_resp_write_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tevent_queue *queue, int fd,
+ struct winbindd_response *wb_resp)
+{
+ struct tevent_req *req, *subreq;
+ struct resp_write_state *state;
+ int count = 1;
+
+ req = tevent_req_create(mem_ctx, &state, struct resp_write_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->iov[0].iov_base = (void *)wb_resp;
+ state->iov[0].iov_len = sizeof(struct winbindd_response);
+
+ if (wb_resp->length > sizeof(struct winbindd_response)) {
+ state->iov[1].iov_base = (void *)wb_resp->extra_data.data;
+ state->iov[1].iov_len =
+ wb_resp->length - sizeof(struct winbindd_response);
+ count = 2;
+ }
+
+ subreq = writev_send(state, ev, queue, fd, true, state->iov, count);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wb_resp_write_done, req);
+ return req;
+}
+
+static void wb_resp_write_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct resp_write_state *state = tevent_req_data(
+ req, struct resp_write_state);
+ int err;
+
+ state->ret = writev_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+ if (state->ret < 0) {
+ tevent_req_error(req, err);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+ssize_t wb_resp_write_recv(struct tevent_req *req, int *err)
+{
+ struct resp_write_state *state = tevent_req_data(
+ req, struct resp_write_state);
+
+ if (tevent_req_is_unix_error(req, err)) {
+ return -1;
+ }
+ return state->ret;
+}
+
+struct wb_simple_trans_state {
+ struct tevent_context *ev;
+ int fd;
+ struct winbindd_response *wb_resp;
+};
+
+static void wb_simple_trans_write_done(struct tevent_req *subreq);
+static void wb_simple_trans_read_done(struct tevent_req *subreq);
+
+struct tevent_req *wb_simple_trans_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tevent_queue *queue, int fd,
+ struct winbindd_request *wb_req)
+{
+ struct tevent_req *req, *subreq;
+ struct wb_simple_trans_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct wb_simple_trans_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ wb_req->length = sizeof(struct winbindd_request);
+
+ state->ev = ev;
+ state->fd = fd;
+
+ subreq = wb_req_write_send(state, ev, queue, fd, wb_req);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wb_simple_trans_write_done, req);
+
+ return req;
+}
+
+static void wb_simple_trans_write_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_simple_trans_state *state = tevent_req_data(
+ req, struct wb_simple_trans_state);
+ ssize_t ret;
+ int err;
+
+ ret = wb_req_write_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ tevent_req_error(req, err);
+ return;
+ }
+ subreq = wb_resp_read_send(state, state->ev, state->fd);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_simple_trans_read_done, req);
+}
+
+static void wb_simple_trans_read_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_simple_trans_state *state = tevent_req_data(
+ req, struct wb_simple_trans_state);
+ ssize_t ret;
+ int err;
+
+ ret = wb_resp_read_recv(subreq, state, &state->wb_resp, &err);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ tevent_req_error(req, err);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+int wb_simple_trans_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct winbindd_response **presponse, int *err)
+{
+ struct wb_simple_trans_state *state = tevent_req_data(
+ req, struct wb_simple_trans_state);
+
+ if (tevent_req_is_unix_error(req, err)) {
+ return -1;
+ }
+ *presponse = talloc_move(mem_ctx, &state->wb_resp);
+ return 0;
+}
diff --git a/nsswitch/wb_reqtrans.h b/nsswitch/wb_reqtrans.h
new file mode 100644
index 0000000..941edf6
--- /dev/null
+++ b/nsswitch/wb_reqtrans.h
@@ -0,0 +1,61 @@
+/*
+ Unix SMB/CIFS implementation.
+ Headers for the async winbind client library
+ Copyright (C) Volker Lendecke 2008
+
+ ** NOTE! The following LGPL license applies to the wbclient
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _WB_REQTRANS_H_
+#define _WB_REQTRANS_H_
+
+#include <talloc.h>
+#include <tevent.h>
+#include "nsswitch/winbind_struct_protocol.h"
+
+struct tevent_req *wb_req_read_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ int fd, size_t max_extra_data);
+ssize_t wb_req_read_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct winbindd_request **preq, int *err);
+
+struct tevent_req *wb_req_write_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tevent_queue *queue, int fd,
+ struct winbindd_request *wb_req);
+ssize_t wb_req_write_recv(struct tevent_req *req, int *err);
+
+struct tevent_req *wb_resp_read_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev, int fd);
+ssize_t wb_resp_read_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct winbindd_response **presp, int *err);
+
+struct tevent_req *wb_resp_write_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tevent_queue *queue, int fd,
+ struct winbindd_response *wb_resp);
+ssize_t wb_resp_write_recv(struct tevent_req *req, int *err);
+
+struct tevent_req *wb_simple_trans_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tevent_queue *queue, int fd,
+ struct winbindd_request *wb_req);
+int wb_simple_trans_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct winbindd_response **presponse, int *err);
+
+#endif /*_WB_REQTRANS_H_*/
diff --git a/nsswitch/wbinfo.c b/nsswitch/wbinfo.c
new file mode 100644
index 0000000..55b9e26
--- /dev/null
+++ b/nsswitch/wbinfo.c
@@ -0,0 +1,3292 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind status program.
+
+ Copyright (C) Tim Potter 2000-2003
+ Copyright (C) Andrew Bartlett 2002-2007
+ Copyright (C) Volker Lendecke 2009
+
+ 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 "includes.h"
+#include "libwbclient/wbclient.h"
+#include "winbind_struct_protocol.h"
+#include "libwbclient/wbclient_internal.h"
+#include "../libcli/auth/libcli_auth.h"
+#include "lib/cmdline/cmdline.h"
+#include "lib/afs/afs_settoken.h"
+#include "lib/util/smb_strtox.h"
+#include "lib/util/string_wrappers.h"
+
+#ifdef DBGC_CLASS
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+#endif
+
+static struct wbcInterfaceDetails *init_interface_details(void)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ static struct wbcInterfaceDetails *details;
+
+ if (details) {
+ return details;
+ }
+
+ wbc_status = wbcInterfaceDetails(&details);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "could not obtain winbind interface "
+ "details: %s\n", wbcErrorString(wbc_status));
+ }
+
+ return details;
+}
+
+static char winbind_separator(void)
+{
+ struct wbcInterfaceDetails *details;
+ static bool got_sep;
+ static char sep;
+
+ if (got_sep)
+ return sep;
+
+ details = init_interface_details();
+
+ if (!details) {
+ d_fprintf(stderr, "could not obtain winbind separator!\n");
+ return 0;
+ }
+
+ sep = details->winbind_separator;
+ got_sep = true;
+
+ if (!sep) {
+ d_fprintf(stderr, "winbind separator was NULL!\n");
+ return 0;
+ }
+
+ return sep;
+}
+
+static const char *get_winbind_domain(void)
+{
+ static struct wbcInterfaceDetails *details;
+
+ details = init_interface_details();
+
+ if (!details) {
+ d_fprintf(stderr, "could not obtain winbind domain name!\n");
+ return 0;
+ }
+
+ return details->netbios_domain;
+}
+
+static const char *get_winbind_netbios_name(void)
+{
+ static struct wbcInterfaceDetails *details;
+
+ details = init_interface_details();
+
+ if (!details) {
+ d_fprintf(stderr, "could not obtain winbind netbios name!\n");
+ return 0;
+ }
+
+ return details->netbios_name;
+}
+
+/* Copy of parse_domain_user from winbindd_util.c. Parse a string of the
+ form DOMAIN/user into a domain and a user */
+
+static bool parse_wbinfo_domain_user(const char *domuser, fstring domain,
+ fstring user)
+{
+
+ char *p = strchr(domuser,winbind_separator());
+
+ if (!p) {
+ /* Maybe it was a UPN? */
+ p = strchr(domuser, '@');
+ if (p != NULL) {
+ fstrcpy(domain, "");
+ fstrcpy(user, domuser);
+ return true;
+ }
+
+ fstrcpy(user, domuser);
+ fstrcpy(domain, get_winbind_domain());
+ return true;
+ }
+
+ fstrcpy(user, p+1);
+ fstrcpy(domain, domuser);
+ domain[PTR_DIFF(p, domuser)] = 0;
+
+ return true;
+}
+
+/* Parse string of "uid,sid" or "gid,sid" into separate int and string values.
+ * Return true if input was valid, false otherwise. */
+static bool parse_mapping_arg(char *arg, int *id, char **sid)
+{
+ char *tmp;
+ int error = 0;
+
+ if (!arg || !*arg)
+ return false;
+
+ tmp = strtok(arg, ",");
+ *sid = strtok(NULL, ",");
+
+ if (!tmp || !*tmp || !*sid || !**sid)
+ return false;
+
+ /* Because atoi() can return 0 on invalid input, which would be a valid
+ * UID/GID we must use strtoul() and do error checking */
+ *id = smb_strtoul(tmp, NULL, 10, &error, SMB_STR_FULL_STR_CONV);
+ if (error != 0)
+ return false;
+
+ return true;
+}
+
+/* pull pwent info for a given user */
+
+static bool wbinfo_get_userinfo(char *user)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct passwd *pwd = NULL;
+
+ wbc_status = wbcGetpwnam(user, &pwd);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcGetpwnam: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ d_printf("%s:%s:%u:%u:%s:%s:%s\n",
+ pwd->pw_name,
+ pwd->pw_passwd,
+ (unsigned int)pwd->pw_uid,
+ (unsigned int)pwd->pw_gid,
+ pwd->pw_gecos,
+ pwd->pw_dir,
+ pwd->pw_shell);
+
+ wbcFreeMemory(pwd);
+
+ return true;
+}
+
+/* pull pwent info for a given uid */
+static bool wbinfo_get_uidinfo(int uid)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct passwd *pwd = NULL;
+
+ wbc_status = wbcGetpwuid(uid, &pwd);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcGetpwuid: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ d_printf("%s:%s:%u:%u:%s:%s:%s\n",
+ pwd->pw_name,
+ pwd->pw_passwd,
+ (unsigned int)pwd->pw_uid,
+ (unsigned int)pwd->pw_gid,
+ pwd->pw_gecos,
+ pwd->pw_dir,
+ pwd->pw_shell);
+
+ wbcFreeMemory(pwd);
+
+ return true;
+}
+
+static bool wbinfo_get_user_sidinfo(const char *sid_str)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct passwd *pwd = NULL;
+ struct wbcDomainSid sid;
+
+ wbc_status = wbcStringToSid(sid_str, &sid);
+ wbc_status = wbcGetpwsid(&sid, &pwd);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcGetpwsid: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ d_printf("%s:%s:%u:%u:%s:%s:%s\n",
+ pwd->pw_name,
+ pwd->pw_passwd,
+ (unsigned int)pwd->pw_uid,
+ (unsigned int)pwd->pw_gid,
+ pwd->pw_gecos,
+ pwd->pw_dir,
+ pwd->pw_shell);
+
+ wbcFreeMemory(pwd);
+
+ return true;
+}
+
+
+/* pull grent for a given group */
+static bool wbinfo_get_groupinfo(const char *group)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct group *grp;
+ char **mem;
+
+ wbc_status = wbcGetgrnam(group, &grp);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcGetgrnam: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ d_printf("%s:%s:%u:",
+ grp->gr_name,
+ grp->gr_passwd,
+ (unsigned int)grp->gr_gid);
+
+ mem = grp->gr_mem;
+ while (*mem != NULL) {
+ d_printf("%s%s", *mem, *(mem+1) != NULL ? "," : "");
+ mem += 1;
+ }
+ d_printf("\n");
+
+ wbcFreeMemory(grp);
+
+ return true;
+}
+
+/* pull grent for a given gid */
+static bool wbinfo_get_gidinfo(int gid)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct group *grp;
+ char **mem;
+
+ wbc_status = wbcGetgrgid(gid, &grp);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcGetgrgid: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ d_printf("%s:%s:%u:",
+ grp->gr_name,
+ grp->gr_passwd,
+ (unsigned int)grp->gr_gid);
+
+ mem = grp->gr_mem;
+ while (*mem != NULL) {
+ d_printf("%s%s", *mem, *(mem+1) != NULL ? "," : "");
+ mem += 1;
+ }
+ d_printf("\n");
+
+ wbcFreeMemory(grp);
+
+ return true;
+}
+
+/* List groups a user is a member of */
+
+static bool wbinfo_get_usergroups(const char *user)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ uint32_t num_groups;
+ uint32_t i;
+ gid_t *groups = NULL;
+
+ /* Send request */
+
+ wbc_status = wbcGetGroups(user, &num_groups, &groups);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcGetGroups: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ for (i = 0; i < num_groups; i++) {
+ d_printf("%d\n", (int)groups[i]);
+ }
+
+ wbcFreeMemory(groups);
+
+ return true;
+}
+
+
+/* List group SIDs a user SID is a member of */
+static bool wbinfo_get_usersids(const char *user_sid_str)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ uint32_t num_sids;
+ uint32_t i;
+ struct wbcDomainSid user_sid, *sids = NULL;
+
+ /* Send request */
+
+ wbc_status = wbcStringToSid(user_sid_str, &user_sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcStringToSid: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ wbc_status = wbcLookupUserSids(&user_sid, false, &num_sids, &sids);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcLookupUserSids: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ for (i = 0; i < num_sids; i++) {
+ char str[WBC_SID_STRING_BUFLEN];
+ wbcSidToStringBuf(&sids[i], str, sizeof(str));
+ d_printf("%s\n", str);
+ }
+
+ wbcFreeMemory(sids);
+
+ return true;
+}
+
+static bool wbinfo_get_userdomgroups(const char *user_sid_str)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ uint32_t num_sids;
+ uint32_t i;
+ struct wbcDomainSid user_sid, *sids = NULL;
+
+ /* Send request */
+
+ wbc_status = wbcStringToSid(user_sid_str, &user_sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcStringToSid: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ wbc_status = wbcLookupUserSids(&user_sid, true, &num_sids, &sids);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcLookupUserSids: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ for (i = 0; i < num_sids; i++) {
+ char str[WBC_SID_STRING_BUFLEN];
+ wbcSidToStringBuf(&sids[i], str, sizeof(str));
+ d_printf("%s\n", str);
+ }
+
+ wbcFreeMemory(sids);
+
+ return true;
+}
+
+static bool wbinfo_get_sidaliases(const char *domain,
+ const char *user_sid_str)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainInfo *dinfo = NULL;
+ uint32_t i;
+ struct wbcDomainSid user_sid;
+ uint32_t *alias_rids = NULL;
+ uint32_t num_alias_rids;
+ char domain_sid_str[WBC_SID_STRING_BUFLEN];
+
+ /* Send request */
+ if ((domain == NULL) || (strequal(domain, ".")) ||
+ (domain[0] == '\0')) {
+ domain = get_winbind_domain();
+ }
+
+ /* Send request */
+
+ wbc_status = wbcDomainInfo(domain, &dinfo);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "wbcDomainInfo(%s) failed: %s\n", domain,
+ wbcErrorString(wbc_status));
+ goto done;
+ }
+ wbc_status = wbcStringToSid(user_sid_str, &user_sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ goto done;
+ }
+
+ wbc_status = wbcGetSidAliases(&dinfo->sid, &user_sid, 1,
+ &alias_rids, &num_alias_rids);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ goto done;
+ }
+
+ wbcSidToStringBuf(&dinfo->sid, domain_sid_str, sizeof(domain_sid_str));
+
+ for (i = 0; i < num_alias_rids; i++) {
+ d_printf("%s-%d\n", domain_sid_str, alias_rids[i]);
+ }
+
+ wbcFreeMemory(alias_rids);
+
+done:
+ wbcFreeMemory(dinfo);
+ return (WBC_ERR_SUCCESS == wbc_status);
+}
+
+
+/* Convert NetBIOS name to IP */
+
+static bool wbinfo_wins_byname(const char *name)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ char *ip = NULL;
+
+ wbc_status = wbcResolveWinsByName(name, &ip);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcResolveWinsByName: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("%s\n", ip);
+
+ wbcFreeMemory(ip);
+
+ return true;
+}
+
+/* Convert IP to NetBIOS name */
+
+static bool wbinfo_wins_byip(const char *ip)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ char *name = NULL;
+
+ wbc_status = wbcResolveWinsByIP(ip, &name);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcResolveWinsByIP: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("%s\n", name);
+
+ wbcFreeMemory(name);
+
+ return true;
+}
+
+/* List all/trusted domains */
+
+static bool wbinfo_list_domains(bool list_all_domains, bool verbose)
+{
+ struct wbcDomainInfo *domain_list = NULL;
+ size_t i, num_domains;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ bool print_all = !list_all_domains && verbose;
+
+ wbc_status = wbcListTrusts(&domain_list, &num_domains);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcListTrusts: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ if (print_all) {
+ d_printf("%-16s%-65s%-12s%-12s%-5s%-5s\n",
+ "Domain Name", "DNS Domain", "Trust Type",
+ "Transitive", "In", "Out");
+ }
+
+ for (i=0; i<num_domains; i++) {
+ if (print_all) {
+ d_printf("%-16s", domain_list[i].short_name);
+ } else {
+ d_printf("%s", domain_list[i].short_name);
+ d_printf("\n");
+ continue;
+ }
+
+ d_printf("%-65s", domain_list[i].dns_name);
+
+ switch(domain_list[i].trust_type) {
+ case WBC_DOMINFO_TRUSTTYPE_NONE:
+ if (domain_list[i].trust_routing != NULL) {
+ d_printf("%s\n", domain_list[i].trust_routing);
+ } else {
+ d_printf("None\n");
+ }
+ continue;
+ case WBC_DOMINFO_TRUSTTYPE_LOCAL:
+ d_printf("Local\n");
+ continue;
+ case WBC_DOMINFO_TRUSTTYPE_RWDC:
+ d_printf("RWDC\n");
+ continue;
+ case WBC_DOMINFO_TRUSTTYPE_RODC:
+ d_printf("RODC\n");
+ continue;
+ case WBC_DOMINFO_TRUSTTYPE_PDC:
+ d_printf("PDC\n");
+ continue;
+ case WBC_DOMINFO_TRUSTTYPE_WKSTA:
+ d_printf("Workstation ");
+ break;
+ case WBC_DOMINFO_TRUSTTYPE_FOREST:
+ d_printf("Forest ");
+ break;
+ case WBC_DOMINFO_TRUSTTYPE_EXTERNAL:
+ d_printf("External ");
+ break;
+ case WBC_DOMINFO_TRUSTTYPE_IN_FOREST:
+ d_printf("In-Forest ");
+ break;
+ }
+
+ if (domain_list[i].trust_flags & WBC_DOMINFO_TRUST_TRANSITIVE) {
+ d_printf("Yes ");
+ } else {
+ d_printf("No ");
+ }
+
+ if (domain_list[i].trust_flags & WBC_DOMINFO_TRUST_INCOMING) {
+ d_printf("Yes ");
+ } else {
+ d_printf("No ");
+ }
+
+ if (domain_list[i].trust_flags & WBC_DOMINFO_TRUST_OUTGOING) {
+ d_printf("Yes ");
+ } else {
+ d_printf("No ");
+ }
+
+ d_printf("\n");
+ }
+
+ wbcFreeMemory(domain_list);
+
+ return true;
+}
+
+/* List own domain */
+
+static bool wbinfo_list_own_domain(void)
+{
+ d_printf("%s\n", get_winbind_domain());
+
+ return true;
+}
+
+/* show sequence numbers */
+static bool wbinfo_show_sequence(const char *domain)
+{
+ d_printf("This command has been deprecated. Please use the "
+ "--online-status option instead.\n");
+ return false;
+}
+
+/* show sequence numbers */
+static bool wbinfo_show_onlinestatus(const char *domain)
+{
+ struct wbcDomainInfo *domain_list = NULL;
+ size_t i, num_domains;
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+
+ wbc_status = wbcListTrusts(&domain_list, &num_domains);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcListTrusts: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ for (i=0; i<num_domains; i++) {
+ bool is_offline;
+
+ if (domain) {
+ if (!strequal(domain_list[i].short_name, domain)) {
+ continue;
+ }
+ }
+
+ is_offline = (domain_list[i].domain_flags &
+ WBC_DOMINFO_DOMAIN_OFFLINE);
+
+ d_printf("%s : %s\n",
+ domain_list[i].short_name,
+ is_offline ? "no active connection" : "active connection" );
+ }
+
+ wbcFreeMemory(domain_list);
+
+ return true;
+}
+
+
+/* Show domain info */
+
+static bool wbinfo_domain_info(const char *domain)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainInfo *dinfo = NULL;
+ char sid_str[WBC_SID_STRING_BUFLEN];
+
+ if ((domain == NULL) || (strequal(domain, ".")) || (domain[0] == '\0')){
+ domain = get_winbind_domain();
+ }
+
+ /* Send request */
+
+ wbc_status = wbcDomainInfo(domain, &dinfo);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcDomainInfo: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ wbcSidToStringBuf(&dinfo->sid, sid_str, sizeof(sid_str));
+
+ /* Display response */
+
+ d_printf("Name : %s\n", dinfo->short_name);
+ d_printf("Alt_Name : %s\n", dinfo->dns_name);
+
+ d_printf("SID : %s\n", sid_str);
+
+ d_printf("Active Directory : %s\n",
+ (dinfo->domain_flags & WBC_DOMINFO_DOMAIN_AD) ? "Yes" : "No");
+ d_printf("Native : %s\n",
+ (dinfo->domain_flags & WBC_DOMINFO_DOMAIN_NATIVE) ?
+ "Yes" : "No");
+
+ d_printf("Primary : %s\n",
+ (dinfo->domain_flags & WBC_DOMINFO_DOMAIN_PRIMARY) ?
+ "Yes" : "No");
+
+ wbcFreeMemory(dinfo);
+
+ return true;
+}
+
+/* Get a foreign DC's name */
+static bool wbinfo_getdcname(const char *domain_name)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ fstrcpy(request.domain_name, domain_name);
+
+ /* Send request */
+
+ wbc_status = wbcRequestResponse(NULL, WINBINDD_GETDCNAME,
+ &request, &response);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "Could not get dc name for %s\n",domain_name);
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("%s\n", response.data.dc_name);
+
+ return true;
+}
+
+/* Find a DC */
+static bool wbinfo_dsgetdcname(const char *domain_name, uint32_t flags)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainControllerInfoEx *dc_info;
+ char *str = NULL;
+
+ wbc_status = wbcLookupDomainControllerEx(domain_name, NULL, NULL,
+ flags | DS_DIRECTORY_SERVICE_REQUIRED,
+ &dc_info);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ printf("Could not find dc for %s\n", domain_name);
+ return false;
+ }
+
+ wbcGuidToString(dc_info->domain_guid, &str);
+
+ d_printf("%s\n", dc_info->dc_unc);
+ d_printf("%s\n", dc_info->dc_address);
+ d_printf("%d\n", dc_info->dc_address_type);
+ d_printf("%s\n", str);
+ d_printf("%s\n", dc_info->domain_name);
+ d_printf("%s\n", dc_info->forest_name);
+ d_printf("0x%08x\n", dc_info->dc_flags);
+ d_printf("%s\n", dc_info->dc_site_name);
+ d_printf("%s\n", dc_info->client_site_name);
+
+ wbcFreeMemory(str);
+ wbcFreeMemory(dc_info);
+
+ return true;
+}
+
+/* Check trust account password */
+
+static bool wbinfo_check_secret(const char *domain)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcAuthErrorInfo *error = NULL;
+ const char *domain_name;
+
+ if (domain) {
+ domain_name = domain;
+ } else {
+ domain_name = get_winbind_domain();
+ }
+
+ wbc_status = wbcCheckTrustCredentials(domain_name, &error);
+
+ d_printf("checking the trust secret for domain %s via RPC calls %s\n",
+ domain_name,
+ WBC_ERROR_IS_OK(wbc_status) ? "succeeded" : "failed");
+
+ if (wbc_status == WBC_ERR_AUTH_ERROR) {
+ d_fprintf(stderr, "wbcCheckTrustCredentials(%s): error code was %s (0x%x)\n",
+ domain_name, error->nt_string, error->nt_status);
+ wbcFreeMemory(error);
+ }
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcCheckTrustCredentials: "
+ "%s\n", wbcErrorString(wbc_status));
+ return false;
+ }
+
+ return true;
+}
+
+/* Find the currently connected DCs */
+
+static bool wbinfo_dc_info(const char *domain_name)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ size_t i, num_dcs;
+ const char **dc_names, **dc_ips;
+
+ wbc_status = wbcDcInfo(domain_name, &num_dcs,
+ &dc_names, &dc_ips);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ printf("Could not find dc info %s\n",
+ domain_name ? domain_name : "our domain");
+ return false;
+ }
+
+ for (i=0; i<num_dcs; i++) {
+ printf("%s (%s)\n", dc_names[i], dc_ips[i]);
+ }
+ wbcFreeMemory(dc_names);
+ wbcFreeMemory(dc_ips);
+
+ return true;
+}
+
+/* Change trust account password */
+
+static bool wbinfo_change_secret(const char *domain)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcAuthErrorInfo *error = NULL;
+ const char *domain_name;
+
+ if (domain) {
+ domain_name = domain;
+ } else {
+ domain_name = get_winbind_domain();
+ }
+
+ wbc_status = wbcChangeTrustCredentials(domain_name, &error);
+
+ d_printf("changing the trust secret for domain %s via RPC calls %s\n",
+ domain_name,
+ WBC_ERROR_IS_OK(wbc_status) ? "succeeded" : "failed");
+
+ if (wbc_status == WBC_ERR_AUTH_ERROR) {
+ d_fprintf(stderr, "wbcChangeTrustCredentials(%s): error code was %s (0x%x)\n",
+ domain_name, error->nt_string, error->nt_status);
+ wbcFreeMemory(error);
+ }
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcChangeTrustCredentials: "
+ "%s\n", wbcErrorString(wbc_status));
+ return false;
+ }
+
+ return true;
+}
+
+/* Check DC connection */
+
+static bool wbinfo_ping_dc(const char *domain)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcAuthErrorInfo *error = NULL;
+ char *dcname = NULL;
+
+ const char *domain_name;
+
+ if (domain) {
+ domain_name = domain;
+ } else {
+ domain_name = get_winbind_domain();
+ }
+
+ wbc_status = wbcPingDc2(domain_name, &error, &dcname);
+
+ d_printf("checking the NETLOGON for domain[%s] dc connection to \"%s\" %s\n",
+ domain_name ? domain_name : "",
+ dcname ? dcname : "",
+ WBC_ERROR_IS_OK(wbc_status) ? "succeeded" : "failed");
+
+ wbcFreeMemory(dcname);
+ if (wbc_status == WBC_ERR_AUTH_ERROR) {
+ d_fprintf(stderr, "wbcPingDc2(%s): error code was %s (0x%x)\n",
+ domain_name, error->nt_string, error->nt_status);
+ wbcFreeMemory(error);
+ return false;
+ }
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcPingDc: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ return true;
+}
+
+/* Convert uid to sid */
+
+static bool wbinfo_uid_to_sid(uid_t uid)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainSid sid;
+ char sid_str[WBC_SID_STRING_BUFLEN];
+
+ /* Send request */
+
+ wbc_status = wbcUidToSid(uid, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcUidToSid: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ wbcSidToStringBuf(&sid, sid_str, sizeof(sid_str));
+
+ /* Display response */
+
+ d_printf("%s\n", sid_str);
+
+ return true;
+}
+
+/* Convert gid to sid */
+
+static bool wbinfo_gid_to_sid(gid_t gid)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainSid sid;
+ char sid_str[WBC_SID_STRING_BUFLEN];
+
+ /* Send request */
+
+ wbc_status = wbcGidToSid(gid, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcGidToSid: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ wbcSidToStringBuf(&sid, sid_str, sizeof(sid_str));
+
+ /* Display response */
+
+ d_printf("%s\n", sid_str);
+
+ return true;
+}
+
+/* Convert sid to uid */
+
+static bool wbinfo_sid_to_uid(const char *sid_str)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainSid sid;
+ uid_t uid;
+
+ /* Send request */
+
+ wbc_status = wbcStringToSid(sid_str, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcStringToSid: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ wbc_status = wbcSidToUid(&sid, &uid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcSidToUid: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("%d\n", (int)uid);
+
+ return true;
+}
+
+static bool wbinfo_sid_to_gid(const char *sid_str)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainSid sid;
+ gid_t gid;
+
+ /* Send request */
+
+ wbc_status = wbcStringToSid(sid_str, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcStringToSid: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ wbc_status = wbcSidToGid(&sid, &gid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcSidToGid: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("%d\n", (int)gid);
+
+ return true;
+}
+
+static bool wbinfo_sids_to_unix_ids(const char *arg)
+{
+ char sidstr[WBC_SID_STRING_BUFLEN];
+ struct wbcDomainSid *sids;
+ struct wbcUnixId *unix_ids;
+ int i, num_sids;
+ const char *p;
+ wbcErr wbc_status;
+
+
+ num_sids = 0;
+ sids = NULL;
+ p = arg;
+
+ while (next_token(&p, sidstr, LIST_SEP, sizeof(sidstr))) {
+ sids = talloc_realloc(talloc_tos(), sids, struct wbcDomainSid,
+ num_sids+1);
+ if (sids == NULL) {
+ d_fprintf(stderr, "talloc failed\n");
+ return false;
+ }
+ wbc_status = wbcStringToSid(sidstr, &sids[num_sids]);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "wbcSidToString(%s) failed: %s\n",
+ sidstr, wbcErrorString(wbc_status));
+ TALLOC_FREE(sids);
+ return false;
+ }
+ num_sids += 1;
+ }
+
+ unix_ids = talloc_array(talloc_tos(), struct wbcUnixId, num_sids);
+ if (unix_ids == NULL) {
+ TALLOC_FREE(sids);
+ return false;
+ }
+
+ wbc_status = wbcSidsToUnixIds(sids, num_sids, unix_ids);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "wbcSidsToUnixIds failed: %s\n",
+ wbcErrorString(wbc_status));
+ TALLOC_FREE(sids);
+ return false;
+ }
+
+ for (i=0; i<num_sids; i++) {
+
+ wbcSidToStringBuf(&sids[i], sidstr, sizeof(sidstr));
+
+ switch(unix_ids[i].type) {
+ case WBC_ID_TYPE_UID:
+ d_printf("%s -> uid %d\n", sidstr, unix_ids[i].id.uid);
+ break;
+ case WBC_ID_TYPE_GID:
+ d_printf("%s -> gid %d\n", sidstr, unix_ids[i].id.gid);
+ break;
+ case WBC_ID_TYPE_BOTH:
+ d_printf("%s -> uid/gid %d\n", sidstr, unix_ids[i].id.uid);
+ break;
+ default:
+ d_printf("%s -> unmapped\n", sidstr);
+ break;
+ }
+ }
+
+ TALLOC_FREE(sids);
+ TALLOC_FREE(unix_ids);
+
+ return true;
+}
+
+static bool wbinfo_xids_to_sids(const char *arg)
+{
+ fstring idstr;
+ struct wbcUnixId *xids = NULL;
+ struct wbcDomainSid *sids;
+ wbcErr wbc_status;
+ int num_xids = 0;
+ const char *p;
+ int i;
+
+ p = arg;
+
+ while (next_token(&p, idstr, LIST_SEP, sizeof(idstr))) {
+ xids = talloc_realloc(talloc_tos(), xids, struct wbcUnixId,
+ num_xids+1);
+ if (xids == NULL) {
+ d_fprintf(stderr, "talloc failed\n");
+ return false;
+ }
+
+ switch (idstr[0]) {
+ case 'u':
+ xids[num_xids] = (struct wbcUnixId) {
+ .type = WBC_ID_TYPE_UID,
+ .id.uid = atoi(&idstr[1])
+ };
+ break;
+ case 'g':
+ xids[num_xids] = (struct wbcUnixId) {
+ .type = WBC_ID_TYPE_GID,
+ .id.gid = atoi(&idstr[1])
+ };
+ break;
+ default:
+ d_fprintf(stderr, "%s is an invalid id\n", idstr);
+ TALLOC_FREE(xids);
+ return false;
+ }
+ num_xids += 1;
+ }
+
+ sids = talloc_array(talloc_tos(), struct wbcDomainSid, num_xids);
+ if (sids == NULL) {
+ d_fprintf(stderr, "talloc failed\n");
+ TALLOC_FREE(xids);
+ return false;
+ }
+
+ wbc_status = wbcUnixIdsToSids(xids, num_xids, sids);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "wbcUnixIdsToSids failed: %s\n",
+ wbcErrorString(wbc_status));
+ TALLOC_FREE(sids);
+ TALLOC_FREE(xids);
+ return false;
+ }
+
+ for (i=0; i<num_xids; i++) {
+ char str[WBC_SID_STRING_BUFLEN];
+ struct wbcDomainSid null_sid = { 0 };
+
+ if (memcmp(&null_sid, &sids[i], sizeof(struct wbcDomainSid)) == 0) {
+ d_printf("NOT MAPPED\n");
+ continue;
+ }
+ wbcSidToStringBuf(&sids[i], str, sizeof(str));
+ d_printf("%s\n", str);
+ }
+
+ return true;
+}
+
+static bool wbinfo_allocate_uid(void)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ uid_t uid;
+
+ /* Send request */
+
+ wbc_status = wbcAllocateUid(&uid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcAllocateUid: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("New uid: %u\n", (unsigned int)uid);
+
+ return true;
+}
+
+static bool wbinfo_allocate_gid(void)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ gid_t gid;
+
+ /* Send request */
+
+ wbc_status = wbcAllocateGid(&gid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcAllocateGid: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("New gid: %u\n", (unsigned int)gid);
+
+ return true;
+}
+
+static bool wbinfo_set_uid_mapping(uid_t uid, const char *sid_str)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainSid sid;
+
+ /* Send request */
+
+ wbc_status = wbcStringToSid(sid_str, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcStringToSid: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ wbc_status = wbcSetUidMapping(uid, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcSetUidMapping: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("uid %u now mapped to sid %s\n",
+ (unsigned int)uid, sid_str);
+
+ return true;
+}
+
+static bool wbinfo_set_gid_mapping(gid_t gid, const char *sid_str)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainSid sid;
+
+ /* Send request */
+
+ wbc_status = wbcStringToSid(sid_str, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcStringToSid: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ wbc_status = wbcSetGidMapping(gid, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcSetGidMapping: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("gid %u now mapped to sid %s\n",
+ (unsigned int)gid, sid_str);
+
+ return true;
+}
+
+static bool wbinfo_remove_uid_mapping(uid_t uid, const char *sid_str)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainSid sid;
+
+ /* Send request */
+
+ wbc_status = wbcStringToSid(sid_str, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcStringToSid: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ wbc_status = wbcRemoveUidMapping(uid, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcRemoveUidMapping: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("Removed uid %u to sid %s mapping\n",
+ (unsigned int)uid, sid_str);
+
+ return true;
+}
+
+static bool wbinfo_remove_gid_mapping(gid_t gid, const char *sid_str)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainSid sid;
+
+ /* Send request */
+
+ wbc_status = wbcStringToSid(sid_str, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcStringToSid: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ wbc_status = wbcRemoveGidMapping(gid, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcRemoveGidMapping: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("Removed gid %u to sid %s mapping\n",
+ (unsigned int)gid, sid_str);
+
+ return true;
+}
+
+/* Convert sid to string */
+
+static bool wbinfo_lookupsid(const char *sid_str)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainSid sid;
+ char *domain;
+ char *name;
+ enum wbcSidType type;
+
+ /* Send off request */
+
+ wbc_status = wbcStringToSid(sid_str, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcStringToSid: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ wbc_status = wbcLookupSid(&sid, &domain, &name, &type);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcLookupSid: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ /* Display response */
+
+ if (type == WBC_SID_NAME_DOMAIN) {
+ d_printf("%s %d\n", domain, type);
+ } else {
+ d_printf("%s%c%s %d\n",
+ domain, winbind_separator(), name, type);
+ }
+
+ wbcFreeMemory(domain);
+ wbcFreeMemory(name);
+
+ return true;
+}
+
+/* Convert sid to fullname */
+
+static bool wbinfo_lookupsid_fullname(const char *sid_str)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainSid sid;
+ char *domain;
+ char *name;
+ enum wbcSidType type;
+
+ /* Send off request */
+
+ wbc_status = wbcStringToSid(sid_str, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcStringToSid: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ wbc_status = wbcGetDisplayName(&sid, &domain, &name, &type);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcGetDisplayName: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ /* Display response */
+
+ d_printf("%s%c%s %d\n",
+ domain, winbind_separator(), name, type);
+
+ wbcFreeMemory(domain);
+ wbcFreeMemory(name);
+
+ return true;
+}
+
+/* Lookup a list of RIDs */
+
+static bool wbinfo_lookuprids(const char *domain, const char *arg)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainSid dsid;
+ char *domain_name = NULL;
+ const char **names = NULL;
+ enum wbcSidType *types = NULL;
+ size_t i, num_rids;
+ uint32_t *rids = NULL;
+ const char *p;
+ char *ridstr;
+ TALLOC_CTX *mem_ctx = NULL;
+ bool ret = false;
+
+ if ((domain == NULL) || (strequal(domain, ".")) || (domain[0] == '\0')){
+ domain = get_winbind_domain();
+ }
+
+ wbc_status = wbcStringToSid(domain, &dsid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ struct wbcDomainInfo *dinfo = NULL;
+
+ wbc_status = wbcDomainInfo(domain, &dinfo);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_printf("wbcDomainInfo(%s) failed: %s\n", domain,
+ wbcErrorString(wbc_status));
+ goto done;
+ }
+
+ dsid = dinfo->sid;
+ wbcFreeMemory(dinfo);
+ }
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ d_printf("talloc_new failed\n");
+ goto done;
+ }
+
+ num_rids = 0;
+ rids = NULL;
+ p = arg;
+
+ while (next_token_talloc(mem_ctx, &p, &ridstr, " ,\n")) {
+ int error = 0;
+ uint32_t rid;
+
+ rid = smb_strtoul(ridstr, NULL, 10, &error, SMB_STR_STANDARD);
+ if (error != 0) {
+ d_printf("failed to convert rid\n");
+ goto done;
+ }
+ rids = talloc_realloc(mem_ctx, rids, uint32_t, num_rids + 1);
+ if (rids == NULL) {
+ d_printf("talloc_realloc failed\n");
+ }
+ rids[num_rids] = rid;
+ num_rids += 1;
+ }
+
+ if (rids == NULL) {
+ d_printf("no rids\n");
+ goto done;
+ }
+
+ wbc_status = wbcLookupRids(
+ &dsid, num_rids, rids, &p, &names, &types);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_printf("winbind_lookup_rids failed: %s\n",
+ wbcErrorString(wbc_status));
+ goto done;
+ }
+
+ domain_name = discard_const_p(char, p);
+ d_printf("Domain: %s\n", domain_name);
+
+ for (i=0; i<num_rids; i++) {
+ d_printf("%8d: %s (%s)\n", rids[i], names[i],
+ wbcSidTypeString(types[i]));
+ }
+
+ ret = true;
+done:
+ wbcFreeMemory(domain_name);
+ wbcFreeMemory(names);
+ wbcFreeMemory(types);
+ TALLOC_FREE(mem_ctx);
+ return ret;
+}
+
+static bool wbinfo_lookup_sids(const char *arg)
+{
+ char sidstr[WBC_SID_STRING_BUFLEN];
+ struct wbcDomainSid *sids;
+ struct wbcDomainInfo *domains;
+ struct wbcTranslatedName *names;
+ int num_domains;
+ int i, num_sids;
+ const char *p;
+ wbcErr wbc_status;
+
+
+ num_sids = 0;
+ sids = NULL;
+ p = arg;
+
+ while (next_token(&p, sidstr, LIST_SEP, sizeof(sidstr))) {
+ sids = talloc_realloc(talloc_tos(), sids, struct wbcDomainSid,
+ num_sids+1);
+ if (sids == NULL) {
+ d_fprintf(stderr, "talloc failed\n");
+ return false;
+ }
+ wbc_status = wbcStringToSid(sidstr, &sids[num_sids]);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "wbcSidToString(%s) failed: %s\n",
+ sidstr, wbcErrorString(wbc_status));
+ TALLOC_FREE(sids);
+ return false;
+ }
+ num_sids += 1;
+ }
+
+ wbc_status = wbcLookupSids(sids, num_sids, &domains, &num_domains,
+ &names);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "wbcLookupSids failed: %s\n",
+ wbcErrorString(wbc_status));
+ TALLOC_FREE(sids);
+ return false;
+ }
+
+ for (i=0; i<num_sids; i++) {
+ const char *domain = NULL;
+
+ wbcSidToStringBuf(&sids[i], sidstr, sizeof(sidstr));
+
+ if (names[i].domain_index >= num_domains) {
+ domain = "<none>";
+ } else if (names[i].domain_index < 0) {
+ domain = "<none>";
+ } else {
+ domain = domains[names[i].domain_index].short_name;
+ }
+
+ if (names[i].type == WBC_SID_NAME_DOMAIN) {
+ d_printf("%s -> %s %d\n", sidstr,
+ domain,
+ names[i].type);
+ } else {
+ d_printf("%s -> %s%c%s %d\n", sidstr,
+ domain,
+ winbind_separator(),
+ names[i].name, names[i].type);
+ }
+ }
+ wbcFreeMemory(names);
+ wbcFreeMemory(domains);
+ return true;
+}
+
+/* Convert string to sid */
+
+static bool wbinfo_lookupname(const char *full_name)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcDomainSid sid;
+ char sid_str[WBC_SID_STRING_BUFLEN];
+ enum wbcSidType type;
+ fstring domain_name;
+ fstring account_name;
+
+ /* Send off request */
+
+ parse_wbinfo_domain_user(full_name, domain_name,
+ account_name);
+
+ wbc_status = wbcLookupName(domain_name, account_name,
+ &sid, &type);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcLookupName: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ wbcSidToStringBuf(&sid, sid_str, sizeof(sid_str));
+
+ /* Display response */
+
+ d_printf("%s %s (%d)\n", sid_str, wbcSidTypeString(type), type);
+
+ return true;
+}
+
+static char *wbinfo_prompt_pass(TALLOC_CTX *mem_ctx,
+ const char *prefix,
+ const char *username)
+{
+ char *prompt;
+ char buf[1024] = {0};
+ int rc;
+
+ prompt = talloc_asprintf(mem_ctx, "Enter %s's ", username);
+ if (!prompt) {
+ return NULL;
+ }
+ if (prefix) {
+ prompt = talloc_asprintf_append(prompt, "%s ", prefix);
+ if (!prompt) {
+ return NULL;
+ }
+ }
+ prompt = talloc_asprintf_append(prompt, "password: ");
+ if (!prompt) {
+ return NULL;
+ }
+
+ rc = samba_getpass(prompt, buf, sizeof(buf), false, false);
+ TALLOC_FREE(prompt);
+ if (rc < 0) {
+ return NULL;
+ }
+
+ return talloc_strdup(mem_ctx, buf);
+}
+
+/* Authenticate a user with a plaintext password */
+
+static bool wbinfo_auth_krb5(char *username, const char *cctype, uint32_t flags)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ char *s = NULL;
+ char *p = NULL;
+ char *password = NULL;
+ char *name = NULL;
+ char *local_cctype = NULL;
+ uid_t uid;
+ struct wbcLogonUserParams params;
+ struct wbcLogonUserInfo *info;
+ struct wbcAuthErrorInfo *error;
+ struct wbcUserPasswordPolicyInfo *policy;
+ TALLOC_CTX *frame = talloc_tos();
+
+ if ((s = talloc_strdup(frame, username)) == NULL) {
+ return false;
+ }
+
+ if ((p = strchr(s, '%')) != NULL) {
+ *p = 0;
+ p++;
+ password = talloc_strdup(frame, p);
+ } else {
+ password = wbinfo_prompt_pass(frame, NULL, username);
+ }
+
+ local_cctype = talloc_strdup(frame, cctype);
+
+ name = s;
+
+ uid = geteuid();
+
+ params.username = name;
+ params.password = password;
+ params.num_blobs = 0;
+ params.blobs = NULL;
+
+ wbc_status = wbcAddNamedBlob(&params.num_blobs,
+ &params.blobs,
+ "flags",
+ 0,
+ (uint8_t *)&flags,
+ sizeof(flags));
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcAddNamedBlob: %s\n",
+ wbcErrorString(wbc_status));
+ goto done;
+ }
+
+ wbc_status = wbcAddNamedBlob(&params.num_blobs,
+ &params.blobs,
+ "user_uid",
+ 0,
+ (uint8_t *)&uid,
+ sizeof(uid));
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcAddNamedBlob: %s\n",
+ wbcErrorString(wbc_status));
+ goto done;
+ }
+
+ wbc_status = wbcAddNamedBlob(&params.num_blobs,
+ &params.blobs,
+ "krb5_cc_type",
+ 0,
+ (uint8_t *)local_cctype,
+ strlen(cctype)+1);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcAddNamedBlob: %s\n",
+ wbcErrorString(wbc_status));
+ goto done;
+ }
+
+ wbc_status = wbcLogonUser(&params, &info, &error, &policy);
+
+ d_printf("plaintext kerberos password authentication for [%s] %s "
+ "(requesting cctype: %s)\n",
+ name, WBC_ERROR_IS_OK(wbc_status) ? "succeeded" : "failed",
+ cctype);
+
+ if (error) {
+ d_fprintf(stderr,
+ "wbcLogonUser(%s): error code was %s (0x%x)\n"
+ "error message was: %s\n",
+ params.username, error->nt_string,
+ error->nt_status,
+ error->display_string);
+ }
+
+ if (WBC_ERROR_IS_OK(wbc_status)) {
+ if (flags & WBFLAG_PAM_INFO3_TEXT) {
+ if (info && info->info && info->info->user_flags &
+ NETLOGON_CACHED_ACCOUNT) {
+ d_printf("user_flgs: "
+ "NETLOGON_CACHED_ACCOUNT\n");
+ }
+ }
+
+ if (info) {
+ size_t i;
+ for (i=0; i < info->num_blobs; i++) {
+ if (strequal(info->blobs[i].name,
+ "krb5ccname")) {
+ d_printf("credentials were put "
+ "in: %s\n",
+ (const char *)
+ info->blobs[i].blob.data);
+ break;
+ }
+ }
+ } else {
+ d_printf("no credentials cached\n");
+ }
+ }
+ done:
+
+ wbcFreeMemory(params.blobs);
+
+ return WBC_ERROR_IS_OK(wbc_status);
+}
+
+/* Authenticate a user with a plaintext password */
+
+static bool wbinfo_auth(char *username)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ char *s = NULL;
+ char *p = NULL;
+ char *password = NULL;
+ char *name = NULL;
+ TALLOC_CTX *frame = talloc_tos();
+
+ if ((s = talloc_strdup(frame, username)) == NULL) {
+ return false;
+ }
+
+ if ((p = strchr(s, '%')) != NULL) {
+ *p = 0;
+ p++;
+ password = talloc_strdup(frame, p);
+ } else {
+ password = wbinfo_prompt_pass(frame, NULL, username);
+ }
+
+ name = s;
+
+ wbc_status = wbcAuthenticateUser(name, password);
+
+ d_printf("plaintext password authentication %s\n",
+ WBC_ERROR_IS_OK(wbc_status) ? "succeeded" : "failed");
+
+#if 0
+ if (response.data.auth.nt_status)
+ d_fprintf(stderr,
+ "error code was %s (0x%x)\nerror message was: %s\n",
+ response.data.auth.nt_status_string,
+ response.data.auth.nt_status,
+ response.data.auth.error_string);
+#endif
+
+ return WBC_ERROR_IS_OK(wbc_status);
+}
+
+/* Authenticate a user with a challenge/response */
+
+static bool wbinfo_auth_crap(char *username, bool use_ntlmv2, bool use_lanman)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcAuthUserParams params;
+ struct wbcAuthUserInfo *info = NULL;
+ struct wbcAuthErrorInfo *err = NULL;
+ DATA_BLOB lm = data_blob_null;
+ DATA_BLOB nt = data_blob_null;
+ fstring name_user;
+ fstring name_domain;
+ char *pass;
+ char *p;
+ TALLOC_CTX *frame = talloc_tos();
+
+ p = strchr(username, '%');
+
+ if (p) {
+ *p = 0;
+ pass = talloc_strdup(frame, p + 1);
+ } else {
+ pass = wbinfo_prompt_pass(frame, NULL, username);
+ }
+
+ parse_wbinfo_domain_user(username, name_domain, name_user);
+
+ params.account_name = name_user;
+ params.domain_name = name_domain;
+ params.workstation_name = NULL;
+
+ params.flags = 0;
+ params.parameter_control= WBC_MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT |
+ WBC_MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
+
+ params.level = WBC_AUTH_USER_LEVEL_RESPONSE;
+
+ generate_random_buffer(params.password.response.challenge, 8);
+
+ if (use_ntlmv2) {
+ DATA_BLOB server_chal;
+ DATA_BLOB names_blob;
+ const char *netbios_name = NULL;
+ const char *domain = NULL;
+
+ netbios_name = get_winbind_netbios_name(),
+ domain = get_winbind_domain();
+ if (domain == NULL) {
+ d_fprintf(stderr, "Failed to get domain from winbindd\n");
+ return false;
+ }
+
+ server_chal = data_blob(params.password.response.challenge, 8);
+
+ /* Pretend this is a login to 'us', for blob purposes */
+ names_blob = NTLMv2_generate_names_blob(NULL,
+ netbios_name,
+ domain);
+
+ if (pass != NULL &&
+ !SMBNTLMv2encrypt(NULL, name_user, name_domain, pass,
+ &server_chal,
+ &names_blob,
+ &lm, &nt, NULL, NULL)) {
+ data_blob_free(&names_blob);
+ data_blob_free(&server_chal);
+ TALLOC_FREE(pass);
+ return false;
+ }
+ data_blob_free(&names_blob);
+ data_blob_free(&server_chal);
+
+ } else {
+ if (use_lanman) {
+ bool ok;
+ lm = data_blob(NULL, 24);
+ ok = SMBencrypt(pass,
+ params.password.response.challenge,
+ lm.data);
+ if (!ok) {
+ data_blob_free(&lm);
+ }
+ }
+ nt = data_blob(NULL, 24);
+ SMBNTencrypt(pass, params.password.response.challenge,
+ nt.data);
+ }
+
+ params.password.response.nt_length = nt.length;
+ params.password.response.nt_data = nt.data;
+ params.password.response.lm_length = lm.length;
+ params.password.response.lm_data = lm.data;
+
+ wbc_status = wbcAuthenticateUserEx(&params, &info, &err);
+
+ /* Display response */
+
+ d_printf("challenge/response password authentication %s\n",
+ WBC_ERROR_IS_OK(wbc_status) ? "succeeded" : "failed");
+
+ if (wbc_status == WBC_ERR_AUTH_ERROR) {
+ d_fprintf(stderr,
+ "wbcAuthenticateUserEx(%s%c%s): error code was "
+ "%s (0x%x, authoritative=%"PRIu8")\n"
+ "error message was: %s\n",
+ name_domain,
+ winbind_separator(),
+ name_user,
+ err->nt_string,
+ err->nt_status,
+ err->authoritative,
+ err->display_string);
+ wbcFreeMemory(err);
+ } else if (WBC_ERROR_IS_OK(wbc_status)) {
+ wbcFreeMemory(info);
+ }
+
+ data_blob_free(&nt);
+ data_blob_free(&lm);
+
+ return WBC_ERROR_IS_OK(wbc_status);
+}
+
+/* Authenticate a user with a plaintext password */
+
+static bool wbinfo_pam_logon(char *username, bool verbose)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct wbcLogonUserParams params;
+ struct wbcLogonUserInfo *info = NULL;
+ struct wbcAuthErrorInfo *error = NULL;
+ char *s = NULL;
+ char *p = NULL;
+ TALLOC_CTX *frame = talloc_tos();
+ uint32_t flags;
+ uint32_t uid;
+
+ ZERO_STRUCT(params);
+
+ if ((s = talloc_strdup(frame, username)) == NULL) {
+ return false;
+ }
+
+ if ((p = strchr(s, '%')) != NULL) {
+ *p = 0;
+ p++;
+ params.password = talloc_strdup(frame, p);
+ } else {
+ params.password = wbinfo_prompt_pass(frame, NULL, username);
+ }
+ params.username = s;
+
+ flags = WBFLAG_PAM_CACHED_LOGIN;
+
+ wbc_status = wbcAddNamedBlob(&params.num_blobs, &params.blobs,
+ "flags", 0,
+ (uint8_t *)&flags, sizeof(flags));
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_printf("wbcAddNamedBlob failed: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ uid = getuid();
+
+ wbc_status = wbcAddNamedBlob(&params.num_blobs, &params.blobs,
+ "user_uid", 0,
+ (uint8_t *)&uid, sizeof(uid));
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_printf("wbcAddNamedBlob failed: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ wbc_status = wbcLogonUser(&params, &info, &error, NULL);
+
+ if (verbose && (info != NULL)) {
+ struct wbcAuthUserInfo *i = info->info;
+ uint32_t j;
+
+ if (i->account_name != NULL) {
+ d_printf("account_name: %s\n", i->account_name);
+ }
+ if (i->user_principal != NULL) {
+ d_printf("user_principal: %s\n", i->user_principal);
+ }
+ if (i->full_name != NULL) {
+ d_printf("full_name: %s\n", i->full_name);
+ }
+ if (i->domain_name != NULL) {
+ d_printf("domain_name: %s\n", i->domain_name);
+ }
+ if (i->dns_domain_name != NULL) {
+ d_printf("dns_domain_name: %s\n", i->dns_domain_name);
+ }
+ if (i->logon_server != NULL) {
+ d_printf("logon_server: %s\n", i->logon_server);
+ }
+ if (i->logon_script != NULL) {
+ d_printf("logon_script: %s\n", i->logon_script);
+ }
+ if (i->profile_path != NULL) {
+ d_printf("profile_path: %s\n", i->profile_path);
+ }
+ if (i->home_directory != NULL) {
+ d_printf("home_directory: %s\n", i->home_directory);
+ }
+ if (i->home_drive != NULL) {
+ d_printf("home_drive: %s\n", i->home_drive);
+ }
+
+ d_printf("sids:");
+
+ for (j=0; j<i->num_sids; j++) {
+ char buf[WBC_SID_STRING_BUFLEN];
+ wbcSidToStringBuf(&i->sids[j].sid, buf, sizeof(buf));
+ d_printf(" %s", buf);
+ }
+ d_printf("\n");
+
+ wbcFreeMemory(info);
+ info = NULL;
+ }
+
+ wbcFreeMemory(params.blobs);
+
+ d_printf("plaintext password authentication %s\n",
+ WBC_ERROR_IS_OK(wbc_status) ? "succeeded" : "failed");
+
+ if (!WBC_ERROR_IS_OK(wbc_status) && (error != NULL)) {
+ d_fprintf(stderr,
+ "wbcLogonUser(%s): error code was %s (0x%x)\n"
+ "error message was: %s\n",
+ params.username,
+ error->nt_string,
+ (int)error->nt_status,
+ error->display_string);
+ wbcFreeMemory(error);
+ }
+ return WBC_ERROR_IS_OK(wbc_status);
+}
+
+/* Save creds with winbind */
+
+static bool wbinfo_ccache_save(char *username)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ char *s = NULL;
+ char *p = NULL;
+ char *password = NULL;
+ char *name = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ s = talloc_strdup(frame, username);
+ if (s == NULL) {
+ return false;
+ }
+
+ p = strchr(s, '%');
+ if (p != NULL) {
+ *p = 0;
+ p++;
+ password = talloc_strdup(frame, p);
+ } else {
+ password = wbinfo_prompt_pass(frame, NULL, username);
+ }
+
+ name = s;
+
+ wbc_status = wbcCredentialSave(name, password);
+
+ d_printf("saving creds %s\n",
+ WBC_ERROR_IS_OK(wbc_status) ? "succeeded" : "failed");
+
+ TALLOC_FREE(frame);
+
+ return WBC_ERROR_IS_OK(wbc_status);
+}
+
+#ifdef WITH_FAKE_KASERVER
+/* Authenticate a user with a plaintext password and set a token */
+
+static bool wbinfo_klog(char *username)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ char *p;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ p = strchr(username, '%');
+
+ if (p) {
+ *p = 0;
+ fstrcpy(request.data.auth.user, username);
+ fstrcpy(request.data.auth.pass, p + 1);
+ *p = '%';
+ } else {
+ fstrcpy(request.data.auth.user, username);
+ (void) samba_getpass("Password: ",
+ request.data.auth.pass,
+ sizeof(request.data.auth.pass),
+ false, false);
+ }
+
+ request.flags |= WBFLAG_PAM_AFS_TOKEN;
+
+ wbc_status = wbcRequestResponse(NULL, WINBINDD_PAM_AUTH,
+ &request, &response);
+
+ /* Display response */
+
+ d_printf("plaintext password authentication %s\n",
+ WBC_ERROR_IS_OK(wbc_status) ? "succeeded" : "failed");
+
+ if (response.data.auth.nt_status)
+ d_fprintf(stderr,
+ "error code was %s (0x%x)\nerror message was: %s\n",
+ response.data.auth.nt_status_string,
+ response.data.auth.nt_status,
+ response.data.auth.error_string);
+
+ if (!WBC_ERROR_IS_OK(wbc_status))
+ return false;
+
+ if (response.extra_data.data == NULL) {
+ d_fprintf(stderr, "Did not get token data\n");
+ return false;
+ }
+
+ if (!afs_settoken_str((char *)response.extra_data.data)) {
+ winbindd_free_response(&response);
+ d_fprintf(stderr, "Could not set token\n");
+ return false;
+ }
+
+ winbindd_free_response(&response);
+ d_printf("Successfully created AFS token\n");
+ return true;
+}
+#else
+static bool wbinfo_klog(char *username)
+{
+ d_fprintf(stderr, "No AFS support compiled in.\n");
+ return false;
+}
+#endif
+
+/* Print domain users */
+
+static bool print_domain_users(const char *domain)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ uint32_t i;
+ uint32_t num_users = 0;
+ const char **users = NULL;
+
+ /* Send request to winbind daemon */
+
+ if (domain == NULL) {
+ domain = get_winbind_domain();
+ } else {
+ /* '.' is the special sign for our own domain */
+ if ((domain[0] == '\0') || strcmp(domain, ".") == 0) {
+ domain = get_winbind_domain();
+ /* '*' is the special sign for all domains */
+ } else if (strcmp(domain, "*") == 0) {
+ domain = NULL;
+ }
+ }
+
+ wbc_status = wbcListUsers(domain, &num_users, &users);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ for (i=0; i < num_users; i++) {
+ d_printf("%s\n", users[i]);
+ }
+
+ wbcFreeMemory(users);
+
+ return true;
+}
+
+/* Print domain groups */
+
+static bool print_domain_groups(const char *domain)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ uint32_t i;
+ uint32_t num_groups = 0;
+ const char **groups = NULL;
+
+ /* Send request to winbind daemon */
+
+ if (domain == NULL) {
+ domain = get_winbind_domain();
+ } else {
+ /* '.' is the special sign for our own domain */
+ if ((domain[0] == '\0') || strcmp(domain, ".") == 0) {
+ domain = get_winbind_domain();
+ /* '*' is the special sign for all domains */
+ } else if (strcmp(domain, "*") == 0) {
+ domain = NULL;
+ }
+ }
+
+ wbc_status = wbcListGroups(domain, &num_groups, &groups);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "failed to call wbcListGroups: %s\n",
+ wbcErrorString(wbc_status));
+ return false;
+ }
+
+ for (i=0; i < num_groups; i++) {
+ d_printf("%s\n", groups[i]);
+ }
+
+ wbcFreeMemory(groups);
+
+ return true;
+}
+
+/* Set the authorised user for winbindd access in secrets.tdb */
+
+static bool wbinfo_set_auth_user(char *username)
+{
+ d_fprintf(stderr, "This functionality was moved to the 'net' utility.\n"
+ "See 'net help setauthuser' for details.\n");
+ return false;
+}
+
+static void wbinfo_get_auth_user(void)
+{
+ d_fprintf(stderr, "This functionality was moved to the 'net' utility.\n"
+ "See 'net help getauthuser' for details.\n");
+}
+
+static bool wbinfo_ping(void)
+{
+ wbcErr wbc_status;
+
+ wbc_status = wbcPing();
+
+ /* Display response */
+
+ d_printf("Ping to winbindd %s\n",
+ WBC_ERROR_IS_OK(wbc_status) ? "succeeded" : "failed");
+
+ return WBC_ERROR_IS_OK(wbc_status);
+}
+
+static bool wbinfo_change_user_password(const char *username)
+{
+ wbcErr wbc_status;
+ char *old_password = NULL;
+ char *new_password = NULL;
+ TALLOC_CTX *frame = talloc_tos();
+
+ old_password = wbinfo_prompt_pass(frame, "old", username);
+ new_password = wbinfo_prompt_pass(frame, "new", username);
+
+ wbc_status = wbcChangeUserPassword(username, old_password,new_password);
+
+ /* Display response */
+
+ d_printf("Password change for user %s %s\n", username,
+ WBC_ERROR_IS_OK(wbc_status) ? "succeeded" : "failed");
+
+ return WBC_ERROR_IS_OK(wbc_status);
+}
+
+/* Main program */
+
+enum {
+ OPT_SET_AUTH_USER = 1000,
+ OPT_GET_AUTH_USER,
+ OPT_DOMAIN_NAME,
+ OPT_SEQUENCE,
+ OPT_GETDCNAME,
+ OPT_DSGETDCNAME,
+ OPT_DC_INFO,
+ OPT_USERDOMGROUPS,
+ OPT_SIDALIASES,
+ OPT_USERSIDS,
+ OPT_LOOKUP_SIDS,
+ OPT_ALLOCATE_UID,
+ OPT_ALLOCATE_GID,
+ OPT_SET_UID_MAPPING,
+ OPT_SET_GID_MAPPING,
+ OPT_REMOVE_UID_MAPPING,
+ OPT_REMOVE_GID_MAPPING,
+ OPT_SIDS_TO_XIDS,
+ OPT_XIDS_TO_SIDS,
+ OPT_SEPARATOR,
+ OPT_LIST_ALL_DOMAINS,
+ OPT_LIST_OWN_DOMAIN,
+ OPT_UID_INFO,
+ OPT_USER_SIDINFO,
+ OPT_GROUP_INFO,
+ OPT_GID_INFO,
+ OPT_VERBOSE,
+ OPT_ONLINESTATUS,
+ OPT_CHANGE_USER_PASSWORD,
+ OPT_CCACHE_SAVE,
+ OPT_SID_TO_FULLNAME,
+ OPT_NTLMV1,
+ OPT_NTLMV2,
+ OPT_PAM_LOGON,
+ OPT_LOGOFF,
+ OPT_LOGOFF_USER,
+ OPT_LOGOFF_UID,
+ OPT_LANMAN,
+ OPT_KRB5CCNAME
+};
+
+int main(int argc, const char **argv, char **envp)
+{
+ int opt;
+ TALLOC_CTX *frame = talloc_stackframe();
+ poptContext pc;
+ static char *string_arg;
+ char *string_subarg = NULL;
+ static char *opt_domain_name;
+ static int int_arg;
+ int int_subarg = -1;
+ int result = 1;
+ bool verbose = false;
+ bool use_ntlmv2 = true;
+ bool use_lanman = false;
+ char *logoff_user = getenv("USER");
+ int logoff_uid = geteuid();
+ const char *opt_krb5ccname = "FILE";
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+
+ /* longName, shortName, argInfo, argPtr, value, descrip,
+ argDesc */
+
+ {
+ .longName = "domain-users",
+ .shortName = 'u',
+ .argInfo = POPT_ARG_NONE,
+ .val = 'u',
+ .descrip = "Lists all domain users",
+ .argDescrip = "domain"
+ },
+ {
+ .longName = "domain-groups",
+ .shortName = 'g',
+ .argInfo = POPT_ARG_NONE,
+ .val = 'g',
+ .descrip = "Lists all domain groups",
+ .argDescrip = "domain"
+ },
+ {
+ .longName = "WINS-by-name",
+ .shortName = 'N',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = 'N',
+ .descrip = "Converts NetBIOS name to IP",
+ .argDescrip = "NETBIOS-NAME"
+ },
+ {
+ .longName = "WINS-by-ip",
+ .shortName = 'I',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = 'I',
+ .descrip = "Converts IP address to NetBIOS name",
+ .argDescrip = "IP"
+ },
+ {
+ .longName = "name-to-sid",
+ .shortName = 'n',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = 'n',
+ .descrip = "Converts name to sid",
+ .argDescrip = "NAME"
+ },
+ {
+ .longName = "sid-to-name",
+ .shortName = 's',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = 's',
+ .descrip = "Converts sid to name",
+ .argDescrip = "SID"
+ },
+ {
+ .longName = "sid-to-fullname",
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = OPT_SID_TO_FULLNAME,
+ .descrip = "Converts sid to fullname",
+ .argDescrip = "SID"
+ },
+ {
+ .longName = "lookup-rids",
+ .shortName = 'R',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = 'R',
+ .descrip = "Converts RIDs to names",
+ .argDescrip = "RIDs"
+ },
+ {
+ .longName = "lookup-sids",
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = OPT_LOOKUP_SIDS,
+ .descrip = "Converts SIDs to types and names",
+ .argDescrip = "Sid-List"
+ },
+ {
+ .longName = "uid-to-sid",
+ .shortName = 'U',
+ .argInfo = POPT_ARG_INT,
+ .arg = &int_arg,
+ .val = 'U',
+ .descrip = "Converts uid to sid",
+ .argDescrip = "UID"
+ },
+ {
+ .longName = "gid-to-sid",
+ .shortName = 'G',
+ .argInfo = POPT_ARG_INT,
+ .arg = &int_arg,
+ .val = 'G',
+ .descrip = "Converts gid to sid",
+ .argDescrip = "GID"
+ },
+ {
+ .longName = "sid-to-uid",
+ .shortName = 'S',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = 'S',
+ .descrip = "Converts sid to uid",
+ .argDescrip = "SID"
+ },
+ {
+ .longName = "sid-to-gid",
+ .shortName = 'Y',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = 'Y',
+ .descrip = "Converts sid to gid",
+ .argDescrip = "SID"
+ },
+ {
+ .longName = "allocate-uid",
+ .argInfo = POPT_ARG_NONE,
+ .val = OPT_ALLOCATE_UID,
+ .descrip = "Get a new UID out of idmap"
+ },
+ {
+ .longName = "allocate-gid",
+ .argInfo = POPT_ARG_NONE,
+ .val = OPT_ALLOCATE_GID,
+ .descrip = "Get a new GID out of idmap"
+ },
+ {
+ .longName = "set-uid-mapping",
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = OPT_SET_UID_MAPPING,
+ .descrip = "Create or modify uid to sid mapping in "
+ "idmap",
+ .argDescrip = "UID,SID"
+ },
+ {
+ .longName = "set-gid-mapping",
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = OPT_SET_GID_MAPPING,
+ .descrip = "Create or modify gid to sid mapping in "
+ "idmap",
+ .argDescrip = "GID,SID"
+ },
+ {
+ .longName = "remove-uid-mapping",
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = OPT_REMOVE_UID_MAPPING,
+ .descrip = "Remove uid to sid mapping in idmap",
+ .argDescrip = "UID,SID"
+ },
+ {
+ .longName = "remove-gid-mapping",
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = OPT_REMOVE_GID_MAPPING,
+ .descrip = "Remove gid to sid mapping in idmap",
+ .argDescrip = "GID,SID",
+ },
+ {
+ .longName = "sids-to-unix-ids",
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = OPT_SIDS_TO_XIDS,
+ .descrip = "Translate SIDs to Unix IDs",
+ .argDescrip = "Sid-List",
+ },
+ {
+ .longName = "unix-ids-to-sids",
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = OPT_XIDS_TO_SIDS,
+ .descrip = "Translate Unix IDs to SIDs",
+ .argDescrip = "ID-List (u<num> g<num>)",
+ },
+ {
+ .longName = "check-secret",
+ .shortName = 't',
+ .argInfo = POPT_ARG_NONE,
+ .val = 't',
+ .descrip = "Check shared secret",
+ },
+ {
+ .longName = "change-secret",
+ .shortName = 'c',
+ .argInfo = POPT_ARG_NONE,
+ .val = 'c',
+ .descrip = "Change shared secret",
+ },
+ {
+ .longName = "ping-dc",
+ .shortName = 'P',
+ .argInfo = POPT_ARG_NONE,
+ .val = 'P',
+ .descrip = "Check the NETLOGON connection",
+ },
+ {
+ .longName = "trusted-domains",
+ .shortName = 'm',
+ .argInfo = POPT_ARG_NONE,
+ .val = 'm',
+ .descrip = "List trusted domains",
+ },
+ {
+ .longName = "all-domains",
+ .argInfo = POPT_ARG_NONE,
+ .val = OPT_LIST_ALL_DOMAINS,
+ .descrip = "List all domains (trusted and own "
+ "domain)",
+ },
+ {
+ .longName = "own-domain",
+ .argInfo = POPT_ARG_NONE,
+ .val = OPT_LIST_OWN_DOMAIN,
+ .descrip = "List own domain",
+ },
+ {
+ .longName = "sequence",
+ .argInfo = POPT_ARG_NONE,
+ .val = OPT_SEQUENCE,
+ .descrip = "Deprecated command, see --online-status",
+ },
+ {
+ .longName = "online-status",
+ .argInfo = POPT_ARG_NONE,
+ .val = OPT_ONLINESTATUS,
+ .descrip = "Show whether domains maintain an active "
+ "connection",
+ },
+ {
+ .longName = "domain-info",
+ .shortName = 'D',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = 'D',
+ .descrip = "Show most of the info we have about the "
+ "domain",
+ },
+ {
+ .longName = "user-info",
+ .shortName = 'i',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = 'i',
+ .descrip = "Get user info",
+ .argDescrip = "USER",
+ },
+ {
+ .longName = "uid-info",
+ .argInfo = POPT_ARG_INT,
+ .arg = &int_arg,
+ .val = OPT_UID_INFO,
+ .descrip = "Get user info from uid",
+ .argDescrip = "UID",
+ },
+ {
+ .longName = "group-info",
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = OPT_GROUP_INFO,
+ .descrip = "Get group info",
+ .argDescrip = "GROUP",
+ },
+ {
+ .longName = "user-sidinfo",
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = OPT_USER_SIDINFO,
+ .descrip = "Get user info from sid",
+ .argDescrip = "SID",
+ },
+ {
+ .longName = "gid-info",
+ .argInfo = POPT_ARG_INT,
+ .arg = &int_arg,
+ .val = OPT_GID_INFO,
+ .descrip = "Get group info from gid",
+ .argDescrip = "GID",
+ },
+ {
+ .longName = "user-groups",
+ .shortName = 'r',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = 'r',
+ .descrip = "Get user groups",
+ .argDescrip = "USER",
+ },
+ {
+ .longName = "user-domgroups",
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = OPT_USERDOMGROUPS,
+ .descrip = "Get user domain groups",
+ .argDescrip = "SID",
+ },
+ {
+ .longName = "sid-aliases",
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = OPT_SIDALIASES,
+ .descrip = "Get sid aliases",
+ .argDescrip = "SID",
+ },
+ {
+ .longName = "user-sids",
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = OPT_USERSIDS,
+ .descrip = "Get user group sids for user SID",
+ .argDescrip = "SID",
+ },
+ {
+ .longName = "authenticate",
+ .shortName = 'a',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = 'a',
+ .descrip = "authenticate user",
+ .argDescrip = "user%password",
+ },
+ {
+ .longName = "pam-logon",
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = OPT_PAM_LOGON,
+ .descrip = "do a pam logon equivalent",
+ .argDescrip = "user%password",
+ },
+ {
+ .longName = "logoff",
+ .argInfo = POPT_ARG_NONE,
+ .val = OPT_LOGOFF,
+ .descrip = "log off user",
+ .argDescrip = "uid",
+ },
+ {
+ .longName = "logoff-user",
+ .argInfo = POPT_ARG_STRING,
+ .arg = &logoff_user,
+ .val = OPT_LOGOFF_USER,
+ .descrip = "username to log off"
+ },
+ {
+ .longName = "logoff-uid",
+ .argInfo = POPT_ARG_INT,
+ .arg = &logoff_uid,
+ .val = OPT_LOGOFF_UID,
+ .descrip = "uid to log off",
+ },
+ {
+ .longName = "set-auth-user",
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = OPT_SET_AUTH_USER,
+ .descrip = "Store user and password used by "
+ "winbindd (root only)",
+ .argDescrip = "user%password",
+ },
+ {
+ .longName = "ccache-save",
+ .shortName = 0,
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = OPT_CCACHE_SAVE,
+ .descrip = "Store user and password for ccache "
+ "operation",
+ .argDescrip = "user%password",
+ },
+ {
+ .longName = "getdcname",
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = OPT_GETDCNAME,
+ .descrip = "Get a DC name for a foreign domain",
+ .argDescrip = "domainname",
+ },
+ {
+ .longName = "dsgetdcname",
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = OPT_DSGETDCNAME,
+ .descrip = "Find a DC for a domain",
+ .argDescrip = "domainname",
+ },
+ {
+ .longName = "dc-info",
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = OPT_DC_INFO,
+ .descrip = "Find the currently known DCs",
+ .argDescrip = "domainname",
+ },
+ {
+ .longName = "get-auth-user",
+ .argInfo = POPT_ARG_NONE,
+ .val = OPT_GET_AUTH_USER,
+ .descrip = "Retrieve user and password used by "
+ "winbindd (root only)",
+ },
+ {
+ .longName = "ping",
+ .shortName = 'p',
+ .argInfo = POPT_ARG_NONE,
+ .arg = 0,
+ .val = 'p',
+ .descrip = "Ping winbindd to see if it is alive",
+ },
+ {
+ .longName = "domain",
+ .shortName = 0,
+ .argInfo = POPT_ARG_STRING,
+ .arg = &opt_domain_name,
+ .val = OPT_DOMAIN_NAME,
+ .descrip = "Define to the domain to restrict "
+ "operation",
+ .argDescrip = "domain",
+ },
+#ifdef WITH_FAKE_KASERVER
+ {
+ .longName = "klog",
+ .shortName = 'k',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = 'k',
+ .descrip = "set an AFS token from winbind",
+ .argDescrip = "user%password",
+ },
+#endif
+#ifdef HAVE_KRB5
+ {
+ .longName = "krb5auth",
+ .shortName = 'K',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = 'K',
+ .descrip = "authenticate user using Kerberos",
+ .argDescrip = "user%password",
+ },
+ /* destroys wbinfo --help output */
+ /* "user%password,DOM\\user%password,user@EXAMPLE.COM,EXAMPLE.COM\\user%password" },
+ */
+ {
+ .longName = "krb5ccname",
+ .argInfo = POPT_ARG_STRING,
+ .arg = &opt_krb5ccname,
+ .val = OPT_KRB5CCNAME,
+ .descrip = "authenticate user using Kerberos and "
+ "specific credential cache type",
+ .argDescrip = "krb5ccname",
+ },
+#endif
+ {
+ .longName = "separator",
+ .argInfo = POPT_ARG_NONE,
+ .val = OPT_SEPARATOR,
+ .descrip = "Get the active winbind separator",
+ },
+ {
+ .longName = "verbose",
+ .argInfo = POPT_ARG_NONE,
+ .val = OPT_VERBOSE,
+ .descrip = "Print additional information per command",
+ },
+ {
+ .longName = "change-user-password",
+ .argInfo = POPT_ARG_STRING,
+ .arg = &string_arg,
+ .val = OPT_CHANGE_USER_PASSWORD,
+ .descrip = "Change the password for a user",
+ },
+ {
+ .longName = "ntlmv1",
+ .argInfo = POPT_ARG_NONE,
+ .val = OPT_NTLMV1,
+ .descrip = "Use NTLMv1 cryptography for user authentication",
+ },
+ {
+ .longName = "ntlmv2",
+ .argInfo = POPT_ARG_NONE,
+ .val = OPT_NTLMV2,
+ .descrip = "Use NTLMv2 cryptography for user authentication",
+ },
+ {
+ .longName = "lanman",
+ .argInfo = POPT_ARG_NONE,
+ .val = OPT_LANMAN,
+ .descrip = "Use lanman cryptography for user authentication",
+ },
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ /* Samba client initialisation */
+ smb_init_locale();
+
+
+ /* Parse options */
+
+ pc = samba_popt_get_context(getprogname(),
+ argc,
+ argv,
+ long_options,
+ 0);
+ if (pc == NULL) {
+ DBG_ERR("Failed to setup popt context!\n");
+ exit(1);
+ }
+
+ /* Parse command line options */
+
+ if (argc == 1) {
+ poptPrintHelp(pc, stderr, 0);
+ return 1;
+ }
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ /* get the generic configuration parameters like --domain */
+ switch (opt) {
+ case OPT_VERBOSE:
+ verbose = true;
+ break;
+ case OPT_NTLMV1:
+ use_ntlmv2 = false;
+ break;
+ case OPT_LANMAN:
+ use_lanman = true;
+ break;
+ }
+ }
+
+ poptFreeContext(pc);
+
+ pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'u':
+ if (!print_domain_users(opt_domain_name)) {
+ d_fprintf(stderr,
+ "Error looking up domain users\n");
+ goto done;
+ }
+ break;
+ case 'g':
+ if (!print_domain_groups(opt_domain_name)) {
+ d_fprintf(stderr,
+ "Error looking up domain groups\n");
+ goto done;
+ }
+ break;
+ case 's':
+ if (!wbinfo_lookupsid(string_arg)) {
+ d_fprintf(stderr,
+ "Could not lookup sid %s\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case OPT_SID_TO_FULLNAME:
+ if (!wbinfo_lookupsid_fullname(string_arg)) {
+ d_fprintf(stderr, "Could not lookup sid %s\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case 'R':
+ if (!wbinfo_lookuprids(opt_domain_name, string_arg)) {
+ d_fprintf(stderr, "Could not lookup RIDs %s\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case OPT_LOOKUP_SIDS:
+ if (!wbinfo_lookup_sids(string_arg)) {
+ d_fprintf(stderr, "Could not lookup SIDs %s\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case 'n':
+ if (!wbinfo_lookupname(string_arg)) {
+ d_fprintf(stderr, "Could not lookup name %s\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case 'N':
+ if (!wbinfo_wins_byname(string_arg)) {
+ d_fprintf(stderr,
+ "Could not lookup WINS by name %s\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case 'I':
+ if (!wbinfo_wins_byip(string_arg)) {
+ d_fprintf(stderr,
+ "Could not lookup WINS by IP %s\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case 'U':
+ if (!wbinfo_uid_to_sid(int_arg)) {
+ d_fprintf(stderr,
+ "Could not convert uid %d to sid\n",
+ int_arg);
+ goto done;
+ }
+ break;
+ case 'G':
+ if (!wbinfo_gid_to_sid(int_arg)) {
+ d_fprintf(stderr,
+ "Could not convert gid %d to sid\n",
+ int_arg);
+ goto done;
+ }
+ break;
+ case 'S':
+ if (!wbinfo_sid_to_uid(string_arg)) {
+ d_fprintf(stderr,
+ "Could not convert sid %s to uid\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case 'Y':
+ if (!wbinfo_sid_to_gid(string_arg)) {
+ d_fprintf(stderr,
+ "Could not convert sid %s to gid\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case OPT_ALLOCATE_UID:
+ if (!wbinfo_allocate_uid()) {
+ d_fprintf(stderr, "Could not allocate a uid\n");
+ goto done;
+ }
+ break;
+ case OPT_ALLOCATE_GID:
+ if (!wbinfo_allocate_gid()) {
+ d_fprintf(stderr, "Could not allocate a gid\n");
+ goto done;
+ }
+ break;
+ case OPT_SET_UID_MAPPING:
+ if (!parse_mapping_arg(string_arg, &int_subarg,
+ &string_subarg) ||
+ !wbinfo_set_uid_mapping(int_subarg, string_subarg))
+ {
+ d_fprintf(stderr, "Could not create or modify "
+ "uid to sid mapping\n");
+ goto done;
+ }
+ break;
+ case OPT_SET_GID_MAPPING:
+ if (!parse_mapping_arg(string_arg, &int_subarg,
+ &string_subarg) ||
+ !wbinfo_set_gid_mapping(int_subarg, string_subarg))
+ {
+ d_fprintf(stderr, "Could not create or modify "
+ "gid to sid mapping\n");
+ goto done;
+ }
+ break;
+ case OPT_REMOVE_UID_MAPPING:
+ if (!parse_mapping_arg(string_arg, &int_subarg,
+ &string_subarg) ||
+ !wbinfo_remove_uid_mapping(int_subarg,
+ string_subarg))
+ {
+ d_fprintf(stderr, "Could not remove uid to sid "
+ "mapping\n");
+ goto done;
+ }
+ break;
+ case OPT_REMOVE_GID_MAPPING:
+ if (!parse_mapping_arg(string_arg, &int_subarg,
+ &string_subarg) ||
+ !wbinfo_remove_gid_mapping(int_subarg,
+ string_subarg))
+ {
+ d_fprintf(stderr, "Could not remove gid to sid "
+ "mapping\n");
+ goto done;
+ }
+ break;
+ case OPT_SIDS_TO_XIDS:
+ if (!wbinfo_sids_to_unix_ids(string_arg)) {
+ d_fprintf(stderr, "wbinfo_sids_to_unix_ids "
+ "failed\n");
+ goto done;
+ }
+ break;
+ case OPT_XIDS_TO_SIDS:
+ if (!wbinfo_xids_to_sids(string_arg)) {
+ d_fprintf(stderr, "wbinfo_xids_to_sids "
+ "failed\n");
+ goto done;
+ }
+ break;
+ case 't':
+ if (!wbinfo_check_secret(opt_domain_name)) {
+ d_fprintf(stderr, "Could not check secret\n");
+ goto done;
+ }
+ break;
+ case 'c':
+ if (!wbinfo_change_secret(opt_domain_name)) {
+ d_fprintf(stderr, "Could not change secret\n");
+ goto done;
+ }
+ break;
+ case 'P':
+ if (!wbinfo_ping_dc(opt_domain_name)) {
+ goto done;
+ }
+ break;
+ case 'm':
+ if (!wbinfo_list_domains(false, verbose)) {
+ d_fprintf(stderr,
+ "Could not list trusted domains\n");
+ goto done;
+ }
+ break;
+ case OPT_SEQUENCE:
+ if (!wbinfo_show_sequence(opt_domain_name)) {
+ d_fprintf(stderr,
+ "Could not show sequence numbers\n");
+ goto done;
+ }
+ break;
+ case OPT_ONLINESTATUS:
+ if (!wbinfo_show_onlinestatus(opt_domain_name)) {
+ d_fprintf(stderr,
+ "Could not show online-status\n");
+ goto done;
+ }
+ break;
+ case 'D':
+ if (!wbinfo_domain_info(string_arg)) {
+ d_fprintf(stderr,
+ "Could not get domain info\n");
+ goto done;
+ }
+ break;
+ case 'i':
+ if (!wbinfo_get_userinfo(string_arg)) {
+ d_fprintf(stderr,
+ "Could not get info for user %s\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case OPT_USER_SIDINFO:
+ if ( !wbinfo_get_user_sidinfo(string_arg)) {
+ d_fprintf(stderr,
+ "Could not get info for user "
+ "sid %s\n", string_arg);
+ goto done;
+ }
+ break;
+ case OPT_UID_INFO:
+ if ( !wbinfo_get_uidinfo(int_arg)) {
+ d_fprintf(stderr, "Could not get info for uid "
+ "%d\n", int_arg);
+ goto done;
+ }
+ break;
+ case OPT_GROUP_INFO:
+ if ( !wbinfo_get_groupinfo(string_arg)) {
+ d_fprintf(stderr, "Could not get info for "
+ "group %s\n", string_arg);
+ goto done;
+ }
+ break;
+ case OPT_GID_INFO:
+ if ( !wbinfo_get_gidinfo(int_arg)) {
+ d_fprintf(stderr, "Could not get info for gid "
+ "%d\n", int_arg);
+ goto done;
+ }
+ break;
+ case 'r':
+ if (!wbinfo_get_usergroups(string_arg)) {
+ d_fprintf(stderr,
+ "Could not get groups for user %s\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case OPT_USERSIDS:
+ if (!wbinfo_get_usersids(string_arg)) {
+ d_fprintf(stderr, "Could not get group SIDs "
+ "for user SID %s\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case OPT_USERDOMGROUPS:
+ if (!wbinfo_get_userdomgroups(string_arg)) {
+ d_fprintf(stderr, "Could not get user's domain "
+ "groups for user SID %s\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case OPT_SIDALIASES:
+ if (!wbinfo_get_sidaliases(opt_domain_name,
+ string_arg)) {
+ d_fprintf(stderr, "Could not get sid aliases "
+ "for user SID %s\n", string_arg);
+ goto done;
+ }
+ break;
+ case 'a': {
+ bool got_error = false;
+
+ if (!wbinfo_auth(string_arg)) {
+ d_fprintf(stderr,
+ "Could not authenticate user "
+ "%s with plaintext "
+ "password\n", string_arg);
+ got_error = true;
+ }
+
+ if (!wbinfo_auth_crap(string_arg, use_ntlmv2,
+ use_lanman)) {
+ d_fprintf(stderr,
+ "Could not authenticate user "
+ "%s with challenge/response\n",
+ string_arg);
+ got_error = true;
+ }
+
+ if (got_error)
+ goto done;
+ break;
+ }
+ case OPT_PAM_LOGON:
+ if (!wbinfo_pam_logon(string_arg, verbose)) {
+ d_fprintf(stderr, "pam_logon failed for %s\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case OPT_LOGOFF:
+ {
+ wbcErr wbc_status;
+
+ wbc_status = wbcLogoffUser(logoff_user, logoff_uid,
+ "");
+ d_printf("Logoff %s (%d): %s\n", logoff_user,
+ logoff_uid, wbcErrorString(wbc_status));
+ break;
+ }
+ case 'K': {
+ uint32_t flags = WBFLAG_PAM_KRB5 |
+ WBFLAG_PAM_CACHED_LOGIN |
+ WBFLAG_PAM_FALLBACK_AFTER_KRB5 |
+ WBFLAG_PAM_INFO3_TEXT |
+ WBFLAG_PAM_CONTACT_TRUSTDOM;
+
+ if (!wbinfo_auth_krb5(string_arg, opt_krb5ccname,
+ flags)) {
+ d_fprintf(stderr,
+ "Could not authenticate user "
+ "[%s] with Kerberos "
+ "(ccache: %s)\n", string_arg,
+ opt_krb5ccname);
+ goto done;
+ }
+ break;
+ }
+ case 'k':
+ if (!wbinfo_klog(string_arg)) {
+ d_fprintf(stderr, "Could not klog user\n");
+ goto done;
+ }
+ break;
+ case 'p':
+ if (!wbinfo_ping()) {
+ d_fprintf(stderr, "could not ping winbindd!\n");
+ goto done;
+ }
+ break;
+ case OPT_SET_AUTH_USER:
+ if (!wbinfo_set_auth_user(string_arg)) {
+ goto done;
+ }
+ break;
+ case OPT_GET_AUTH_USER:
+ wbinfo_get_auth_user();
+ goto done;
+ break;
+ case OPT_CCACHE_SAVE:
+ if (!wbinfo_ccache_save(string_arg)) {
+ goto done;
+ }
+ break;
+ case OPT_GETDCNAME:
+ if (!wbinfo_getdcname(string_arg)) {
+ goto done;
+ }
+ break;
+ case OPT_DSGETDCNAME:
+ if (!wbinfo_dsgetdcname(string_arg, 0)) {
+ goto done;
+ }
+ break;
+ case OPT_DC_INFO:
+ if (!wbinfo_dc_info(string_arg)) {
+ goto done;
+ }
+ break;
+ case OPT_SEPARATOR: {
+ const char sep = winbind_separator();
+ if ( !sep ) {
+ goto done;
+ }
+ d_printf("%c\n", sep);
+ break;
+ }
+ case OPT_LIST_ALL_DOMAINS:
+ if (!wbinfo_list_domains(true, verbose)) {
+ goto done;
+ }
+ break;
+ case OPT_LIST_OWN_DOMAIN:
+ if (!wbinfo_list_own_domain()) {
+ goto done;
+ }
+ break;
+ case OPT_CHANGE_USER_PASSWORD:
+ if (!wbinfo_change_user_password(string_arg)) {
+ d_fprintf(stderr,
+ "Could not change user password "
+ "for user %s\n", string_arg);
+ goto done;
+ }
+ break;
+
+ /* generic configuration options */
+ case OPT_DOMAIN_NAME:
+ case OPT_VERBOSE:
+ case OPT_NTLMV1:
+ case OPT_NTLMV2:
+ case OPT_LANMAN:
+ case OPT_LOGOFF_USER:
+ case OPT_LOGOFF_UID:
+ case OPT_KRB5CCNAME:
+ break;
+ default:
+ d_fprintf(stderr, "Invalid option\n");
+ poptPrintHelp(pc, stderr, 0);
+ goto done;
+ }
+ }
+
+ result = 0;
+
+ /* Exit code */
+
+ done:
+ talloc_free(frame);
+
+ poptFreeContext(pc);
+ return result;
+}
diff --git a/nsswitch/winbind_client.h b/nsswitch/winbind_client.h
new file mode 100644
index 0000000..93b5672
--- /dev/null
+++ b/nsswitch/winbind_client.h
@@ -0,0 +1,57 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ winbind client common code
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Andrew Tridgell 2000
+ Copyright (C) Andrew Bartlett 2002
+ Copyright (C) Matthew Newton 2015
+
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _NSSWITCH_WINBIND_CLIENT_H_
+#define _NSSWITCH_WINBIND_CLIENT_H_
+
+#include "winbind_nss_config.h"
+#include "winbind_struct_protocol.h"
+
+struct winbindd_context;
+
+struct winbindd_context *winbindd_ctx_create(void);
+void winbindd_ctx_free(struct winbindd_context *ctx);
+
+NSS_STATUS winbindd_request_response(struct winbindd_context *ctx,
+ int req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response);
+NSS_STATUS winbindd_priv_request_response(struct winbindd_context *ctx,
+ int req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response);
+
+void winbind_set_client_name(const char *name);
+
+#define winbind_env_set() \
+ (strcmp(getenv(WINBINDD_DONT_ENV)?getenv(WINBINDD_DONT_ENV):"0","1") == 0)
+
+#define winbind_off() \
+ (setenv(WINBINDD_DONT_ENV, "1", 1) == 0)
+
+#define winbind_on() \
+ (setenv(WINBINDD_DONT_ENV, "0", 1) == 0)
+
+#endif /* _NSSWITCH_WINBIND_CLIENT_H_ */
diff --git a/nsswitch/winbind_nss.h b/nsswitch/winbind_nss.h
new file mode 100644
index 0000000..53250d6
--- /dev/null
+++ b/nsswitch/winbind_nss.h
@@ -0,0 +1,82 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ A common place to work out how to define NSS_STATUS on various
+ platforms.
+
+ Copyright (C) Tim Potter 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _NSSWITCH_NSS_H
+#define _NSSWITCH_NSS_H
+
+#ifdef HAVE_NSS_COMMON_H
+
+/*
+ * Sun Solaris
+ */
+
+#include "nsswitch/winbind_nss_solaris.h"
+#include "nsswitch/winbind_nss_linux.h"
+
+#elif defined(HAVE_NSS_H)
+
+/*
+ * Linux (glibc)
+ */
+
+#include <nss.h>
+
+typedef enum nss_status NSS_STATUS;
+
+#include "nsswitch/winbind_nss_linux.h"
+
+#elif defined(HAVE_NS_API_H)
+
+/*
+ * SGI IRIX
+ */
+
+#include "nsswitch/winbind_nss_irix.h"
+
+#elif defined(HPUX) && defined(HAVE_NSSWITCH_H)
+
+/* HP-UX 11 */
+
+#include "nsswitch/winbind_nss_hpux.h"
+
+#elif defined(__NetBSD__) && defined(HAVE_GETPWENT_R)
+
+/*
+ * NetBSD 3 and newer
+ */
+
+#include "nsswitch/winbind_nss_netbsd.h"
+#include "nsswitch/winbind_nss_linux.h"
+
+#else /* Nothing's defined. Neither gnu nor netbsd nor sun nor hp */
+
+typedef enum
+{
+ NSS_STATUS_SUCCESS=0,
+ NSS_STATUS_NOTFOUND=1,
+ NSS_STATUS_UNAVAIL=2,
+ NSS_STATUS_TRYAGAIN=3
+} NSS_STATUS;
+
+#endif
+
+#endif /* _NSSWITCH_NSS_H */
diff --git a/nsswitch/winbind_nss_aix.c b/nsswitch/winbind_nss_aix.c
new file mode 100644
index 0000000..f1f00e9
--- /dev/null
+++ b/nsswitch/winbind_nss_aix.c
@@ -0,0 +1,1129 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ AIX loadable authentication module, providing identification and
+ authentication routines against Samba winbind/Windows NT Domain
+
+ Copyright (C) Tim Potter 2003
+ Copyright (C) Steve Roylance 2003
+ Copyright (C) Andrew Tridgell 2003-2004
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+
+ To install this module copy nsswitch/WINBIND to /usr/lib/security and add
+ "WINBIND" in /usr/lib/security/methods.cfg and /etc/security/user
+
+ Note that this module also provides authentication and password
+ changing routines, so you do not need to install the winbind PAM
+ module.
+
+ see
+ http://publib16.boulder.ibm.com/doc_link/en_US/a_doc_lib/aixprggd/kernextc/sec_load_mod.htm
+ for some information in the interface that this module implements
+
+ Many thanks to Julianne Haugh for explaining some of the finer
+ details of this interface.
+
+ To debug this module use uess_test.c (which you can get from tridge)
+ or set "options=debug" in /usr/lib/security/methods.cfg
+
+*/
+
+#include "winbind_client.h"
+#include <usersec.h>
+
+/* enable this to log which entry points have not been
+ completed yet */
+#define LOG_UNIMPLEMENTED_CALLS 0
+
+
+#define WB_AIX_ENCODED '_'
+
+static int debug_enabled;
+
+
+static void logit(const char *format, ...)
+{
+ va_list ap;
+ FILE *f;
+ if (!debug_enabled) {
+ return;
+ }
+ f = fopen("/tmp/WINBIND_DEBUG.log", "a");
+ if (!f) return;
+ va_start(ap, format);
+ vfprintf(f, format, ap);
+ va_end(ap);
+ fclose(f);
+}
+
+
+#define HANDLE_ERRORS(ret) do { \
+ if ((ret) == NSS_STATUS_NOTFOUND) { \
+ errno = ENOENT; \
+ return NULL; \
+ } else if ((ret) != NSS_STATUS_SUCCESS) { \
+ errno = EIO; \
+ return NULL; \
+ } \
+} while (0)
+
+#define STRCPY_RET(dest, src) \
+do { \
+ if (strlen(src)+1 > sizeof(dest)) { errno = EINVAL; return -1; } \
+ strncpy(dest, src, sizeof(dest)); \
+ dest[sizeof(dest)-1] = '\0'; \
+} while (0)
+
+#define STRCPY_RETNULL(dest, src) \
+do { \
+ if (strlen(src)+1 > sizeof(dest)) { errno = EINVAL; return NULL; } \
+ strncpy(dest, src, sizeof(dest)); \
+ dest[sizeof(dest)-1] = '\0'; \
+} while (0)
+
+
+/* free a passwd structure */
+static void free_pwd(struct passwd *pwd)
+{
+ free(pwd->pw_name);
+ free(pwd->pw_passwd);
+ free(pwd->pw_gecos);
+ free(pwd->pw_dir);
+ free(pwd->pw_shell);
+ free(pwd);
+}
+
+/* free a group structure */
+static void free_grp(struct group *grp)
+{
+ int i;
+
+ free(grp->gr_name);
+ free(grp->gr_passwd);
+
+ if (!grp->gr_mem) {
+ free(grp);
+ return;
+ }
+
+ for (i=0; grp->gr_mem[i]; i++) {
+ free(grp->gr_mem[i]);
+ }
+
+ free(grp->gr_mem);
+ free(grp);
+}
+
+
+/* replace commas with nulls, and null terminate */
+static void replace_commas(char *s)
+{
+ char *p, *p0=s;
+ for (p=strchr(s, ','); p; p = strchr(p+1, ',')) {
+ *p=0;
+ p0 = p+1;
+ }
+
+ p0[strlen(p0)+1] = 0;
+}
+
+
+/* the decode_*() routines are used to cope with the fact that AIX 5.2
+ and below cannot handle user or group names longer than 8
+ characters in some interfaces. We use the normalize method to
+ provide a mapping to a username that fits, by using the form '_UID'
+ or '_GID'.
+
+ this only works if you can guarantee that the WB_AIX_ENCODED char
+ is not used as the first char of any other username
+*/
+static unsigned decode_id(const char *name)
+{
+ unsigned id;
+ sscanf(name+1, "%u", &id);
+ return id;
+}
+
+static struct passwd *wb_aix_getpwuid(uid_t uid);
+
+static char *decode_user(const char *name)
+{
+ struct passwd *pwd;
+ unsigned id;
+ char *ret;
+
+ sscanf(name+1, "%u", &id);
+ pwd = wb_aix_getpwuid(id);
+ if (!pwd) {
+ return NULL;
+ }
+ ret = strdup(pwd->pw_name);
+
+ free_pwd(pwd);
+
+ logit("decoded '%s' -> '%s'\n", name, ret);
+
+ return ret;
+}
+
+
+/*
+ fill a struct passwd from a winbindd_pw struct, allocating as a single block
+*/
+static struct passwd *fill_pwent(struct winbindd_pw *pw)
+{
+ struct passwd *result;
+
+ result = calloc(1, sizeof(struct passwd));
+ if (!result) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ result->pw_uid = pw->pw_uid;
+ result->pw_gid = pw->pw_gid;
+ result->pw_name = strdup(pw->pw_name);
+ result->pw_passwd = strdup(pw->pw_passwd);
+ result->pw_gecos = strdup(pw->pw_gecos);
+ result->pw_dir = strdup(pw->pw_dir);
+ result->pw_shell = strdup(pw->pw_shell);
+
+ return result;
+}
+
+
+/*
+ fill a struct group from a winbindd_pw struct, allocating as a single block
+*/
+static struct group *fill_grent(struct winbindd_gr *gr, char *gr_mem)
+{
+ int i;
+ struct group *result;
+ char *p, *name;
+
+ result = calloc(1, sizeof(struct group));
+ if (!result) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ result->gr_gid = gr->gr_gid;
+
+ result->gr_name = strdup(gr->gr_name);
+ result->gr_passwd = strdup(gr->gr_passwd);
+
+ /* Group membership */
+ if ((gr->num_gr_mem < 0) || !gr_mem) {
+ gr->num_gr_mem = 0;
+ }
+
+ if (gr->num_gr_mem == 0) {
+ /* Group is empty */
+ return result;
+ }
+
+ result->gr_mem = (char **)malloc(sizeof(char *) * (gr->num_gr_mem+1));
+ if (!result->gr_mem) {
+ free(result->gr_name);
+ free(result->gr_passwd);
+ free(result);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ /* Start looking at extra data */
+ i=0;
+ for (name = strtok_r(gr_mem, ",", &p);
+ name;
+ name = strtok_r(NULL, ",", &p)) {
+ if (i == gr->num_gr_mem) {
+ break;
+ }
+ result->gr_mem[i] = strdup(name);
+ i++;
+ }
+
+ /* Terminate list */
+ result->gr_mem[i] = NULL;
+
+ return result;
+}
+
+
+
+/* take a group id and return a filled struct group */
+static struct group *wb_aix_getgrgid(gid_t gid)
+{
+ struct winbindd_request request = {
+ .wb_flags = WBFLAG_FROM_NSS,
+ };
+ struct winbindd_response response = {
+ .length = 0,
+ };
+ struct group *grp;
+ NSS_STATUS ret;
+
+ logit("getgrgid %d\n", gid);
+
+ request.data.gid = gid;
+
+ ret = winbindd_request_response(NULL, WINBINDD_GETGRGID,
+ &request, &response);
+
+ logit("getgrgid ret=%d\n", ret);
+
+ HANDLE_ERRORS(ret);
+
+ grp = fill_grent(&response.data.gr, response.extra_data.data);
+
+ winbindd_free_response(&response);
+
+ return grp;
+}
+
+/* take a group name and return a filled struct group */
+static struct group *wb_aix_getgrnam(const char *name)
+{
+ struct winbindd_request request = {
+ .wb_flags = WBFLAG_FROM_NSS,
+ };
+ struct winbindd_response response = {
+ .length = 0,
+ };
+ NSS_STATUS ret;
+ struct group *grp;
+
+ if (*name == WB_AIX_ENCODED) {
+ return wb_aix_getgrgid(decode_id(name));
+ }
+
+ logit("getgrnam '%s'\n", name);
+
+ STRCPY_RETNULL(request.data.groupname, name);
+
+ ret = winbindd_request_response(NULL, WINBINDD_GETGRNAM,
+ &request, &response);
+
+ HANDLE_ERRORS(ret);
+
+ grp = fill_grent(&response.data.gr, response.extra_data.data);
+
+ winbindd_free_response(&response);
+
+ return grp;
+}
+
+
+/* this call doesn't have to fill in the gr_mem, but we do anyway
+ for simplicity */
+static struct group *wb_aix_getgracct(void *id, int type)
+{
+ if (type == 1) {
+ return wb_aix_getgrnam((char *)id);
+ }
+ if (type == 0) {
+ return wb_aix_getgrgid(*(int *)id);
+ }
+ errno = EINVAL;
+ return NULL;
+}
+
+
+/* take a username and return a string containing a comma-separated
+ list of group id numbers to which the user belongs */
+static char *wb_aix_getgrset(char *user)
+{
+ struct winbindd_request request = {
+ .wb_flags = WBFLAG_FROM_NSS,
+ };
+ struct winbindd_response response = {
+ .length = 0,
+ };
+ NSS_STATUS ret;
+ int i, idx;
+ char *tmpbuf;
+ int num_gids;
+ gid_t *gid_list;
+ char *r_user = user;
+
+ if (*user == WB_AIX_ENCODED) {
+ r_user = decode_user(r_user);
+ if (!r_user) {
+ errno = ENOENT;
+ return NULL;
+ }
+ }
+
+ logit("getgrset '%s'\n", r_user);
+
+ STRCPY_RETNULL(request.data.username, r_user);
+
+ if (*user == WB_AIX_ENCODED) {
+ free(r_user);
+ }
+
+ ret = winbindd_request_response(NULL, WINBINDD_GETGROUPS,
+ &request, &response);
+
+ HANDLE_ERRORS(ret);
+
+ num_gids = response.data.num_entries;
+ gid_list = (gid_t *)response.extra_data.data;
+
+ /* allocate a space large enough to contruct the string */
+ tmpbuf = malloc(num_gids*12);
+ if (!tmpbuf) {
+ return NULL;
+ }
+
+ for (idx=i=0; i < num_gids-1; i++) {
+ idx += sprintf(tmpbuf+idx, "%u,", gid_list[i]);
+ }
+ idx += sprintf(tmpbuf+idx, "%u", gid_list[i]);
+
+ winbindd_free_response(&response);
+
+ return tmpbuf;
+}
+
+
+/* take a uid and return a filled struct passwd */
+static struct passwd *wb_aix_getpwuid(uid_t uid)
+{
+ struct winbindd_request request = {
+ .wb_flags = WBFLAG_FROM_NSS,
+ };
+ struct winbindd_response response = {
+ .length = 0,
+ };
+ NSS_STATUS ret;
+ struct passwd *pwd;
+
+ logit("getpwuid '%d'\n", uid);
+
+ request.data.uid = uid;
+
+ ret = winbindd_request_response(NULL, WINBINDD_GETPWUID,
+ &request, &response);
+
+ HANDLE_ERRORS(ret);
+
+ pwd = fill_pwent(&response.data.pw);
+
+ winbindd_free_response(&response);
+
+ logit("getpwuid gave ptr %p\n", pwd);
+
+ return pwd;
+}
+
+
+/* take a username and return a filled struct passwd */
+static struct passwd *wb_aix_getpwnam(const char *name)
+{
+ struct winbindd_request request = {
+ .wb_flags = WBFLAG_FROM_NSS,
+ };
+ struct winbindd_response response = {
+ .length = 0,
+ };
+ NSS_STATUS ret;
+ struct passwd *pwd;
+
+ if (*name == WB_AIX_ENCODED) {
+ return wb_aix_getpwuid(decode_id(name));
+ }
+
+ logit("getpwnam '%s'\n", name);
+
+ STRCPY_RETNULL(request.data.username, name);
+
+ ret = winbindd_request_response(NULL, WINBINDD_GETPWNAM,
+ &request, &response);
+
+ HANDLE_ERRORS(ret);
+
+ pwd = fill_pwent(&response.data.pw);
+
+ winbindd_free_response(&response);
+
+ logit("getpwnam gave ptr %p\n", pwd);
+
+ return pwd;
+}
+
+/*
+ list users
+*/
+static int wb_aix_lsuser(char *attributes[], attrval_t results[], int size)
+{
+ NSS_STATUS ret;
+ struct winbindd_request request = {
+ .wb_flags = WBFLAG_FROM_NSS,
+ };
+ struct winbindd_response response = {
+ .length = 0,
+ };
+ int len;
+ char *s;
+
+ if (size != 1 || strcmp(attributes[0], S_USERS) != 0) {
+ logit("invalid lsuser op\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ ret = winbindd_request_response(NULL, WINBINDD_LIST_USERS,
+ &request, &response);
+ if (ret != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ len = strlen(response.extra_data.data);
+
+ s = malloc(len+2);
+ if (!s) {
+ winbindd_free_response(&response);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ memcpy(s, response.extra_data.data, len+1);
+
+ replace_commas(s);
+
+ results[0].attr_un.au_char = s;
+ results[0].attr_flag = 0;
+
+ winbindd_free_response(&response);
+
+ return 0;
+}
+
+
+/*
+ list groups
+*/
+static int wb_aix_lsgroup(char *attributes[], attrval_t results[], int size)
+{
+ NSS_STATUS ret;
+ struct winbindd_request request = {
+ .wb_flags = WBFLAG_FROM_NSS,
+ };
+ struct winbindd_response response = {
+ .length = 0,
+ };
+ int len;
+ char *s;
+
+ if (size != 1 || strcmp(attributes[0], S_GROUPS) != 0) {
+ logit("invalid lsgroup op\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ ret = winbindd_request_response(NULL, WINBINDD_LIST_GROUPS,
+ &request, &response);
+ if (ret != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ len = strlen(response.extra_data.data);
+
+ s = malloc(len+2);
+ if (!s) {
+ winbindd_free_response(&response);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ memcpy(s, response.extra_data.data, len+1);
+
+ replace_commas(s);
+
+ results[0].attr_un.au_char = s;
+ results[0].attr_flag = 0;
+
+ winbindd_free_response(&response);
+
+ return 0;
+}
+
+
+static attrval_t pwd_to_group(struct passwd *pwd)
+{
+ attrval_t r = {
+ .attr_flag = EINVAL,
+ };
+ struct group *grp = wb_aix_getgrgid(pwd->pw_gid);
+
+ if (grp != NULL) {
+ r.attr_flag = 0;
+ r.attr_un.au_char = strdup(grp->gr_name);
+ free_grp(grp);
+ }
+
+ return r;
+}
+
+static attrval_t pwd_to_groupsids(struct passwd *pwd)
+{
+ attrval_t r = {
+ .attr_flag = EINVAL,
+ };
+ char *s, *p;
+ size_t mlen;
+
+ if ( (s = wb_aix_getgrset(pwd->pw_name)) == NULL ) {
+ r.attr_flag = EINVAL;
+ return r;
+ }
+
+ mlen = strlen(s)+2;
+ if ( (p = malloc(mlen)) == NULL ) {
+ r.attr_flag = ENOMEM;
+ return r;
+ }
+
+ strncpy(p, s, mlen);
+ p[mlen-1] = '\0';
+ replace_commas(p);
+ free(s);
+
+ r.attr_flag = 0;
+ r.attr_un.au_char = p;
+
+ return r;
+}
+
+static attrval_t pwd_to_sid(struct passwd *pwd)
+{
+ char buf[(1 /* U/G */ + 10 /* 2^32 */ + 1 /* \n */) + 1] = { 0, };
+ int len;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ attrval_t r = {
+ .attr_flag = ENOENT,
+ };
+
+ len = snprintf(buf, sizeof(buf),
+ "U%"PRIu32"\n",
+ (uint32_t)pwd->pw_uid);
+ if (len >= sizeof(buf)) {
+ r = (attrval_t) {
+ .attr_flag = EINVAL,
+ };
+ return r;
+ }
+
+ request = (struct winbindd_request) {
+ .wb_flags = WBFLAG_FROM_NSS,
+ .extra_data.data = buf,
+ .extra_len = strlen(buf)+1,
+ };
+ response = (struct winbindd_response) {
+ .length = 0,
+ };
+
+ result = winbindd_request_response(NULL, WINBINDD_XIDS_TO_SIDS,
+ &request, &response);
+ if (result == NSS_STATUS_SUCCESS) {
+ r.attr_flag = 0;
+ r.attr_un.au_char = strdup(response.data.sid.sid);
+ }
+
+ return r;
+}
+
+static int wb_aix_user_attrib(const char *key, char *attributes[],
+ attrval_t results[], int size)
+{
+ struct passwd *pwd;
+ int i;
+
+ pwd = wb_aix_getpwnam(key);
+ if (!pwd) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ for (i=0;i<size;i++) {
+ results[i] = (attrval_t) {
+ .attr_flag = 0,
+ };
+
+ if (strcmp(attributes[i], S_ID) == 0) {
+ results[i].attr_un.au_int = pwd->pw_uid;
+#ifdef _AIXVERSION_530
+ } else if (strcmp(attributes[i], S_PGID) == 0) {
+ results[i].attr_un.au_int = pwd->pw_gid;
+#endif
+ } else if (strcmp(attributes[i], S_PWD) == 0) {
+ results[i].attr_un.au_char = strdup(pwd->pw_passwd);
+ } else if (strcmp(attributes[i], S_HOME) == 0) {
+ results[i].attr_un.au_char = strdup(pwd->pw_dir);
+ } else if (strcmp(attributes[i], S_SHELL) == 0) {
+ results[i].attr_un.au_char = strdup(pwd->pw_shell);
+ } else if (strcmp(attributes[i], S_REGISTRY) == 0) {
+ results[i].attr_un.au_char = strdup("WINBIND");
+ } else if (strcmp(attributes[i], S_GECOS) == 0) {
+ results[i].attr_un.au_char = strdup(pwd->pw_gecos);
+ } else if (strcmp(attributes[i], S_PGRP) == 0) {
+ results[i] = pwd_to_group(pwd);
+ } else if (strcmp(attributes[i], S_GROUPS) == 0) {
+ results[i] = pwd_to_groupsids(pwd);
+ } else if (strcmp(attributes[i], S_GROUPSIDS) == 0) {
+ results[i] = pwd_to_groupsids(pwd);
+ } else if (strcmp(attributes[i], "SID") == 0) {
+ results[i] = pwd_to_sid(pwd);
+ } else {
+ logit("Unknown user attribute '%s'\n", attributes[i]);
+ results[i].attr_flag = EINVAL;
+ }
+ }
+
+ free_pwd(pwd);
+
+ return 0;
+}
+
+static int wb_aix_group_attrib(const char *key, char *attributes[],
+ attrval_t results[], int size)
+{
+ struct group *grp;
+ int i;
+
+ grp = wb_aix_getgrnam(key);
+ if (!grp) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ for (i=0;i<size;i++) {
+ results[i].attr_flag = 0;
+
+ if (strcmp(attributes[i], S_PWD) == 0) {
+ results[i].attr_un.au_char = strdup(grp->gr_passwd);
+ } else if (strcmp(attributes[i], S_ID) == 0) {
+ results[i].attr_un.au_int = grp->gr_gid;
+ } else {
+ logit("Unknown group attribute '%s'\n", attributes[i]);
+ results[i].attr_flag = EINVAL;
+ }
+ }
+
+ free_grp(grp);
+
+ return 0;
+}
+
+
+/*
+ called for user/group enumerations
+*/
+static int wb_aix_getentry(char *key, char *table, char *attributes[],
+ attrval_t results[], int size)
+{
+ logit("Got getentry with key='%s' table='%s' size=%d attributes[0]='%s'\n",
+ key, table, size, attributes[0]);
+
+ if (strcmp(key, "ALL") == 0 &&
+ strcmp(table, "user") == 0) {
+ return wb_aix_lsuser(attributes, results, size);
+ }
+
+ if (strcmp(key, "ALL") == 0 &&
+ strcmp(table, "group") == 0) {
+ return wb_aix_lsgroup(attributes, results, size);
+ }
+
+ if (strcmp(table, "user") == 0) {
+ return wb_aix_user_attrib(key, attributes, results, size);
+ }
+
+ if (strcmp(table, "group") == 0) {
+ return wb_aix_group_attrib(key, attributes, results, size);
+ }
+
+ logit("Unknown getentry operation key='%s' table='%s'\n", key, table);
+
+ errno = ENOSYS;
+ return -1;
+}
+
+
+
+/*
+ called to start the backend
+*/
+static void *wb_aix_open(const char *name, const char *domain, int mode, char *options)
+{
+ if (strstr(options, "debug")) {
+ debug_enabled = 1;
+ }
+ logit("open name='%s' mode=%d domain='%s' options='%s'\n", name, domain,
+ mode, options);
+ return NULL;
+}
+
+static void wb_aix_close(void *token)
+{
+ logit("close\n");
+ return;
+}
+
+#ifdef HAVE_STRUCT_SECMETHOD_TABLE_METHOD_ATTRLIST
+/*
+ return a list of additional attributes supported by the backend
+*/
+static attrlist_t **wb_aix_attrlist(void)
+{
+ /* pretty confusing but we are allocating the array of pointers
+ and the structures we'll be pointing to all at once. So
+ you need N+1 pointers and N structures. */
+
+ attrlist_t **ret = NULL;
+ attrlist_t *offset = NULL;
+ int i;
+ int n;
+ size_t size;
+
+ struct attr_types {
+ const char *name;
+ int flags;
+ int type;
+ } attr_list[] = {
+ /* user attributes */
+ {S_ID, AL_USERATTR, SEC_INT},
+ {S_PGRP, AL_USERATTR, SEC_CHAR},
+ {S_HOME, AL_USERATTR, SEC_CHAR},
+ {S_SHELL, AL_USERATTR, SEC_CHAR},
+#ifdef _AIXVERSION_530
+ {S_PGID, AL_USERATTR, SEC_INT},
+#endif
+ {S_GECOS, AL_USERATTR, SEC_CHAR},
+ {S_SHELL, AL_USERATTR, SEC_CHAR},
+ {S_PGRP, AL_USERATTR, SEC_CHAR},
+ {S_GROUPS, AL_USERATTR, SEC_LIST},
+ {S_GROUPSIDS, AL_USERATTR, SEC_LIST},
+ {"SID", AL_USERATTR, SEC_CHAR},
+
+ /* group attributes */
+ {S_ID, AL_GROUPATTR, SEC_INT}
+ };
+
+ logit("method attrlist called\n");
+
+ n = sizeof(attr_list) / sizeof(struct attr_types);
+ size = ((n + 1) * sizeof(attrlist_t *));
+
+ if ( (ret = malloc( size )) == NULL ) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ /* offset to where the structures start in the buffer */
+
+ offset = (attrlist_t *)(ret + n);
+
+ /* now loop over the user_attr_list[] array and add
+ all the members */
+
+ for ( i=0; i<n; i++ ) {
+ attrlist_t *a = malloc(sizeof(attrlist_t));
+
+ if ( !a ) {
+ /* this is bad. Just bail */
+ return NULL;
+ }
+
+ a->al_name = strdup(attr_list[i].name);
+ a->al_flags = attr_list[i].flags;
+ a->al_type = attr_list[i].type;
+
+ ret[i] = a;
+ }
+ ret[n] = NULL;
+
+ return ret;
+}
+#endif
+
+
+/*
+ turn a long username into a short one. Needed to cope with the 8 char
+ username limit in AIX 5.2 and below
+*/
+static int wb_aix_normalize(char *longname, char *shortname)
+{
+ struct passwd *pwd;
+
+ logit("normalize '%s'\n", longname);
+
+ /* automatically cope with AIX 5.3 with longer usernames
+ when it comes out */
+ if (S_NAMELEN > strlen(longname)) {
+ strncpy(shortname, longname, S_NAMELEN);
+ shortname[S_NAMELEN-1] = '\0';
+ return 1;
+ }
+
+ pwd = wb_aix_getpwnam(longname);
+ if (!pwd) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ sprintf(shortname, "%c%07u", WB_AIX_ENCODED, pwd->pw_uid);
+
+ free_pwd(pwd);
+
+ return 1;
+}
+
+
+/*
+ authenticate a user
+ */
+static int wb_aix_authenticate(char *user, char *pass,
+ int *reenter, char **message)
+{
+ struct winbindd_request request = {
+ .wb_flags = WBFLAG_FROM_NSS,
+ };
+ struct winbindd_response response = {
+ .length = 0,
+ };
+ NSS_STATUS result;
+ char *r_user = user;
+
+ logit("authenticate '%s' response='%s'\n", user, pass);
+
+ *reenter = 0;
+ *message = NULL;
+
+ /* Send off request */
+ if (*user == WB_AIX_ENCODED) {
+ r_user = decode_user(r_user);
+ if (!r_user) {
+ return AUTH_NOTFOUND;
+ }
+ }
+
+ STRCPY_RET(request.data.auth.user, r_user);
+ STRCPY_RET(request.data.auth.pass, pass);
+
+ if (*user == WB_AIX_ENCODED) {
+ free(r_user);
+ }
+
+ result = winbindd_request_response(NULL, WINBINDD_PAM_AUTH,
+ &request, &response);
+
+ winbindd_free_response(&response);
+
+ logit("auth result %d for '%s'\n", result, user);
+
+ if (result == NSS_STATUS_SUCCESS) {
+ errno = 0;
+ return AUTH_SUCCESS;
+ }
+
+ return AUTH_FAILURE;
+}
+
+
+/*
+ change a user password
+*/
+static int wb_aix_chpass(char *user, char *oldpass, char *newpass, char **message)
+{
+ struct winbindd_request request = {
+ .wb_flags = WBFLAG_FROM_NSS,
+ };
+ struct winbindd_response response = {
+ .length = 0,
+ };
+ NSS_STATUS result;
+ char *r_user = user;
+
+ if (*user == WB_AIX_ENCODED) {
+ r_user = decode_user(r_user);
+ if (!r_user) {
+ errno = ENOENT;
+ return -1;
+ }
+ }
+
+ logit("chpass '%s' old='%s' new='%s'\n", r_user, oldpass, newpass);
+
+ *message = NULL;
+
+ /* Send off request */
+ STRCPY_RET(request.data.chauthtok.user, r_user);
+ STRCPY_RET(request.data.chauthtok.oldpass, oldpass);
+ STRCPY_RET(request.data.chauthtok.newpass, newpass);
+
+ if (*user == WB_AIX_ENCODED) {
+ free(r_user);
+ }
+
+ result = winbindd_request_response(NULL, WINBINDD_PAM_CHAUTHTOK,
+ &request, &response);
+
+ winbindd_free_response(&response);
+
+ if (result == NSS_STATUS_SUCCESS) {
+ errno = 0;
+ return 0;
+ }
+
+ errno = EINVAL;
+ return -1;
+}
+
+/*
+ don't do any password strength testing for now
+*/
+static int wb_aix_passwdrestrictions(char *user, char *newpass, char *oldpass,
+ char **message)
+{
+ logit("passwdresrictions called for '%s'\n", user);
+ return 0;
+}
+
+
+static int wb_aix_passwdexpired(char *user, char **message)
+{
+ logit("passwdexpired '%s'\n", user);
+ /* we should check the account bits here */
+ return 0;
+}
+
+
+/*
+ we can't return a crypt() password
+*/
+static char *wb_aix_getpasswd(char *user)
+{
+ logit("getpasswd '%s'\n", user);
+ errno = ENOSYS;
+ return NULL;
+}
+
+/*
+ this is called to update things like the last login time. We don't
+ currently pass this onto the DC
+*/
+static int wb_aix_putentry(char *key, char *table, char *attributes[],
+ attrval_t values[], int size)
+{
+ logit("putentry key='%s' table='%s' attrib='%s'\n",
+ key, table, size>=1?attributes[0]:"<null>");
+ errno = ENOSYS;
+ return -1;
+}
+
+static int wb_aix_commit(char *key, char *table)
+{
+ logit("commit key='%s' table='%s'\n");
+ errno = ENOSYS;
+ return -1;
+}
+
+static int wb_aix_getgrusers(char *group, void *result, int type, int *size)
+{
+ logit("getgrusers group='%s'\n", group);
+ errno = ENOSYS;
+ return -1;
+}
+
+
+#define DECL_METHOD(x) \
+int method_ ## x(void) \
+{ \
+ logit("UNIMPLEMENTED METHOD '%s'\n", #x); \
+ errno = EINVAL; \
+ return -1; \
+}
+
+#if LOG_UNIMPLEMENTED_CALLS
+DECL_METHOD(delgroup);
+DECL_METHOD(deluser);
+DECL_METHOD(newgroup);
+DECL_METHOD(newuser);
+DECL_METHOD(putgrent);
+DECL_METHOD(putgrusers);
+DECL_METHOD(putpwent);
+DECL_METHOD(lock);
+DECL_METHOD(unlock);
+DECL_METHOD(getcred);
+DECL_METHOD(setcred);
+DECL_METHOD(deletecred);
+#endif
+
+int wb_aix_init(struct secmethod_table *methods)
+{
+ ZERO_STRUCTP(methods);
+
+#ifdef HAVE_STRUCT_SECMETHOD_TABLE_METHOD_VERSION
+ methods->method_version = SECMETHOD_VERSION_520;
+#endif
+
+ methods->method_getgrgid = wb_aix_getgrgid;
+ methods->method_getgrnam = wb_aix_getgrnam;
+ methods->method_getgrset = wb_aix_getgrset;
+ methods->method_getpwnam = wb_aix_getpwnam;
+ methods->method_getpwuid = wb_aix_getpwuid;
+ methods->method_getentry = wb_aix_getentry;
+ methods->method_open = wb_aix_open;
+ methods->method_close = wb_aix_close;
+ methods->method_normalize = wb_aix_normalize;
+ methods->method_passwdexpired = wb_aix_passwdexpired;
+ methods->method_putentry = wb_aix_putentry;
+ methods->method_getpasswd = wb_aix_getpasswd;
+ methods->method_authenticate = wb_aix_authenticate;
+ methods->method_commit = wb_aix_commit;
+ methods->method_chpass = wb_aix_chpass;
+ methods->method_passwdrestrictions = wb_aix_passwdrestrictions;
+ methods->method_getgracct = wb_aix_getgracct;
+ methods->method_getgrusers = wb_aix_getgrusers;
+#ifdef HAVE_STRUCT_SECMETHOD_TABLE_METHOD_ATTRLIST
+ methods->method_attrlist = wb_aix_attrlist;
+#endif
+
+#if LOG_UNIMPLEMENTED_CALLS
+ methods->method_delgroup = method_delgroup;
+ methods->method_deluser = method_deluser;
+ methods->method_newgroup = method_newgroup;
+ methods->method_newuser = method_newuser;
+ methods->method_putgrent = method_putgrent;
+ methods->method_putgrusers = method_putgrusers;
+ methods->method_putpwent = method_putpwent;
+ methods->method_lock = method_lock;
+ methods->method_unlock = method_unlock;
+ methods->method_getcred = method_getcred;
+ methods->method_setcred = method_setcred;
+ methods->method_deletecred = method_deletecred;
+#endif
+
+ return AUTH_SUCCESS;
+}
diff --git a/nsswitch/winbind_nss_config.h b/nsswitch/winbind_nss_config.h
new file mode 100644
index 0000000..fb3299d
--- /dev/null
+++ b/nsswitch/winbind_nss_config.h
@@ -0,0 +1,67 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _WINBIND_NSS_CONFIG_H
+#define _WINBIND_NSS_CONFIG_H
+
+/* shutup the compiler warnings due to krb5.h on 64-bit sles9 */
+#ifdef SIZEOF_LONG
+#undef SIZEOF_LONG
+#endif
+
+/* Include header files from data in config.h file */
+
+#ifndef NO_CONFIG_H
+#include "../replace/replace.h"
+#endif
+
+#include "system/filesys.h"
+#include "system/network.h"
+#include "system/passwd.h"
+
+#include "nsswitch/winbind_nss.h"
+
+/* I'm trying really hard not to include anything from smb.h with the
+ result of some silly looking redeclaration of structures. */
+
+#ifndef FSTRING_LEN
+#define FSTRING_LEN 256
+typedef char fstring[FSTRING_LEN];
+#ifndef fstrcpy
+#define fstrcpy(d,s) \
+do { \
+ const char *_fstrcpy_src = (const char *)(s); \
+ strlcpy((d),_fstrcpy_src ? _fstrcpy_src : "",sizeof(fstring)); \
+} while (0)
+#endif
+#endif
+
+/* Some systems (SCO) treat UNIX domain sockets as FIFOs */
+
+#ifndef S_IFSOCK
+#define S_IFSOCK S_IFIFO
+#endif
+
+#ifndef S_ISSOCK
+#define S_ISSOCK(mode) ((mode & S_IFSOCK) == S_IFSOCK)
+#endif
+
+#endif
diff --git a/nsswitch/winbind_nss_freebsd.c b/nsswitch/winbind_nss_freebsd.c
new file mode 100644
index 0000000..d3f5489
--- /dev/null
+++ b/nsswitch/winbind_nss_freebsd.c
@@ -0,0 +1,137 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ AIX loadable authentication module, providing identification
+ routines against Samba winbind/Windows NT Domain
+
+ Copyright (C) Aaron Collins 2003
+ Copyright (C) Timur I. Bakeyev 2013
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "winbind_client.h"
+
+/* Make sure that the module gets registered needed by freebsd 5.1 */
+ns_mtab *nss_module_register(const char *, unsigned int *, nss_module_unregister_fn *);
+
+NSS_METHOD_PROTOTYPE(__nss_compat_getgrnam_r);
+NSS_METHOD_PROTOTYPE(__nss_compat_getgrgid_r);
+NSS_METHOD_PROTOTYPE(__nss_compat_getgrent_r);
+NSS_METHOD_PROTOTYPE(__nss_compat_setgrent);
+NSS_METHOD_PROTOTYPE(__nss_compat_endgrent);
+
+NSS_METHOD_PROTOTYPE(__nss_compat_getpwnam_r);
+NSS_METHOD_PROTOTYPE(__nss_compat_getpwuid_r);
+NSS_METHOD_PROTOTYPE(__nss_compat_getpwent_r);
+NSS_METHOD_PROTOTYPE(__nss_compat_setpwent);
+NSS_METHOD_PROTOTYPE(__nss_compat_endpwent);
+NSS_METHOD_PROTOTYPE(__nss_compat_endpwent);
+
+static NSS_METHOD_PROTOTYPE(__freebsd_getgroupmembership);
+
+static ns_mtab methods[] = {
+{ NSDB_GROUP, "getgrnam_r", __nss_compat_getgrnam_r, _nss_winbind_getgrnam_r },
+{ NSDB_GROUP, "getgrgid_r", __nss_compat_getgrgid_r, _nss_winbind_getgrgid_r },
+{ NSDB_GROUP, "getgrent_r", __nss_compat_getgrent_r, _nss_winbind_getgrent_r },
+{ NSDB_GROUP, "setgrent", __nss_compat_setgrent, _nss_winbind_setgrent },
+{ NSDB_GROUP, "endgrent", __nss_compat_endgrent, _nss_winbind_endgrent },
+{ NSDB_GROUP, "getgroupmembership", __freebsd_getgroupmembership, NULL },
+
+{ NSDB_PASSWD, "getpwnam_r", __nss_compat_getpwnam_r, _nss_winbind_getpwnam_r },
+{ NSDB_PASSWD, "getpwuid_r", __nss_compat_getpwuid_r, _nss_winbind_getpwuid_r },
+{ NSDB_PASSWD, "getpwent_r", __nss_compat_getpwent_r, _nss_winbind_getpwent_r },
+{ NSDB_PASSWD, "setpwent", __nss_compat_setpwent, _nss_winbind_setpwent },
+{ NSDB_PASSWD, "endpwent", __nss_compat_endpwent, _nss_winbind_endpwent },
+
+};
+
+/* Taken from libc */
+static int
+gr_addgid(gid_t gid, gid_t *groups, int maxgrp, int *grpcnt)
+{
+ int ret, dupc;
+
+ /* skip duplicates */
+ for (dupc = 0; dupc < MIN(maxgrp, *grpcnt); dupc++) {
+ if (groups[dupc] == gid)
+ return 1;
+ }
+
+ ret = 1;
+ if (*grpcnt < maxgrp) /* add this gid */
+ groups[*grpcnt] = gid;
+ else
+ ret = 0;
+
+ (*grpcnt)++;
+
+ return ret;
+}
+
+/*
+ rv = _nsdispatch(NULL, dtab, NSDB_GROUP, "getgroupmembership",
+ defaultsrc, uname, agroup, groups, maxgrp, grpcnt);
+*/
+
+static int
+__freebsd_getgroupmembership(void *retval, void *mdata, va_list ap)
+{
+ const char *uname = va_arg(ap, const char *);
+ gid_t group = va_arg(ap, gid_t);
+ gid_t *groups = va_arg(ap, gid_t *);
+ int maxgrp = va_arg(ap, int);
+ int *groupc = va_arg(ap, int *);
+
+ NSS_STATUS ret;
+ long int lcount, lsize;
+ int i, errnop;
+ gid_t *tmpgroups;
+
+ /* Can be realloc() inside _nss_winbind_initgroups_dyn() */
+ if ((tmpgroups=calloc(maxgrp, sizeof(gid_t))) == NULL) {
+ errno = ENOMEM;
+ return NS_TRYAGAIN;
+ }
+
+ lcount = 0;
+ lsize = maxgrp;
+ /* insert primary membership(possibly already there) */
+ gr_addgid(group, groups, maxgrp, groupc);
+ /* Don't limit number of groups, we want to know total size */
+ ret = _nss_winbind_initgroups_dyn(discard_const(uname),
+ group,
+ &lcount,
+ &lsize,
+ &tmpgroups,
+ 0,
+ &errnop);
+ if (ret == NSS_STATUS_SUCCESS) {
+ /* lcount potentially can be bigger than maxgrp, so would groupc */
+ for (i = 0; i < lcount; i++)
+ gr_addgid(tmpgroups[i], groups, maxgrp, groupc);
+ }
+ free(tmpgroups);
+ /* Let following nsswitch backend(s) add more groups(?) */
+ return NSS_STATUS_NOTFOUND;
+}
+
+_PUBLIC_ ns_mtab *
+nss_module_register(const char *source, unsigned int *mtabsize,
+ nss_module_unregister_fn *unreg)
+{
+ *mtabsize = sizeof(methods)/sizeof(methods[0]);
+ *unreg = NULL;
+ return (methods);
+}
diff --git a/nsswitch/winbind_nss_hpux.h b/nsswitch/winbind_nss_hpux.h
new file mode 100644
index 0000000..dba70a7
--- /dev/null
+++ b/nsswitch/winbind_nss_hpux.h
@@ -0,0 +1,142 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Donated by HP to enable Winbindd to build on HPUX 11.x.
+ Copyright (C) Jeremy Allison 2002.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _WINBIND_NSS_HPUX_H
+#define _WINBIND_NSS_HPUX_H
+
+#include <nsswitch.h>
+
+#define NSS_STATUS_SUCCESS NSS_SUCCESS
+#define NSS_STATUS_NOTFOUND NSS_NOTFOUND
+#define NSS_STATUS_UNAVAIL NSS_UNAVAIL
+#define NSS_STATUS_TRYAGAIN NSS_TRYAGAIN
+
+#ifdef HAVE_SYNCH_H
+#include <synch.h>
+#endif
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+
+typedef enum {
+ NSS_SUCCESS,
+ NSS_NOTFOUND,
+ NSS_UNAVAIL,
+ NSS_TRYAGAIN
+} nss_status_t;
+
+typedef nss_status_t NSS_STATUS;
+
+struct nss_backend;
+
+typedef nss_status_t (*nss_backend_op_t)(struct nss_backend *, void *args);
+
+struct nss_backend {
+ nss_backend_op_t *ops;
+ int n_ops;
+};
+typedef struct nss_backend nss_backend_t;
+typedef int nss_dbop_t;
+
+#include <errno.h>
+#include <netdb.h>
+#include <limits.h>
+
+#ifndef NSS_INCLUDE_UNSAFE
+#define NSS_INCLUDE_UNSAFE 1 /* Build old, MT-unsafe interfaces, */
+#endif /* NSS_INCLUDE_UNSAFE */
+
+enum nss_netgr_argn {
+ NSS_NETGR_MACHINE,
+ NSS_NETGR_USER,
+ NSS_NETGR_DOMAIN,
+ NSS_NETGR_N
+};
+
+enum nss_netgr_status {
+ NSS_NETGR_FOUND,
+ NSS_NETGR_NO,
+ NSS_NETGR_NOMEM
+};
+
+typedef unsigned nss_innetgr_argc;
+typedef char **nss_innetgr_argv;
+
+struct nss_innetgr_1arg {
+ nss_innetgr_argc argc;
+ nss_innetgr_argv argv;
+};
+
+typedef struct {
+ void *result; /* "result" parameter to getXbyY_r() */
+ char *buffer; /* "buffer" " " */
+ int buflen; /* "buflen" " " */
+} nss_XbyY_buf_t;
+
+extern nss_XbyY_buf_t *_nss_XbyY_buf_alloc(int struct_size, int buffer_size);
+extern void _nss_XbyY_buf_free(nss_XbyY_buf_t *);
+
+union nss_XbyY_key {
+ uid_t uid;
+ gid_t gid;
+ const char *name;
+ int number;
+ struct {
+ long net;
+ int type;
+ } netaddr;
+ struct {
+ const char *addr;
+ int len;
+ int type;
+ } hostaddr;
+ struct {
+ union {
+ const char *name;
+ int port;
+ } serv;
+ const char *proto;
+ } serv;
+ void *ether;
+};
+
+typedef struct nss_XbyY_args {
+ nss_XbyY_buf_t buf;
+ int stayopen;
+ /*
+ * Support for setXXXent(stayopen)
+ * Used only in hosts, protocols,
+ * networks, rpc, and services.
+ */
+ int (*str2ent)(const char *instr, int instr_len, void *ent, char *buffer, int buflen);
+ union nss_XbyY_key key;
+
+ void *returnval;
+ int erange;
+ /*
+ * h_errno is defined as function call macro for multithreaded applications
+ * in HP-UX. *this* h_errno is not used in the HP-UX codepath of our nss
+ * modules, so let's simply rename it:
+ */
+ int h_errno_unused;
+ nss_status_t status;
+} nss_XbyY_args_t;
+
+#endif /* _WINBIND_NSS_HPUX_H */
diff --git a/nsswitch/winbind_nss_linux.c b/nsswitch/winbind_nss_linux.c
new file mode 100644
index 0000000..1d647ca
--- /dev/null
+++ b/nsswitch/winbind_nss_linux.c
@@ -0,0 +1,1178 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Windows NT Domain nsswitch module
+
+ Copyright (C) Tim Potter 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "winbind_client.h"
+
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+
+#ifdef HAVE_PTHREAD
+static pthread_mutex_t winbind_nss_mutex = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+/* Maximum number of users to pass back over the unix domain socket
+ per call. This is not a static limit on the total number of users
+ or groups returned in total. */
+
+#define MAX_GETPWENT_USERS 250
+#define MAX_GETGRENT_USERS 250
+
+/*************************************************************************
+ ************************************************************************/
+
+#ifdef DEBUG_NSS
+static const char *nss_err_str(NSS_STATUS ret)
+{
+ switch (ret) {
+ case NSS_STATUS_TRYAGAIN:
+ return "NSS_STATUS_TRYAGAIN";
+ case NSS_STATUS_SUCCESS:
+ return "NSS_STATUS_SUCCESS";
+ case NSS_STATUS_NOTFOUND:
+ return "NSS_STATUS_NOTFOUND";
+ case NSS_STATUS_UNAVAIL:
+ return "NSS_STATUS_UNAVAIL";
+#ifdef NSS_STATUS_RETURN
+ case NSS_STATUS_RETURN:
+ return "NSS_STATUS_RETURN";
+#endif
+ default:
+ return "UNKNOWN RETURN CODE!!!!!!!";
+ }
+}
+#endif
+
+/* Prototypes from wb_common.c */
+
+/* Allocate some space from the nss static buffer. The buffer and buflen
+ are the pointers passed in by the C library to the _nss_ntdom_*
+ functions. */
+
+static char *get_static(char **buffer, size_t *buflen, size_t len)
+{
+ char *result;
+
+ /* Error check. We return false if things aren't set up right, or
+ there isn't enough buffer space left. */
+
+ if ((buffer == NULL) || (buflen == NULL) || (*buflen < len)) {
+ return NULL;
+ }
+
+ /* Return an index into the static buffer */
+
+ result = *buffer;
+ *buffer += len;
+ *buflen -= len;
+
+ return result;
+}
+
+/* I've copied the strtok() replacement function next_token_Xalloc() from
+ lib/util_str.c as I really don't want to have to link in any other
+ objects if I can possibly avoid it. */
+
+static bool next_token_alloc(const char **ptr,
+ char **pp_buff,
+ const char *sep)
+{
+ const char *s;
+ const char *saved_s;
+ char *pbuf;
+ bool quoted;
+ size_t len=1;
+
+ *pp_buff = NULL;
+ if (!ptr) {
+ return(false);
+ }
+
+ s = *ptr;
+
+ /* default to simple separators */
+ if (!sep) {
+ sep = " \t\n\r";
+ }
+
+ /* find the first non sep char */
+ while (*s && strchr(sep,*s)) {
+ s++;
+ }
+
+ /* nothing left? */
+ if (!*s) {
+ return false;
+ }
+
+ /* When restarting we need to go from here. */
+ saved_s = s;
+
+ /* Work out the length needed. */
+ for (quoted = false; *s &&
+ (quoted || !strchr(sep,*s)); s++) {
+ if (*s == '\"') {
+ quoted = !quoted;
+ } else {
+ len++;
+ }
+ }
+
+ /* We started with len = 1 so we have space for the nul. */
+ *pp_buff = (char *)malloc(len);
+ if (!*pp_buff) {
+ return false;
+ }
+
+ /* copy over the token */
+ pbuf = *pp_buff;
+ s = saved_s;
+ for (quoted = false; *s &&
+ (quoted || !strchr(sep,*s)); s++) {
+ if ( *s == '\"' ) {
+ quoted = !quoted;
+ } else {
+ *pbuf++ = *s;
+ }
+ }
+
+ *ptr = (*s) ? s+1 : s;
+ *pbuf = 0;
+
+ return true;
+}
+
+/* Fill a pwent structure from a winbindd_response structure. We use
+ the static data passed to us by libc to put strings and stuff in.
+ Return NSS_STATUS_TRYAGAIN if we run out of memory. */
+
+static NSS_STATUS fill_pwent(struct passwd *result,
+ struct winbindd_pw *pw,
+ char **buffer, size_t *buflen)
+{
+ size_t len;
+
+ /* User name */
+ len = strlen(pw->pw_name) + 1;
+
+ if ((result->pw_name =
+ get_static(buffer, buflen, len)) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ memcpy(result->pw_name, pw->pw_name, len);
+
+ /* Password */
+ len = strlen(pw->pw_passwd) + 1;
+
+ if ((result->pw_passwd =
+ get_static(buffer, buflen, len)) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ memcpy(result->pw_passwd, pw->pw_passwd, len);
+
+ /* [ug]id */
+
+ result->pw_uid = pw->pw_uid;
+ result->pw_gid = pw->pw_gid;
+
+ /* GECOS */
+ len = strlen(pw->pw_gecos) + 1;
+
+ if ((result->pw_gecos =
+ get_static(buffer, buflen, len)) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ memcpy(result->pw_gecos, pw->pw_gecos, len);
+
+ /* Home directory */
+ len = strlen(pw->pw_dir) + 1;
+
+ if ((result->pw_dir =
+ get_static(buffer, buflen, len)) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ memcpy(result->pw_dir, pw->pw_dir, len);
+
+ /* Logon shell */
+ len = strlen(pw->pw_shell) + 1;
+
+ if ((result->pw_shell =
+ get_static(buffer, buflen, len)) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ memcpy(result->pw_shell, pw->pw_shell, len);
+
+ /* The struct passwd for Solaris has some extra fields which must
+ be initialised or nscd crashes. */
+
+#ifdef HAVE_PASSWD_PW_COMMENT
+ result->pw_comment = "";
+#endif
+
+#ifdef HAVE_PASSWD_PW_AGE
+ result->pw_age = "";
+#endif
+
+ return NSS_STATUS_SUCCESS;
+}
+
+/* Fill a grent structure from a winbindd_response structure. We use
+ the static data passed to us by libc to put strings and stuff in.
+ Return NSS_STATUS_TRYAGAIN if we run out of memory. */
+
+static NSS_STATUS fill_grent(struct group *result, struct winbindd_gr *gr,
+ const char *gr_mem, char **buffer, size_t *buflen)
+{
+ char *name;
+ int i;
+ char *tst;
+ size_t len;
+
+ /* Group name */
+ len = strlen(gr->gr_name) + 1;
+
+ if ((result->gr_name =
+ get_static(buffer, buflen, len)) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ memcpy(result->gr_name, gr->gr_name, len);
+
+ /* Password */
+ len = strlen(gr->gr_passwd) + 1;
+
+ if ((result->gr_passwd =
+ get_static(buffer, buflen, len)) == NULL) {
+
+ /* Out of memory */
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ memcpy(result->gr_passwd, gr->gr_passwd, len);
+
+ /* gid */
+
+ result->gr_gid = gr->gr_gid;
+
+ /* Group membership */
+
+ if (!gr_mem) {
+ gr->num_gr_mem = 0;
+ }
+
+ /* this next value is a pointer to a pointer so let's align it */
+
+ /* Calculate number of extra bytes needed to align on pointer size boundry */
+ if ((i = (unsigned long)(*buffer) % sizeof(char*)) != 0)
+ i = sizeof(char*) - i;
+
+ if ((tst = get_static(buffer, buflen, ((gr->num_gr_mem + 1) *
+ sizeof(char *)+i))) == NULL) {
+
+ /* Out of memory */
+
+ return NSS_STATUS_TRYAGAIN;
+ }
+ result->gr_mem = (char **)(tst + i);
+
+ if (gr->num_gr_mem == 0) {
+
+ /* Group is empty */
+
+ *(result->gr_mem) = NULL;
+ return NSS_STATUS_SUCCESS;
+ }
+
+ /* Start looking at extra data */
+
+ i = 0;
+
+ while(next_token_alloc((const char **)&gr_mem, &name, ",")) {
+ /* Allocate space for member */
+ len = strlen(name) + 1;
+
+ if (((result->gr_mem)[i] =
+ get_static(buffer, buflen, len)) == NULL) {
+ free(name);
+ /* Out of memory */
+ return NSS_STATUS_TRYAGAIN;
+ }
+ memcpy((result->gr_mem)[i], name, len);
+ free(name);
+ i++;
+ }
+
+ /* Terminate list */
+
+ (result->gr_mem)[i] = NULL;
+
+ return NSS_STATUS_SUCCESS;
+}
+
+/*
+ * NSS user functions
+ */
+
+static struct winbindd_response getpwent_response;
+
+static int ndx_pw_cache; /* Current index into pwd cache */
+static int num_pw_cache; /* Current size of pwd cache */
+
+/* Rewind "file pointer" to start of ntdom password database */
+
+_PUBLIC_ON_LINUX_
+NSS_STATUS
+_nss_winbind_setpwent(void)
+{
+ NSS_STATUS ret;
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: setpwent\n", getpid());
+#endif
+
+#ifdef HAVE_PTHREAD
+ pthread_mutex_lock(&winbind_nss_mutex);
+#endif
+
+ if (num_pw_cache > 0) {
+ ndx_pw_cache = num_pw_cache = 0;
+ winbindd_free_response(&getpwent_response);
+ }
+
+ winbind_set_client_name("nss_winbind");
+ ret = winbindd_request_response(NULL, WINBINDD_SETPWENT, NULL, NULL);
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: setpwent returns %s (%d)\n", getpid(),
+ nss_err_str(ret), ret);
+#endif
+
+#ifdef HAVE_PTHREAD
+ pthread_mutex_unlock(&winbind_nss_mutex);
+#endif
+ return ret;
+}
+
+/* Close ntdom password database "file pointer" */
+
+_PUBLIC_ON_LINUX_
+NSS_STATUS
+_nss_winbind_endpwent(void)
+{
+ NSS_STATUS ret;
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: endpwent\n", getpid());
+#endif
+
+#ifdef HAVE_PTHREAD
+ pthread_mutex_lock(&winbind_nss_mutex);
+#endif
+
+ if (num_pw_cache > 0) {
+ ndx_pw_cache = num_pw_cache = 0;
+ winbindd_free_response(&getpwent_response);
+ }
+
+ winbind_set_client_name("nss_winbind");
+ ret = winbindd_request_response(NULL, WINBINDD_ENDPWENT, NULL, NULL);
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: endpwent returns %s (%d)\n", getpid(),
+ nss_err_str(ret), ret);
+#endif
+
+#ifdef HAVE_PTHREAD
+ pthread_mutex_unlock(&winbind_nss_mutex);
+#endif
+
+ return ret;
+}
+
+/* Fetch the next password entry from ntdom password database */
+
+_PUBLIC_ON_LINUX_
+NSS_STATUS
+_nss_winbind_getpwent_r(struct passwd *result, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ struct winbindd_request request;
+ static int called_again;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getpwent\n", getpid());
+#endif
+
+#ifdef HAVE_PTHREAD
+ pthread_mutex_lock(&winbind_nss_mutex);
+#endif
+
+ /* Return an entry from the cache if we have one, or if we are
+ called again because we exceeded our static buffer. */
+
+ if ((ndx_pw_cache < num_pw_cache) || called_again) {
+ goto return_result;
+ }
+
+ /* Else call winbindd to get a bunch of entries */
+
+ if (num_pw_cache > 0) {
+ winbindd_free_response(&getpwent_response);
+ }
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(getpwent_response);
+
+ request.data.num_entries = MAX_GETPWENT_USERS;
+
+ winbind_set_client_name("nss_winbind");
+ ret = winbindd_request_response(NULL, WINBINDD_GETPWENT, &request,
+ &getpwent_response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+ struct winbindd_pw *pw_cache;
+
+ /* Fill cache */
+
+ ndx_pw_cache = 0;
+ num_pw_cache = getpwent_response.data.num_entries;
+
+ /* Return a result */
+
+ return_result:
+
+ pw_cache = (struct winbindd_pw *)
+ getpwent_response.extra_data.data;
+
+ /* Check data is valid */
+
+ if (pw_cache == NULL) {
+ ret = NSS_STATUS_NOTFOUND;
+ goto done;
+ }
+
+ ret = fill_pwent(result, &pw_cache[ndx_pw_cache],
+ &buffer, &buflen);
+
+ /* Out of memory - try again */
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ called_again = true;
+ *errnop = errno = ERANGE;
+ goto done;
+ }
+
+ *errnop = errno = 0;
+ called_again = false;
+ ndx_pw_cache++;
+
+ /* If we've finished with this lot of results free cache */
+
+ if (ndx_pw_cache == num_pw_cache) {
+ ndx_pw_cache = num_pw_cache = 0;
+ winbindd_free_response(&getpwent_response);
+ }
+ }
+ done:
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getpwent returns %s (%d)\n", getpid(),
+ nss_err_str(ret), ret);
+#endif
+
+#ifdef HAVE_PTHREAD
+ pthread_mutex_unlock(&winbind_nss_mutex);
+#endif
+ return ret;
+}
+
+/* Return passwd struct from uid */
+
+_PUBLIC_ON_LINUX_
+NSS_STATUS
+_nss_winbind_getpwuid_r(uid_t uid, struct passwd *result, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ static struct winbindd_response response;
+ struct winbindd_request request;
+ static int keep_response;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getpwuid_r %d\n", getpid(), (unsigned int)uid);
+#endif
+
+#ifdef HAVE_PTHREAD
+ pthread_mutex_lock(&winbind_nss_mutex);
+#endif
+
+ /* If our static buffer needs to be expanded we are called again */
+ if (!keep_response || uid != response.data.pw.pw_uid) {
+
+ /* Call for the first time */
+
+ response = (struct winbindd_response) {
+ .length = 0,
+ };
+ request = (struct winbindd_request) {
+ .wb_flags = WBFLAG_FROM_NSS,
+ .data = {
+ .uid = uid,
+ },
+ };
+
+ winbind_set_client_name("nss_winbind");
+ ret = winbindd_request_response(NULL, WINBINDD_GETPWUID, &request, &response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+ ret = fill_pwent(result, &response.data.pw,
+ &buffer, &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = true;
+ *errnop = errno = ERANGE;
+ goto done;
+ }
+ }
+
+ } else {
+
+ /* We've been called again */
+
+ ret = fill_pwent(result, &response.data.pw, &buffer, &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ *errnop = errno = ERANGE;
+ goto done;
+ }
+
+ keep_response = false;
+ *errnop = errno = 0;
+ }
+
+ winbindd_free_response(&response);
+
+ done:
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getpwuid %d returns %s (%d)\n", getpid(),
+ (unsigned int)uid, nss_err_str(ret), ret);
+#endif
+
+#ifdef HAVE_PTHREAD
+ pthread_mutex_unlock(&winbind_nss_mutex);
+#endif
+
+ return ret;
+}
+
+/* Return passwd struct from username */
+_PUBLIC_ON_LINUX_
+NSS_STATUS
+_nss_winbind_getpwnam_r(const char *name, struct passwd *result, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ static struct winbindd_response response;
+ struct winbindd_request request;
+ static int keep_response;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getpwnam_r %s\n", getpid(), name);
+#endif
+
+#ifdef HAVE_PTHREAD
+ pthread_mutex_lock(&winbind_nss_mutex);
+#endif
+
+ /* If our static buffer needs to be expanded we are called again */
+
+ if (!keep_response || strcmp(name,response.data.pw.pw_name) != 0) {
+
+ /* Call for the first time */
+
+ response = (struct winbindd_response) {
+ .length = 0,
+ };
+ request = (struct winbindd_request) {
+ .wb_flags = WBFLAG_FROM_NSS,
+ };
+
+ strncpy(request.data.username, name,
+ sizeof(request.data.username) - 1);
+ request.data.username
+ [sizeof(request.data.username) - 1] = '\0';
+
+ winbind_set_client_name("nss_winbind");
+ ret = winbindd_request_response(NULL, WINBINDD_GETPWNAM, &request, &response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+ ret = fill_pwent(result, &response.data.pw, &buffer,
+ &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = true;
+ *errnop = errno = ERANGE;
+ goto done;
+ }
+ }
+
+ } else {
+
+ /* We've been called again */
+
+ ret = fill_pwent(result, &response.data.pw, &buffer, &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = true;
+ *errnop = errno = ERANGE;
+ goto done;
+ }
+
+ keep_response = false;
+ *errnop = errno = 0;
+ }
+
+ winbindd_free_response(&response);
+ done:
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getpwnam %s returns %s (%d)\n", getpid(),
+ name, nss_err_str(ret), ret);
+#endif
+
+#ifdef HAVE_PTHREAD
+ pthread_mutex_unlock(&winbind_nss_mutex);
+#endif
+
+ return ret;
+}
+
+/*
+ * NSS group functions
+ */
+
+static struct winbindd_response getgrent_response;
+
+static int ndx_gr_cache; /* Current index into grp cache */
+static int num_gr_cache; /* Current size of grp cache */
+
+/* Rewind "file pointer" to start of ntdom group database */
+
+_PUBLIC_ON_LINUX_
+NSS_STATUS
+_nss_winbind_setgrent(void)
+{
+ NSS_STATUS ret;
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: setgrent\n", getpid());
+#endif
+
+#ifdef HAVE_PTHREAD
+ pthread_mutex_lock(&winbind_nss_mutex);
+#endif
+
+ if (num_gr_cache > 0) {
+ ndx_gr_cache = num_gr_cache = 0;
+ winbindd_free_response(&getgrent_response);
+ }
+
+ winbind_set_client_name("nss_winbind");
+ ret = winbindd_request_response(NULL, WINBINDD_SETGRENT, NULL, NULL);
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: setgrent returns %s (%d)\n", getpid(),
+ nss_err_str(ret), ret);
+#endif
+
+#ifdef HAVE_PTHREAD
+ pthread_mutex_unlock(&winbind_nss_mutex);
+#endif
+
+ return ret;
+}
+
+/* Close "file pointer" for ntdom group database */
+
+_PUBLIC_ON_LINUX_
+NSS_STATUS
+_nss_winbind_endgrent(void)
+{
+ NSS_STATUS ret;
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: endgrent\n", getpid());
+#endif
+
+#ifdef HAVE_PTHREAD
+ pthread_mutex_lock(&winbind_nss_mutex);
+#endif
+
+ if (num_gr_cache > 0) {
+ ndx_gr_cache = num_gr_cache = 0;
+ winbindd_free_response(&getgrent_response);
+ }
+
+ winbind_set_client_name("nss_winbind");
+ ret = winbindd_request_response(NULL, WINBINDD_ENDGRENT, NULL, NULL);
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: endgrent returns %s (%d)\n", getpid(),
+ nss_err_str(ret), ret);
+#endif
+
+#ifdef HAVE_PTHREAD
+ pthread_mutex_unlock(&winbind_nss_mutex);
+#endif
+
+ return ret;
+}
+
+/* Get next entry from ntdom group database */
+
+static NSS_STATUS
+winbind_getgrent(enum winbindd_cmd cmd,
+ struct group *result,
+ char *buffer, size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ static struct winbindd_request request;
+ static int called_again;
+
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getgrent\n", getpid());
+#endif
+
+#ifdef HAVE_PTHREAD
+ pthread_mutex_lock(&winbind_nss_mutex);
+#endif
+
+ /* Return an entry from the cache if we have one, or if we are
+ called again because we exceeded our static buffer. */
+
+ if ((ndx_gr_cache < num_gr_cache) || called_again) {
+ goto return_result;
+ }
+
+ /* Else call winbindd to get a bunch of entries */
+
+ if (num_gr_cache > 0) {
+ winbindd_free_response(&getgrent_response);
+ }
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(getgrent_response);
+
+ request.data.num_entries = MAX_GETGRENT_USERS;
+
+ winbind_set_client_name("nss_winbind");
+ ret = winbindd_request_response(NULL, cmd, &request,
+ &getgrent_response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+ struct winbindd_gr *gr_cache;
+ int mem_ofs;
+
+ /* Fill cache */
+
+ ndx_gr_cache = 0;
+ num_gr_cache = getgrent_response.data.num_entries;
+
+ /* Return a result */
+
+ return_result:
+
+ gr_cache = (struct winbindd_gr *)
+ getgrent_response.extra_data.data;
+
+ /* Check data is valid */
+
+ if (gr_cache == NULL) {
+ ret = NSS_STATUS_NOTFOUND;
+ goto done;
+ }
+
+ /* Fill group membership. The offset into the extra data
+ for the group membership is the reported offset plus the
+ size of all the winbindd_gr records returned. */
+
+ mem_ofs = gr_cache[ndx_gr_cache].gr_mem_ofs +
+ num_gr_cache * sizeof(struct winbindd_gr);
+
+ ret = fill_grent(result, &gr_cache[ndx_gr_cache],
+ ((char *)getgrent_response.extra_data.data)+mem_ofs,
+ &buffer, &buflen);
+
+ /* Out of memory - try again */
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ called_again = true;
+ *errnop = errno = ERANGE;
+ goto done;
+ }
+
+ *errnop = 0;
+ called_again = false;
+ ndx_gr_cache++;
+
+ /* If we've finished with this lot of results free cache */
+
+ if (ndx_gr_cache == num_gr_cache) {
+ ndx_gr_cache = num_gr_cache = 0;
+ winbindd_free_response(&getgrent_response);
+ }
+ }
+ done:
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getgrent returns %s (%d)\n", getpid(),
+ nss_err_str(ret), ret);
+#endif
+
+#ifdef HAVE_PTHREAD
+ pthread_mutex_unlock(&winbind_nss_mutex);
+#endif
+
+ return ret;
+}
+
+
+_PUBLIC_ON_LINUX_
+NSS_STATUS
+_nss_winbind_getgrent_r(struct group *result,
+ char *buffer, size_t buflen, int *errnop)
+{
+ return winbind_getgrent(WINBINDD_GETGRENT, result, buffer, buflen, errnop);
+}
+
+_PUBLIC_ON_LINUX_
+NSS_STATUS
+_nss_winbind_getgrlst_r(struct group *result,
+ char *buffer, size_t buflen, int *errnop)
+{
+ return winbind_getgrent(WINBINDD_GETGRLST, result, buffer, buflen, errnop);
+}
+
+/* Return group struct from group name */
+
+_PUBLIC_ON_LINUX_
+NSS_STATUS
+_nss_winbind_getgrnam_r(const char *name,
+ struct group *result, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ static struct winbindd_response response;
+ struct winbindd_request request;
+ static int keep_response;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getgrnam %s\n", getpid(), name);
+#endif
+
+#ifdef HAVE_PTHREAD
+ pthread_mutex_lock(&winbind_nss_mutex);
+#endif
+
+ /* If our static buffer needs to be expanded we are called again */
+ /* Or if the stored response group name differs from the request. */
+
+ if (!keep_response || strcmp(name,response.data.gr.gr_name) != 0) {
+
+ /* Call for the first time */
+
+ response = (struct winbindd_response) {
+ .length = 0,
+ };
+ request = (struct winbindd_request) {
+ .wb_flags = WBFLAG_FROM_NSS,
+ };
+
+ strncpy(request.data.groupname, name,
+ sizeof(request.data.groupname));
+ request.data.groupname
+ [sizeof(request.data.groupname) - 1] = '\0';
+
+ winbind_set_client_name("nss_winbind");
+ ret = winbindd_request_response(NULL, WINBINDD_GETGRNAM,
+ &request, &response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+ ret = fill_grent(result, &response.data.gr,
+ (char *)response.extra_data.data,
+ &buffer, &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = true;
+ *errnop = errno = ERANGE;
+ goto done;
+ }
+ }
+
+ } else {
+
+ /* We've been called again */
+
+ ret = fill_grent(result, &response.data.gr,
+ (char *)response.extra_data.data, &buffer,
+ &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = true;
+ *errnop = errno = ERANGE;
+ goto done;
+ }
+
+ keep_response = false;
+ *errnop = 0;
+ }
+
+ winbindd_free_response(&response);
+ done:
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getgrnam %s returns %s (%d)\n", getpid(),
+ name, nss_err_str(ret), ret);
+#endif
+
+#ifdef HAVE_PTHREAD
+ pthread_mutex_unlock(&winbind_nss_mutex);
+#endif
+
+ return ret;
+}
+
+/* Return group struct from gid */
+
+_PUBLIC_ON_LINUX_
+NSS_STATUS
+_nss_winbind_getgrgid_r(gid_t gid,
+ struct group *result, char *buffer,
+ size_t buflen, int *errnop)
+{
+ NSS_STATUS ret;
+ static struct winbindd_response response;
+ struct winbindd_request request;
+ static int keep_response;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getgrgid %d\n", getpid(), gid);
+#endif
+
+#ifdef HAVE_PTHREAD
+ pthread_mutex_lock(&winbind_nss_mutex);
+#endif
+
+ /* If our static buffer needs to be expanded we are called again */
+ /* Or if the stored response group name differs from the request. */
+
+ if (!keep_response || gid != response.data.gr.gr_gid) {
+
+ /* Call for the first time */
+
+ response = (struct winbindd_response) {
+ .length = 0,
+ };
+ request = (struct winbindd_request) {
+ .wb_flags = WBFLAG_FROM_NSS,
+ };
+
+
+ request.data.gid = gid;
+
+ winbind_set_client_name("nss_winbind");
+ ret = winbindd_request_response(NULL, WINBINDD_GETGRGID,
+ &request, &response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+
+ ret = fill_grent(result, &response.data.gr,
+ (char *)response.extra_data.data,
+ &buffer, &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = true;
+ *errnop = errno = ERANGE;
+ goto done;
+ }
+ }
+
+ } else {
+
+ /* We've been called again */
+
+ ret = fill_grent(result, &response.data.gr,
+ (char *)response.extra_data.data, &buffer,
+ &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = true;
+ *errnop = errno = ERANGE;
+ goto done;
+ }
+
+ keep_response = false;
+ *errnop = 0;
+ }
+
+ winbindd_free_response(&response);
+ done:
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: getgrgid %d returns %s (%d)\n", getpid(),
+ (unsigned int)gid, nss_err_str(ret), ret);
+#endif
+
+#ifdef HAVE_PTHREAD
+ pthread_mutex_unlock(&winbind_nss_mutex);
+#endif
+ return ret;
+}
+
+/* Initialise supplementary groups */
+
+_PUBLIC_ON_LINUX_
+NSS_STATUS
+_nss_winbind_initgroups_dyn(const char *user, gid_t group, long int *start,
+ long int *size, gid_t **groups, long int limit,
+ int *errnop)
+{
+ NSS_STATUS ret;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int i;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: initgroups %s (%d)\n", getpid(),
+ user, group);
+#endif
+
+#ifdef HAVE_PTHREAD
+ pthread_mutex_lock(&winbind_nss_mutex);
+#endif
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ strncpy(request.data.username, user,
+ sizeof(request.data.username) - 1);
+
+ winbind_set_client_name("nss_winbind");
+ ret = winbindd_request_response(NULL, WINBINDD_GETGROUPS,
+ &request, &response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+ int num_gids = response.data.num_entries;
+ gid_t *gid_list = (gid_t *)response.extra_data.data;
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: initgroups %s: got NSS_STATUS_SUCCESS "
+ "and %d gids\n", getpid(),
+ user, num_gids);
+#endif
+ if (gid_list == NULL) {
+ ret = NSS_STATUS_NOTFOUND;
+ goto done;
+ }
+
+ /* Copy group list to client */
+
+ for (i = 0; i < num_gids; i++) {
+
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: initgroups %s (%d): "
+ "processing gid %d \n", getpid(),
+ user, group, gid_list[i]);
+#endif
+
+ /* Skip primary group */
+
+ if (gid_list[i] == group) {
+ continue;
+ }
+
+ /* Skip groups without a mapping */
+ if (gid_list[i] == (uid_t)-1) {
+ continue;
+ }
+
+ /* Filled buffer ? If so, resize. */
+
+ if (*start == *size) {
+ long int newsize;
+ gid_t *newgroups;
+
+ newsize = 2 * (*size);
+ if (limit > 0) {
+ if (*size == limit) {
+ goto done;
+ }
+ if (newsize > limit) {
+ newsize = limit;
+ }
+ }
+
+ newgroups = (gid_t *)
+ realloc((*groups),
+ newsize * sizeof(**groups));
+ if (!newgroups) {
+ *errnop = ENOMEM;
+ ret = NSS_STATUS_NOTFOUND;
+ goto done;
+ }
+ *groups = newgroups;
+ *size = newsize;
+ }
+
+ /* Add to buffer */
+
+ (*groups)[*start] = gid_list[i];
+ *start += 1;
+ }
+ }
+
+ /* Back to your regularly scheduled programming */
+
+ done:
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: initgroups %s returns %s (%d)\n", getpid(),
+ user, nss_err_str(ret), ret);
+#endif
+
+#ifdef HAVE_PTHREAD
+ pthread_mutex_unlock(&winbind_nss_mutex);
+#endif
+
+ return ret;
+}
diff --git a/nsswitch/winbind_nss_linux.h b/nsswitch/winbind_nss_linux.h
new file mode 100644
index 0000000..a85e09d
--- /dev/null
+++ b/nsswitch/winbind_nss_linux.h
@@ -0,0 +1,56 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _WINBIND_NSS_LINUX_H
+#define _WINBIND_NSS_LINUX_H
+
+#ifndef _PUBLIC_ON_LINUX_
+/* If _PUBLIC_ON_LINUX_ is not defined via the wscript_build
+ * section we should mark the symbols as _PRIVATE_ because
+ * the Linux symbols are only used internally in order to
+ * implement the glue for other platforms on top.
+ */
+#define _PUBLIC_ON_LINUX_ _PRIVATE_
+#endif
+
+NSS_STATUS _nss_winbind_setpwent(void);
+NSS_STATUS _nss_winbind_endpwent(void);
+NSS_STATUS _nss_winbind_getpwent_r(struct passwd *result, char *buffer,
+ size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_getpwuid_r(uid_t uid, struct passwd *result,
+ char *buffer, size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_getpwnam_r(const char *name, struct passwd *result,
+ char *buffer, size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_setgrent(void);
+NSS_STATUS _nss_winbind_endgrent(void);
+NSS_STATUS _nss_winbind_getgrent_r(struct group *result, char *buffer,
+ size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_getgrlst_r(struct group *result, char *buffer,
+ size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_getgrnam_r(const char *name, struct group *result,
+ char *buffer, size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_getgrgid_r(gid_t gid, struct group *result, char *buffer,
+ size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_initgroups_dyn(const char *user, gid_t group, long int *start,
+ long int *size, gid_t **groups,
+ long int limit, int *errnop);
+
+#endif /* _WINBIND_NSS_LINUX_H */
diff --git a/nsswitch/winbind_nss_netbsd.c b/nsswitch/winbind_nss_netbsd.c
new file mode 100644
index 0000000..9455263
--- /dev/null
+++ b/nsswitch/winbind_nss_netbsd.c
@@ -0,0 +1,405 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ NetBSD loadable authentication module, providing identification
+ routines against Samba winbind/Windows NT Domain
+
+ Copyright (C) Luke Mewburn 2004-2005
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "winbind_client.h"
+
+#include <sys/param.h>
+#include <stdarg.h>
+#include <syslog.h>
+
+ /* dynamic nsswitch with "new" getpw* nsdispatch API available */
+#if defined(NSS_MODULE_INTERFACE_VERSION) && defined(HAVE_GETPWENT_R)
+
+/*
+ group functions
+ ---------------
+*/
+
+static struct group _winbind_group;
+static char _winbind_groupbuf[1024];
+
+int
+netbsdwinbind_endgrent(void *nsrv, void *nscb, va_list ap)
+{
+ int rv;
+
+ rv = _nss_winbind_endgrent();
+ return rv;
+}
+
+int
+netbsdwinbind_setgrent(void *nsrv, void *nscb, va_list ap)
+{
+ int rv;
+
+ rv = _nss_winbind_setgrent();
+ return rv;
+}
+
+int
+netbsdwinbind_getgrent(void *nsrv, void *nscb, va_list ap)
+{
+ struct group **retval = va_arg(ap, struct group **);
+
+ int rv, rerrno;
+
+ *retval = NULL;
+ rv = _nss_winbind_getgrent_r(&_winbind_group,
+ _winbind_groupbuf, sizeof(_winbind_groupbuf), &rerrno);
+ if (rv == NS_SUCCESS)
+ *retval = &_winbind_group;
+ return rv;
+}
+
+int
+netbsdwinbind_getgrent_r(void *nsrv, void *nscb, va_list ap)
+{
+ int *retval = va_arg(ap, int *);
+ struct group *grp = va_arg(ap, struct group *);
+ char *buffer = va_arg(ap, char *);
+ size_t buflen = va_arg(ap, size_t);
+ struct group **result = va_arg(ap, struct group **);
+
+ int rv, rerrno;
+
+ *result = NULL;
+ rerrno = 0;
+
+ rv = _nss_winbind_getgrent_r(grp, buffer, buflen, &rerrno);
+ if (rv == NS_SUCCESS)
+ *result = grp;
+ else
+ *retval = rerrno;
+ return rv;
+}
+
+int
+netbsdwinbind_getgrgid(void *nsrv, void *nscb, va_list ap)
+{
+ struct group **retval = va_arg(ap, struct group **);
+ gid_t gid = va_arg(ap, gid_t);
+
+ int rv, rerrno;
+
+ *retval = NULL;
+ rv = _nss_winbind_getgrgid_r(gid, &_winbind_group,
+ _winbind_groupbuf, sizeof(_winbind_groupbuf), &rerrno);
+ if (rv == NS_SUCCESS)
+ *retval = &_winbind_group;
+ return rv;
+}
+
+int
+netbsdwinbind_getgrgid_r(void *nsrv, void *nscb, va_list ap)
+{
+ int *retval = va_arg(ap, int *);
+ gid_t gid = va_arg(ap, gid_t);
+ struct group *grp = va_arg(ap, struct group *);
+ char *buffer = va_arg(ap, char *);
+ size_t buflen = va_arg(ap, size_t);
+ struct group **result = va_arg(ap, struct group **);
+
+ int rv, rerrno;
+
+ *result = NULL;
+ rerrno = 0;
+
+ rv = _nss_winbind_getgrgid_r(gid, grp, buffer, buflen, &rerrno);
+ if (rv == NS_SUCCESS)
+ *result = grp;
+ else
+ *retval = rerrno;
+ return rv;
+}
+
+int
+netbsdwinbind_getgrnam(void *nsrv, void *nscb, va_list ap)
+{
+ struct group **retval = va_arg(ap, struct group **);
+ const char *name = va_arg(ap, const char *);
+
+ int rv, rerrno;
+
+ *retval = NULL;
+ rv = _nss_winbind_getgrnam_r(name, &_winbind_group,
+ _winbind_groupbuf, sizeof(_winbind_groupbuf), &rerrno);
+ if (rv == NS_SUCCESS)
+ *retval = &_winbind_group;
+ return rv;
+}
+
+int
+netbsdwinbind_getgrnam_r(void *nsrv, void *nscb, va_list ap)
+{
+ int *retval = va_arg(ap, int *);
+ const char *name = va_arg(ap, const char *);
+ struct group *grp = va_arg(ap, struct group *);
+ char *buffer = va_arg(ap, char *);
+ size_t buflen = va_arg(ap, size_t);
+ struct group **result = va_arg(ap, struct group **);
+
+ int rv, rerrno;
+
+ *result = NULL;
+ rerrno = 0;
+
+ rv = _nss_winbind_getgrnam_r(name, grp, buffer, buflen, &rerrno);
+ if (rv == NS_SUCCESS)
+ *result = grp;
+ else
+ *retval = rerrno;
+ return rv;
+}
+
+int
+netbsdwinbind_getgroupmembership(void *nsrv, void *nscb, va_list ap)
+{
+ int *result = va_arg(ap, int *);
+ const char *uname = va_arg(ap, const char *);
+ gid_t *groups = va_arg(ap, gid_t *);
+ int maxgrp = va_arg(ap, int);
+ int *groupc = va_arg(ap, int *);
+
+ struct winbindd_request request = {
+ .wb_flags = WBFLAG_FROM_NSS,
+ };
+ struct winbindd_response response = {
+ .length = 0,
+ }
+ gid_t *wblistv;
+ int wblistc, i, isdup, dupc;
+
+ strncpy(request.data.username, uname,
+ sizeof(request.data.username) - 1);
+ i = winbindd_request_response(NULL, WINBINDD_GETGROUPS,
+ &request, &response);
+ if (i != NSS_STATUS_SUCCESS)
+ return NS_NOTFOUND;
+ wblistv = (gid_t *)response.extra_data.data;
+ wblistc = response.data.num_entries;
+
+ for (i = 0; i < wblistc; i++) { /* add winbind gids */
+ isdup = 0; /* skip duplicates */
+ for (dupc = 0; dupc < MIN(maxgrp, *groupc); dupc++) {
+ if (groups[dupc] == wblistv[i]) {
+ isdup = 1;
+ break;
+ }
+ }
+ if (isdup)
+ continue;
+ if (*groupc < maxgrp) /* add this gid */
+ groups[*groupc] = wblistv[i];
+ else
+ *result = -1;
+ (*groupc)++;
+ }
+ SAFE_FREE(wblistv);
+ return NS_NOTFOUND;
+}
+
+
+/*
+ passwd functions
+ ----------------
+*/
+
+static struct passwd _winbind_passwd;
+static char _winbind_passwdbuf[1024];
+
+int
+netbsdwinbind_endpwent(void *nsrv, void *nscb, va_list ap)
+{
+ int rv;
+
+ rv = _nss_winbind_endpwent();
+ return rv;
+}
+
+int
+netbsdwinbind_setpwent(void *nsrv, void *nscb, va_list ap)
+{
+ int rv;
+
+ rv = _nss_winbind_setpwent();
+ return rv;
+}
+
+int
+netbsdwinbind_getpwent(void *nsrv, void *nscb, va_list ap)
+{
+ struct passwd **retval = va_arg(ap, struct passwd **);
+
+ int rv, rerrno;
+
+ *retval = NULL;
+
+ rv = _nss_winbind_getpwent_r(&_winbind_passwd,
+ _winbind_passwdbuf, sizeof(_winbind_passwdbuf), &rerrno);
+ if (rv == NS_SUCCESS)
+ *retval = &_winbind_passwd;
+ return rv;
+}
+
+int
+netbsdwinbind_getpwent_r(void *nsrv, void *nscb, va_list ap)
+{
+ int *retval = va_arg(ap, int *);
+ struct passwd *pw = va_arg(ap, struct passwd *);
+ char *buffer = va_arg(ap, char *);
+ size_t buflen = va_arg(ap, size_t);
+ struct passwd **result = va_arg(ap, struct passwd **);
+
+ int rv, rerrno;
+
+ *result = NULL;
+ rerrno = 0;
+
+ rv = _nss_winbind_getpwent_r(pw, buffer, buflen, &rerrno);
+ if (rv == NS_SUCCESS)
+ *result = pw;
+ else
+ *retval = rerrno;
+ return rv;
+}
+
+int
+netbsdwinbind_getpwnam(void *nsrv, void *nscb, va_list ap)
+{
+ struct passwd **retval = va_arg(ap, struct passwd **);
+ const char *name = va_arg(ap, const char *);
+
+ int rv, rerrno;
+
+ *retval = NULL;
+ rv = _nss_winbind_getpwnam_r(name, &_winbind_passwd,
+ _winbind_passwdbuf, sizeof(_winbind_passwdbuf), &rerrno);
+ if (rv == NS_SUCCESS)
+ *retval = &_winbind_passwd;
+ return rv;
+}
+
+int
+netbsdwinbind_getpwnam_r(void *nsrv, void *nscb, va_list ap)
+{
+ int *retval = va_arg(ap, int *);
+ const char *name = va_arg(ap, const char *);
+ struct passwd *pw = va_arg(ap, struct passwd *);
+ char *buffer = va_arg(ap, char *);
+ size_t buflen = va_arg(ap, size_t);
+ struct passwd **result = va_arg(ap, struct passwd **);
+
+ int rv, rerrno;
+
+ *result = NULL;
+ rerrno = 0;
+
+ rv = _nss_winbind_getpwnam_r(name, pw, buffer, buflen, &rerrno);
+ if (rv == NS_SUCCESS)
+ *result = pw;
+ else
+ *retval = rerrno;
+ return rv;
+}
+
+int
+netbsdwinbind_getpwuid(void *nsrv, void *nscb, va_list ap)
+{
+ struct passwd **retval = va_arg(ap, struct passwd **);
+ uid_t uid = va_arg(ap, uid_t);
+
+ int rv, rerrno;
+
+ *retval = NULL;
+ rv = _nss_winbind_getpwuid_r(uid, &_winbind_passwd,
+ _winbind_passwdbuf, sizeof(_winbind_passwdbuf), &rerrno);
+ if (rv == NS_SUCCESS)
+ *retval = &_winbind_passwd;
+ return rv;
+}
+
+int
+netbsdwinbind_getpwuid_r(void *nsrv, void *nscb, va_list ap)
+{
+ int *retval = va_arg(ap, int *);
+ uid_t uid = va_arg(ap, uid_t);
+ struct passwd *pw = va_arg(ap, struct passwd *);
+ char *buffer = va_arg(ap, char *);
+ size_t buflen = va_arg(ap, size_t);
+ struct passwd **result = va_arg(ap, struct passwd **);
+
+ int rv, rerrno;
+
+ *result = NULL;
+ rerrno = 0;
+
+ rv = _nss_winbind_getpwuid_r(uid, pw, buffer, buflen, &rerrno);
+ if (rv == NS_SUCCESS)
+ *result = pw;
+ else
+ *retval = rerrno;
+ return rv;
+}
+
+
+/*
+ nsswitch module setup
+ ---------------------
+*/
+
+
+static ns_mtab winbind_methods[] = {
+
+{ NSDB_GROUP, "endgrent", netbsdwinbind_endgrent, NULL },
+{ NSDB_GROUP, "getgrent", netbsdwinbind_getgrent, NULL },
+{ NSDB_GROUP, "getgrent_r", netbsdwinbind_getgrent_r, NULL },
+{ NSDB_GROUP, "getgrgid", netbsdwinbind_getgrgid, NULL },
+{ NSDB_GROUP, "getgrgid_r", netbsdwinbind_getgrgid_r, NULL },
+{ NSDB_GROUP, "getgrnam", netbsdwinbind_getgrnam, NULL },
+{ NSDB_GROUP, "getgrnam_r", netbsdwinbind_getgrnam_r, NULL },
+{ NSDB_GROUP, "setgrent", netbsdwinbind_setgrent, NULL },
+{ NSDB_GROUP, "setgroupent", netbsdwinbind_setgrent, NULL },
+{ NSDB_GROUP, "getgroupmembership", netbsdwinbind_getgroupmembership, NULL },
+
+{ NSDB_PASSWD, "endpwent", netbsdwinbind_endpwent, NULL },
+{ NSDB_PASSWD, "getpwent", netbsdwinbind_getpwent, NULL },
+{ NSDB_PASSWD, "getpwent_r", netbsdwinbind_getpwent_r, NULL },
+{ NSDB_PASSWD, "getpwnam", netbsdwinbind_getpwnam, NULL },
+{ NSDB_PASSWD, "getpwnam_r", netbsdwinbind_getpwnam_r, NULL },
+{ NSDB_PASSWD, "getpwuid", netbsdwinbind_getpwuid, NULL },
+{ NSDB_PASSWD, "getpwuid_r", netbsdwinbind_getpwuid_r, NULL },
+{ NSDB_PASSWD, "setpassent", netbsdwinbind_setpwent, NULL },
+{ NSDB_PASSWD, "setpwent", netbsdwinbind_setpwent, NULL },
+
+};
+
+ns_mtab *
+nss_module_register(const char *source, unsigned int *mtabsize,
+ nss_module_unregister_fn *unreg)
+{
+ *mtabsize = sizeof(winbind_methods)/sizeof(winbind_methods[0]);
+ *unreg = NULL;
+ return (winbind_methods);
+}
+
+#endif /* NSS_MODULE_INTERFACE_VERSION && HAVE_GETPWENT_R */
diff --git a/nsswitch/winbind_nss_netbsd.h b/nsswitch/winbind_nss_netbsd.h
new file mode 100644
index 0000000..5aeb2b9
--- /dev/null
+++ b/nsswitch/winbind_nss_netbsd.h
@@ -0,0 +1,40 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ NetBSD loadable authentication module, providing identification
+ routines against Samba winbind/Windows NT Domain
+
+ Copyright (C) Luke Mewburn 2004-2005
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _WINBIND_NSS_NETBSD_H
+#define _WINBIND_NSS_NETBSD_H
+
+#include <nsswitch.h>
+
+ /* dynamic nsswitch with "new" getpw* nsdispatch API available */
+#if defined(NSS_MODULE_INTERFACE_VERSION) && defined(HAVE_GETPWENT_R)
+
+typedef int NSS_STATUS;
+
+#define NSS_STATUS_SUCCESS NS_SUCCESS
+#define NSS_STATUS_NOTFOUND NS_NOTFOUND
+#define NSS_STATUS_UNAVAIL NS_UNAVAIL
+#define NSS_STATUS_TRYAGAIN NS_TRYAGAIN
+
+#endif /* NSS_MODULE_INTERFACE_VERSION && HAVE_GETPWENT_R */
+
+#endif /* _WINBIND_NSS_NETBSD_H */
diff --git a/nsswitch/winbind_nss_solaris.c b/nsswitch/winbind_nss_solaris.c
new file mode 100644
index 0000000..495854f
--- /dev/null
+++ b/nsswitch/winbind_nss_solaris.c
@@ -0,0 +1,665 @@
+/*
+ Solaris NSS wrapper for winbind
+ - Shirish Kalele 2000
+
+ Based on Luke Howard's ldap_nss module for Solaris
+ */
+
+/*
+ Copyright (C) 1997-2003 Luke Howard.
+ This file is part of the nss_ldap library.
+
+ The nss_ldap library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 3 of the
+ License, or (at your option) any later version.
+
+ The nss_ldap library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the nss_ldap library; see the file COPYING.LIB. If not,
+ see <http://www.gnu.org/licenses/>.
+*/
+
+#undef DEVELOPER
+
+
+#include "winbind_client.h"
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <string.h>
+#include <pwd.h>
+#include <syslog.h>
+
+#if !defined(HPUX)
+#include <sys/syslog.h>
+#endif /*hpux*/
+
+#if defined(HAVE_NSS_COMMON_H) || defined(HPUX)
+
+#undef NSS_DEBUG
+
+#ifdef NSS_DEBUG
+#define NSS_DEBUG(str) syslog(LOG_DEBUG, "nss_winbind: %s", str);
+#else
+#define NSS_DEBUG(str) ;
+#endif
+
+#if !defined(SMB_MALLOC_P)
+#define SMB_MALLOC_P(type) (type *)malloc(sizeof(type))
+#endif
+
+#define NSS_ARGS(args) ((nss_XbyY_args_t *)args)
+
+#ifdef HPUX
+
+/*
+ * HP-UX 11 has no definiton of the nss_groupsbymem structure. This
+ * definition is taken from the nss_ldap project at:
+ * http://www.padl.com/OSS/nss_ldap.html
+ */
+
+struct nss_groupsbymem {
+ const char *username;
+ gid_t *gid_array;
+ int maxgids;
+ int force_slow_way;
+ int (*str2ent)(const char *instr, int instr_len, void *ent,
+ char *buffer, int buflen);
+ nss_status_t (*process_cstr)(const char *instr, int instr_len,
+ struct nss_groupsbymem *);
+ int numgids;
+};
+
+#endif /* HPUX */
+
+static NSS_STATUS _nss_winbind_setpwent_solwrap (nss_backend_t* be, void* args)
+{
+ NSS_DEBUG("_nss_winbind_setpwent_solwrap");
+ return _nss_winbind_setpwent();
+}
+
+static NSS_STATUS
+_nss_winbind_endpwent_solwrap (nss_backend_t * be, void *args)
+{
+ NSS_DEBUG("_nss_winbind_endpwent_solwrap");
+ return _nss_winbind_endpwent();
+}
+
+static NSS_STATUS
+_nss_winbind_getpwent_solwrap (nss_backend_t* be, void *args)
+{
+ NSS_STATUS ret;
+ char* buffer = NSS_ARGS(args)->buf.buffer;
+ int buflen = NSS_ARGS(args)->buf.buflen;
+ struct passwd* result = (struct passwd*) NSS_ARGS(args)->buf.result;
+ int* errnop = &NSS_ARGS(args)->erange;
+ char logmsg[80];
+
+ ret = _nss_winbind_getpwent_r(result, buffer,
+ buflen, errnop);
+
+ if(ret == NSS_STATUS_SUCCESS)
+ {
+ snprintf(logmsg, 79, "_nss_winbind_getpwent_solwrap: Returning user: %s\n",
+ result->pw_name);
+ NSS_DEBUG(logmsg);
+ NSS_ARGS(args)->returnval = (void*) result;
+ } else {
+ snprintf(logmsg, 79, "_nss_winbind_getpwent_solwrap: Returning error: %d.\n",ret);
+ NSS_DEBUG(logmsg);
+ }
+
+ return ret;
+}
+
+static NSS_STATUS
+_nss_winbind_getpwnam_solwrap (nss_backend_t* be, void* args)
+{
+ NSS_STATUS ret;
+ struct passwd* result = (struct passwd*) NSS_ARGS(args)->buf.result;
+
+ NSS_DEBUG("_nss_winbind_getpwnam_solwrap");
+
+ ret = _nss_winbind_getpwnam_r (NSS_ARGS(args)->key.name,
+ result,
+ NSS_ARGS(args)->buf.buffer,
+ NSS_ARGS(args)->buf.buflen,
+ &NSS_ARGS(args)->erange);
+ if(ret == NSS_STATUS_SUCCESS)
+ NSS_ARGS(args)->returnval = (void*) result;
+
+ return ret;
+}
+
+static NSS_STATUS
+_nss_winbind_getpwuid_solwrap(nss_backend_t* be, void* args)
+{
+ NSS_STATUS ret;
+ struct passwd* result = (struct passwd*) NSS_ARGS(args)->buf.result;
+
+ NSS_DEBUG("_nss_winbind_getpwuid_solwrap");
+ ret = _nss_winbind_getpwuid_r (NSS_ARGS(args)->key.uid,
+ result,
+ NSS_ARGS(args)->buf.buffer,
+ NSS_ARGS(args)->buf.buflen,
+ &NSS_ARGS(args)->erange);
+ if(ret == NSS_STATUS_SUCCESS)
+ NSS_ARGS(args)->returnval = (void*) result;
+
+ return ret;
+}
+
+static NSS_STATUS _nss_winbind_passwd_destr (nss_backend_t * be, void *args)
+{
+ SAFE_FREE(be);
+ NSS_DEBUG("_nss_winbind_passwd_destr");
+ return NSS_STATUS_SUCCESS;
+}
+
+static nss_backend_op_t passwd_ops[] =
+{
+ _nss_winbind_passwd_destr,
+ _nss_winbind_endpwent_solwrap, /* NSS_DBOP_ENDENT */
+ _nss_winbind_setpwent_solwrap, /* NSS_DBOP_SETENT */
+ _nss_winbind_getpwent_solwrap, /* NSS_DBOP_GETENT */
+ _nss_winbind_getpwnam_solwrap, /* NSS_DBOP_PASSWD_BYNAME */
+ _nss_winbind_getpwuid_solwrap /* NSS_DBOP_PASSWD_BYUID */
+};
+
+nss_backend_t*
+_nss_winbind_passwd_constr (const char* db_name,
+ const char* src_name,
+ const char* cfg_args)
+{
+ nss_backend_t *be;
+
+ if(!(be = SMB_MALLOC_P(nss_backend_t)) )
+ return NULL;
+
+ be->ops = passwd_ops;
+ be->n_ops = sizeof(passwd_ops) / sizeof(nss_backend_op_t);
+
+ NSS_DEBUG("Initialized nss_winbind passwd backend");
+ return be;
+}
+
+/*****************************************************************
+ GROUP database backend
+ *****************************************************************/
+
+static NSS_STATUS _nss_winbind_setgrent_solwrap (nss_backend_t* be, void* args)
+{
+ NSS_DEBUG("_nss_winbind_setgrent_solwrap");
+ return _nss_winbind_setgrent();
+}
+
+static NSS_STATUS
+_nss_winbind_endgrent_solwrap (nss_backend_t * be, void *args)
+{
+ NSS_DEBUG("_nss_winbind_endgrent_solwrap");
+ return _nss_winbind_endgrent();
+}
+
+static NSS_STATUS
+_nss_winbind_getgrent_solwrap(nss_backend_t* be, void* args)
+{
+ NSS_STATUS ret;
+ char* buffer = NSS_ARGS(args)->buf.buffer;
+ int buflen = NSS_ARGS(args)->buf.buflen;
+ struct group* result = (struct group*) NSS_ARGS(args)->buf.result;
+ int* errnop = &NSS_ARGS(args)->erange;
+ char logmsg[80];
+
+ ret = _nss_winbind_getgrent_r(result, buffer,
+ buflen, errnop);
+
+ if(ret == NSS_STATUS_SUCCESS)
+ {
+ snprintf(logmsg, 79, "_nss_winbind_getgrent_solwrap: Returning group: %s\n", result->gr_name);
+ NSS_DEBUG(logmsg);
+ NSS_ARGS(args)->returnval = (void*) result;
+ } else {
+ snprintf(logmsg, 79, "_nss_winbind_getgrent_solwrap: Returning error: %d.\n", ret);
+ NSS_DEBUG(logmsg);
+ }
+
+ return ret;
+
+}
+
+static NSS_STATUS
+_nss_winbind_getgrnam_solwrap(nss_backend_t* be, void* args)
+{
+ NSS_STATUS ret;
+ struct group* result = (struct group*) NSS_ARGS(args)->buf.result;
+
+ NSS_DEBUG("_nss_winbind_getgrnam_solwrap");
+ ret = _nss_winbind_getgrnam_r(NSS_ARGS(args)->key.name,
+ result,
+ NSS_ARGS(args)->buf.buffer,
+ NSS_ARGS(args)->buf.buflen,
+ &NSS_ARGS(args)->erange);
+
+ if(ret == NSS_STATUS_SUCCESS)
+ NSS_ARGS(args)->returnval = (void*) result;
+
+ if (NSS_ARGS(args)->erange == ERANGE && ret == NSS_STATUS_TRYAGAIN)
+ return NSS_STATUS_UNAVAIL;
+
+ return ret;
+}
+
+static NSS_STATUS
+_nss_winbind_getgrgid_solwrap(nss_backend_t* be, void* args)
+{
+ NSS_STATUS ret;
+ struct group* result = (struct group*) NSS_ARGS(args)->buf.result;
+
+ NSS_DEBUG("_nss_winbind_getgrgid_solwrap");
+ ret = _nss_winbind_getgrgid_r (NSS_ARGS(args)->key.gid,
+ result,
+ NSS_ARGS(args)->buf.buffer,
+ NSS_ARGS(args)->buf.buflen,
+ &NSS_ARGS(args)->erange);
+
+ if(ret == NSS_STATUS_SUCCESS)
+ NSS_ARGS(args)->returnval = (void*) result;
+
+ if (NSS_ARGS(args)->erange == ERANGE && ret == NSS_STATUS_TRYAGAIN)
+ return NSS_STATUS_UNAVAIL;
+
+ return ret;
+}
+
+static NSS_STATUS
+_nss_winbind_getgroupsbymember_solwrap(nss_backend_t* be, void* args)
+{
+ int errnop;
+ struct nss_groupsbymem *gmem = (struct nss_groupsbymem *)args;
+ long int numgids = gmem->numgids;
+ long int maxgids = gmem->maxgids;
+
+ NSS_DEBUG("_nss_winbind_getgroupsbymember");
+
+ _nss_winbind_initgroups_dyn(gmem->username,
+ gmem->gid_array[0], /* Primary Group */
+ &numgids,
+ &maxgids,
+ &gmem->gid_array,
+ gmem->maxgids,
+ &errnop);
+
+ gmem->numgids = numgids;
+ gmem->maxgids = maxgids;
+
+ /*
+ * If the maximum number of gids have been found, return
+ * SUCCESS so the switch engine will stop searching. Otherwise
+ * return NOTFOUND so nsswitch will continue to get groups
+ * from the remaining database backends specified in the
+ * nsswitch.conf file.
+ */
+ return (gmem->numgids == gmem->maxgids ? NSS_STATUS_SUCCESS : NSS_STATUS_NOTFOUND);
+}
+
+static NSS_STATUS
+_nss_winbind_group_destr (nss_backend_t* be, void* args)
+{
+ SAFE_FREE(be);
+ NSS_DEBUG("_nss_winbind_group_destr");
+ return NSS_STATUS_SUCCESS;
+}
+
+static nss_backend_op_t group_ops[] =
+{
+ _nss_winbind_group_destr,
+ _nss_winbind_endgrent_solwrap,
+ _nss_winbind_setgrent_solwrap,
+ _nss_winbind_getgrent_solwrap,
+ _nss_winbind_getgrnam_solwrap,
+ _nss_winbind_getgrgid_solwrap,
+ _nss_winbind_getgroupsbymember_solwrap
+};
+
+nss_backend_t*
+_nss_winbind_group_constr (const char* db_name,
+ const char* src_name,
+ const char* cfg_args)
+{
+ nss_backend_t* be;
+
+ if(!(be = SMB_MALLOC_P(nss_backend_t)) )
+ return NULL;
+
+ be->ops = group_ops;
+ be->n_ops = sizeof(group_ops) / sizeof(nss_backend_op_t);
+
+ NSS_DEBUG("Initialized nss_winbind group backend");
+ return be;
+}
+
+/*****************************************************************
+ hosts and ipnodes backend
+ *****************************************************************/
+#if defined(SUNOS5) /* not compatible with HP-UX */
+
+/* this parser is shared between get*byname and get*byaddr, as key type
+ in request is stored in different locations, I had to provide the
+ address family as an argument, caller must free the winbind response. */
+
+static NSS_STATUS
+parse_response(int af, nss_XbyY_args_t* argp, struct winbindd_response *response)
+{
+ struct hostent *he = (struct hostent *)argp->buf.result;
+ char *buffer = argp->buf.buffer;
+ int buflen = argp->buf.buflen;
+ NSS_STATUS ret;
+
+ char *p, *data;
+ int addrcount = 0;
+ int len = 0;
+ struct in_addr *addrp;
+#if defined(AF_INET6)
+ struct in6_addr *addrp6;
+#endif
+ int i;
+
+ /* response is tab separated list of ip addresses with hostname
+ and newline at the end. so at first we will strip newline
+ then construct list of addresses for hostent.
+ */
+ p = strchr(response->data.winsresp, '\n');
+ if(p) *p = '\0';
+ else {/* it must be broken */
+ argp->h_errno = NO_DATA;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ for(; p != response->data.winsresp; p--) {
+ if(*p == '\t') addrcount++;
+ }
+
+ if(addrcount == 0) {/* it must be broken */
+ argp->h_errno = NO_DATA;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ /* allocate space for addresses and h_addr_list */
+ he->h_addrtype = af;
+ if( he->h_addrtype == AF_INET) {
+ he->h_length = sizeof(struct in_addr);
+ addrp = (struct in_addr *)ROUND_DOWN(buffer + buflen,
+ sizeof(struct in_addr));
+ addrp -= addrcount;
+ he->h_addr_list = (char **)ROUND_DOWN(addrp, sizeof (char*));
+ he->h_addr_list -= addrcount+1;
+ }
+#if defined(AF_INET6)
+ else {
+ he->h_length = sizeof(struct in6_addr);
+ addrp6 = (struct in6_addr *)ROUND_DOWN(buffer + buflen,
+ sizeof(struct in6_addr));
+ addrp6 -= addrcount;
+ he->h_addr_list = (char **)ROUND_DOWN(addrp6, sizeof (char*));
+ he->h_addr_list -= addrcount+1;
+ }
+#endif
+
+ /* buffer too small?! */
+ if((char *)he->h_addr_list < buffer ) {
+ argp->erange = 1;
+ return NSS_STR_PARSE_ERANGE;
+ }
+
+ data = response->data.winsresp;
+ for( i = 0; i < addrcount; i++) {
+ p = strchr(data, '\t');
+ if(p == NULL) break; /* just in case... */
+
+ *p = '\0'; /* terminate the string */
+ if(he->h_addrtype == AF_INET) {
+ he->h_addr_list[i] = (char *)&addrp[i];
+ if ((addrp[i].s_addr = inet_addr(data)) == -1) {
+ argp->erange = 1;
+ return NSS_STR_PARSE_ERANGE;
+ }
+ }
+#if defined(AF_INET6)
+ else {
+ he->h_addr_list[i] = (char *)&addrp6[i];
+ if (strchr(data, ':') != 0) {
+ if (inet_pton(AF_INET6, data, &addrp6[i]) != 1) {
+ argp->erange = 1;
+ return NSS_STR_PARSE_ERANGE;
+ }
+ } else {
+ struct in_addr in4;
+ if ((in4.s_addr = inet_addr(data)) == -1) {
+ argp->erange = 1;
+ return NSS_STR_PARSE_ERANGE;
+ }
+ IN6_INADDR_TO_V4MAPPED(&in4, &addrp6[i]);
+ }
+ }
+#endif
+ data = p+1;
+ }
+
+ he->h_addr_list[i] = (char *)NULL;
+
+ len = strlen(data);
+ if(len > he->h_addr_list - (char**)argp->buf.buffer) {
+ argp->erange = 1;
+ return NSS_STR_PARSE_ERANGE;
+ }
+
+ /* this is a bit overkill to use _nss_netdb_aliases here since
+ there seems to be no aliases but it will create all data for us */
+ he->h_aliases = _nss_netdb_aliases(data, len, buffer,
+ ((char*) he->h_addr_list) - buffer);
+ if(he->h_aliases == NULL) {
+ argp->erange = 1;
+ ret = NSS_STR_PARSE_ERANGE;
+ } else {
+ he->h_name = he->h_aliases[0];
+ he->h_aliases++;
+ ret = NSS_STR_PARSE_SUCCESS;
+ }
+
+ argp->returnval = (void*)he;
+ return ret;
+}
+
+static NSS_STATUS
+_nss_winbind_ipnodes_getbyname(nss_backend_t* be, void *args)
+{
+ nss_XbyY_args_t *argp = (nss_XbyY_args_t*) args;
+ struct winbindd_request request = {
+ .wb_flags = WBFLAG_FROM_NSS,
+ };
+ struct winbindd_response response = {
+ .length = 0,
+ };
+ NSS_STATUS ret;
+ int af;
+
+ /* I assume there that AI_ADDRCONFIG cases are handled in nss
+ frontend code, at least it seems done so in solaris...
+
+ we will give NO_DATA for pure IPv6; IPv4 will be returned for
+ AF_INET or for AF_INET6 and AI_ALL|AI_V4MAPPED we have to map
+ IPv4 to IPv6.
+ */
+#if defined(AF_INET6)
+#ifdef HAVE_NSS_XBYY_KEY_IPNODE
+ af = argp->key.ipnode.af_family;
+ if(af == AF_INET6 && argp->key.ipnode.flags == 0) {
+ argp->h_errno = NO_DATA;
+ return NSS_STATUS_UNAVAIL;
+ }
+#else
+ /* I'm not that sure if this is correct, but... */
+ af = AF_INET6;
+#endif
+#endif
+
+ strncpy(request.data.winsreq, argp->key.name, sizeof(request.data.winsreq) - 1);
+ request.data.winsreq[sizeof(request.data.winsreq) - 1] = '\0';
+
+ if( (ret = winbindd_request_response(NULL, WINBINDD_WINS_BYNAME,
+ &request, &response))
+ == NSS_STATUS_SUCCESS ) {
+ ret = parse_response(af, argp, &response);
+ }
+
+ winbindd_free_response(&response);
+ return ret;
+}
+
+static NSS_STATUS
+_nss_winbind_hosts_getbyname(nss_backend_t* be, void *args)
+{
+ nss_XbyY_args_t *argp = (nss_XbyY_args_t*) args;
+ struct winbindd_request request = {
+ .wb_flags = WBFLAG_FROM_NSS,
+ };
+ struct winbindd_response response = {
+ .length = 0,
+ };
+ NSS_STATUS ret;
+
+ strncpy(request.data.winsreq, argp->key.name, sizeof(request.data.winsreq) - 1);
+ request.data.winsreq[sizeof(request.data.winsreq) - 1] = '\0';
+
+ if( (ret = winbindd_request_response(NULL, WINBINDD_WINS_BYNAME,
+ &request, &response))
+ == NSS_STATUS_SUCCESS ) {
+ ret = parse_response(AF_INET, argp, &response);
+ }
+
+ winbindd_free_response(&response);
+ return ret;
+}
+
+static NSS_STATUS
+_nss_winbind_hosts_getbyaddr(nss_backend_t* be, void *args)
+{
+ NSS_STATUS ret;
+ struct winbindd_request request = {
+ .wb_flags = WBFLAG_FROM_NSS,
+ };
+ struct winbindd_response response = {
+ .length = 0,
+ };
+ nss_XbyY_args_t *argp = (nss_XbyY_args_t *)args;
+ const char *p;
+
+#if defined(AF_INET6)
+ /* winbindd currently does not resolve IPv6 */
+ if(argp->key.hostaddr.type == AF_INET6) {
+ argp->h_errno = NO_DATA;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ p = inet_ntop(argp->key.hostaddr.type, argp->key.hostaddr.addr,
+ request.data.winsreq, sizeof request.data.winsreq);
+#else
+ snprintf(request.data.winsreq, sizeof request.data.winsreq,
+ "%u.%u.%u.%u",
+ ((unsigned char *)argp->key.hostaddr.addr)[0],
+ ((unsigned char *)argp->key.hostaddr.addr)[1],
+ ((unsigned char *)argp->key.hostaddr.addr)[2],
+ ((unsigned char *)argp->key.hostaddr.addr)[3]);
+#endif
+
+ ret = winbindd_request_response(NULL, WINBINDD_WINS_BYIP,
+ &request, &response);
+
+ if( ret == NSS_STATUS_SUCCESS) {
+ parse_response(argp->key.hostaddr.type, argp, &response);
+ }
+ winbindd_free_response(&response);
+ return ret;
+}
+
+/* winbind does not provide setent, getent, endent for wins */
+static NSS_STATUS
+_nss_winbind_common_endent(nss_backend_t* be, void *args)
+{
+ return (NSS_STATUS_UNAVAIL);
+}
+
+static NSS_STATUS
+_nss_winbind_common_setent(nss_backend_t* be, void *args)
+{
+ return (NSS_STATUS_UNAVAIL);
+}
+
+static NSS_STATUS
+_nss_winbind_common_getent(nss_backend_t* be, void *args)
+{
+ return (NSS_STATUS_UNAVAIL);
+}
+
+static nss_backend_t*
+_nss_winbind_common_constr (nss_backend_op_t ops[], int n_ops)
+{
+ nss_backend_t* be;
+
+ if(!(be = SMB_MALLOC_P(nss_backend_t)) )
+ return NULL;
+
+ be->ops = ops;
+ be->n_ops = n_ops;
+
+ return be;
+}
+
+static NSS_STATUS
+_nss_winbind_common_destr (nss_backend_t* be, void* args)
+{
+ SAFE_FREE(be);
+ return NSS_STATUS_SUCCESS;
+}
+
+static nss_backend_op_t ipnodes_ops[] = {
+ _nss_winbind_common_destr,
+ _nss_winbind_common_endent,
+ _nss_winbind_common_setent,
+ _nss_winbind_common_getent,
+ _nss_winbind_ipnodes_getbyname,
+ _nss_winbind_hosts_getbyaddr,
+};
+
+nss_backend_t *
+_nss_winbind_ipnodes_constr(dummy1, dummy2, dummy3)
+ const char *dummy1, *dummy2, *dummy3;
+{
+ return (_nss_winbind_common_constr(ipnodes_ops,
+ sizeof (ipnodes_ops) / sizeof (ipnodes_ops[0])));
+}
+
+static nss_backend_op_t host_ops[] = {
+ _nss_winbind_common_destr,
+ _nss_winbind_common_endent,
+ _nss_winbind_common_setent,
+ _nss_winbind_common_getent,
+ _nss_winbind_hosts_getbyname,
+ _nss_winbind_hosts_getbyaddr,
+};
+
+nss_backend_t *
+_nss_winbind_hosts_constr(dummy1, dummy2, dummy3)
+ const char *dummy1, *dummy2, *dummy3;
+{
+ return (_nss_winbind_common_constr(host_ops,
+ sizeof (host_ops) / sizeof (host_ops[0])));
+}
+
+#endif /* defined(SUNOS5) */
+#endif /* defined(HAVE_NSS_COMMON_H) || defined(HPUX) */
diff --git a/nsswitch/winbind_nss_solaris.h b/nsswitch/winbind_nss_solaris.h
new file mode 100644
index 0000000..8e26d0d
--- /dev/null
+++ b/nsswitch/winbind_nss_solaris.h
@@ -0,0 +1,37 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _WINBIND_NSS_SOLARIS_H
+#define _WINBIND_NSS_SOLARIS_H
+
+#include <nss_common.h>
+#include <nss_dbdefs.h>
+#include <nsswitch.h>
+#include "system/passwd.h"
+
+typedef nss_status_t NSS_STATUS;
+
+#define NSS_STATUS_SUCCESS NSS_SUCCESS
+#define NSS_STATUS_NOTFOUND NSS_NOTFOUND
+#define NSS_STATUS_UNAVAIL NSS_UNAVAIL
+#define NSS_STATUS_TRYAGAIN NSS_TRYAGAIN
+
+#endif /* _WINBIND_NSS_SOLARIS_H */
diff --git a/nsswitch/winbind_struct_protocol.h b/nsswitch/winbind_struct_protocol.h
new file mode 100644
index 0000000..9365b31
--- /dev/null
+++ b/nsswitch/winbind_struct_protocol.h
@@ -0,0 +1,535 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Gerald Carter 2006
+
+ You are free to use this interface definition in any way you see
+ fit, including without restriction, using this header in your own
+ products. You do not need to give any attribution.
+*/
+
+#ifndef SAFE_FREE
+#define SAFE_FREE(x) do { if(x) {free(x); x=NULL;} } while(0)
+#endif
+
+#ifndef FSTRING_LEN
+#define FSTRING_LEN 256
+typedef char fstring[FSTRING_LEN];
+#endif
+
+#ifndef _WINBINDD_NTDOM_H
+#define _WINBINDD_NTDOM_H
+
+#define WINBINDD_SOCKET_NAME "pipe" /* Name of PF_UNIX socket */
+
+/* We let the build environment set the public winbindd socket
+ * location. Therefore we no longer set
+ *
+ * #define WINBINDD_SOCKET_DIR "/tmp/.winbindd"
+ *
+ * A number of different distributions set different paths, and so it
+ * needs to come from configure in Samba. External users of this header will
+ * need to know where the path is on their system by some other
+ * mechanism.
+ */
+
+#define WINBINDD_PRIV_SOCKET_SUBDIR "winbindd_privileged" /* name of subdirectory of lp_lock_directory() to hold the 'privileged' pipe */
+#define WINBINDD_DOMAIN_ENV "WINBINDD_DOMAIN" /* Environment variables */
+#define WINBINDD_DONT_ENV "_NO_WINBINDD"
+#define WINBINDD_LOCATOR_KDC_ADDRESS "WINBINDD_LOCATOR_KDC_ADDRESS"
+
+/* Update this when you change the interface.
+ * 21: added WINBINDD_GETPWSID
+ * added WINBINDD_GETSIDALIASES
+ * 22: added WINBINDD_PING_DC
+ * 23: added session_key to ccache_ntlm_auth response
+ * added WINBINDD_CCACHE_SAVE
+ * 24: Fill in num_entries WINBINDD_LIST_USERS and WINBINDD_LIST_GROUPS
+ * 25: removed WINBINDD_SET_HWM
+ * removed WINBINDD_SET_MAPPING
+ * removed WINBINDD_REMOVE_MAPPING
+ * 26: added WINBINDD_DC_INFO
+ * 27: added WINBINDD_LOOKUPSIDS
+ * 28: added WINBINDD_XIDS_TO_SIDS
+ * removed WINBINDD_SID_TO_UID
+ * removed WINBINDD_SID_TO_GID
+ * removed WINBINDD_GID_TO_SID
+ * removed WINBINDD_UID_TO_SID
+ * 29: added "authoritative" to response.data.auth
+ * 30: added "validation_level" and "info6" to response.data.auth
+ * 31: added "client_name" to the request
+ * 32: added "traceid" to the request
+ * removed WINBINDD_INIT_CONNECTION
+ */
+#define WINBIND_INTERFACE_VERSION 32
+
+/* Have to deal with time_t being 4 or 8 bytes due to structure alignment.
+ On a 64bit Linux box, we have to support a constant structure size
+ between /lib/libnss_winbind.so.2 and /lib64/libnss_winbind.so.2.
+ The easiest way to do this is to always use 8byte values for time_t. */
+
+#define SMB_TIME_T int64_t
+
+/* Socket commands */
+
+enum winbindd_cmd {
+
+ WINBINDD_INTERFACE_VERSION, /* Always a well known value */
+
+ /* Get users and groups */
+
+ WINBINDD_GETPWNAM,
+ WINBINDD_GETPWUID,
+ WINBINDD_GETPWSID,
+ WINBINDD_GETGRNAM,
+ WINBINDD_GETGRGID,
+ WINBINDD_GETGROUPS,
+
+ /* Enumerate users and groups */
+
+ WINBINDD_SETPWENT,
+ WINBINDD_ENDPWENT,
+ WINBINDD_GETPWENT,
+ WINBINDD_SETGRENT,
+ WINBINDD_ENDGRENT,
+ WINBINDD_GETGRENT,
+
+ /* PAM authenticate and password change */
+
+ WINBINDD_PAM_AUTH,
+ WINBINDD_PAM_AUTH_CRAP,
+ WINBINDD_PAM_CHAUTHTOK,
+ WINBINDD_PAM_LOGOFF,
+ WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP,
+
+ /* List various things */
+
+ WINBINDD_LIST_USERS, /* List w/o rid->id mapping */
+ WINBINDD_LIST_GROUPS, /* Ditto */
+ WINBINDD_LIST_TRUSTDOM,
+
+ /* SID conversion */
+
+ WINBINDD_LOOKUPSID,
+ WINBINDD_LOOKUPNAME,
+ WINBINDD_LOOKUPRIDS,
+ WINBINDD_LOOKUPSIDS,
+
+ /* Lookup functions */
+
+ WINBINDD_SIDS_TO_XIDS,
+ WINBINDD_XIDS_TO_SIDS,
+
+ WINBINDD_ALLOCATE_UID,
+ WINBINDD_ALLOCATE_GID,
+
+ /* Miscellaneous other stuff */
+
+ WINBINDD_CHECK_MACHACC, /* Check machine account pw works */
+ WINBINDD_CHANGE_MACHACC, /* Change machine account pw */
+ WINBINDD_PING_DC, /* Ping the DC through NETLOGON */
+ WINBINDD_PING, /* Just tell me winbind is running */
+ WINBINDD_INFO, /* Various bit of info. Currently just tidbits */
+ WINBINDD_DOMAIN_NAME, /* The domain this winbind server is a member of (lp_workgroup()) */
+
+ WINBINDD_DOMAIN_INFO, /* Most of what we know from
+ struct winbindd_domain */
+ WINBINDD_GETDCNAME, /* Issue a GetDCName Request */
+ WINBINDD_DSGETDCNAME, /* Issue a DsGetDCName Request */
+ WINBINDD_DC_INFO, /* Which DC are we connected to? */
+
+ WINBINDD_SHOW_SEQUENCE, /* display sequence numbers of domains */
+
+ /* WINS commands */
+
+ WINBINDD_WINS_BYIP,
+ WINBINDD_WINS_BYNAME,
+
+ /* this is like GETGRENT but gives an empty group list */
+ WINBINDD_GETGRLST,
+
+ WINBINDD_NETBIOS_NAME, /* The netbios name of the server */
+
+ /* find the location of our privileged pipe */
+ WINBINDD_PRIV_PIPE_DIR,
+
+ /* return a list of group sids for a user sid */
+ WINBINDD_GETUSERSIDS,
+
+ /* Various group queries */
+ WINBINDD_GETUSERDOMGROUPS,
+
+ /* lookup local groups */
+ WINBINDD_GETSIDALIASES,
+
+ /* Blocking calls that are not allowed on the main winbind pipe, only
+ * between parent and children */
+ WINBINDD_DUAL_SID2UID,
+ WINBINDD_DUAL_SID2GID,
+ WINBINDD_DUAL_SIDS2XIDS,
+ WINBINDD_DUAL_UID2SID,
+ WINBINDD_DUAL_GID2SID,
+
+ /* Wrapper around possibly blocking unix nss calls */
+ WINBINDD_DUAL_USERINFO,
+ WINBINDD_DUAL_GETSIDALIASES,
+
+ WINBINDD_DUAL_NDRCMD,
+
+ /* Complete the challenge phase of the NTLM authentication
+ protocol using cached password. */
+ WINBINDD_CCACHE_NTLMAUTH,
+ WINBINDD_CCACHE_SAVE,
+
+ WINBINDD_NUM_CMDS
+};
+
+typedef struct winbindd_pw {
+ fstring pw_name;
+ fstring pw_passwd;
+ uid_t pw_uid;
+ gid_t pw_gid;
+ fstring pw_gecos;
+ fstring pw_dir;
+ fstring pw_shell;
+} WINBINDD_PW;
+
+
+typedef struct winbindd_gr {
+ fstring gr_name;
+ fstring gr_passwd;
+ gid_t gr_gid;
+ uint32_t num_gr_mem;
+ uint32_t gr_mem_ofs; /* offset to group membership */
+} WINBINDD_GR;
+
+/* Request flags */
+#define WBFLAG_PAM_INFO3_NDR 0x00000001
+#define WBFLAG_PAM_INFO3_TEXT 0x00000002
+#define WBFLAG_PAM_USER_SESSION_KEY 0x00000004
+#define WBFLAG_PAM_LMKEY 0x00000008
+#define WBFLAG_PAM_CONTACT_TRUSTDOM 0x00000010
+#define WBFLAG_QUERY_ONLY 0x00000020 /* not used */
+#define WBFLAG_PAM_AUTH_PAC 0x00000040
+#define WBFLAG_PAM_UNIX_NAME 0x00000080
+#define WBFLAG_PAM_AFS_TOKEN 0x00000100
+#define WBFLAG_PAM_NT_STATUS_SQUASH 0x00000200
+/* This is a flag that can only be sent from parent to child */
+#define WBFLAG_IS_PRIVILEGED 0x00000400 /* not used */
+/* Flag to say this is a winbindd internal send - don't recurse. */
+#define WBFLAG_RECURSE 0x00000800
+#define WBFLAG_PAM_KRB5 0x00001000
+#define WBFLAG_PAM_FALLBACK_AFTER_KRB5 0x00002000
+#define WBFLAG_PAM_CACHED_LOGIN 0x00004000
+#define WBFLAG_PAM_GET_PWD_POLICY 0x00008000
+/* Flag to tell winbind the NTLMv2 blob is too big for the struct and is in the
+ * extra_data field */
+#define WBFLAG_BIG_NTLMV2_BLOB 0x00010000
+#define WBFLAG_FROM_NSS 0x00020000
+
+#define WINBINDD_MAX_EXTRA_DATA (128*1024)
+
+/* Winbind request structure */
+
+/*******************************************************************************
+ * This structure MUST be the same size in the 32bit and 64bit builds
+ * for compatibility between /lib64/libnss_winbind.so and /lib/libnss_winbind.so
+ *
+ * DO NOT CHANGE THIS STRUCTURE WITHOUT TESTING THE 32BIT NSS LIB AGAINST
+ * A 64BIT WINBINDD --jerry
+ ******************************************************************************/
+
+struct winbindd_request {
+ uint32_t length;
+ enum winbindd_cmd cmd; /* Winbindd command to execute */
+ enum winbindd_cmd original_cmd; /* Original Winbindd command
+ issued to parent process */
+ pid_t pid; /* pid of calling process */
+ uint32_t wb_flags; /* generic flags */
+ uint32_t flags; /* flags relevant *only* to a given request */
+ fstring domain_name; /* name of domain for which the request applies */
+ char client_name[32]; /* The client process sending the request */
+ uint64_t traceid; /* debug traceid is sent from parent to child */
+
+ union {
+ fstring winsreq; /* WINS request */
+ fstring username; /* getpwnam */
+ fstring groupname; /* getgrnam */
+ uid_t uid; /* getpwuid, uid_to_sid */
+ gid_t gid; /* getgrgid, gid_to_sid */
+ uint32_t ndrcmd;
+ struct {
+ /* We deliberately don't split into domain/user to
+ avoid having the client know what the separator
+ character is. */
+ fstring user;
+ fstring pass;
+ char require_membership_of_sid[1024];
+ fstring krb5_cc_type;
+ uid_t uid;
+ } auth; /* pam_winbind auth module */
+ struct {
+ uint8_t chal[8];
+ uint32_t logon_parameters;
+ fstring user;
+ fstring domain;
+ fstring lm_resp;
+ uint32_t lm_resp_len;
+ fstring nt_resp;
+ uint32_t nt_resp_len;
+ fstring workstation;
+ fstring require_membership_of_sid;
+ } auth_crap;
+ struct {
+ fstring user;
+ fstring oldpass;
+ fstring newpass;
+ } chauthtok; /* pam_winbind passwd module */
+ struct {
+ fstring user;
+ fstring domain;
+ uint8_t new_nt_pswd[516];
+ uint16_t new_nt_pswd_len;
+ uint8_t old_nt_hash_enc[16];
+ uint16_t old_nt_hash_enc_len;
+ uint8_t new_lm_pswd[516];
+ uint16_t new_lm_pswd_len;
+ uint8_t old_lm_hash_enc[16];
+ uint16_t old_lm_hash_enc_len;
+ } chng_pswd_auth_crap;/* pam_winbind passwd module */
+ struct {
+ fstring user;
+ fstring krb5ccname;
+ uid_t uid;
+ } logoff; /* pam_winbind session module */
+ fstring sid; /* lookupsid, sid_to_[ug]id */
+ struct {
+ fstring dom_name; /* lookupname */
+ fstring name;
+ } name;
+ uint32_t num_entries; /* getpwent, getgrent */
+ struct {
+ fstring username;
+ fstring groupname;
+ } acct_mgt;
+ struct {
+ bool is_primary;
+ fstring dcname;
+ } init_conn;
+ struct {
+ fstring sid;
+ fstring name;
+ } dual_sid2id;
+ struct {
+ fstring sid;
+ uint32_t type;
+ uint32_t id;
+ } dual_idmapset;
+ bool list_all_domains;
+
+ struct {
+ uid_t uid;
+ fstring user;
+ /* the effective uid of the client, must be the uid for 'user'.
+ This is checked by the main daemon, trusted by children. */
+ /* if the blobs are length zero, then this doesn't
+ produce an actual challenge response. It merely
+ succeeds if there are cached credentials available
+ that could be used. */
+ uint32_t initial_blob_len; /* blobs in extra_data */
+ uint32_t challenge_blob_len;
+ } ccache_ntlm_auth;
+ struct {
+ uid_t uid;
+ fstring user;
+ fstring pass;
+ } ccache_save;
+ struct {
+ fstring domain_name;
+ fstring domain_guid;
+ fstring site_name;
+ uint32_t flags;
+ } dsgetdcname;
+
+ /* padding -- needed to fix alignment between 32bit and 64bit libs.
+ The size is the sizeof the union without the padding aligned on
+ an 8 byte boundary. --jerry */
+
+ char padding[1800];
+ } data;
+ union {
+ SMB_TIME_T padding;
+ char *data;
+ } extra_data;
+ uint32_t extra_len;
+ char null_term;
+};
+
+/* Response values */
+
+enum winbindd_result {
+ WINBINDD_ERROR,
+ WINBINDD_PENDING,
+ WINBINDD_OK
+};
+
+/* Winbind response structure */
+
+/*******************************************************************************
+ * This structure MUST be the same size in the 32bit and 64bit builds
+ * for compatibility between /lib64/libnss_winbind.so and /lib/libnss_winbind.so
+ *
+ * DO NOT CHANGE THIS STRUCTURE WITHOUT TESTING THE 32BIT NSS LIB AGAINST
+ * A 64BIT WINBINDD --jerry
+ ******************************************************************************/
+
+struct winbindd_response {
+
+ /* Header information */
+
+ uint32_t length; /* Length of response */
+ enum winbindd_result result; /* Result code */
+
+ /* Fixed length return data */
+
+ union {
+ int interface_version; /* Try to ensure this is always in the same spot... */
+
+ fstring winsresp; /* WINS response */
+
+ /* getpwnam, getpwuid */
+
+ struct winbindd_pw pw;
+
+ /* getgrnam, getgrgid */
+
+ struct winbindd_gr gr;
+
+ uint32_t num_entries; /* getpwent, getgrent */
+ struct winbindd_sid {
+ fstring sid; /* lookupname, [ug]id_to_sid */
+ int type;
+ } sid;
+ struct winbindd_name {
+ fstring dom_name; /* lookupsid */
+ fstring name;
+ int type;
+ } name;
+ uid_t uid; /* sid_to_uid */
+ gid_t gid; /* sid_to_gid */
+ struct winbindd_info {
+ char winbind_separator;
+ fstring samba_version;
+ } info;
+ fstring domain_name;
+ fstring netbios_name;
+ fstring dc_name;
+
+ struct auth_reply {
+ uint32_t nt_status;
+ fstring nt_status_string;
+ fstring error_string;
+ int pam_error;
+ char user_session_key[16];
+ char first_8_lm_hash[8];
+ fstring krb5ccname;
+ uint32_t reject_reason;
+ uint8_t authoritative;
+ uint8_t padding[1];
+ uint16_t validation_level;
+ struct policy_settings {
+ uint32_t min_length_password;
+ uint32_t password_history;
+ uint32_t password_properties;
+ uint32_t padding;
+ SMB_TIME_T expire;
+ SMB_TIME_T min_passwordage;
+ } policy;
+ struct info3_text {
+ SMB_TIME_T logon_time;
+ SMB_TIME_T logoff_time;
+ SMB_TIME_T kickoff_time;
+ SMB_TIME_T pass_last_set_time;
+ SMB_TIME_T pass_can_change_time;
+ SMB_TIME_T pass_must_change_time;
+ uint32_t logon_count;
+ uint32_t bad_pw_count;
+ uint32_t user_rid;
+ uint32_t group_rid;
+ uint32_t num_groups;
+ uint32_t user_flgs;
+ uint32_t acct_flags;
+ uint32_t num_other_sids;
+ fstring dom_sid;
+ fstring user_name;
+ fstring full_name;
+ fstring logon_script;
+ fstring profile_path;
+ fstring home_dir;
+ fstring dir_drive;
+ fstring logon_srv;
+ fstring logon_dom;
+ } info3;
+ struct info6_text {
+ fstring dns_domainname;
+ fstring principal_name;
+ } info6;
+ fstring unix_username;
+ } auth;
+ struct {
+ fstring name;
+ fstring alt_name;
+ fstring sid;
+ bool native_mode;
+ bool active_directory;
+ bool primary;
+ } domain_info;
+ uint32_t sequence_number;
+ struct {
+ fstring acct_name;
+ fstring full_name;
+ fstring homedir;
+ fstring shell;
+ uint32_t primary_gid;
+ uint32_t group_rid;
+ } user_info;
+ struct {
+ uint8_t session_key[16];
+ uint32_t auth_blob_len; /* blob in extra_data */
+ uint8_t new_spnego;
+ } ccache_ntlm_auth;
+ struct {
+ fstring dc_unc;
+ fstring dc_address;
+ uint32_t dc_address_type;
+ fstring domain_guid;
+ fstring domain_name;
+ fstring forest_name;
+ uint32_t dc_flags;
+ fstring dc_site_name;
+ fstring client_site_name;
+ } dsgetdcname;
+ } data;
+
+ /* Variable length return data */
+
+ union {
+ SMB_TIME_T padding;
+ void *data;
+ } extra_data;
+};
+
+/* Free a response structure */
+
+static inline void winbindd_free_response(struct winbindd_response *response)
+{
+ /* Free any allocated extra_data */
+
+ if (response)
+ SAFE_FREE(response->extra_data.data);
+}
+
+#endif
diff --git a/nsswitch/wins.c b/nsswitch/wins.c
new file mode 100644
index 0000000..a310477
--- /dev/null
+++ b/nsswitch/wins.c
@@ -0,0 +1,400 @@
+/*
+ Unix SMB/CIFS implementation.
+ a WINS nsswitch module
+ Copyright (C) Andrew Tridgell 1999
+
+ 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 "includes.h"
+#include "nsswitch/winbind_client.h"
+#include "nsswitch/libwbclient/wbclient.h"
+#include "lib/util/string_wrappers.h"
+
+#ifdef HAVE_NS_API_H
+
+#include <ns_daemon.h>
+#endif
+
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+
+#ifdef HAVE_PTHREAD
+static pthread_mutex_t wins_nss_mutex = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+#ifndef INADDRSZ
+#define INADDRSZ 4
+#endif
+
+#ifndef NETDB_INTERNAL
+#define NETDB_INTERNAL -1
+#endif
+
+#ifndef NETDB_SUCCESS
+#define NETDB_SUCCESS 0
+#endif
+
+_PUBLIC_ON_LINUX_
+NSS_STATUS _nss_wins_gethostbyname_r(const char *hostname,
+ struct hostent *he,
+ char *buffer,
+ size_t buflen,
+ int *errnop,
+ int *h_errnop);
+NSS_STATUS _nss_wins_gethostbyname2_r(const char *name,
+ int af,
+ struct hostent *he,
+ char *buffer,
+ size_t buflen,
+ int *errnop,
+ int *h_errnop);
+
+static char *lookup_byname_backend(const char *name)
+{
+ const char *p;
+ char *ip, *ipp;
+ size_t nbt_len;
+ wbcErr result;
+
+ nbt_len = strlen(name);
+ if (nbt_len > MAX_NETBIOSNAME_LEN - 1) {
+ return NULL;
+ }
+ p = strchr(name, '.');
+ if (p != NULL) {
+ return NULL;
+ }
+
+ wbcSetClientProcessName("nss_wins");
+ result = wbcResolveWinsByName(name, &ip);
+ if (result != WBC_ERR_SUCCESS) {
+ return NULL;
+ }
+
+ ipp = strchr(ip, '\t');
+ if (ipp != NULL) {
+ *ipp = '\0';
+ }
+
+ return ip;
+}
+
+#ifdef HAVE_NS_API_H
+
+static char *lookup_byaddr_backend(const char *ip)
+{
+ wbcErr result;
+ char *name = NULL;
+
+ wbcSetClientProcessName("nss_wins");
+ result = wbcResolveWinsByIP(ip, &name);
+ if (result != WBC_ERR_SUCCESS) {
+ return NULL;
+ }
+
+ return name;
+}
+
+/* IRIX version */
+
+int init(void)
+{
+ bool ok;
+
+ nsd_logprintf(NSD_LOG_MIN, "entering init (wins)\n");
+
+ ok = nss_wins_init();
+ if (!ok) {
+ return NSD_ERROR;
+ }
+
+ return NSD_OK;
+}
+
+int lookup(nsd_file_t *rq)
+{
+ char *map;
+ char *key;
+ char *addr;
+ int i, count, len, size;
+ char response[1024];
+ bool found = False;
+
+ nsd_logprintf(NSD_LOG_MIN, "entering lookup (wins)\n");
+ if (! rq)
+ return NSD_ERROR;
+
+ map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
+ if (! map) {
+ rq->f_status = NS_FATAL;
+ return NSD_ERROR;
+ }
+
+ key = nsd_attr_fetch_string(rq->f_attrs, "key", (char*)0);
+ if (! key || ! *key) {
+ rq->f_status = NS_FATAL;
+ return NSD_ERROR;
+ }
+
+ response[0] = '\0';
+ len = sizeof(response) - 2;
+
+ /*
+ * response needs to be a string of the following format
+ * ip_address[ ip_address]*\tname[ alias]*
+ */
+ if (strcasecmp_m(map,"hosts.byaddr") == 0) {
+ char *name;
+
+ name = lookup_byaddr_backend(key);
+ if (name != NULL) {
+ size = strlen(key) + 1;
+ if (size > len) {
+ return NSD_ERROR;
+ }
+ len -= size;
+ strncat(response,key,size);
+ strncat(response,"\t",1);
+
+ size = strlen(name) + 1;
+ if (size > len) {
+ return NSD_ERROR;
+ }
+ len -= size;
+ strncat(response, name, size);
+ strncat(response, " ", 1);
+ found = True;
+ }
+ response[strlen(response)-1] = '\n';
+ } else if (strcasecmp_m(map,"hosts.byname") == 0) {
+ char *ip;
+
+ ip = lookup_byname_backend(key);
+ if (ip != NULL) {
+ size = strlen(ip) + 1;
+ if (size > len) {
+ wbcFreeMemory(ip);
+ return NSD_ERROR;
+ }
+ len -= size;
+ strncat(response,ip,size);
+ strncat(response,"\t",1);
+ size = strlen(key) + 1;
+ wbcFreeMemory(ip);
+ if (size > len) {
+ return NSD_ERROR;
+ }
+ strncat(response,key,size);
+ strncat(response,"\n",1);
+
+ found = True;
+ }
+ }
+
+ if (found) {
+ nsd_logprintf(NSD_LOG_LOW, "lookup (wins %s) %s\n",map,response);
+ nsd_set_result(rq,NS_SUCCESS,response,strlen(response),VOLATILE);
+ return NSD_OK;
+ }
+ nsd_logprintf(NSD_LOG_LOW, "lookup (wins) not found\n");
+ rq->f_status = NS_NOTFOUND;
+ return NSD_NEXT;
+}
+
+#else
+
+/* Allocate some space from the nss static buffer. The buffer and buflen
+ are the pointers passed in by the C library to the _nss_*_*
+ functions. */
+
+static char *get_static(char **buffer, size_t *buflen, size_t len)
+{
+ char *result;
+
+ /* Error check. We return false if things aren't set up right, or
+ there isn't enough buffer space left. */
+
+ if ((buffer == NULL) || (buflen == NULL) || (*buflen < len)) {
+ return NULL;
+ }
+
+ /* Return an index into the static buffer */
+
+ result = *buffer;
+ *buffer += len;
+ *buflen -= len;
+
+ return result;
+}
+
+/****************************************************************************
+gethostbyname() - we ignore any domain portion of the name and only
+handle names that are at most 15 characters long
+ **************************************************************************/
+_PUBLIC_ON_LINUX_
+NSS_STATUS
+_nss_wins_gethostbyname_r(const char *hostname,
+ struct hostent *he,
+ char *buffer,
+ size_t buflen,
+ int *errnop,
+ int *h_errnop)
+{
+ NSS_STATUS nss_status = NSS_STATUS_SUCCESS;
+ char *ip;
+ struct in_addr in;
+ int i;
+ fstring name;
+ size_t namelen;
+ int rc;
+
+#ifdef HAVE_PTHREAD
+ pthread_mutex_lock(&wins_nss_mutex);
+#endif
+
+ memset(he, '\0', sizeof(*he));
+ fstrcpy(name, hostname);
+
+ /* Do lookup */
+
+ ip = lookup_byname_backend(name);
+ if (ip == NULL) {
+ *h_errnop = HOST_NOT_FOUND;
+ nss_status = NSS_STATUS_NOTFOUND;
+ goto out;
+ }
+
+ rc = inet_pton(AF_INET, ip, &in);
+ wbcFreeMemory(ip);
+ if (rc == 0) {
+ *errnop = errno;
+ *h_errnop = NETDB_INTERNAL;
+ nss_status = NSS_STATUS_TRYAGAIN;
+ goto out;
+ }
+
+ /* Copy h_name */
+
+ namelen = strlen(name) + 1;
+
+ if ((he->h_name = get_static(&buffer, &buflen, namelen)) == NULL) {
+ *errnop = EAGAIN;
+ *h_errnop = NETDB_INTERNAL;
+ nss_status = NSS_STATUS_TRYAGAIN;
+ goto out;
+ }
+
+ memcpy(he->h_name, name, namelen);
+
+ /* Copy h_addr_list, align to pointer boundary first */
+
+ if ((i = (unsigned long)(buffer) % sizeof(char*)) != 0)
+ i = sizeof(char*) - i;
+
+ if (get_static(&buffer, &buflen, i) == NULL) {
+ *errnop = EAGAIN;
+ *h_errnop = NETDB_INTERNAL;
+ nss_status = NSS_STATUS_TRYAGAIN;
+ goto out;
+ }
+
+ if ((he->h_addr_list = (char **)get_static(
+ &buffer, &buflen, 2 * sizeof(char *))) == NULL) {
+ *errnop = EAGAIN;
+ *h_errnop = NETDB_INTERNAL;
+ nss_status = NSS_STATUS_TRYAGAIN;
+ goto out;
+ }
+
+ if ((he->h_addr_list[0] = get_static(&buffer, &buflen,
+ INADDRSZ)) == NULL) {
+ *errnop = EAGAIN;
+ *h_errnop = NETDB_INTERNAL;
+ nss_status = NSS_STATUS_TRYAGAIN;
+ goto out;
+ }
+
+ memcpy(he->h_addr_list[0], &in, INADDRSZ);
+
+ he->h_addr_list[1] = NULL;
+
+ /* Set h_addr_type and h_length */
+
+ he->h_addrtype = AF_INET;
+ he->h_length = INADDRSZ;
+
+ /* Set h_aliases */
+
+ if ((i = (unsigned long)(buffer) % sizeof(char*)) != 0)
+ i = sizeof(char*) - i;
+
+ if (get_static(&buffer, &buflen, i) == NULL) {
+ *errnop = EAGAIN;
+ *h_errnop = NETDB_INTERNAL;
+ nss_status = NSS_STATUS_TRYAGAIN;
+ goto out;
+ }
+
+ if ((he->h_aliases = (char **)get_static(
+ &buffer, &buflen, sizeof(char *))) == NULL) {
+ *errnop = EAGAIN;
+ *h_errnop = NETDB_INTERNAL;
+ nss_status = NSS_STATUS_TRYAGAIN;
+ goto out;
+ }
+
+ he->h_aliases[0] = NULL;
+
+ *h_errnop = NETDB_SUCCESS;
+ nss_status = NSS_STATUS_SUCCESS;
+
+ out:
+
+#ifdef HAVE_PTHREAD
+ pthread_mutex_unlock(&wins_nss_mutex);
+#endif
+ return nss_status;
+}
+
+
+_PUBLIC_ON_LINUX_
+NSS_STATUS
+_nss_wins_gethostbyname2_r(const char *name,
+ int af,
+ struct hostent *he,
+ char *buffer,
+ size_t buflen,
+ int *errnop,
+ int *h_errnop)
+{
+ NSS_STATUS nss_status;
+
+ if(af!=AF_INET) {
+ *errnop = EAFNOSUPPORT;
+ *h_errnop = NO_DATA;
+ nss_status = NSS_STATUS_UNAVAIL;
+ } else {
+ nss_status = _nss_wins_gethostbyname_r(name,
+ he,
+ buffer,
+ buflen,
+ errnop,
+ h_errnop);
+ }
+ return nss_status;
+}
+#endif
diff --git a/nsswitch/wins_freebsd.c b/nsswitch/wins_freebsd.c
new file mode 100644
index 0000000..8637ce6
--- /dev/null
+++ b/nsswitch/wins_freebsd.c
@@ -0,0 +1,81 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Timur I. Bakeyev 2007
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "winbind_client.h"
+
+extern enum nss_status _nss_wins_gethostbyname2_r(const char *name, int af, struct hostent *he,
+ char *buffer, size_t buflen, int *h_errnop);
+
+ns_mtab *nss_module_register(const char *source, unsigned int *, nss_module_unregister_fn *);
+
+static NSS_METHOD_PROTOTYPE(__nss_wins_freebsd_gethostbyname2_r);
+
+static ns_mtab methods[] =
+{
+ { NSDB_HOSTS, "getaddrinfo", NULL, NULL },
+ { NSDB_HOSTS, "ghbyname", NULL, NULL },
+ { NSDB_HOSTS, "ghbyaddr", NULL, NULL },
+ { NSDB_HOSTS, "gethostbyaddr_r", NULL, NULL },
+ { NSDB_HOSTS, "gethostbyname2_r", __nss_wins_freebsd_gethostbyname2_r, _nss_wins_gethostbyname2_r },
+ { NSDB_HOSTS, "getnetbyname_r", NULL, NULL },
+ { NSDB_HOSTS, "getnetbyaddr_r", NULL, NULL },
+ { NSDB_HOSTS, "gethostbyname", NULL, NULL },
+ { NSDB_HOSTS, "gethostbyaddr", NULL, NULL },
+ { NSDB_HOSTS, "getnetbyname", NULL, NULL },
+ { NSDB_HOSTS, "getnetbyaddr", NULL, NULL }
+};
+
+static int
+__nss_wins_freebsd_gethostbyname2_r(void *retval, void *mdata, va_list ap)
+{
+ int (*fn)(const char *, int, struct hostent *, char *, size_t, int *);
+ const char *hostname;
+ int af;
+ struct hostent *he;
+ char *buffer;
+ size_t buflen;
+ int *h_errnop;
+ enum nss_status status;
+
+ fn = mdata;
+ hostname = va_arg(ap, const char *);
+ af = va_arg(ap, int);
+ he = va_arg(ap, struct hostent *);
+ buffer = va_arg(ap, char *);
+ buflen = va_arg(ap, size_t);
+ h_errnop = va_arg(ap, int *);
+
+ status = fn(hostname, af, he, buffer, buflen, h_errnop);
+ status = __nss_compat_result(status, *h_errnop);
+ if (status == NS_SUCCESS)
+ *(struct hostent **)retval = he;
+
+ return (status);
+}
+
+_PUBLIC_ ns_mtab *
+nss_module_register(const char *source __unused, unsigned int *mtabsize,
+ nss_module_unregister_fn *unreg)
+{
+ *mtabsize = sizeof(methods) / sizeof(methods[0]);
+ *unreg = NULL;
+ return (methods);
+}
diff --git a/nsswitch/wscript_build b/nsswitch/wscript_build
new file mode 100644
index 0000000..3247b6c
--- /dev/null
+++ b/nsswitch/wscript_build
@@ -0,0 +1,176 @@
+#!/usr/bin/env python
+from waflib import Utils
+import sys
+host_os = sys.platform
+
+bld.SAMBA_BINARY('nsstest',
+ source='nsstest.c',
+ deps='replace dl',
+ for_selftest=True
+ )
+
+if bld.CONFIG_SET('HAVE_PTHREAD'):
+ bld.SAMBA_BINARY('stress-nss-libwbclient',
+ source='stress-nss-libwbclient.c',
+ deps='wbclient pthread',
+ for_selftest=True
+ )
+
+# The nss_wrapper code relies strictly on the linux implementation and
+# name, so compile but do not install a copy under this name.
+bld.SAMBA_PLUGIN('nss_wrapper_winbind',
+ cflags='-D_PUBLIC_ON_LINUX_=_PUBLIC_',
+ source='winbind_nss_linux.c',
+ deps='wbclient',
+ realname='libnss_wrapper_winbind.so.2',
+ install=False,
+ vnum='2')
+
+# FIXME: original was *linux* | gnu* | k*bsd*-gnu | kopensolaris*-gnu)
+# the search for .rfind('gnu') covers gnu* and *-gnu is that too broad?
+
+if (Utils.unversioned_sys_platform() == 'linux' or (host_os.rfind('gnu') > -1)):
+ bld.SAMBA_PLUGIN('nss_winbind',
+ keep_underscore=True,
+ cflags='-D_PUBLIC_ON_LINUX_=_PUBLIC_',
+ source='winbind_nss_linux.c',
+ deps='wbclient',
+ realname='libnss_winbind.so.2',
+ soname='libnss_winbind.so.2',
+ vnum='2')
+
+ bld.SAMBA3_PLUGIN('nss_wins',
+ keep_underscore=True,
+ cflags='-D_PUBLIC_ON_LINUX_=_PUBLIC_',
+ source='wins.c',
+ deps='wbclient replace',
+ realname='libnss_wins.so.2',
+ soname='libnss_wins.so.2',
+ vnum='2')
+elif (host_os.rfind('freebsd') > -1):
+ # FreeBSD winbind client is implemented as a wrapper around
+ # the Linux version.
+ bld.SAMBA_PLUGIN('nss_winbind',
+ source='winbind_nss_linux.c winbind_nss_freebsd.c',
+ deps='wbclient',
+ realname='nss_winbind.so.1',
+ vnum='1')
+
+ bld.SAMBA3_PLUGIN('nss_wins',
+ source='wins.c wins_freebsd.c',
+ deps='''wbclient''',
+ realname='nss_wins.so.1',
+ vnum='1')
+
+elif (host_os.rfind('netbsd') > -1):
+ # NetBSD winbind client is implemented as a wrapper
+ # around the Linux version. It needs getpwent_r() to
+ # indicate libc's use of the correct nsdispatch API.
+
+ if bld.CONFIG_SET("HAVE_GETPWENT_R"):
+ bld.SAMBA_PLUGIN('nss_winbind',
+ source='winbind_nss_linux.c winbind_nss_netbsd.c',
+ deps='wbclient',
+ realname='libnss_winbind.so')
+elif Utils.unversioned_sys_platform() == 'sunos':
+ bld.SAMBA_PLUGIN('nss_winbind',
+ source='winbind_nss_solaris.c winbind_nss_linux.c',
+ deps='wbclient',
+ realname='nss_winbind.so.1',
+ vnum='1')
+elif (host_os.rfind('hpux') > -1):
+ bld.SAMBA_PLUGIN('nss_winbind',
+ source='winbind_nss_linux.c',
+ deps='wbclient',
+ realname='libnss_winbind.so')
+elif (host_os.rfind('aix') > -1):
+ bld.SAMBA_PLUGIN('nss_winbind',
+ source='winbind_nss_aix.c',
+ deps='wbclient',
+ realname='WINBIND')
+
+if bld.CONFIG_SET('WITH_PAM_MODULES') and bld.CONFIG_SET('HAVE_PAM_START'):
+ bld.SAMBA_PLUGIN('pamwinbind',
+ source='pam_winbind.c',
+ deps='talloc wbclient tiniparser pam samba_intl',
+ cflags='-DLOCALEDIR=\"%s/locale\"' % bld.env.DATADIR,
+ realname='pam_winbind.so',
+ install_path='${PAMMODULESDIR}'
+ )
+
+if bld.CONFIG_GET("USING_SYSTEM_KRB5"):
+ # If we build locator plugins for a
+ # system library we should use builtin
+ # linking of our own subsystems,
+ # while we may link to the system
+ # krb5 libraries.
+ winbind_krb5_require_builtin_deps = True
+else:
+ # If we build locator plugins for our own heimdal
+ # version we don't want to do builtin linking.
+ # As we're already using private libraries
+ # it's not a problem...
+ winbind_krb5_require_builtin_deps = False
+
+if bld.CONFIG_SET('HAVE_KRB5_LOCATE_PLUGIN_H'):
+ bld.SAMBA_PLUGIN('winbind_krb5_locator',
+ source='krb5_plugin/winbind_krb5_locator.c',
+ deps='wbclient krb5 com_err',
+ require_builtin_deps=winbind_krb5_require_builtin_deps,
+ realname='winbind_krb5_locator.so',
+ install_path='${MODULESDIR}/krb5')
+
+if bld.CONFIG_SET('HAVE_KRB5_LOCATE_PLUGIN_H'):
+ # libkrb5.so scans it's plugin directories for files
+ # and calls dlopen()/dlsym() on them. The actual path
+ # depends on MIT vs. Heimdal.
+ #
+ # The async_dns_krb5_locator don't use winbind,
+ # instead it uses almost all of samba directly,
+ # which means everything will be injected
+ # into all processes using the libkrb5.so.
+ #
+ # See https://bugzilla.samba.org/show_bug.cgi?id=14780
+ # why this is a bad idea.
+ #
+ # We install this plugin (and also the other krb5 plugins)
+ # under Samba's MODULESDIR, it's not available to
+ # libkrb5.so by default.
+ #
+ # Packagers should leave it that way and allow admins
+ # to create symlinks for the plugins they actually want
+ # to be used.
+ bld.SAMBA_PLUGIN('async_dns_krb5_locator',
+ source='krb5_plugin/async_dns_krb5_locator.c',
+ deps='''
+ talloc
+ addns
+ samba_intl
+ libsmb
+ smbconf
+ KRBCLIENT
+ smbd_base
+ krb5
+ com_err
+ ''',
+ require_builtin_deps=False,
+ realname='async_dns_krb5_locator.so',
+ install_path='${MODULESDIR}/krb5')
+
+if bld.CONFIG_SET('HAVE_KRB5_LOCALAUTH_PLUGIN_H'):
+ bld.SAMBA_PLUGIN('winbind_krb5_localauth',
+ source='krb5_plugin/winbind_krb5_localauth.c',
+ deps='wbclient krb5 com_err',
+ require_builtin_deps=winbind_krb5_require_builtin_deps,
+ realname='winbind_krb5_localauth.so',
+ install_path='${MODULESDIR}/krb5')
+
+bld.SAMBA_SUBSYSTEM('WB_REQTRANS',
+ source='wb_reqtrans.c',
+ deps='talloc tevent LIBASYNC_REQ'
+ )
+
+bld.SAMBA_BINARY('wbinfo',
+ source='wbinfo.c',
+ deps='samba-util LIBCLI_AUTH popt cmdline wbclient LIBAFS_SETTOKEN'
+ )
diff --git a/nsswitch/wscript_configure b/nsswitch/wscript_configure
new file mode 100644
index 0000000..48685a8
--- /dev/null
+++ b/nsswitch/wscript_configure
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+
+conf.CHECK_HEADERS('nss.h nss_common.h ns_api.h')
+
+conf.CHECK_HEADERS('security/pam_appl.h security/pam_modules.h pam/pam_modules.h', together=True)
+conf.CHECK_FUNCS_IN('pam_start', 'pam', checklibc=True, headers='security/pam_appl.h')
+
+# Solaris 10 does have new member in nss_XbyY_key
+conf.CHECK_STRUCTURE_MEMBER('union nss_XbyY_key', 'ipnode.af_family',
+ define='HAVE_NSS_XBYY_KEY_IPNODE',
+ headers='nss_dbdefs.h')
+
+# Solaris has some extra fields in struct passwd that need to be
+# initialised otherwise nscd crashes.
+
+conf.CHECK_STRUCTURE_MEMBER('struct passwd', 'pw_comment',
+ define='HAVE_PASSWD_PW_COMMENT',
+ headers='pwd.h')
+
+conf.CHECK_STRUCTURE_MEMBER('struct passwd', 'pw_age',
+ define='HAVE_PASSWD_PW_AGE',
+ headers='pwd.h')
+
+# AIX 4.3.x and 5.1 do not have as many members in
+# struct secmethod_table as AIX 5.2
+conf.CHECK_STRUCTURE_MEMBER('struct secmethod_table', 'method_attrlist',
+ define='HAVE_STRUCT_SECMETHOD_TABLE_METHOD_ATTRLIST',
+ headers='usersec.h')
+conf.CHECK_STRUCTURE_MEMBER('struct secmethod_table', 'method_version',
+ define='HAVE_STRUCT_SECMETHOD_TABLE_METHOD_VERSION',
+ headers='usersec.h')