diff options
Diffstat (limited to 'source4/nsswitch')
-rw-r--r-- | source4/nsswitch/config.m4 | 12 | ||||
-rw-r--r-- | source4/nsswitch/config.mk | 34 | ||||
-rw-r--r-- | source4/nsswitch/nsstest.c | 411 | ||||
-rw-r--r-- | source4/nsswitch/nsstest.h | 115 | ||||
-rw-r--r-- | source4/nsswitch/nsstest.m4 | 9 | ||||
-rwxr-xr-x | source4/nsswitch/tests/test_wbinfo.sh | 187 | ||||
-rw-r--r-- | source4/nsswitch/wb_common.c | 704 | ||||
-rw-r--r-- | source4/nsswitch/wbinfo.c | 1291 | ||||
-rw-r--r-- | source4/nsswitch/winbind_client.h | 25 | ||||
-rw-r--r-- | source4/nsswitch/winbind_nss.h | 76 | ||||
-rw-r--r-- | source4/nsswitch/winbind_nss_config.h | 64 | ||||
-rw-r--r-- | source4/nsswitch/winbind_nss_hpux.h | 137 | ||||
-rw-r--r-- | source4/nsswitch/winbind_nss_irix.h | 42 | ||||
-rw-r--r-- | source4/nsswitch/winbind_nss_linux.c | 1281 | ||||
-rw-r--r-- | source4/nsswitch/winbind_nss_linux.h | 29 | ||||
-rw-r--r-- | source4/nsswitch/winbind_nss_solaris.h | 89 | ||||
-rw-r--r-- | source4/nsswitch/winbind_struct_protocol.h | 499 |
17 files changed, 5005 insertions, 0 deletions
diff --git a/source4/nsswitch/config.m4 b/source4/nsswitch/config.m4 new file mode 100644 index 0000000000..207b7fa53f --- /dev/null +++ b/source4/nsswitch/config.m4 @@ -0,0 +1,12 @@ +AC_CHECK_HEADERS(nss.h nss_common.h ns_api.h ) + +case "$host_os" in + *linux*) + SMB_LIBRARY(nss_winbind, + [nsswitch/winbind_nss_linux.o], + [LIBWINBIND-CLIENT]) + ;; + *) + ;; +esac + diff --git a/source4/nsswitch/config.mk b/source4/nsswitch/config.mk new file mode 100644 index 0000000000..e8b9600882 --- /dev/null +++ b/source4/nsswitch/config.mk @@ -0,0 +1,34 @@ +[SUBSYSTEM::LIBWINBIND-CLIENT] +PRIVATE_DEPENDENCIES = SOCKET_WRAPPER + +LIBWINBIND-CLIENT_OBJ_FILES = $(nsswitchsrcdir)/wb_common.o + +################################# +# Start BINARY nsstest +[BINARY::nsstest] +INSTALLDIR = BINDIR +PRIVATE_DEPENDENCIES = \ + LIBSAMBA-UTIL \ + LIBREPLACE_EXT \ + LIBSAMBA-HOSTCONFIG +# End BINARY nsstest +################################# + +nsstest_OBJ_FILES = $(nsswitchsrcdir)/nsstest.o + +################################# +# Start BINARY wbinfo +[BINARY::wbinfo] +INSTALLDIR = BINDIR +PRIVATE_DEPENDENCIES = \ + LIBSAMBA-UTIL \ + LIBREPLACE_EXT \ + LIBCLI_AUTH \ + LIBPOPT \ + POPT_SAMBA \ + LIBWINBIND-CLIENT +# End BINARY nsstest +################################# + +wbinfo_OBJ_FILES = \ + $(nsswitchsrcdir)/wbinfo.o diff --git a/source4/nsswitch/nsstest.c b/source4/nsswitch/nsstest.c new file mode 100644 index 0000000000..df7f33f8e0 --- /dev/null +++ b/source4/nsswitch/nsstest.c @@ -0,0 +1,411 @@ +/* + Unix SMB/CIFS implementation. + nss tester for winbindd + Copyright (C) Andrew Tridgell 2001 + + 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/nsstest.h" + +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[1024]; + static void *h; + void *res; + + snprintf(s,sizeof(s), "_nss_%s_%s", nss_name, name); + + 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); + return NULL; + } + 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 *) = find_fn("getpwent_r"); + static struct passwd pwd; + static char buf[1000]; + NSS_STATUS status; + + 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 *) = find_fn("getpwnam_r"); + static struct passwd pwd; + static char buf[1000]; + NSS_STATUS status; + + 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 *) = find_fn("getpwuid_r"); + static struct passwd pwd; + static char buf[1000]; + NSS_STATUS status; + + 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 nss_setpwent(void) +{ + NSS_STATUS (*_nss_setpwent)(void) = find_fn("setpwent"); + NSS_STATUS status; + status = _nss_setpwent(); + if (status != NSS_STATUS_SUCCESS) { + report_nss_error("setpwent", status); + } +} + +static void nss_endpwent(void) +{ + NSS_STATUS (*_nss_endpwent)(void) = find_fn("endpwent"); + NSS_STATUS status; + 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 *) = find_fn("getgrent_r"); + static struct group grp; + static char *buf; + static int buflen = 1024; + NSS_STATUS status; + + if (!buf) buf = malloc_array_p(char, buflen); + +again: + status = _nss_getgrent_r(&grp, buf, buflen, &nss_errno); + if (status == NSS_STATUS_TRYAGAIN) { + buflen *= 2; + buf = realloc_p(buf, char, buflen); + goto again; + } + if (status == NSS_STATUS_NOTFOUND) { + return NULL; + } + if (status != NSS_STATUS_SUCCESS) { + report_nss_error("getgrent", status); + 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 *) = find_fn("getgrnam_r"); + static struct group grp; + static char *buf; + static int buflen = 1000; + NSS_STATUS status; + + if (!buf) buf = malloc_array_p(char, buflen); +again: + status = _nss_getgrnam_r(name, &grp, buf, buflen, &nss_errno); + if (status == NSS_STATUS_TRYAGAIN) { + buflen *= 2; + buf = realloc_p(buf, char, buflen); + goto again; + } + if (status == NSS_STATUS_NOTFOUND) { + return NULL; + } + if (status != NSS_STATUS_SUCCESS) { + report_nss_error("getgrnam", status); + 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 *) = find_fn("getgrgid_r"); + static struct group grp; + static char *buf; + static int buflen = 1000; + NSS_STATUS status; + + if (!buf) buf = malloc_array_p(char, buflen); +again: + status = _nss_getgrgid_r(gid, &grp, buf, buflen, &nss_errno); + if (status == NSS_STATUS_TRYAGAIN) { + buflen *= 2; + buf = realloc_p(buf, char, buflen); + goto again; + } + if (status == NSS_STATUS_NOTFOUND) { + return NULL; + } + if (status != NSS_STATUS_SUCCESS) { + report_nss_error("getgrgid", status); + return NULL; + } + return &grp; +} + +static void nss_setgrent(void) +{ + NSS_STATUS (*_nss_setgrent)(void) = find_fn("setgrent"); + NSS_STATUS status; + status = _nss_setgrent(); + if (status != NSS_STATUS_SUCCESS) { + report_nss_error("setgrent", status); + } +} + +static void nss_endgrent(void) +{ + NSS_STATUS (*_nss_endgrent)(void) = find_fn("endgrent"); + NSS_STATUS status; + 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 *) = + 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:%d:%d:%s:%s:%s\n", + pwd->pw_name, + pwd->pw_passwd, + pwd->pw_uid, + pwd->pw_gid, + pwd->pw_gecos, + pwd->pw_dir, + pwd->pw_shell); +} + +static void print_group(struct group *grp) +{ + int i; + printf("%s:%s:%d: ", + grp->gr_name, + grp->gr_passwd, + 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_array_p(gid_t, size); + 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("%d, ", groups[i]); + } + printf("%d\n", groups[i]); +} + + +static void nss_test_users(void) +{ + struct passwd *pwd; + + 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"); + } + nss_endpwent(); +} + +static void nss_test_groups(void) +{ + struct group *grp; + + 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"); + } + 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 existant user gave error %d\n", last_error); + } + + pwd = getpwuid(0xFFF0); + if (pwd || last_error != NSS_STATUS_NOTFOUND) { + total_errors++; + printf("ERROR Non existant uid gave error %d\n", last_error); + } + + grp = getgrnam("nosuchgroup"); + if (grp || last_error != NSS_STATUS_NOTFOUND) { + total_errors++; + printf("ERROR Non existant group gave error %d\n", last_error); + } + + grp = getgrgid(0xFFF0); + if (grp || last_error != NSS_STATUS_NOTFOUND) { + total_errors++; + printf("ERROR Non existant 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/source4/nsswitch/nsstest.h b/source4/nsswitch/nsstest.h new file mode 100644 index 0000000000..599faf2ee2 --- /dev/null +++ b/source4/nsswitch/nsstest.h @@ -0,0 +1,115 @@ +/* + Unix SMB/CIFS implementation. + nss includes for the nss tester + Copyright (C) Kai Blin 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _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 HAVE_NSS_H + +/* + * Linux (glibc) + */ + +#include <nss.h> +typedef enum nss_status NSS_STATUS; + +#elif 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/source4/nsswitch/nsstest.m4 b/source4/nsswitch/nsstest.m4 new file mode 100644 index 0000000000..2856f9de15 --- /dev/null +++ b/source4/nsswitch/nsstest.m4 @@ -0,0 +1,9 @@ +case "$host_os" in + *linux*) + SMB_ENABLE(nsstest,YES) + ;; + *) + SMB_ENABLE(nsstest,NO) + ;; +esac + diff --git a/source4/nsswitch/tests/test_wbinfo.sh b/source4/nsswitch/tests/test_wbinfo.sh new file mode 100755 index 0000000000..dfd633b656 --- /dev/null +++ b/source4/nsswitch/tests/test_wbinfo.sh @@ -0,0 +1,187 @@ +#!/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=`dirname $0`/../../bin +wbinfo=$samba4bindir/wbinfo + +. `dirname $0`/../../../testprogs/blackbox/subunit.sh + +testfail() { + name="$1" + shift + cmdline="$*" + echo "test: $name" + $cmdline + status=$? + if [ x$status = x0 ]; then + echo "failure: $name" + else + echo "success: $name" + fi + return $status +} + +knownfail() { + name="$1" + shift + cmdline="$*" + echo "test: $name" + $cmdline + status=$? + if [ x$status = x0 ]; then + echo "failure: $name [unexpected success]" + status=1 + else + echo "knownfail: $name" + status=0 + fi + return $status +} + + +testit "wbinfo -u against $TARGET" $wbinfo -u || failed=`expr $failed + 1` +# Does not work yet +knownfail "wbinfo -g against $TARGET" $wbinfo -g || failed=`expr $failed + 1` +knownfail "wbinfo -N against $TARGET" $wbinfo -N || failed=`expr $failed + 1` +knownfail "wbinfo -I against $TARGET" $wbinfo -I || failed=`expr $failed + 1` +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 + +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 + +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 -U $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` + +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" +fi + +# this does not work +knownfail "wbinfo --sequence against $TARGET" $wbinfo --sequence +knownfail "wbinfo -D against $TARGET" $wbinfo -D $DOMAIN || failed=`expr $failed + 1` + +testit "wbinfo -i against $TARGET" $wbinfo -i "$DOMAIN/$USERNAME" || failed=`expr $failed + 1` + +# this does not work +knownfail "wbinfo --uid-info against $TARGET" $wbinfo --uid-info $admin_sid +knownfail "wbinfo --group-info against $TARGET" $wbinfo --group-info "S-1-22-2-0" +knownfail "wbinfo -r against $TARGET" $wbinfo -r "$DOMAIN/$USERNAME" + +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` + +# this does not work +knwonfail "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 -K "$DOMAIN/$USERNAME"%"$PASSWORD" || failed=`expr $failed + 1` + +testit "wbinfo --separator against $TARGET" $wbinfo --separator || failed=`expr $failed + 1` + +exit $failed + diff --git a/source4/nsswitch/wb_common.c b/source4/nsswitch/wb_common.c new file mode 100644 index 0000000000..2ae85dcb1e --- /dev/null +++ b/source4/nsswitch/wb_common.c @@ -0,0 +1,704 @@ +/* + Unix SMB/CIFS implementation. + + winbind client common code + + Copyright (C) Tim Potter 2000 + Copyright (C) Andrew Tridgell 2000 + Copyright (C) Andrew Bartlett 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 program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "winbind_client.h" + +/* Global variables. These are effectively the client state information */ + +int winbindd_fd = -1; /* fd for winbindd socket */ +static int is_privileged = 0; + +/* Free a response structure */ + +void winbindd_free_response(struct winbindd_response *response) +{ + /* Free any allocated extra_data */ + + if (response) + SAFE_FREE(response->extra_data.data); +} + +/* Initialise a request structure */ + +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(); + +} + +/* Initialise a response structure */ + +static void init_response(struct winbindd_response *response) +{ + /* Initialise return value */ + + response->result = WINBINDD_ERROR; +} + +/* Close established socket */ + +void winbind_close_sock(void) +{ + if (winbindd_fd != -1) { + close(winbindd_fd); + winbindd_fd = -1; + } +} + +#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; +} + +/* Connect to winbindd socket */ + +static int winbind_named_pipe_sock(const char *dir) +{ + struct sockaddr_un sunaddr; + struct stat st; + pstring path; + int fd; + int wait_time; + int slept; + + /* Check permissions on unix socket directory */ + + if (lstat(dir, &st) == -1) { + return -1; + } + + if (!S_ISDIR(st.st_mode) || + (st.st_uid != 0 && st.st_uid != geteuid())) { + return -1; + } + + /* Connect to socket */ + + strncpy(path, dir, sizeof(path) - 1); + path[sizeof(path) - 1] = '\0'; + + strncat(path, "/", sizeof(path) - 1 - strlen(path)); + path[sizeof(path) - 1] = '\0'; + + strncat(path, WINBINDD_SOCKET_NAME, sizeof(path) - 1 - strlen(path)); + path[sizeof(path) - 1] = '\0'; + + ZERO_STRUCT(sunaddr); + sunaddr.sun_family = AF_UNIX; + strncpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path) - 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(path, &st) == -1) { + return -1; + } + + /* Check permissions on unix socket file */ + + if (!S_ISSOCK(st.st_mode) || + (st.st_uid != 0 && st.st_uid != geteuid())) { + 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 timeval tv; + fd_set w_fds; + int ret; + int connect_errno = 0; + socklen_t errnosize; + + if (wait_time >= CONNECT_TIMEOUT) + goto error_out; + + switch (errno) { + case EINPROGRESS: + FD_ZERO(&w_fds); + FD_SET(fd, &w_fds); + tv.tv_sec = CONNECT_TIMEOUT - wait_time; + tv.tv_usec = 0; + + ret = select(fd + 1, NULL, &w_fds, NULL, &tv); + + 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) +{ +#ifdef SOCKET_WRAPPER + const char *env_dir; + + env_dir = getenv(WINBINDD_SOCKET_DIR_ENVVAR); + if (env_dir) { + return env_dir; + } +#endif + + return WINBINDD_SOCKET_DIR; +} + +/* Connect to winbindd socket */ + +static int winbind_open_pipe_sock(int recursing, int need_priv) +{ +#ifdef HAVE_UNIXSOCKET + static pid_t our_pid; + struct winbindd_request request; + struct winbindd_response response; + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + if (our_pid != getpid()) { + winbind_close_sock(); + our_pid = getpid(); + } + + if ((need_priv != 0) && (is_privileged == 0)) { + winbind_close_sock(); + } + + if (winbindd_fd != -1) { + return winbindd_fd; + } + + if (recursing) { + return -1; + } + + if ((winbindd_fd = winbind_named_pipe_sock(winbindd_socket_dir())) == -1) { + return -1; + } + + is_privileged = 0; + + /* version-check the socket */ + + request.wb_flags = WBFLAG_RECURSE; + if ((winbindd_request_response(WINBINDD_INTERFACE_VERSION, &request, &response) != NSS_STATUS_SUCCESS) || (response.data.interface_version != WINBIND_INTERFACE_VERSION)) { + winbind_close_sock(); + return -1; + } + + /* try and get priv pipe */ + + request.wb_flags = WBFLAG_RECURSE; + if (winbindd_request_response(WINBINDD_PRIV_PIPE_DIR, &request, &response) == NSS_STATUS_SUCCESS) { + int fd; + if ((fd = winbind_named_pipe_sock((char *)response.extra_data.data)) != -1) { + close(winbindd_fd); + winbindd_fd = fd; + is_privileged = 1; + } + } + + if ((need_priv != 0) && (is_privileged == 0)) { + return -1; + } + + SAFE_FREE(response.extra_data.data); + + return winbindd_fd; +#else + return -1; +#endif /* HAVE_UNIXSOCKET */ +} + +/* Write data to winbindd socket */ + +int winbind_write_sock(void *buffer, int count, int recursing, int need_priv) +{ + int result, nwritten; + + /* Open connection to winbind daemon */ + + restart: + + if (winbind_open_pipe_sock(recursing, need_priv) == -1) { + return -1; + } + + /* Write data to socket */ + + nwritten = 0; + + while(nwritten < count) { + struct timeval tv; + fd_set r_fds; + + /* Catch pipe close on other end by checking if a read() + call would not block by calling select(). */ + + FD_ZERO(&r_fds); + FD_SET(winbindd_fd, &r_fds); + ZERO_STRUCT(tv); + + if (select(winbindd_fd + 1, &r_fds, NULL, NULL, &tv) == -1) { + winbind_close_sock(); + return -1; /* Select error */ + } + + /* Write should be OK if fd not available for reading */ + + if (!FD_ISSET(winbindd_fd, &r_fds)) { + + /* Do the write */ + + result = write(winbindd_fd, + (char *)buffer + nwritten, + count - nwritten); + + if ((result == -1) || (result == 0)) { + + /* Write failed */ + + winbind_close_sock(); + return -1; + } + + nwritten += result; + + } else { + + /* Pipe has closed on remote end */ + + winbind_close_sock(); + goto restart; + } + } + + return nwritten; +} + +/* Read data from winbindd socket */ + +int winbind_read_sock(void *buffer, int count) +{ + int nread = 0; + int total_time = 0, selret; + + if (winbindd_fd == -1) { + return -1; + } + + /* Read data from socket */ + while(nread < count) { + struct timeval tv; + fd_set r_fds; + + /* Catch pipe close on other end by checking if a read() + call would not block by calling select(). */ + + FD_ZERO(&r_fds); + FD_SET(winbindd_fd, &r_fds); + ZERO_STRUCT(tv); + /* Wait for 5 seconds for a reply. May need to parameterise this... */ + tv.tv_sec = 5; + + if ((selret = select(winbindd_fd + 1, &r_fds, NULL, NULL, &tv)) == -1) { + winbind_close_sock(); + return -1; /* Select error */ + } + + if (selret == 0) { + /* Not ready for read yet... */ + if (total_time >= 30) { + /* Timeout */ + winbind_close_sock(); + return -1; + } + total_time += 5; + continue; + } + + if (FD_ISSET(winbindd_fd, &r_fds)) { + + /* Do the Read */ + + int result = read(winbindd_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(); + return -1; + } + + nread += result; + + } + } + + return nread; +} + +/* Read reply */ + +int winbindd_read_reply(struct winbindd_response *response) +{ + int result1, result2 = 0; + + if (!response) { + return -1; + } + + /* Read fixed length response */ + + result1 = winbind_read_sock(response, + sizeof(struct winbindd_response)); + if (result1 == -1) { + return -1; + } + + /* 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; + + /* 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(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; +} + +bool winbind_env_set(void) +{ + char *env; + + if ((env=getenv(WINBINDD_DONT_ENV)) != NULL) { + if(strcmp(env, "1") == 0) { + return true; + } + } + return false; +} + +/* + * send simple types of requests + */ + +NSS_STATUS winbindd_send_request(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(request, sizeof(*request), + request->wb_flags & WBFLAG_RECURSE, + need_priv) == -1) { + return NSS_STATUS_UNAVAIL; + } + + if ((request->extra_len != 0) && + (winbind_write_sock(request->extra_data.data, + request->extra_len, + request->wb_flags & WBFLAG_RECURSE, + need_priv) == -1)) { + return NSS_STATUS_UNAVAIL; + } + + return NSS_STATUS_SUCCESS; +} + +/* + * Get results from winbindd request + */ + +NSS_STATUS winbindd_get_response(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(response) == -1) { + 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(int req_type, + struct winbindd_request *request, + struct winbindd_response *response) +{ + NSS_STATUS status = NSS_STATUS_UNAVAIL; + int count = 0; + + while ((status == NSS_STATUS_UNAVAIL) && (count < 10)) { + status = winbindd_send_request(req_type, 0, request); + if (status != NSS_STATUS_SUCCESS) + return(status); + status = winbindd_get_response(response); + count += 1; + } + + return status; +} + +NSS_STATUS winbindd_priv_request_response(int req_type, + struct winbindd_request *request, + struct winbindd_response *response) +{ + NSS_STATUS status = NSS_STATUS_UNAVAIL; + int count = 0; + + while ((status == NSS_STATUS_UNAVAIL) && (count < 10)) { + status = winbindd_send_request(req_type, 1, request); + if (status != NSS_STATUS_SUCCESS) + return(status); + status = winbindd_get_response(response); + count += 1; + } + + return status; +} + +/************************************************************************* + A couple of simple functions to disable winbindd lookups and re- + enable them + ************************************************************************/ + +bool winbind_off(void) +{ + return setenv(WINBINDD_DONT_ENV, "1", 1) != -1; +} + +bool winbind_on(void) +{ + return setenv(WINBINDD_DONT_ENV, "0", 1) != -1; +} + +/************************************************************************* + ************************************************************************/ + +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!!!!!!!"; + } +} diff --git a/source4/nsswitch/wbinfo.c b/source4/nsswitch/wbinfo.c new file mode 100644 index 0000000000..150d9a68ee --- /dev/null +++ b/source4/nsswitch/wbinfo.c @@ -0,0 +1,1291 @@ +/* + Unix SMB/CIFS implementation. + + Winbind status program. + + Copyright (C) Tim Potter 2000-2003 + Copyright (C) Andrew Bartlett 2002-2007 + + 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 "pstring.h" +#include "winbind_client.h" +#include "librpc/gen_ndr/ndr_netlogon.h" +#include "libcli/auth/libcli_auth.h" +#include "libcli/security/security.h" +#include "lib/cmdline/popt_common.h" +#include "dynconfig/dynconfig.h" +#include "param/param.h" + +extern int winbindd_fd; + +static char winbind_separator_int(bool strict) +{ + struct winbindd_response response; + static bool got_sep; + static char sep; + + if (got_sep) + return sep; + + ZERO_STRUCT(response); + + /* Send off request */ + + if (winbindd_request_response(WINBINDD_INFO, NULL, &response) != + NSS_STATUS_SUCCESS) { + d_fprintf(stderr, "could not obtain winbind separator!\n"); + if (strict) { + return 0; + } + /* HACK: (this module should not call lp_ funtions) */ + return *lp_winbind_separator(cmdline_lp_ctx); + } + + sep = response.data.info.winbind_separator; + got_sep = true; + + if (!sep) { + d_fprintf(stderr, "winbind separator was NULL!\n"); + if (strict) { + return 0; + } + /* HACK: (this module should not call lp_ funtions) */ + sep = *lp_winbind_separator(cmdline_lp_ctx); + } + + return sep; +} + +static char winbind_separator(void) +{ + return winbind_separator_int(false); +} + +static const char *get_winbind_domain(void) +{ + struct winbindd_response response; + static fstring winbind_domain; + + ZERO_STRUCT(response); + + /* Send off request */ + + if (winbindd_request_response(WINBINDD_DOMAIN_NAME, NULL, &response) != + NSS_STATUS_SUCCESS) { + d_fprintf(stderr, "could not obtain winbind domain name!\n"); + + /* HACK: (this module should not call lp_ funtions) */ + return lp_workgroup(cmdline_lp_ctx); + } + + fstrcpy(winbind_domain, response.data.domain_name); + + return winbind_domain; + +} + +/* 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) { + fstrcpy(user, domuser); + fstrcpy(domain, get_winbind_domain()); + return true; + } + + fstrcpy(user, p+1); + fstrcpy(domain, domuser); + domain[PTR_DIFF(p, domuser)] = 0; + strupper_m(domain); + + return true; +} + +/* pull pwent info for a given user */ + +static bool wbinfo_get_userinfo(char *user) +{ + struct winbindd_request request; + struct winbindd_response response; + NSS_STATUS result; + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + /* Send request */ + + fstrcpy(request.data.username, user); + + result = winbindd_request_response(WINBINDD_GETPWNAM, &request, &response); + + if (result != NSS_STATUS_SUCCESS) + return false; + + d_printf( "%s:%s:%d:%d:%s:%s:%s\n", + response.data.pw.pw_name, + response.data.pw.pw_passwd, + response.data.pw.pw_uid, + response.data.pw.pw_gid, + response.data.pw.pw_gecos, + response.data.pw.pw_dir, + response.data.pw.pw_shell ); + + return true; +} + +/* pull pwent info for a given uid */ +static bool wbinfo_get_uidinfo(int uid) +{ + struct winbindd_request request; + struct winbindd_response response; + NSS_STATUS result; + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + request.data.uid = uid; + + result = winbindd_request_response(WINBINDD_GETPWUID, &request, &response); + + if (result != NSS_STATUS_SUCCESS) + return false; + + d_printf( "%s:%s:%d:%d:%s:%s:%s\n", + response.data.pw.pw_name, + response.data.pw.pw_passwd, + response.data.pw.pw_uid, + response.data.pw.pw_gid, + response.data.pw.pw_gecos, + response.data.pw.pw_dir, + response.data.pw.pw_shell ); + + return true; +} + +/* pull grent for a given group */ +static bool wbinfo_get_groupinfo(char *group) +{ + struct winbindd_request request; + struct winbindd_response response; + NSS_STATUS result; + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + /* Send request */ + + fstrcpy(request.data.groupname, group); + + result = winbindd_request_response(WINBINDD_GETGRNAM, &request, + &response); + + if ( result != NSS_STATUS_SUCCESS) + return false; + + d_printf( "%s:%s:%d\n", + response.data.gr.gr_name, + response.data.gr.gr_passwd, + response.data.gr.gr_gid ); + + return true; +} + +/* List groups a user is a member of */ + +static bool wbinfo_get_usergroups(char *user) +{ + struct winbindd_request request; + struct winbindd_response response; + NSS_STATUS result; + int i; + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + /* Send request */ + + fstrcpy(request.data.username, user); + + result = winbindd_request_response(WINBINDD_GETGROUPS, &request, &response); + + if (result != NSS_STATUS_SUCCESS) + return false; + + for (i = 0; i < response.data.num_entries; i++) + d_printf("%d\n", (int)((gid_t *)response.extra_data.data)[i]); + + SAFE_FREE(response.extra_data.data); + + return true; +} + + +/* List group SIDs a user SID is a member of */ +static bool wbinfo_get_usersids(char *user_sid) +{ + struct winbindd_request request; + struct winbindd_response response; + NSS_STATUS result; + int i; + const char *s; + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + /* Send request */ + fstrcpy(request.data.sid, user_sid); + + result = winbindd_request_response(WINBINDD_GETUSERSIDS, &request, &response); + + if (result != NSS_STATUS_SUCCESS) + return false; + + s = (const char *)response.extra_data.data; + for (i = 0; i < response.data.num_entries; i++) { + d_printf("%s\n", s); + s += strlen(s) + 1; + } + + SAFE_FREE(response.extra_data.data); + + return true; +} + +static bool wbinfo_get_userdomgroups(const char *user_sid) +{ + struct winbindd_request request; + struct winbindd_response response; + NSS_STATUS result; + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + /* Send request */ + fstrcpy(request.data.sid, user_sid); + + result = winbindd_request_response(WINBINDD_GETUSERDOMGROUPS, &request, + &response); + + if (result != NSS_STATUS_SUCCESS) + return false; + + if (response.data.num_entries != 0) + printf("%s", (char *)response.extra_data.data); + + SAFE_FREE(response.extra_data.data); + + return true; +} + +/* Convert NetBIOS name to IP */ + +static bool wbinfo_wins_byname(char *name) +{ + struct winbindd_request request; + struct winbindd_response response; + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + /* Send request */ + + fstrcpy(request.data.winsreq, name); + + if (winbindd_request_response(WINBINDD_WINS_BYNAME, &request, &response) != + NSS_STATUS_SUCCESS) { + return false; + } + + /* Display response */ + + d_printf("%s\n", response.data.winsresp); + + return true; +} + +/* Convert IP to NetBIOS name */ + +static bool wbinfo_wins_byip(char *ip) +{ + struct winbindd_request request; + struct winbindd_response response; + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + /* Send request */ + + fstrcpy(request.data.winsreq, ip); + + if (winbindd_request_response(WINBINDD_WINS_BYIP, &request, &response) != + NSS_STATUS_SUCCESS) { + return false; + } + + /* Display response */ + + d_printf("%s\n", response.data.winsresp); + + return true; +} + +/* List trusted domains */ + +static bool wbinfo_list_domains(bool list_all_domains) +{ + struct winbindd_request request; + struct winbindd_response response; + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + /* Send request */ + + request.data.list_all_domains = list_all_domains; + + if (winbindd_request_response(WINBINDD_LIST_TRUSTDOM, &request, &response) != + NSS_STATUS_SUCCESS) + return false; + + /* Display response */ + + if (response.extra_data.data) { + const char *extra_data = (char *)response.extra_data.data; + fstring name; + char *p; + + while(next_token(&extra_data, name, "\n", sizeof(fstring))) { + p = strchr(name, '\\'); + if (p == 0) { + d_fprintf(stderr, "Got invalid response: %s\n", + extra_data); + return false; + } + *p = 0; + d_printf("%s\n", name); + } + + SAFE_FREE(response.extra_data.data); + } + + 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) +{ + struct winbindd_request request; + struct winbindd_response response; + + ZERO_STRUCT(response); + ZERO_STRUCT(request); + + if ( domain ) + fstrcpy( request.domain_name, domain ); + + /* Send request */ + + if (winbindd_request_response(WINBINDD_SHOW_SEQUENCE, &request, &response) != + NSS_STATUS_SUCCESS) + return false; + + /* Display response */ + + if (response.extra_data.data) { + char *extra_data = (char *)response.extra_data.data; + d_printf("%s", extra_data); + SAFE_FREE(response.extra_data.data); + } + + return true; +} + +/* Show domain info */ + +static bool wbinfo_domain_info(const char *domain_name) +{ + struct winbindd_request request; + struct winbindd_response response; + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + if ((strequal(domain_name, ".")) || (domain_name[0] == '\0')) + fstrcpy(request.domain_name, get_winbind_domain()); + else + fstrcpy(request.domain_name, domain_name); + + /* Send request */ + + if (winbindd_request_response(WINBINDD_DOMAIN_INFO, &request, &response) != + NSS_STATUS_SUCCESS) + return false; + + /* Display response */ + + d_printf("Name : %s\n", response.data.domain_info.name); + d_printf("Alt_Name : %s\n", response.data.domain_info.alt_name); + + d_printf("SID : %s\n", response.data.domain_info.sid); + + d_printf("Active Directory : %s\n", + response.data.domain_info.active_directory ? "Yes" : "No"); + d_printf("Native : %s\n", + response.data.domain_info.native_mode ? "Yes" : "No"); + + d_printf("Primary : %s\n", + response.data.domain_info.primary ? "Yes" : "No"); + + return true; +} + +/* Get a foreign DC's name */ +static bool wbinfo_getdcname(const char *domain_name) +{ + struct winbindd_request request; + struct winbindd_response response; + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + fstrcpy(request.domain_name, domain_name); + + /* Send request */ + + if (winbindd_request_response(WINBINDD_GETDCNAME, &request, &response) != + NSS_STATUS_SUCCESS) { + 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; +} + +/* Check trust account password */ + +static bool wbinfo_check_secret(void) +{ + struct winbindd_response response; + NSS_STATUS result; + + ZERO_STRUCT(response); + + result = winbindd_request_response(WINBINDD_CHECK_MACHACC, NULL, &response); + + d_printf("checking the trust secret via RPC calls %s\n", + (result == NSS_STATUS_SUCCESS) ? "succeeded" : "failed"); + + if (result != NSS_STATUS_SUCCESS) + d_fprintf(stderr, "error code was %s (0x%x)\n", + response.data.auth.nt_status_string, + response.data.auth.nt_status); + + return result == NSS_STATUS_SUCCESS; +} + +/* Convert uid to sid */ + +static bool wbinfo_uid_to_sid(uid_t uid) +{ + struct winbindd_request request; + struct winbindd_response response; + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + /* Send request */ + + request.data.uid = uid; + + if (winbindd_request_response(WINBINDD_UID_TO_SID, &request, &response) != + NSS_STATUS_SUCCESS) + return false; + + /* Display response */ + + d_printf("%s\n", response.data.sid.sid); + + return true; +} + +/* Convert gid to sid */ + +static bool wbinfo_gid_to_sid(gid_t gid) +{ + struct winbindd_request request; + struct winbindd_response response; + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + /* Send request */ + + request.data.gid = gid; + + if (winbindd_request_response(WINBINDD_GID_TO_SID, &request, &response) != + NSS_STATUS_SUCCESS) + return false; + + /* Display response */ + + d_printf("%s\n", response.data.sid.sid); + + return true; +} + +/* Convert sid to uid */ + +static bool wbinfo_sid_to_uid(char *sid) +{ + struct winbindd_request request; + struct winbindd_response response; + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + /* Send request */ + + fstrcpy(request.data.sid, sid); + + if (winbindd_request_response(WINBINDD_SID_TO_UID, &request, &response) != + NSS_STATUS_SUCCESS) + return false; + + /* Display response */ + + d_printf("%d\n", (int)response.data.uid); + + return true; +} + +static bool wbinfo_sid_to_gid(char *sid) +{ + struct winbindd_request request; + struct winbindd_response response; + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + /* Send request */ + + fstrcpy(request.data.sid, sid); + + if (winbindd_request_response(WINBINDD_SID_TO_GID, &request, &response) != + NSS_STATUS_SUCCESS) + return false; + + /* Display response */ + + d_printf("%d\n", (int)response.data.gid); + + return true; +} + +static const char *sid_type_lookup(enum lsa_SidType r) +{ + switch (r) { + case SID_NAME_USE_NONE: return "SID_NAME_USE_NONE"; break; + case SID_NAME_USER: return "SID_NAME_USER"; break; + case SID_NAME_DOM_GRP: return "SID_NAME_DOM_GRP"; break; + case SID_NAME_DOMAIN: return "SID_NAME_DOMAIN"; break; + case SID_NAME_ALIAS: return "SID_NAME_ALIAS"; break; + case SID_NAME_WKN_GRP: return "SID_NAME_WKN_GRP"; break; + case SID_NAME_DELETED: return "SID_NAME_DELETED"; break; + case SID_NAME_INVALID: return "SID_NAME_INVALID"; break; + case SID_NAME_UNKNOWN: return "SID_NAME_UNKNOWN"; break; + case SID_NAME_COMPUTER: return "SID_NAME_COMPUTER"; break; + } + return "Invalid sid type\n"; +} + +/* Convert sid to string */ + +static bool wbinfo_lookupsid(char *sid) +{ + struct winbindd_request request; + struct winbindd_response response; + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + /* Send off request */ + + fstrcpy(request.data.sid, sid); + + if (winbindd_request_response(WINBINDD_LOOKUPSID, &request, &response) != + NSS_STATUS_SUCCESS) + return false; + + /* Display response */ + + d_printf("%s%c%s %s\n", response.data.name.dom_name, + winbind_separator(), response.data.name.name, + sid_type_lookup(response.data.name.type)); + + return true; +} + +/* Convert string to sid */ + +static bool wbinfo_lookupname(char *name) +{ + struct winbindd_request request; + struct winbindd_response response; + + /* Send off request */ + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + parse_wbinfo_domain_user(name, request.data.name.dom_name, + request.data.name.name); + + if (winbindd_request_response(WINBINDD_LOOKUPNAME, &request, &response) != + NSS_STATUS_SUCCESS) + return false; + + /* Display response */ + + d_printf("%s %s (%d)\n", response.data.sid.sid, sid_type_lookup(response.data.sid.type), response.data.sid.type); + + return true; +} + +/* Authenticate a user with a plaintext password */ + +static bool wbinfo_auth_krb5(char *username, const char *cctype, uint32_t flags) +{ + struct winbindd_request request; + struct winbindd_response response; + NSS_STATUS result; + 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); + + request.flags = flags; + + fstrcpy(request.data.auth.krb5_cc_type, cctype); + + request.data.auth.uid = geteuid(); + + result = winbindd_request_response(WINBINDD_PAM_AUTH, &request, &response); + + /* Display response */ + + d_printf("plaintext kerberos password authentication for [%s] %s (requesting cctype: %s)\n", + username, (result == NSS_STATUS_SUCCESS) ? "succeeded" : "failed", cctype); + + if (response.data.auth.nt_status) + d_fprintf(stderr, "error code was %s (0x%x)\nerror messsage was: %s\n", + response.data.auth.nt_status_string, + response.data.auth.nt_status, + response.data.auth.error_string); + + if (result == NSS_STATUS_SUCCESS) { + + if (request.flags & WBFLAG_PAM_INFO3_TEXT) { + if (response.data.auth.info3.user_flgs & NETLOGON_CACHED_ACCOUNT) { + d_printf("user_flgs: NETLOGON_CACHED_ACCOUNT\n"); + } + } + + if (response.data.auth.krb5ccname[0] != '\0') { + d_printf("credentials were put in: %s\n", response.data.auth.krb5ccname); + } else { + d_printf("no credentials cached\n"); + } + } + + return result == NSS_STATUS_SUCCESS; +} + +/* Authenticate a user with a plaintext password */ + +static bool wbinfo_auth(char *username) +{ + struct winbindd_request request; + struct winbindd_response response; + NSS_STATUS result; + 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); + + result = winbindd_request_response(WINBINDD_PAM_AUTH, &request, &response); + + /* Display response */ + + d_printf("plaintext password authentication %s\n", + (result == NSS_STATUS_SUCCESS) ? "succeeded" : "failed"); + + if (response.data.auth.nt_status) + d_fprintf(stderr, "error code was %s (0x%x)\nerror messsage was: %s\n", + response.data.auth.nt_status_string, + response.data.auth.nt_status, + response.data.auth.error_string); + + return result == NSS_STATUS_SUCCESS; +} + +/* Authenticate a user with a challenge/response */ + +static bool wbinfo_auth_crap(struct loadparm_context *lp_ctx, char *username) +{ + struct winbindd_request request; + struct winbindd_response response; + NSS_STATUS result; + fstring name_user; + fstring name_domain; + fstring pass; + char *p; + + /* Send off request */ + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + p = strchr(username, '%'); + + if (p) { + *p = 0; + fstrcpy(pass, p + 1); + } + + parse_wbinfo_domain_user(username, name_domain, name_user); + + request.data.auth_crap.logon_parameters = MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT | MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT; + + fstrcpy(request.data.auth_crap.user, name_user); + + fstrcpy(request.data.auth_crap.domain, + name_domain); + + generate_random_buffer(request.data.auth_crap.chal, 8); + + if (lp_client_ntlmv2_auth(lp_ctx)) { + DATA_BLOB server_chal; + DATA_BLOB names_blob; + + DATA_BLOB lm_response; + DATA_BLOB nt_response; + + TALLOC_CTX *mem_ctx; + mem_ctx = talloc_new(NULL); + if (mem_ctx == NULL) { + d_printf("talloc_new failed\n"); + return false; + } + + server_chal = data_blob(request.data.auth_crap.chal, 8); + + /* Pretend this is a login to 'us', for blob purposes */ + names_blob = NTLMv2_generate_names_blob(mem_ctx, lp_iconv_convenience(lp_ctx), lp_netbios_name(lp_ctx), lp_workgroup(lp_ctx)); + + if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain, pass, &server_chal, + &names_blob, + &lm_response, &nt_response, NULL, NULL)) { + data_blob_free(&names_blob); + data_blob_free(&server_chal); + return false; + } + data_blob_free(&names_blob); + data_blob_free(&server_chal); + + memcpy(request.data.auth_crap.nt_resp, nt_response.data, + MIN(nt_response.length, + sizeof(request.data.auth_crap.nt_resp))); + request.data.auth_crap.nt_resp_len = nt_response.length; + + memcpy(request.data.auth_crap.lm_resp, lm_response.data, + MIN(lm_response.length, + sizeof(request.data.auth_crap.lm_resp))); + request.data.auth_crap.lm_resp_len = lm_response.length; + + data_blob_free(&nt_response); + data_blob_free(&lm_response); + + } else { + if (lp_client_lanman_auth(lp_ctx) + && SMBencrypt(pass, request.data.auth_crap.chal, + (unsigned char *)request.data.auth_crap.lm_resp)) { + request.data.auth_crap.lm_resp_len = 24; + } else { + request.data.auth_crap.lm_resp_len = 0; + } + SMBNTencrypt(pass, request.data.auth_crap.chal, + (unsigned char *)request.data.auth_crap.nt_resp); + + request.data.auth_crap.nt_resp_len = 24; + } + + result = winbindd_request_response(WINBINDD_PAM_AUTH_CRAP, &request, &response); + + /* Display response */ + + d_printf("challenge/response password authentication %s\n", + (result == NSS_STATUS_SUCCESS) ? "succeeded" : "failed"); + + if (response.data.auth.nt_status) + d_fprintf(stderr, "error code was %s (0x%x)\nerror messsage was: %s\n", + response.data.auth.nt_status_string, + response.data.auth.nt_status, + response.data.auth.error_string); + + return result == NSS_STATUS_SUCCESS; +} + +/* Print domain users */ + +static bool print_domain_users(const char *domain) +{ + struct winbindd_request request; + struct winbindd_response response; + const char *extra_data; + fstring name; + + /* Send request to winbind daemon */ + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + if (domain) { + /* '.' is the special sign for our own domain */ + if ( strequal(domain, ".") ) + fstrcpy( request.domain_name, get_winbind_domain() ); + else + fstrcpy( request.domain_name, domain ); + } + + if (winbindd_request_response(WINBINDD_LIST_USERS, &request, &response) != + NSS_STATUS_SUCCESS) + return false; + + /* Look through extra data */ + + if (!response.extra_data.data) + return false; + + extra_data = (const char *)response.extra_data.data; + + while(next_token(&extra_data, name, ",", sizeof(fstring))) + d_printf("%s\n", name); + + SAFE_FREE(response.extra_data.data); + + return true; +} + +/* Print domain groups */ + +static bool print_domain_groups(const char *domain) +{ + struct winbindd_request request; + struct winbindd_response response; + const char *extra_data; + fstring name; + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + if (domain) { + if ( strequal(domain, ".") ) + fstrcpy( request.domain_name, get_winbind_domain() ); + else + fstrcpy( request.domain_name, domain ); + } + + if (winbindd_request_response(WINBINDD_LIST_GROUPS, &request, &response) != + NSS_STATUS_SUCCESS) + return false; + + /* Look through extra data */ + + if (!response.extra_data.data) + return false; + + extra_data = (const char *)response.extra_data.data; + + while(next_token(&extra_data, name, ",", sizeof(fstring))) + d_printf("%s\n", name); + + SAFE_FREE(response.extra_data.data); + + return true; +} + +static bool wbinfo_ping(void) +{ + NSS_STATUS result; + + result = winbindd_request_response(WINBINDD_PING, NULL, NULL); + + /* Display response */ + + d_printf("Ping to winbindd %s on fd %d\n", + (result == NSS_STATUS_SUCCESS) ? "succeeded" : "failed", winbindd_fd); + + return result == NSS_STATUS_SUCCESS; +} + +/* Main program */ + +enum { + OPT_SET_AUTH_USER = 1000, + OPT_GET_AUTH_USER, + OPT_DOMAIN_NAME, + OPT_SEQUENCE, + OPT_GETDCNAME, + OPT_USERDOMGROUPS, + OPT_USERSIDS, + OPT_ALLOCATE_UID, + OPT_ALLOCATE_GID, + OPT_SEPARATOR, + OPT_LIST_ALL_DOMAINS, + OPT_LIST_OWN_DOMAIN, + OPT_UID_INFO, + OPT_GROUP_INFO, +}; + +int main(int argc, char **argv, char **envp) +{ + int opt; + + poptContext pc; + static char *string_arg; + static char *opt_domain_name; + static int int_arg; + int result = 1; + + struct poptOption long_options[] = { + POPT_AUTOHELP + + /* longName, shortName, argInfo, argPtr, value, descrip, + argDesc */ + + { "domain-users", 'u', POPT_ARG_NONE, 0, 'u', "Lists all domain users", "domain"}, + { "domain-groups", 'g', POPT_ARG_NONE, 0, 'g', "Lists all domain groups", "domain" }, + { "WINS-by-name", 'N', POPT_ARG_STRING, &string_arg, 'N', "Converts NetBIOS name to IP", "NETBIOS-NAME" }, + { "WINS-by-ip", 'I', POPT_ARG_STRING, &string_arg, 'I', "Converts IP address to NetBIOS name", "IP" }, + { "name-to-sid", 'n', POPT_ARG_STRING, &string_arg, 'n', "Converts name to sid", "NAME" }, + { "sid-to-name", 's', POPT_ARG_STRING, &string_arg, 's', "Converts sid to name", "SID" }, + { "uid-to-sid", 'U', POPT_ARG_INT, &int_arg, 'U', "Converts uid to sid" , "UID" }, + { "gid-to-sid", 'G', POPT_ARG_INT, &int_arg, 'G', "Converts gid to sid", "GID" }, + { "sid-to-uid", 'S', POPT_ARG_STRING, &string_arg, 'S', "Converts sid to uid", "SID" }, + { "sid-to-gid", 'Y', POPT_ARG_STRING, &string_arg, 'Y', "Converts sid to gid", "SID" }, + { "check-secret", 't', POPT_ARG_NONE, 0, 't', "Check shared secret" }, + { "trusted-domains", 'm', POPT_ARG_NONE, 0, 'm', "List trusted domains" }, + { "all-domains", 0, POPT_ARG_NONE, 0, OPT_LIST_ALL_DOMAINS, "List all domains (trusted and own domain)" }, + { "own-domain", 0, POPT_ARG_NONE, 0, OPT_LIST_OWN_DOMAIN, "List own domain" }, + { "sequence", 0, POPT_ARG_NONE, 0, OPT_SEQUENCE, "Show sequence numbers of all domains" }, + { "domain-info", 'D', POPT_ARG_STRING, &string_arg, 'D', "Show most of the info we have about the domain" }, + { "user-info", 'i', POPT_ARG_STRING, &string_arg, 'i', "Get user info", "USER" }, + { "uid-info", 0, POPT_ARG_INT, &int_arg, OPT_UID_INFO, "Get user info from uid", "UID" }, + { "group-info", 0, POPT_ARG_STRING, &string_arg, OPT_GROUP_INFO, "Get group info", "GROUP" }, + { "user-groups", 'r', POPT_ARG_STRING, &string_arg, 'r', "Get user groups", "USER" }, + { "user-domgroups", 0, POPT_ARG_STRING, &string_arg, + OPT_USERDOMGROUPS, "Get user domain groups", "SID" }, + { "user-sids", 0, POPT_ARG_STRING, &string_arg, OPT_USERSIDS, "Get user group sids for user SID", "SID" }, + { "authenticate", 'a', POPT_ARG_STRING, &string_arg, 'a', "authenticate user", "user%password" }, + { "getdcname", 0, POPT_ARG_STRING, &string_arg, OPT_GETDCNAME, + "Get a DC name for a foreign domain", "domainname" }, + { "ping", 'p', POPT_ARG_NONE, 0, 'p', "Ping winbindd to see if it is alive" }, + { "domain", 0, POPT_ARG_STRING, &opt_domain_name, OPT_DOMAIN_NAME, "Define to the domain to restrict operation", "domain" }, +#ifdef HAVE_KRB5 + { "krb5auth", 'K', POPT_ARG_STRING, &string_arg, 'K', "authenticate user using Kerberos", "user%password" }, + /* destroys wbinfo --help output */ + /* "user%password,DOM\\user%password,user@EXAMPLE.COM,EXAMPLE.COM\\user%password" }, */ +#endif + { "separator", 0, POPT_ARG_NONE, 0, OPT_SEPARATOR, "Get the active winbind separator", NULL }, + POPT_COMMON_VERSION + POPT_COMMON_SAMBA + POPT_TABLEEND + }; + + /* Parse options */ + + pc = poptGetContext("wbinfo", argc, (const char **)argv, long_options, 0); + + /* 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 */ + } + + 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 '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 't': + if (!wbinfo_check_secret()) { + d_fprintf(stderr, "Could not check secret\n"); + goto done; + } + break; + case 'm': + if (!wbinfo_list_domains(false)) { + 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 '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_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 '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 '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(cmdline_lp_ctx, string_arg)) { + d_fprintf(stderr, "Could not authenticate user %s with " + "challenge/response\n", string_arg); + got_error = true; + } + + if (got_error) + goto done; + break; + } + case 'K': { + uint32_t flags = WBFLAG_PAM_KRB5 | + WBFLAG_PAM_CACHED_LOGIN | + WBFLAG_PAM_FALLBACK_AFTER_KRB5 | + WBFLAG_PAM_INFO3_TEXT; + + if (!wbinfo_auth_krb5(string_arg, "FILE", flags)) { + d_fprintf(stderr, "Could not authenticate user [%s] with " + "Kerberos (ccache: %s)\n", string_arg, "FILE"); + goto done; + } + break; + } + case 'p': + if (!wbinfo_ping()) { + d_fprintf(stderr, "could not ping winbindd!\n"); + goto done; + } + break; + case OPT_GETDCNAME: + if (!wbinfo_getdcname(string_arg)) { + goto done; + } + break; + case OPT_SEPARATOR: { + const char sep = winbind_separator_int(true); + if ( !sep ) { + goto done; + } + d_printf("%c\n", sep); + break; + } + case OPT_LIST_ALL_DOMAINS: + if (!wbinfo_list_domains(true)) { + goto done; + } + break; + case OPT_LIST_OWN_DOMAIN: + if (!wbinfo_list_own_domain()) { + goto done; + } + break; + /* generic configuration options */ + case OPT_DOMAIN_NAME: + break; + default: + d_fprintf(stderr, "Invalid option\n"); + poptPrintHelp(pc, stderr, 0); + goto done; + } + } + + result = 0; + + /* Exit code */ + + done: + poptFreeContext(pc); + return result; +} diff --git a/source4/nsswitch/winbind_client.h b/source4/nsswitch/winbind_client.h new file mode 100644 index 0000000000..2a3956e1fd --- /dev/null +++ b/source4/nsswitch/winbind_client.h @@ -0,0 +1,25 @@ +#include "winbind_nss_config.h" +#include "winbind_struct_protocol.h" + +void winbindd_init_request(struct winbindd_request *req,int rq_type); +void winbindd_free_response(struct winbindd_response *response); +NSS_STATUS winbindd_send_request(int req_type, int need_priv, + struct winbindd_request *request); +NSS_STATUS winbindd_get_response(struct winbindd_response *response); +NSS_STATUS winbindd_request_response(int req_type, + struct winbindd_request *request, + struct winbindd_response *response); +NSS_STATUS winbindd_priv_request_response(int req_type, + struct winbindd_request *request, + struct winbindd_response *response); +int winbindd_read_reply(struct winbindd_response *response); + +bool winbind_env_set(void); +bool winbind_off(void); +bool winbind_on(void); + +int winbind_write_sock(void *buffer, int count, int recursing, int need_priv); +int winbind_read_sock(void *buffer, int count); +void winbind_close_sock(void); + +const char *nss_err_str(NSS_STATUS ret); diff --git a/source4/nsswitch/winbind_nss.h b/source4/nsswitch/winbind_nss.h new file mode 100644 index 0000000000..0a3bc7cefa --- /dev/null +++ b/source4/nsswitch/winbind_nss.h @@ -0,0 +1,76 @@ +/* + 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" + +#elif HAVE_NSS_H + +/* + * Linux (glibc) + */ + +#include "nsswitch/winbind_nss_linux.h" + +#elif 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" + +#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/source4/nsswitch/winbind_nss_config.h b/source4/nsswitch/winbind_nss_config.h new file mode 100644 index 0000000000..e0828dc905 --- /dev/null +++ b/source4/nsswitch/winbind_nss_config.h @@ -0,0 +1,64 @@ +/* + 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 "lib/replace/replace.h" +#endif + +#include "system/passwd.h" +#include "system/filesys.h" +#include "system/network.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 _PSTRING +#define _PSTRING +#define PSTRING_LEN 1024 +#define FSTRING_LEN 256 +typedef char pstring[PSTRING_LEN]; +typedef char fstring[FSTRING_LEN]; +#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/source4/nsswitch/winbind_nss_hpux.h b/source4/nsswitch/winbind_nss_hpux.h new file mode 100644 index 0000000000..62cf3c26c5 --- /dev/null +++ b/source4/nsswitch/winbind_nss_hpux.h @@ -0,0 +1,137 @@ +/* + 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; + int h_errno; + nss_status_t status; +} nss_XbyY_args_t; + +#endif /* _WINBIND_NSS_HPUX_H */ diff --git a/source4/nsswitch/winbind_nss_irix.h b/source4/nsswitch/winbind_nss_irix.h new file mode 100644 index 0000000000..b40b14b0b0 --- /dev/null +++ b/source4/nsswitch/winbind_nss_irix.h @@ -0,0 +1,42 @@ +/* + 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_IRIX_H +#define _WINBIND_NSS_IRIX_H + +/* following required to prevent warnings of double definition + * of datum from ns_api.h +*/ +#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; + +#endif /* _WINBIND_NSS_IRIX_H */ diff --git a/source4/nsswitch/winbind_nss_linux.c b/source4/nsswitch/winbind_nss_linux.c new file mode 100644 index 0000000000..ac53979ced --- /dev/null +++ b/source4/nsswitch/winbind_nss_linux.c @@ -0,0 +1,1281 @@ +/* + 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" + +/* 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 + +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(char *user, gid_t group, long int *start, + long int *size, gid_t **groups, + long int limit, int *errnop); +NSS_STATUS _nss_winbind_getusersids(const char *user_sid, char **group_sids, + int *num_groups, char *buffer, size_t buf_size, + int *errnop); +NSS_STATUS _nss_winbind_nametosid(const char *name, char **sid, char *buffer, + size_t buflen, int *errnop); +NSS_STATUS _nss_winbind_sidtoname(const char *sid, char **name, char *buffer, + size_t buflen, int *errnop); +NSS_STATUS _nss_winbind_sidtouid(const char *sid, uid_t *uid, int *errnop); +NSS_STATUS _nss_winbind_sidtogid(const char *sid, gid_t *gid, int *errnop); +NSS_STATUS _nss_winbind_uidtosid(uid_t uid, char **sid, char *buffer, + size_t buflen, int *errnop); +NSS_STATUS _nss_winbind_gidtosid(gid_t gid, char **sid, char *buffer, + size_t buflen, int *errnop); + +/* Prototypes from wb_common.c */ + +extern int winbindd_fd; + +/* 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() 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(char **ptr,char *buff,const char *sep, size_t bufsize) +{ + char *s; + bool quoted; + size_t len=1; + + 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; + + /* copy over the token */ + for (quoted = false; len < bufsize && *s && (quoted || !strchr(sep,*s)); s++) { + if (*s == '\"') { + quoted = !quoted; + } else { + len++; + *buff++ = *s; + } + } + + *ptr = (*s) ? s+1 : s; + *buff = 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) +{ + /* User name */ + + if ((result->pw_name = + get_static(buffer, buflen, strlen(pw->pw_name) + 1)) == NULL) { + + /* Out of memory */ + + return NSS_STATUS_TRYAGAIN; + } + + strcpy(result->pw_name, pw->pw_name); + + /* Password */ + + if ((result->pw_passwd = + get_static(buffer, buflen, strlen(pw->pw_passwd) + 1)) == NULL) { + + /* Out of memory */ + + return NSS_STATUS_TRYAGAIN; + } + + strcpy(result->pw_passwd, pw->pw_passwd); + + /* [ug]id */ + + result->pw_uid = pw->pw_uid; + result->pw_gid = pw->pw_gid; + + /* GECOS */ + + if ((result->pw_gecos = + get_static(buffer, buflen, strlen(pw->pw_gecos) + 1)) == NULL) { + + /* Out of memory */ + + return NSS_STATUS_TRYAGAIN; + } + + strcpy(result->pw_gecos, pw->pw_gecos); + + /* Home directory */ + + if ((result->pw_dir = + get_static(buffer, buflen, strlen(pw->pw_dir) + 1)) == NULL) { + + /* Out of memory */ + + return NSS_STATUS_TRYAGAIN; + } + + strcpy(result->pw_dir, pw->pw_dir); + + /* Logon shell */ + + if ((result->pw_shell = + get_static(buffer, buflen, strlen(pw->pw_shell) + 1)) == NULL) { + + /* Out of memory */ + + return NSS_STATUS_TRYAGAIN; + } + + strcpy(result->pw_shell, pw->pw_shell); + + /* The struct passwd for Solaris has some extra fields which must + be initialised or nscd crashes. */ + +#if HAVE_PASSWD_PW_COMMENT + result->pw_comment = ""; +#endif + +#if 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, + char *gr_mem, char **buffer, size_t *buflen) +{ + fstring name; + int i; + char *tst; + + /* Group name */ + + if ((result->gr_name = + get_static(buffer, buflen, strlen(gr->gr_name) + 1)) == NULL) { + + /* Out of memory */ + + return NSS_STATUS_TRYAGAIN; + } + + strcpy(result->gr_name, gr->gr_name); + + /* Password */ + + if ((result->gr_passwd = + get_static(buffer, buflen, strlen(gr->gr_passwd) + 1)) == NULL) { + + /* Out of memory */ + + return NSS_STATUS_TRYAGAIN; + } + + strcpy(result->gr_passwd, gr->gr_passwd); + + /* gid */ + + result->gr_gid = gr->gr_gid; + + /* Group membership */ + + if ((gr->num_gr_mem < 0) || !gr_mem) { + gr->num_gr_mem = 0; + } + + /* this next value is a pointer to a pointer so let's align it */ + + /* Calculate number of extra bytes needed to align on pointer size boundry */ + if ((i = (unsigned long)(*buffer) % sizeof(char*)) != 0) + i = sizeof(char*) - i; + + if ((tst = get_static(buffer, buflen, ((gr->num_gr_mem + 1) * + sizeof(char *)+i))) == NULL) { + + /* Out of memory */ + + return NSS_STATUS_TRYAGAIN; + } + result->gr_mem = (char **)(tst + i); + + if (gr->num_gr_mem == 0) { + + /* Group is empty */ + + *(result->gr_mem) = NULL; + return NSS_STATUS_SUCCESS; + } + + /* Start looking at extra data */ + + i = 0; + + while(next_token((char **)&gr_mem, name, ",", sizeof(fstring))) { + + /* Allocate space for member */ + + if (((result->gr_mem)[i] = + get_static(buffer, buflen, strlen(name) + 1)) == NULL) { + + /* Out of memory */ + + return NSS_STATUS_TRYAGAIN; + } + + strcpy((result->gr_mem)[i], name); + i++; + } + + /* Terminate list */ + + (result->gr_mem)[i] = NULL; + + return NSS_STATUS_SUCCESS; +} + +/* + * NSS user functions + */ + +static struct winbindd_response getpwent_response; + +static int ndx_pw_cache; /* Current index into pwd cache */ +static int num_pw_cache; /* Current size of pwd cache */ + +/* Rewind "file pointer" to start of ntdom password database */ + +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); + } + + ret = winbindd_request_response(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" */ + +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); + } + + ret = winbindd_request_response(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 */ + +NSS_STATUS +_nss_winbind_getpwent_r(struct passwd *result, char *buffer, + size_t buflen, int *errnop) +{ + NSS_STATUS ret; + struct winbindd_request request; + static int called_again; + +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: getpwent\n", getpid()); +#endif + + /* 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; + + ret = winbindd_request_response(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 */ + +NSS_STATUS +_nss_winbind_getpwuid_r(uid_t uid, struct passwd *result, char *buffer, + size_t buflen, int *errnop) +{ + NSS_STATUS ret; + static struct winbindd_response response; + struct winbindd_request request; + static int keep_response=0; + +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: getpwuid %d\n", getpid(), (unsigned int)uid); +#endif + + /* If our static buffer needs to be expanded we are called again */ + if (!keep_response) { + + /* Call for the first time */ + + ZERO_STRUCT(response); + ZERO_STRUCT(request); + + request.data.uid = uid; + + ret = winbindd_request_response(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) { + 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]: getpwuid %d returns %s (%d)\n", getpid(), + (unsigned int)uid, nss_err_str(ret), ret); +#endif + return ret; +} + +/* Return passwd struct from username */ +NSS_STATUS +_nss_winbind_getpwnam_r(const char *name, struct passwd *result, char *buffer, + size_t buflen, int *errnop) +{ + NSS_STATUS ret; + static struct winbindd_response response; + struct winbindd_request request; + static int keep_response; + +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: getpwnam %s\n", getpid(), name); +#endif + + /* If our static buffer needs to be expanded we are called again */ + + if (!keep_response) { + + /* Call for the first time */ + + ZERO_STRUCT(response); + ZERO_STRUCT(request); + + strncpy(request.data.username, name, + sizeof(request.data.username) - 1); + request.data.username + [sizeof(request.data.username) - 1] = '\0'; + + ret = winbindd_request_response(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 struct winbindd_response getgrent_response; + +static int ndx_gr_cache; /* Current index into grp cache */ +static int num_gr_cache; /* Current size of grp cache */ + +/* Rewind "file pointer" to start of ntdom group database */ + +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); + } + + ret = winbindd_request_response(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 */ + +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); + } + + ret = winbindd_request_response(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 struct winbindd_request request; + static 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; + + ret = winbindd_request_response(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; +} + + +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); +} + +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 */ + +NSS_STATUS +_nss_winbind_getgrnam_r(const char *name, + struct group *result, char *buffer, + size_t buflen, int *errnop) +{ + NSS_STATUS ret; + static struct winbindd_response response; + struct winbindd_request request; + static int keep_response; + +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: getgrnam %s\n", getpid(), name); +#endif + + /* If our static buffer needs to be expanded we are called again */ + + if (!keep_response) { + + /* Call for the first time */ + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + strncpy(request.data.groupname, name, + sizeof(request.data.groupname)); + request.data.groupname + [sizeof(request.data.groupname) - 1] = '\0'; + + ret = winbindd_request_response(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 */ + +NSS_STATUS +_nss_winbind_getgrgid_r(gid_t gid, + struct group *result, char *buffer, + size_t buflen, int *errnop) +{ + NSS_STATUS ret; + static struct winbindd_response response; + struct winbindd_request request; + static int keep_response; + +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: getgrgid %d\n", getpid(), gid); +#endif + + /* If our static buffer needs to be expanded we are called again */ + + if (!keep_response) { + + /* Call for the first time */ + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + request.data.gid = gid; + + ret = winbindd_request_response(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 */ + +NSS_STATUS +_nss_winbind_initgroups_dyn(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); + + ret = winbindd_request_response(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; + } + + /* Filled buffer ? If so, resize. */ + + if (*start == *size) { + long int newsize; + gid_t *newgroups; + + newsize = 2 * (*size); + if (limit > 0) { + if (*size == limit) { + goto done; + } + if (newsize > limit) { + newsize = limit; + } + } + + newgroups = (gid_t *) + realloc((*groups), + newsize * sizeof(**groups)); + if (!newgroups) { + *errnop = ENOMEM; + ret = NSS_STATUS_NOTFOUND; + goto done; + } + *groups = newgroups; + *size = newsize; + } + + /* Add to buffer */ + + (*groups)[*start] = gid_list[i]; + *start += 1; + } + } + + /* Back to your regularly scheduled programming */ + + done: +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: initgroups %s returns %s (%d)\n", getpid(), + user, nss_err_str(ret), ret); +#endif + return ret; +} + + +/* return a list of group SIDs for a user SID */ +NSS_STATUS +_nss_winbind_getusersids(const char *user_sid, char **group_sids, + int *num_groups, + char *buffer, size_t buf_size, int *errnop) +{ + NSS_STATUS ret; + struct winbindd_request request; + struct winbindd_response response; + +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: getusersids %s\n", getpid(), user_sid); +#endif + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + strncpy(request.data.sid, user_sid,sizeof(request.data.sid) - 1); + request.data.sid[sizeof(request.data.sid) - 1] = '\0'; + + ret = winbindd_request_response(WINBINDD_GETUSERSIDS, &request, &response); + + if (ret != NSS_STATUS_SUCCESS) { + goto done; + } + + if (buf_size < response.length - sizeof(response)) { + ret = NSS_STATUS_TRYAGAIN; + errno = *errnop = ERANGE; + goto done; + } + + *num_groups = response.data.num_entries; + *group_sids = buffer; + memcpy(buffer, response.extra_data.data, response.length - sizeof(response)); + errno = *errnop = 0; + + done: + winbindd_free_response(&response); + return ret; +} + + +/* map a user or group name to a SID string */ +NSS_STATUS +_nss_winbind_nametosid(const char *name, char **sid, char *buffer, + size_t buflen, int *errnop) +{ + NSS_STATUS ret; + struct winbindd_response response; + struct winbindd_request request; + +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: nametosid %s\n", getpid(), name); +#endif + + ZERO_STRUCT(response); + ZERO_STRUCT(request); + + strncpy(request.data.name.name, name, + sizeof(request.data.name.name) - 1); + request.data.name.name[sizeof(request.data.name.name) - 1] = '\0'; + + ret = winbindd_request_response(WINBINDD_LOOKUPNAME, &request, &response); + if (ret != NSS_STATUS_SUCCESS) { + *errnop = errno = EINVAL; + goto failed; + } + + if (buflen < strlen(response.data.sid.sid)+1) { + ret = NSS_STATUS_TRYAGAIN; + *errnop = errno = ERANGE; + goto failed; + } + + *errnop = errno = 0; + *sid = buffer; + strcpy(*sid, response.data.sid.sid); + +failed: + winbindd_free_response(&response); + return ret; +} + +/* map a sid string to a user or group name */ +NSS_STATUS +_nss_winbind_sidtoname(const char *sid, char **name, char *buffer, + size_t buflen, int *errnop) +{ + NSS_STATUS ret; + struct winbindd_response response; + struct winbindd_request request; + static char sep_char; + unsigned needed; + +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: sidtoname %s\n", getpid(), sid); +#endif + + ZERO_STRUCT(response); + ZERO_STRUCT(request); + + /* we need to fetch the separator first time through */ + if (!sep_char) { + ret = winbindd_request_response(WINBINDD_INFO, &request, &response); + if (ret != NSS_STATUS_SUCCESS) { + *errnop = errno = EINVAL; + goto failed; + } + + sep_char = response.data.info.winbind_separator; + winbindd_free_response(&response); + } + + + strncpy(request.data.sid, sid, + sizeof(request.data.sid) - 1); + request.data.sid[sizeof(request.data.sid) - 1] = '\0'; + + ret = winbindd_request_response(WINBINDD_LOOKUPSID, &request, &response); + if (ret != NSS_STATUS_SUCCESS) { + *errnop = errno = EINVAL; + goto failed; + } + + needed = + strlen(response.data.name.dom_name) + + strlen(response.data.name.name) + 2; + + if (buflen < needed) { + ret = NSS_STATUS_TRYAGAIN; + *errnop = errno = ERANGE; + goto failed; + } + + snprintf(buffer, needed, "%s%c%s", + response.data.name.dom_name, + sep_char, + response.data.name.name); + + *name = buffer; + *errnop = errno = 0; + +failed: + winbindd_free_response(&response); + return ret; +} + +/* map a sid to a uid */ +NSS_STATUS +_nss_winbind_sidtouid(const char *sid, uid_t *uid, int *errnop) +{ + NSS_STATUS ret; + struct winbindd_response response; + struct winbindd_request request; + +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: sidtouid %s\n", getpid(), sid); +#endif + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + strncpy(request.data.sid, sid, sizeof(request.data.sid) - 1); + request.data.sid[sizeof(request.data.sid) - 1] = '\0'; + + ret = winbindd_request_response(WINBINDD_SID_TO_UID, &request, &response); + if (ret != NSS_STATUS_SUCCESS) { + *errnop = errno = EINVAL; + goto failed; + } + + *uid = response.data.uid; + +failed: + return ret; +} + +/* map a sid to a gid */ +NSS_STATUS +_nss_winbind_sidtogid(const char *sid, gid_t *gid, int *errnop) +{ + NSS_STATUS ret; + struct winbindd_response response; + struct winbindd_request request; + +#ifdef DEBUG_NSS + fprintf(stderr, "[%5d]: sidtogid %s\n", getpid(), sid); +#endif + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + strncpy(request.data.sid, sid, sizeof(request.data.sid) - 1); + request.data.sid[sizeof(request.data.sid) - 1] = '\0'; + + ret = winbindd_request_response(WINBINDD_SID_TO_GID, &request, &response); + if (ret != NSS_STATUS_SUCCESS) { + *errnop = errno = EINVAL; + goto failed; + } + + *gid = response.data.gid; + +failed: + return ret; +} + +/* map a uid to a SID string */ +NSS_STATUS +_nss_winbind_uidtosid(uid_t uid, char **sid, char *buffer, + size_t buflen, int *errnop) +{ + NSS_STATUS ret; + struct winbindd_response response; + struct winbindd_request request; + +#ifdef DEBUG_NSS + fprintf(stderr, "[%5u]: uidtosid %u\n", (unsigned int)getpid(), (unsigned int)uid); +#endif + + ZERO_STRUCT(response); + ZERO_STRUCT(request); + + request.data.uid = uid; + + ret = winbindd_request_response(WINBINDD_UID_TO_SID, &request, &response); + if (ret != NSS_STATUS_SUCCESS) { + *errnop = errno = EINVAL; + goto failed; + } + + if (buflen < strlen(response.data.sid.sid)+1) { + ret = NSS_STATUS_TRYAGAIN; + *errnop = errno = ERANGE; + goto failed; + } + + *errnop = errno = 0; + *sid = buffer; + strcpy(*sid, response.data.sid.sid); + +failed: + winbindd_free_response(&response); + return ret; +} + +/* map a gid to a SID string */ +NSS_STATUS +_nss_winbind_gidtosid(gid_t gid, char **sid, char *buffer, + size_t buflen, int *errnop) +{ + NSS_STATUS ret; + struct winbindd_response response; + struct winbindd_request request; + +#ifdef DEBUG_NSS + fprintf(stderr, "[%5u]: gidtosid %u\n", (unsigned int)getpid(), (unsigned int)gid); +#endif + + ZERO_STRUCT(response); + ZERO_STRUCT(request); + + request.data.gid = gid; + + ret = winbindd_request_response(WINBINDD_GID_TO_SID, &request, &response); + if (ret != NSS_STATUS_SUCCESS) { + *errnop = errno = EINVAL; + goto failed; + } + + if (buflen < strlen(response.data.sid.sid)+1) { + ret = NSS_STATUS_TRYAGAIN; + *errnop = errno = ERANGE; + goto failed; + } + + *errnop = errno = 0; + *sid = buffer; + strcpy(*sid, response.data.sid.sid); + +failed: + winbindd_free_response(&response); + return ret; +} diff --git a/source4/nsswitch/winbind_nss_linux.h b/source4/nsswitch/winbind_nss_linux.h new file mode 100644 index 0000000000..74aaec5ce6 --- /dev/null +++ b/source4/nsswitch/winbind_nss_linux.h @@ -0,0 +1,29 @@ +/* + 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 + +#include <nss.h> + +typedef enum nss_status NSS_STATUS; + +#endif /* _WINBIND_NSS_LINUX_H */ diff --git a/source4/nsswitch/winbind_nss_solaris.h b/source4/nsswitch/winbind_nss_solaris.h new file mode 100644 index 0000000000..941b3e66df --- /dev/null +++ b/source4/nsswitch/winbind_nss_solaris.h @@ -0,0 +1,89 @@ +/* + 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 + +/* Solaris has a broken nss_common header file containing C++ reserved names. */ +#ifndef __cplusplus +#undef class +#undef private +#undef public +#undef protected +#undef template +#undef this +#undef new +#undef delete +#undef friend +#endif + +#include <nss_common.h> + +/* +TODO: we need to cleanup samba4's headers.. + +#ifndef __cplusplus +#define class #error DONT_USE_CPLUSPLUS_RESERVED_NAMES +#define private #error DONT_USE_CPLUSPLUS_RESERVED_NAMES +#define public #error DONT_USE_CPLUSPLUS_RESERVED_NAMES +#define protected #error DONT_USE_CPLUSPLUS_RESERVED_NAMES +#define template #error DONT_USE_CPLUSPLUS_RESERVED_NAMES +#define this #error DONT_USE_CPLUSPLUS_RESERVED_NAMES +#define new #error DONT_USE_CPLUSPLUS_RESERVED_NAMES +#define delete #error DONT_USE_CPLUSPLUS_RESERVED_NAMES +#define friend #error DONT_USE_CPLUSPLUS_RESERVED_NAMES +#endif +*/ + +#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 + +/* The solaris winbind is implemented as a wrapper around the linux + version. */ + +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, struct passwd*, 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_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); + +#endif /* _WINBIND_NSS_SOLARIS_H */ diff --git a/source4/nsswitch/winbind_struct_protocol.h b/source4/nsswitch/winbind_struct_protocol.h new file mode 100644 index 0000000000..fba45230a9 --- /dev/null +++ b/source4/nsswitch/winbind_struct_protocol.h @@ -0,0 +1,499 @@ +/* + 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 _WINBINDD_NTDOM_H +#define _WINBINDD_NTDOM_H + +#define WINBINDD_SOCKET_NAME "pipe" /* Name of PF_UNIX socket */ + +/* Let the build environment override the public winbindd socket location. This + * is needed for launchd support -- jpeach. + */ +#ifndef WINBINDD_SOCKET_DIR +#define WINBINDD_SOCKET_DIR "/tmp/.winbindd" /* Name of PF_UNIX dir */ +#endif + +/* + * when compiled with socket_wrapper support + * the location of the WINBINDD_SOCKET_DIR + * can be overwritten via an environment variable + */ +#define WINBINDD_SOCKET_DIR_ENVVAR "WINBINDD_SOCKET_DIR" + +#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. */ + +#define WINBIND_INTERFACE_VERSION 19 + +/* 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 /li64/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_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, + + /* Lookup functions */ + + WINBINDD_SID_TO_UID, + WINBINDD_SID_TO_GID, + WINBINDD_SIDS_TO_XIDS, + WINBINDD_UID_TO_SID, + WINBINDD_GID_TO_SID, + + WINBINDD_ALLOCATE_UID, + WINBINDD_ALLOCATE_GID, + WINBINDD_SET_MAPPING, + WINBINDD_SET_HWM, + + /* Miscellaneous other stuff */ + + WINBINDD_DUMP_MAPS, + + WINBINDD_CHECK_MACHACC, /* Check machine account pw works */ + 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_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, + + /* Initialize connection in a child */ + WINBINDD_INIT_CONNECTION, + + /* 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, + WINBINDD_DUAL_SET_MAPPING, + WINBINDD_DUAL_SET_HWM, + WINBINDD_DUAL_DUMP_MAPS, + + /* Wrapper around possibly blocking unix nss calls */ + WINBINDD_DUAL_UID2NAME, + WINBINDD_DUAL_NAME2UID, + WINBINDD_DUAL_GID2NAME, + WINBINDD_DUAL_NAME2GID, + + WINBINDD_DUAL_USERINFO, + WINBINDD_DUAL_GETSIDALIASES, + + /* Complete the challenge phase of the NTLM authentication + protocol using cached password. */ + WINBINDD_CCACHE_NTLMAUTH, + + 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; + +/* PAM specific 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_PAM_UNIX_NAME 0x00000080 +#define WBFLAG_PAM_AFS_TOKEN 0x00000100 +#define WBFLAG_PAM_NT_STATUS_SQUASH 0x00000200 +#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 /* not used */ + +/* generic request flags */ +#define WBFLAG_QUERY_ONLY 0x00000020 /* not used */ +/* 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 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 */ + + 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 */ + struct { + /* We deliberatedly 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; + + /* 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; + uint32_t padding; + 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; + } 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 { + uint32_t auth_blob_len; /* blob in extra_data */ + } ccache_ntlm_auth; + } data; + + /* Variable length return data */ + + union { + SMB_TIME_T padding; + void *data; + } extra_data; +}; + +struct WINBINDD_MEMORY_CREDS { + struct WINBINDD_MEMORY_CREDS *next, *prev; + const char *username; /* lookup key. */ + uid_t uid; + int ref_count; + size_t len; + uint8_t *nt_hash; /* Base pointer for the following 2 */ + uint8_t *lm_hash; + char *pass; +}; + +struct WINBINDD_CCACHE_ENTRY { + struct WINBINDD_CCACHE_ENTRY *next, *prev; + const char *principal_name; + const char *ccname; + const char *service; + const char *username; + const char *realm; + struct WINBINDD_MEMORY_CREDS *cred_ptr; + int ref_count; + uid_t uid; + time_t create_time; + time_t renew_until; + time_t refresh_time; + struct timed_event *event; +}; + +#endif |