diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
commit | 8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch) | |
tree | 4099e8021376c7d8c05bdf8503093d80e9c7bad0 /nsswitch | |
parent | Initial commit. (diff) | |
download | samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip |
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'nsswitch')
66 files changed, 28490 insertions, 0 deletions
diff --git a/nsswitch/b15464-testcase.c b/nsswitch/b15464-testcase.c new file mode 100644 index 0000000..decb474 --- /dev/null +++ b/nsswitch/b15464-testcase.c @@ -0,0 +1,77 @@ +#include "replace.h" +#include "system/wait.h" +#include "system/threads.h" +#include <assert.h> + +int main(int argc, const char *argv[]) +{ + pid_t pid; + int wstatus; + pthread_key_t k1; + pthread_key_t k2; + pthread_key_t k3; + char *val = NULL; + const char *nss_winbind = (argc >= 2 ? argv[1] : "bin/plugins/libnss_winbind.so.2"); + void *nss_winbind_handle = NULL; + union { + int (*fn)(void); + void *symbol; + } nss_winbind_endpwent = { .symbol = NULL, }; + + /* + * load and invoke something simple like + * _nss_winbind_endpwent in order to + * get the libnss_winbind internal going + */ + nss_winbind_handle = dlopen(nss_winbind, RTLD_NOW); + printf("%d: nss_winbind[%s] nss_winbind_handle[%p]\n", + getpid(), nss_winbind, nss_winbind_handle); + assert(nss_winbind_handle != NULL); + + nss_winbind_endpwent.symbol = dlsym(nss_winbind_handle, + "_nss_winbind_endpwent"); + printf("%d: nss_winbind_handle[%p] _nss_winbind_endpwent[%p]\n", + getpid(), nss_winbind_handle, nss_winbind_endpwent.symbol); + assert(nss_winbind_endpwent.symbol != NULL); + (void)nss_winbind_endpwent.fn(); + + val = malloc(1); + assert(val != NULL); + + pthread_key_create(&k1, NULL); + pthread_setspecific(k1, val); + printf("%d: k1=%d\n", getpid(), k1); + + pid = fork(); + if (pid) { + free(val); + wait(&wstatus); + return WEXITSTATUS(wstatus); + } + + pthread_key_create(&k2, NULL); + pthread_setspecific(k2, val); + + printf("%d: Hello after fork, k1=%d, k2=%d\n", getpid(), k1, k2); + + pid = fork(); + + if (pid) { + free(val); + wait(&wstatus); + return WEXITSTATUS(wstatus); + } + + pthread_key_create(&k3, NULL); + pthread_setspecific(k3, val); + + printf("%d: Hello after fork2, k1=%d, k2=%d, k3=%d\n", getpid(), k1, k2, k3); + + if (k1 == k2 || k2 == k3) { + printf("%d: FAIL inconsistent keys\n", getpid()); + return 1; + } + + printf("%d: OK consistent keys\n", getpid()); + return 0; +} 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.16.sigs b/nsswitch/libwbclient/ABI/wbclient-0.16.sigs new file mode 100644 index 0000000..f30c865 --- /dev/null +++ b/nsswitch/libwbclient/ABI/wbclient-0.16.sigs @@ -0,0 +1,135 @@ +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 **) +wbcChangeTrustCredentialsAt: wbcErr (const char *, 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 **) +wbcCtxChangeTrustCredentialsAt: wbcErr (struct wbcContext *, const char *, 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(¶ms, &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(¶ms, &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(¶ms, 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(¶ms, &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(¶ms.num_blobs, ¶ms.blobs, + "foo", 0, discard_const_p(uint8_t, "bar"), 4); + torture_assert_wbc_equal(tctx, ret, WBC_ERR_SUCCESS, + "%s", "wbcAddNamedBlob failed"); + + ret = wbcLogonUser(¶ms, &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(¶ms, &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(¶ms.num_blobs, ¶ms.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(¶ms, &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(¶ms.num_blobs, ¶ms.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(¶ms, &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(¶ms.num_blobs, ¶ms.blobs, "flags", 0, + (uint8_t *)&flags, sizeof(flags)); + torture_assert_wbc_equal(tctx, ret, WBC_ERR_SUCCESS, + "%s", "wbcAddNamedBlob failed"); + + ret = wbcLogonUser(¶ms, &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..786d578 --- /dev/null +++ b/nsswitch/libwbclient/wbc_pam.c @@ -0,0 +1,1508 @@ +/* + 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, ¶ms, 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); + winbindd_free_response(&sep_response); + } 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 wbcCtxChangeTrustCredentialsAt(struct wbcContext *ctx, + const char *domain, + const char *dcname, + 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); + } + + if (dcname != NULL) { + strncpy(request.data.init_conn.dcname, dcname, + sizeof(request.data.init_conn.dcname)-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 wbcChangeTrustCredentialsAt(const char *domain, + const char *dcname, + struct wbcAuthErrorInfo **error) +{ + return wbcCtxChangeTrustCredentialsAt(NULL, domain, dcname, error); +} + +_PUBLIC_ +wbcErr wbcCtxChangeTrustCredentials(struct wbcContext *ctx, + const char *domain, + struct wbcAuthErrorInfo **error) +{ + return wbcCtxChangeTrustCredentialsAt(ctx, domain, NULL, error); +} + +_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, ¶ms, + 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 = ¶ms->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 = ¶ms->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..fd82510 --- /dev/null +++ b/nsswitch/libwbclient/wbc_sid.c @@ -0,0 +1,1124 @@ +/* + 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) +{ + int i; + size_t 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..3ff1fe6 --- /dev/null +++ b/nsswitch/libwbclient/wbclient.h @@ -0,0 +1,2103 @@ +/* + 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() + * 0.16: Added wbcChangeTrustCredentialsAt() + **/ +#define WBCLIENT_MAJOR_VERSION 0 +#define WBCLIENT_MINOR_VERSION 16 +#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 Encrypted 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 implemented 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 implemented 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 implemented 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 implemented 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 implemented 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 implemented 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 change of the trust credentials for a specific domain + * on the optionally given domain controller + * + * @param *ctx wbclient Context + * @param *domain The name of the domain. + * @param *dcname The host name of the domain controller. + * @param error Output details on WBC_ERR_AUTH_ERROR + * + * @return #wbcErr + * + * @see wbcCtxChangeTrustCredentials() + **/ +wbcErr wbcCtxChangeTrustCredentialsAt(struct wbcContext *ctx, + const char *domain, + const char *dcname, + struct wbcAuthErrorInfo **error); + +/** + * @brief Trigger a change of the trust credentials for a specific domain + * on the optionally given domain controller + * + * @param *domain The name of the domain. + * @param *dcname The host name of the domain controller. + * @param error Output details on WBC_ERR_AUTH_ERROR + * + * @return #wbcErr + * + * @see wbcChangeTrustCredentials() + **/ +wbcErr wbcChangeTrustCredentialsAt(const char *domain, + const char *dcname, + 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..e81cd92 --- /dev/null +++ b/nsswitch/libwbclient/wscript @@ -0,0 +1,61 @@ +#!/usr/bin/env python + +from waflib import Options, Logs + +# Remember to also update wbclient.h +VERSION="0.16" + +# 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") + + wbclient_internal_deps = 'replace' + if bld.CONFIG_SET('HAVE_PTHREAD'): + wbclient_internal_deps += ' pthread' + + bld.SAMBA_SUBSYSTEM('wbclient-internal', + source='../wb_common.c', + deps=wbclient_internal_deps, + 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..5e5cf0a --- /dev/null +++ b/nsswitch/pam_winbind.c @@ -0,0 +1,3529 @@ +/* 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, + ¶ms, + &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; + char result; + + 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'; + } + + result = details->winbind_separator; + wbcFreeMemory(details); + return result; +} + + +/** + * 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) { + const 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..d9dc3b5 --- /dev/null +++ b/nsswitch/stress-nss-libwbclient.c @@ -0,0 +1,323 @@ +/* + 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> +#include <sys/socket.h> +#include <errno.h> +#include <assert.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]; + ssize_t nread, nwritten; + int p[2]; + int rc; + struct passwd pwd, *result; + pid_t pid; + + 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); + } + + rc = socketpair(AF_UNIX, SOCK_STREAM, 0, p); + if (rc != 0) { + state->fail = true; + return NULL; + } + + /* + * Check getpwnam_r() still works after a fork, + * both in parent and child. + */ + + pid = fork(); + if (pid == -1) { + return NULL; + } + if (pid == 0) { + /* Child */ + rc = getpwnam_r(state->username, + &pwd, + buf, + sizeof(buf), + &result); + if (rc != 0 || result == NULL) { + fprintf(stderr, + "getpwnam_r failed with rc='%s' result=%p\n", + strerror(rc), + result); + rc = 1; + nwritten = write(p[0], &rc, sizeof(int)); + assert(nwritten == sizeof(int)); + exit(1); + } + printf("child: getpwnam_r in child succeeded\n"); + rc = 0; + nwritten = write(p[0], &rc, sizeof(int)); + assert(nwritten == sizeof(int)); + exit(1); + } + + /* Parent */ + + /* Check result from child */ + nread = read(p[1], &rc, sizeof(int)); + if (nread != sizeof(int)) { + fprintf(stderr, + "read from child failed with errno='%s' nread=%zd\n", + strerror(errno), + nread); + state->fail = true; + return NULL; + } + + if (rc != 0) { + fprintf(stderr, + "getpwnam_r failed in the child\n"); + state->fail = true; + return NULL; + } + printf("parent: getpwnam_r in child succeeded\n"); + + /* Verify getpwnam_r() in parent after fork */ + rc = getpwnam_r(state->username, + &pwd, + buf, + sizeof(buf), + &result); + if (rc != 0 || result == NULL) { + fprintf(stderr, + "getpwnam_r failed with rc='%s' result=%p\n", + strerror(rc), + result); + state->fail = true; + return NULL; + } + printf("parent: getpwnam_r in parent succeeded\n"); + return NULL; +} + +static void *query_wbc_thread(void *ptr) +{ + struct thread_state *state = ptr; + struct passwd *ppwd; + wbcErr wbc_status; + pid_t pid; + ssize_t nread, nwritten; + int p[2]; + int rc; + + 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); + } + + rc = socketpair(AF_UNIX, SOCK_STREAM, 0, p); + if (rc != 0) { + state->fail = true; + return NULL; + } + + /* + * Check wbcGetpwnam() still works after a fork, + * both in parent and child. + */ + + pid = fork(); + if (pid == -1) { + return NULL; + } + if (pid == 0) { + /* Child */ + wbc_status = wbcGetpwnam(state->username, &ppwd); + if (!WBC_ERROR_IS_OK(wbc_status)) { + fprintf(stderr, + "wbcGetpwnam failed with %s\n", + wbcErrorString(wbc_status)); + rc = 1; + nwritten = write(p[0], &rc, sizeof(int)); + assert(nwritten == sizeof(int)); + exit(1); + } + wbcFreeMemory(ppwd); + printf("child: wbcGetpwnam in child succeeded\n"); + rc = 0; + nwritten = write(p[0], &rc, sizeof(int)); + assert(nwritten == sizeof(int)); + exit(1); + } + + /* Parent */ + + /* Check result from child */ + nread = read(p[1], &rc, sizeof(int)); + if (nread != sizeof(int)) { + fprintf(stderr, + "read from child failed with errno='%s' nread=%zd\n", + strerror(errno), + nread); + state->fail = true; + return NULL; + } + + if (rc != 0) { + fprintf(stderr, + "wbcGetpwnam failed in the child\n"); + state->fail = true; + return NULL; + } + printf("parent: wbcGetpwnam in child succeeded\n"); + + /* Verify wbcGetpwnam() in parent after fork */ + wbc_status = wbcGetpwnam(state->username, &ppwd); + if (!WBC_ERROR_IS_OK(wbc_status)) { + fprintf(stderr, + "wbcGetpwnam failed with %s\n", + wbcErrorString(wbc_status)); + state->fail = true; + return NULL; + } + wbcFreeMemory(ppwd); + printf("parent: wbcGetpwnam in parent succeeded\n"); + 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..7ae112a --- /dev/null +++ b/nsswitch/tests/test_idmap_ad.sh @@ -0,0 +1,269 @@ +#!/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} ldbmodify" +if [ -x "${BINDIR}/ldbmodify" ]; then + ldbmodify="${VALGRIND} ${BINDIR}/ldbmodify" +fi + +ldbsearch="${VALGRIND} ldbsearch" +if [ -x "${BINDIR}/ldbsearch" ]; then + ldbsearch="${VALGRIND} ${BINDIR}/ldbsearch" +fi + +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 + +dn: CN=Domain Users,CN=Users,$BASE_DN +changetype: modify +add: gidNumber +gidNumber: 2000001 + +dn: CN=Domain Admins,CN=Users,$BASE_DN +changetype: modify +add: gidNumber +gidNumber: 2000002 + +dn: ou=sub,$BASE_DN +changetype: add +objectClass: organizationalUnit + +dn: cn=forbidden,ou=sub,$BASE_DN +changetype: add +objectClass: user +samaccountName: forbidden +uidNumber: 2000003 +gidNumber: 2000001 +unixHomeDirectory: /home/forbidden +loginShell: /bin/tcsh +gecos: User in forbidden OU +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 + +dn: CN=Domain Users,CN=Users,$TRUST_BASE_DN +changetype: modify +add: gidNumber +gidNumber: 2500001 + +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) + +# +# Test 5: Make sure deny_ou is really denied +# This depends on the "deny ous" setting in Samba3.pm +# + +sid="$($wbinfo -n $DOMAIN/forbidden | awk '{print $1}')" +testit "Could create forbidden" test -n "$sid" || failed=$(expr $failed + 1) +if [ -n "$sid" ] +then + uid="$($wbinfo --sid-to-uid $sid)" + testit "Can not resolve forbidden user" test -z "$uid" || + failed=$(($failed + 1)) +fi + +# +# 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 + +dn: CN=Domain Users,CN=Users,$BASE_DN +changetype: modify +delete: gidNumber +gidNumber: 2000001 + +dn: CN=Domain Admins,CN=Users,$BASE_DN +changetype: modify +delete: gidNumber +gidNumber: 2000002 + +dn: cn=forbidden,ou=sub,$BASE_DN +changetype: delete + +dn: ou=sub,$BASE_DN +changetype: delete +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 + +dn: CN=Domain Users,CN=Users,$TRUST_BASE_DN +changetype: modify +delete: gidNumber +gidNumber: 2500001 + +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_nss_use_upn.sh b/nsswitch/tests/test_idmap_nss_use_upn.sh new file mode 100755 index 0000000..df2c672 --- /dev/null +++ b/nsswitch/tests/test_idmap_nss_use_upn.sh @@ -0,0 +1,79 @@ +#!/bin/sh + +wbinfo="$BINDIR/wbinfo" +smbcontrol="$BINDIR/smbcontrol" +net="$BINDIR/net" +global_inject_conf=$(dirname $SMB_CONF_PATH)/global_inject.conf + +failed=0 + +. $(dirname $0)/../../testprogs/blackbox/subunit.sh + +# Reset idmap_nss configuration and clear cache +echo "idmap config $DOMAIN : use_upn = no" >$global_inject_conf +$smbcontrol winbindd reload-config +if [ $? -ne 0 ]; then + echo "Could not reload config" | subunit_fail_test "test_idmap_nss_use_upn" +fi + +$net cache flush +if [ $? -ne 0 ]; then + echo "Could not flush cache" | subunit_fail_test "test_idmap_nss_use_upn" +fi + +# Get the user SID +USER="bob" +USER_SID=$($wbinfo --name-to-sid="$USER") +if [ $? -ne 0 ]; then + echo "Could not find SID for user '$USER'" | subunit_fail_test "test_idmap_nss_use_upn" + exit 1 +fi + +USER_SID=$(echo $USER_SID | cut -d " " -f 1) +if [ $? -ne 0 ]; then + echo "Could not find SID for user '$USER'" | subunit_fail_test "test_idmap_nss_use_upn" + exit 1 +fi + +testit "SID to UID (use_upn = no)" $wbinfo --sid-to-uid=${USER_SID} || failed=$(expr $failed + 1) + +echo "idmap config $DOMAIN : use_upn = yes" >$global_inject_conf +$smbcontrol winbindd reload-config +if [ $? -ne 0 ]; then + echo "Could not reload config" | subunit_fail_test "test_idmap_nss_use_upn" +fi + +$net cache flush +if [ $? -ne 0 ]; then + echo "Could not flush cache" | subunit_fail_test "test_idmap_nss_use_upn" +fi + +# The following test will fail because idmap_nss will search ADDOMAIN/bob, which does not +# exists in NSS_WRAPPER_PASSWD +testit_expect_failure "SID to UID (use_upn = yes)" $wbinfo --sid-to-uid=${USER_SID} || failed=$(expr $failed + 1) + +$net cache flush +if [ $? -ne 0 ]; then + echo "Could not flush cache" | subunit_fail_test "test_idmap_nss_use_upn" +fi + +# Add the ADDOMAIN/bob temporarily +ENTRY="$(getent passwd bob)" +ENTRY="$DOMAIN/${ENTRY}" +sed -i "1i ${ENTRY}" $NSS_WRAPPER_PASSWD +testit "Get user UID (use_upn = yes)" $wbinfo --sid-to-uid=${USER_SID} || failed=$(expr $failed + 1) +sed -i "1d" $NSS_WRAPPER_PASSWD + +# Reset config +echo "idmap config $DOMAIN : use_upn = no" >$global_inject_conf +$smbcontrol winbindd reload-config +if [ $? -ne 0 ]; then + echo "Could not reload config" | subunit_fail_test "test_idmap_nss_use_upn" +fi + +$net cache flush +if [ $? -ne 0 ]; then + echo "Could not flush cache" | subunit_fail_test "test_idmap_nss_use_upn" +fi + +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..2cd43a7 --- /dev/null +++ b/nsswitch/tests/test_idmap_rid.sh @@ -0,0 +1,202 @@ +#!/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) +# Compare only 'groupname:x:gid' part, drop the members +normalized_out=$(echo "$out" | cut -d: -f1-3) +test "$normalized_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) +# Compare only 'groupname:x:gid' part, drop the members +normalized_out=$(echo "$out" | cut -d: -f1-3) +test "$normalized_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..440a924 --- /dev/null +++ b/nsswitch/tests/test_rfc2307_mapping.sh @@ -0,0 +1,159 @@ +#!/bin/sh +# Blackbox test for wbinfo and rfc2307 mappings +if [ $# -lt 7 ]; then + cat <<EOF +Usage: test_rfc2307_mapping.sh DOMAIN USERNAME PASSWORD SERVER UID_RFC2307TEST GID_RFC2307TEST CONFIGURATION +EOF + exit 1 +fi + +DOMAIN=$1 +USERNAME=$2 +PASSWORD=$3 +SERVER=$4 +UID_RFC2307TEST=$5 +GID_RFC2307TEST=$6 +CONFIGURATION=${7} +shift 6 + +failed=0 +samba4bindir="$BINDIR" +wbinfo="$VALGRIND $samba4bindir/wbinfo" +samba_tool="$VALGRIND $samba4bindir/samba-tool" + +. $(dirname $0)/../../testprogs/blackbox/subunit.sh +. "$(dirname "${0}")/../../testprogs/blackbox/common_test_fns.inc" + +ldbmodify=$(system_or_builddir_binary ldbmodify "${BINDIR}") + +# Create new testing account +testit "user add" $PYTHON $samba_tool user create --given-name="rfc2307" \ + --surname="Tester" --initial="UT" rfc2307_test_user testp@ssw0Rd \ + "${CONFIGURATION}" "$@" + +#test creation of six different groups +testit "group add" $PYTHON $samba_tool group add \ + --group-scope='Domain' --group-type='Security' rfc2307_test_group \ + "${CONFIGURATION}" "$@" + +# 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 "${CONFIGURATION}" "$@" +testit "user delete" $PYTHON $samba_tool user delete rfc2307_test_user "${CONFIGURATION}" "$@" + +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..b36e4c6 --- /dev/null +++ b/nsswitch/tests/test_wbinfo.sh @@ -0,0 +1,322 @@ +#!/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" + return 1 + else + echo "success: $name" + return 0 + fi +} + +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..048fdfc --- /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_name_lookup.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..b7f8443 --- /dev/null +++ b/nsswitch/wb_common.c @@ -0,0 +1,1021 @@ +/* + 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" +#include "lib/util/dlinklist.h" +#include <assert.h> + +#ifdef HAVE_PTHREAD_H +#include <pthread.h> +#endif + +static __thread char client_name[32]; + +/* Global context */ + +struct winbindd_context { + struct winbindd_context *prev, *next; + int winbindd_fd; /* winbind file descriptor */ + bool is_privileged; /* using the privileged socket? */ + pid_t our_pid; /* calling process pid */ + bool autofree; /* this is a thread global context */ +}; + +static struct wb_global_ctx { +#ifdef HAVE_PTHREAD + pthread_once_t control; + pthread_key_t key; + bool key_initialized; +#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP +#define WB_GLOBAL_MUTEX_INITIALIZER PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP +#else +#define WB_GLOBAL_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER +#endif +#define WB_GLOBAL_LIST_LOCK do { \ + int __pret = pthread_mutex_lock(&wb_global_ctx.list_mutex); \ + assert(__pret == 0); \ +} while(0) +#define WB_GLOBAL_LIST_UNLOCK do { \ + int __pret = pthread_mutex_unlock(&wb_global_ctx.list_mutex); \ + assert(__pret == 0); \ +} while(0) + pthread_mutex_t list_mutex; +#else /* => not HAVE_PTHREAD */ +#define WB_GLOBAL_LIST_LOCK do { } while(0) +#define WB_GLOBAL_LIST_UNLOCK do { } while(0) +#endif /* not HAVE_PTHREAD */ + struct winbindd_context *list; +} wb_global_ctx = { +#ifdef HAVE_PTHREAD + .control = PTHREAD_ONCE_INIT, + .list_mutex = WB_GLOBAL_MUTEX_INITIALIZER, +#endif + .list = NULL, +}; + +static void winbind_close_sock(struct winbindd_context *ctx); +static void winbind_ctx_free_locked(struct winbindd_context *ctx); +static void winbind_cleanup_list(void); + +#ifdef HAVE_PTHREAD +static void wb_thread_ctx_initialize(void); + +static void wb_atfork_prepare(void) +{ + WB_GLOBAL_LIST_LOCK; +} + +static void wb_atfork_parent(void) +{ + WB_GLOBAL_LIST_UNLOCK; +} + +static void wb_atfork_child(void) +{ + wb_global_ctx.list_mutex = (pthread_mutex_t)WB_GLOBAL_MUTEX_INITIALIZER; + + if (wb_global_ctx.key_initialized) { + int ret; + + /* + * After a fork the child still believes + * it is the same thread as in the parent. + * So pthread_getspecific() would return the + * value of the thread that called fork(). + * + * But we don't want that behavior, so + * we just clear the reference and let + * winbind_cleanup_list() below 'autofree' + * the parent threads global context. + */ + ret = pthread_setspecific(wb_global_ctx.key, NULL); + assert(ret == 0); + } + + /* + * But we need to close/cleanup the global state + * of the parents threads. + */ + winbind_cleanup_list(); +} + +static void wb_thread_ctx_destructor(void *p) +{ + struct winbindd_context *ctx = (struct winbindd_context *)p; + + winbindd_ctx_free(ctx); +} + +static void wb_thread_ctx_initialize(void) +{ + int ret; + + ret = pthread_atfork(wb_atfork_prepare, + wb_atfork_parent, + wb_atfork_child); + assert(ret == 0); + + ret = pthread_key_create(&wb_global_ctx.key, + wb_thread_ctx_destructor); + assert(ret == 0); + + wb_global_ctx.key_initialized = true; +} + +static struct winbindd_context *get_wb_thread_ctx(void) +{ + struct winbindd_context *ctx = NULL; + int ret; + + ret = pthread_once(&wb_global_ctx.control, + wb_thread_ctx_initialize); + assert(ret == 0); + + ctx = (struct winbindd_context *)pthread_getspecific( + wb_global_ctx.key); + if (ctx != NULL) { + return ctx; + } + + ctx = malloc(sizeof(struct winbindd_context)); + if (ctx == NULL) { + return NULL; + } + + *ctx = (struct winbindd_context) { + .winbindd_fd = -1, + .is_privileged = false, + .our_pid = 0, + .autofree = true, + }; + + WB_GLOBAL_LIST_LOCK; + DLIST_ADD_END(wb_global_ctx.list, ctx); + WB_GLOBAL_LIST_UNLOCK; + + ret = pthread_setspecific(wb_global_ctx.key, ctx); + if (ret != 0) { + free(ctx); + return NULL; + } + return ctx; +} +#endif /* HAVE_PTHREAD */ + +static struct winbindd_context *get_wb_global_ctx(void) +{ + struct winbindd_context *ctx = NULL; +#ifndef HAVE_PTHREAD + static struct winbindd_context _ctx = { + .winbindd_fd = -1, + .is_privileged = false, + .our_pid = 0, + .autofree = false, + }; +#endif + +#ifdef HAVE_PTHREAD + ctx = get_wb_thread_ctx(); +#else + ctx = &_ctx; + if (ctx->prev == NULL && ctx->next == NULL) { + DLIST_ADD_END(wb_global_ctx.list, ctx); + } +#endif + + return ctx; +} + +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; + } +} + +static void winbind_ctx_free_locked(struct winbindd_context *ctx) +{ + winbind_close_sock(ctx); + DLIST_REMOVE(wb_global_ctx.list, ctx); + free(ctx); +} + +static void winbind_cleanup_list(void) +{ + struct winbindd_context *ctx = NULL, *next = NULL; + + WB_GLOBAL_LIST_LOCK; + for (ctx = wb_global_ctx.list; ctx != NULL; ctx = next) { + next = ctx->next; + + if (ctx->autofree) { + winbind_ctx_free_locked(ctx); + } else { + winbind_close_sock(ctx); + } + } + WB_GLOBAL_LIST_UNLOCK; +} + +/* 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) +{ +#ifdef HAVE_PTHREAD + if (wb_global_ctx.key_initialized) { + int ret; + ret = pthread_key_delete(wb_global_ctx.key); + assert(ret == 0); + wb_global_ctx.key_initialized = false; + } + + wb_global_ctx.control = (pthread_once_t)PTHREAD_ONCE_INIT; +#endif /* HAVE_PTHREAD */ + + winbind_cleanup_list(); +} + +#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; + + if (ctx == NULL) { + ctx = get_wb_global_ctx(); + } + + status = winbindd_send_request(ctx, req_type, 0, request); + if (status != NSS_STATUS_SUCCESS) { + goto out; + } + status = winbindd_get_response(ctx, response); + +out: + 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; + + if (ctx == NULL) { + ctx = get_wb_global_ctx(); + } + + status = winbindd_send_request(ctx, req_type, 1, request); + if (status != NSS_STATUS_SUCCESS) { + goto out; + } + status = winbindd_get_response(ctx, response); + +out: + 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; + + WB_GLOBAL_LIST_LOCK; + DLIST_ADD_END(wb_global_ctx.list, ctx); + WB_GLOBAL_LIST_UNLOCK; + + return ctx; +} + +void winbindd_ctx_free(struct winbindd_context *ctx) +{ + WB_GLOBAL_LIST_LOCK; + winbind_ctx_free_locked(ctx); + WB_GLOBAL_LIST_UNLOCK; +} 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..6148b20 --- /dev/null +++ b/nsswitch/wbinfo.c @@ -0,0 +1,3343 @@ +/* + 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; +} + +/* Change trust account password chose Domain Controller */ + +static bool wbinfo_change_secret_at(const char *domain, + const char *domain_controller) +{ + 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 = wbcChangeTrustCredentialsAt( + domain_name, domain_controller, &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(¶ms.num_blobs, + ¶ms.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(¶ms.num_blobs, + ¶ms.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(¶ms.num_blobs, + ¶ms.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(¶ms, &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(¶ms, &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(¶ms.num_blobs, ¶ms.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(¶ms.num_blobs, ¶ms.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(¶ms, &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, + OPT_CHANGE_SECRET_AT +}; + +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 = "change-secret-at", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &string_arg, + .val = OPT_CHANGE_SECRET_AT, + .descrip = "Change shared secret at Domain Controller" }, + { + .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 OPT_CHANGE_SECRET_AT: + if (!wbinfo_change_secret_at(opt_domain_name, string_arg)) { + 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..36fc39d --- /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 construct 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..0c16234 --- /dev/null +++ b/nsswitch/winbind_nss_linux.c @@ -0,0 +1,1090 @@ +/* + 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 + +/* 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 boundary */ + 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 __thread struct winbindd_response getpwent_response; + +static __thread int ndx_pw_cache; /* Current index into pwd cache */ +static __thread 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 + + 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 + + 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 + + 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 + + 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 __thread int called_again; + +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: getpwent\n", getpid()); +#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 + + 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 __thread struct winbindd_response response; + struct winbindd_request request; + static __thread int keep_response; + +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: getpwuid_r %d\n", getpid(), (unsigned int)uid); +#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 + + 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 __thread struct winbindd_response response; + struct winbindd_request request; + static __thread int keep_response; + +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: getpwnam_r %s\n", getpid(), name); +#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 + + return ret; +} + +/* + * NSS group functions + */ + +static __thread struct winbindd_response getgrent_response; + +static __thread int ndx_gr_cache; /* Current index into grp cache */ +static __thread 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 + + 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 + + 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 + + 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 + + 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 __thread struct winbindd_request request; + static __thread int called_again; + + +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: getgrent\n", getpid()); +#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 + + 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 __thread struct winbindd_response response; + struct winbindd_request request; + static __thread int keep_response; + +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: getgrnam %s\n", getpid(), name); +#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 + + 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 __thread struct winbindd_response response; + struct winbindd_request request; + static __thread int keep_response; + +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: getgrgid %d\n", getpid(), gid); +#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 + + 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 + + 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: + winbindd_free_response(&response); +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: initgroups %s returns %s (%d)\n", getpid(), + user, nss_err_str(ret), ret); +#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..473fa50 --- /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..b8b92bc --- /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 definition 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..4c5f708 --- /dev/null +++ b/nsswitch/wscript_build @@ -0,0 +1,179 @@ +#!/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 + ) + bld.SAMBA_BINARY('b15464-testcase', + source='b15464-testcase.c', + deps='replace pthread dl', + 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', + 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', + 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') |