summaryrefslogtreecommitdiff
path: root/source4/nsswitch
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2003-08-13 01:53:07 +0000
committerAndrew Tridgell <tridge@samba.org>2003-08-13 01:53:07 +0000
commitef2e26c91b80556af033d3335e55f5dfa6fff31d (patch)
treefaa21bfd7e7b5247250b47c7891dc1a5ebee6be9 /source4/nsswitch
downloadsamba-ef2e26c91b80556af033d3335e55f5dfa6fff31d.tar.gz
samba-ef2e26c91b80556af033d3335e55f5dfa6fff31d.tar.bz2
samba-ef2e26c91b80556af033d3335e55f5dfa6fff31d.zip
first public release of samba4 code
(This used to be commit b0510b5428b3461aeb9bbe3cc95f62fc73e2b97f)
Diffstat (limited to 'source4/nsswitch')
-rw-r--r--source4/nsswitch/.cvsignore4
-rw-r--r--source4/nsswitch/README13
-rw-r--r--source4/nsswitch/hp_nss_common.h51
-rw-r--r--source4/nsswitch/hp_nss_dbdefs.h105
-rw-r--r--source4/nsswitch/nss.h104
-rw-r--r--source4/nsswitch/pam_winbind.c754
-rw-r--r--source4/nsswitch/pam_winbind.h93
-rw-r--r--source4/nsswitch/wb_client.c307
-rw-r--r--source4/nsswitch/wb_common.c433
-rw-r--r--source4/nsswitch/wbinfo.c891
-rw-r--r--source4/nsswitch/winbind_client.h16
-rw-r--r--source4/nsswitch/winbind_nss.c1341
-rw-r--r--source4/nsswitch/winbind_nss_config.h165
-rw-r--r--source4/nsswitch/winbind_nss_solaris.c301
-rw-r--r--source4/nsswitch/winbindd.c951
-rw-r--r--source4/nsswitch/winbindd.h230
-rw-r--r--source4/nsswitch/winbindd_ads.c837
-rw-r--r--source4/nsswitch/winbindd_cache.c1016
-rw-r--r--source4/nsswitch/winbindd_cm.c954
-rw-r--r--source4/nsswitch/winbindd_dual.c210
-rw-r--r--source4/nsswitch/winbindd_group.c896
-rw-r--r--source4/nsswitch/winbindd_idmap.c196
-rw-r--r--source4/nsswitch/winbindd_idmap_tdb.c441
-rw-r--r--source4/nsswitch/winbindd_misc.c235
-rw-r--r--source4/nsswitch/winbindd_nss.h242
-rw-r--r--source4/nsswitch/winbindd_pam.c368
-rw-r--r--source4/nsswitch/winbindd_rpc.c776
-rw-r--r--source4/nsswitch/winbindd_sid.c235
-rw-r--r--source4/nsswitch/winbindd_user.c607
-rw-r--r--source4/nsswitch/winbindd_util.c553
-rw-r--r--source4/nsswitch/winbindd_wins.c210
-rw-r--r--source4/nsswitch/wins.c322
32 files changed, 13857 insertions, 0 deletions
diff --git a/source4/nsswitch/.cvsignore b/source4/nsswitch/.cvsignore
new file mode 100644
index 0000000000..658d50a680
--- /dev/null
+++ b/source4/nsswitch/.cvsignore
@@ -0,0 +1,4 @@
+*.po
+*.po32
+diffs
+winbindd_proto.h
diff --git a/source4/nsswitch/README b/source4/nsswitch/README
new file mode 100644
index 0000000000..9f0c581df6
--- /dev/null
+++ b/source4/nsswitch/README
@@ -0,0 +1,13 @@
+This extension provides a "wins" module for NSS on glibc2/Linux. This
+allows you to use a WINS entry in /etc/nsswitch.conf for hostname
+resolution, allowing you to resolve netbios names via start unix
+gethostbyname() calls. The end result is that you can use netbios
+names as host names in unix apps.
+
+1) run configure
+2) run "make nsswitch"
+3) cp nsswitch/libnss_wins.so /lib/libnss_wins.so.2
+4) add a wins entry to the hosts line in /etc/nsswitch.conf
+5) use it
+
+tridge@linuxcare.com
diff --git a/source4/nsswitch/hp_nss_common.h b/source4/nsswitch/hp_nss_common.h
new file mode 100644
index 0000000000..5bd5374182
--- /dev/null
+++ b/source4/nsswitch/hp_nss_common.h
@@ -0,0 +1,51 @@
+#ifndef _HP_NSS_COMMON_H
+#define _HP_NSS_COMMON_H
+
+/*
+ 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 Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#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;
+
+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;
+
+#endif /* _HP_NSS_COMMON_H */
diff --git a/source4/nsswitch/hp_nss_dbdefs.h b/source4/nsswitch/hp_nss_dbdefs.h
new file mode 100644
index 0000000000..bd24772e33
--- /dev/null
+++ b/source4/nsswitch/hp_nss_dbdefs.h
@@ -0,0 +1,105 @@
+#ifndef _HP_NSS_DBDEFS_H
+#define _HP_NSS_DBDEFS_H
+
+/*
+ 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 Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <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 /* _NSS_DBDEFS_H */
diff --git a/source4/nsswitch/nss.h b/source4/nsswitch/nss.h
new file mode 100644
index 0000000000..d83a5e237e
--- /dev/null
+++ b/source4/nsswitch/nss.h
@@ -0,0 +1,104 @@
+#ifndef _NSSWITCH_NSS_H
+#define _NSSWITCH_NSS_H
+/*
+ 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 Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#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
+
+/* GNU */
+
+#include <nss.h>
+
+typedef enum nss_status NSS_STATUS;
+
+#elif HAVE_NS_API_H
+
+/* SGI IRIX */
+
+/* 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;
+
+#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/hp_nss_common.h"
+#include "nsswitch/hp_nss_dbdefs.h"
+#include <nsswitch.h>
+
+#ifndef _HAVE_TYPEDEF_NSS_STATUS
+#define _HAVE_TYPEDEF_NSS_STATUS
+typedef nss_status_t NSS_STATUS;
+
+#define NSS_STATUS_SUCCESS NSS_SUCCESS
+#define NSS_STATUS_NOTFOUND NSS_NOTFOUND
+#define NSS_STATUS_UNAVAIL NSS_UNAVAIL
+#define NSS_STATUS_TRYAGAIN NSS_TRYAGAIN
+#endif /* HPUX */
+
+#else /* Nothing's defined. Neither 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 /* _NSSWITCH_NSS_H */
diff --git a/source4/nsswitch/pam_winbind.c b/source4/nsswitch/pam_winbind.c
new file mode 100644
index 0000000000..123f670366
--- /dev/null
+++ b/source4/nsswitch/pam_winbind.c
@@ -0,0 +1,754 @@
+/* pam_winbind module
+
+ Copyright Andrew Tridgell <tridge@samba.org> 2000
+ Copyright Tim Potter <tpot@samba.org> 2000
+ Copyright Andrew Bartlett <abartlet@samba.org> 2002
+
+ largely based on pam_userdb by Christian Gafton <gafton@redhat.com>
+ also contains large slabs of code from pam_unix by Elliot Lee <sopwith@redhat.com>
+ (see copyright below for full details)
+*/
+
+#include "pam_winbind.h"
+
+/* data tokens */
+
+#define MAX_PASSWD_TRIES 3
+
+/* some syslogging */
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog(MODULE_NAME, LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+static int _pam_parse(int argc, const char **argv)
+{
+ int ctrl;
+ /* step through arguments */
+ for (ctrl = 0; argc-- > 0; ++argv) {
+
+ /* generic options */
+
+ if (!strcmp(*argv,"debug"))
+ ctrl |= WINBIND_DEBUG_ARG;
+ else if (!strcasecmp(*argv, "use_authtok"))
+ ctrl |= WINBIND_USE_AUTHTOK_ARG;
+ else if (!strcasecmp(*argv, "use_first_pass"))
+ ctrl |= WINBIND_USE_FIRST_PASS_ARG;
+ else if (!strcasecmp(*argv, "try_first_pass"))
+ ctrl |= WINBIND_TRY_FIRST_PASS_ARG;
+ else if (!strcasecmp(*argv, "unknown_ok"))
+ ctrl |= WINBIND_UNKNOWN_OK_ARG;
+ else {
+ _pam_log(LOG_ERR, "pam_parse: unknown option; %s", *argv);
+ }
+ }
+
+ return ctrl;
+}
+
+/* --- authentication management functions --- */
+
+/* Attempt a conversation */
+
+static int converse(pam_handle_t *pamh, int nargs,
+ struct pam_message **message,
+ struct pam_response **response)
+{
+ int retval;
+ struct pam_conv *conv;
+
+ retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv ) ;
+ if (retval == PAM_SUCCESS) {
+ retval = conv->conv(nargs, (const struct pam_message **)message,
+ response, conv->appdata_ptr);
+ }
+
+ return retval; /* propagate error status */
+}
+
+
+static int _make_remark(pam_handle_t * pamh, int type, const char *text)
+{
+ int retval = PAM_SUCCESS;
+
+ struct pam_message *pmsg[1], msg[1];
+ struct pam_response *resp;
+
+ pmsg[0] = &msg[0];
+ msg[0].msg = text;
+ msg[0].msg_style = type;
+
+ resp = NULL;
+ retval = converse(pamh, 1, pmsg, &resp);
+
+ if (resp) {
+ _pam_drop_reply(resp, 1);
+ }
+ return retval;
+}
+
+static int pam_winbind_request(enum winbindd_cmd req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response)
+{
+
+ /* Fill in request and send down pipe */
+ init_request(request, req_type);
+
+ if (write_sock(request, sizeof(*request)) == -1) {
+ _pam_log(LOG_ERR, "write to socket failed!");
+ close_sock();
+ return PAM_SERVICE_ERR;
+ }
+
+ /* Wait for reply */
+ if (read_reply(response) == -1) {
+ _pam_log(LOG_ERR, "read from socket failed!");
+ close_sock();
+ return PAM_SERVICE_ERR;
+ }
+
+ /* We are done with the socket - close it and avoid mischeif */
+ close_sock();
+
+ /* Copy reply data from socket */
+ if (response->result != WINBINDD_OK) {
+ if (response->data.auth.pam_error != PAM_SUCCESS) {
+ _pam_log(LOG_ERR, "request failed: %s, PAM error was %d, NT error was %s",
+ response->data.auth.error_string,
+ response->data.auth.pam_error,
+ response->data.auth.nt_status_string);
+ return response->data.auth.pam_error;
+ } else {
+ _pam_log(LOG_ERR, "request failed, but PAM error 0!");
+ return PAM_SERVICE_ERR;
+ }
+ }
+
+ return PAM_SUCCESS;
+}
+
+static int pam_winbind_request_log(enum winbindd_cmd req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response,
+ int ctrl,
+ const char *user)
+{
+ int retval;
+
+ retval = pam_winbind_request(req_type, request, response);
+
+ switch (retval) {
+ case PAM_AUTH_ERR:
+ /* incorrect password */
+ _pam_log(LOG_WARNING, "user `%s' denied access (incorrect password)", user);
+ return retval;
+ case PAM_ACCT_EXPIRED:
+ /* account expired */
+ _pam_log(LOG_WARNING, "user `%s' account expired", user);
+ return retval;
+ case PAM_AUTHTOK_EXPIRED:
+ /* password expired */
+ _pam_log(LOG_WARNING, "user `%s' password expired", user);
+ return retval;
+ case PAM_NEW_AUTHTOK_REQD:
+ /* password expired */
+ _pam_log(LOG_WARNING, "user `%s' new password required", user);
+ return retval;
+ case PAM_USER_UNKNOWN:
+ /* the user does not exist */
+ if (ctrl & WINBIND_DEBUG_ARG)
+ _pam_log(LOG_NOTICE, "user `%s' not found",
+ user);
+ if (ctrl & WINBIND_UNKNOWN_OK_ARG) {
+ return PAM_IGNORE;
+ }
+ return retval;
+ case PAM_SUCCESS:
+ if (req_type == WINBINDD_PAM_AUTH) {
+ /* Otherwise, the authentication looked good */
+ _pam_log(LOG_NOTICE, "user '%s' granted acces", user);
+ } else if (req_type == WINBINDD_PAM_CHAUTHTOK) {
+ /* Otherwise, the authentication looked good */
+ _pam_log(LOG_NOTICE, "user '%s' password changed", user);
+ } else {
+ /* Otherwise, the authentication looked good */
+ _pam_log(LOG_NOTICE, "user '%s' OK", user);
+ }
+ return retval;
+ default:
+ /* we don't know anything about this return value */
+ _pam_log(LOG_ERR, "internal module error (retval = %d, user = `%s'",
+ retval, user);
+ return retval;
+ }
+}
+
+/* talk to winbindd */
+static int winbind_auth_request(const char *user, const char *pass, int ctrl)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+
+ strncpy(request.data.auth.user, user,
+ sizeof(request.data.auth.user)-1);
+
+ strncpy(request.data.auth.pass, pass,
+ sizeof(request.data.auth.pass)-1);
+
+
+ return pam_winbind_request_log(WINBINDD_PAM_AUTH, &request, &response, ctrl, user);
+}
+
+/* talk to winbindd */
+static int winbind_chauthtok_request(const char *user, const char *oldpass,
+ const char *newpass, int ctrl)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ ZERO_STRUCT(request);
+
+ if (request.data.chauthtok.user == NULL) return -2;
+
+ strncpy(request.data.chauthtok.user, user,
+ sizeof(request.data.chauthtok.user) - 1);
+
+ if (oldpass != NULL) {
+ strncpy(request.data.chauthtok.oldpass, oldpass,
+ sizeof(request.data.chauthtok.oldpass) - 1);
+ } else {
+ request.data.chauthtok.oldpass[0] = '\0';
+ }
+
+ if (newpass != NULL) {
+ strncpy(request.data.chauthtok.newpass, newpass,
+ sizeof(request.data.chauthtok.newpass) - 1);
+ } else {
+ request.data.chauthtok.newpass[0] = '\0';
+ }
+
+ return pam_winbind_request_log(WINBINDD_PAM_CHAUTHTOK, &request, &response, ctrl, user);
+}
+
+/*
+ * Checks if a user has an account
+ *
+ * return values:
+ * 1 = User not found
+ * 0 = OK
+ * -1 = System error
+ */
+static int valid_user(const char *user)
+{
+ if (getpwnam(user)) return 0;
+ return 1;
+}
+
+static char *_pam_delete(register char *xx)
+{
+ _pam_overwrite(xx);
+ _pam_drop(xx);
+ return NULL;
+}
+
+/*
+ * obtain a password from the user
+ */
+
+static int _winbind_read_password(pam_handle_t * pamh
+ ,unsigned int ctrl
+ ,const char *comment
+ ,const char *prompt1
+ ,const char *prompt2
+ ,const char **pass)
+{
+ int authtok_flag;
+ int retval;
+ const char *item;
+ char *token;
+
+ /*
+ * make sure nothing inappropriate gets returned
+ */
+
+ *pass = token = NULL;
+
+ /*
+ * which authentication token are we getting?
+ */
+
+ authtok_flag = on(WINBIND__OLD_PASSWORD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
+
+ /*
+ * should we obtain the password from a PAM item ?
+ */
+
+ if (on(WINBIND_TRY_FIRST_PASS_ARG, ctrl) || on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
+ retval = pam_get_item(pamh, authtok_flag, (const void **) &item);
+ if (retval != PAM_SUCCESS) {
+ /* very strange. */
+ _pam_log(LOG_ALERT,
+ "pam_get_item returned error to unix-read-password"
+ );
+ return retval;
+ } else if (item != NULL) { /* we have a password! */
+ *pass = item;
+ item = NULL;
+ return PAM_SUCCESS;
+ } else if (on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
+ return PAM_AUTHTOK_RECOVER_ERR; /* didn't work */
+ } else if (on(WINBIND_USE_AUTHTOK_ARG, ctrl)
+ && off(WINBIND__OLD_PASSWORD, ctrl)) {
+ return PAM_AUTHTOK_RECOVER_ERR;
+ }
+ }
+ /*
+ * getting here implies we will have to get the password from the
+ * user directly.
+ */
+
+ {
+ struct pam_message msg[3], *pmsg[3];
+ struct pam_response *resp;
+ int i, replies;
+
+ /* prepare to converse */
+
+ if (comment != NULL) {
+ pmsg[0] = &msg[0];
+ msg[0].msg_style = PAM_TEXT_INFO;
+ msg[0].msg = comment;
+ i = 1;
+ } else {
+ i = 0;
+ }
+
+ pmsg[i] = &msg[i];
+ msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[i++].msg = prompt1;
+ replies = 1;
+
+ if (prompt2 != NULL) {
+ pmsg[i] = &msg[i];
+ msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
+ msg[i++].msg = prompt2;
+ ++replies;
+ }
+ /* so call the conversation expecting i responses */
+ resp = NULL;
+ retval = converse(pamh, i, pmsg, &resp);
+
+ if (resp != NULL) {
+
+ /* interpret the response */
+
+ if (retval == PAM_SUCCESS) { /* a good conversation */
+
+ token = x_strdup(resp[i - replies].resp);
+ if (token != NULL) {
+ if (replies == 2) {
+
+ /* verify that password entered correctly */
+ if (!resp[i - 1].resp
+ || strcmp(token, resp[i - 1].resp)) {
+ _pam_delete(token); /* mistyped */
+ retval = PAM_AUTHTOK_RECOVER_ERR;
+ _make_remark(pamh ,PAM_ERROR_MSG, MISTYPED_PASS);
+ }
+ }
+ } else {
+ _pam_log(LOG_NOTICE
+ ,"could not recover authentication token");
+ }
+
+ }
+ /*
+ * tidy up the conversation (resp_retcode) is ignored
+ * -- what is it for anyway? AGM
+ */
+
+ _pam_drop_reply(resp, i);
+
+ } else {
+ retval = (retval == PAM_SUCCESS)
+ ? PAM_AUTHTOK_RECOVER_ERR : retval;
+ }
+ }
+
+ if (retval != PAM_SUCCESS) {
+ if (on(WINBIND_DEBUG_ARG, ctrl))
+ _pam_log(LOG_DEBUG,
+ "unable to obtain a password");
+ return retval;
+ }
+ /* 'token' is the entered password */
+
+ /* we store this password as an item */
+
+ retval = pam_set_item(pamh, authtok_flag, token);
+ _pam_delete(token); /* clean it up */
+ if (retval != PAM_SUCCESS
+ || (retval = pam_get_item(pamh, authtok_flag
+ ,(const void **) &item))
+ != PAM_SUCCESS) {
+
+ _pam_log(LOG_CRIT, "error manipulating password");
+ return retval;
+
+ }
+
+ *pass = item;
+ item = NULL; /* break link to password */
+
+ return PAM_SUCCESS;
+}
+
+PAM_EXTERN
+int pam_sm_authenticate(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ const char *username;
+ const char *password;
+ int retval = PAM_AUTH_ERR;
+
+ /* parse arguments */
+ int ctrl = _pam_parse(argc, argv);
+
+ /* Get the username */
+ retval = pam_get_user(pamh, &username, NULL);
+ if ((retval != PAM_SUCCESS) || (!username)) {
+ if (ctrl & WINBIND_DEBUG_ARG)
+ _pam_log(LOG_DEBUG,"can not get the username");
+ return PAM_SERVICE_ERR;
+ }
+
+ retval = _winbind_read_password(pamh, ctrl, NULL,
+ "Password: ", NULL,
+ &password);
+
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_ERR, "Could not retrieve user's password");
+ return PAM_AUTHTOK_ERR;
+ }
+
+ if (ctrl & WINBIND_DEBUG_ARG) {
+
+ /* Let's not give too much away in the log file */
+
+#ifdef DEBUG_PASSWORD
+ _pam_log(LOG_INFO, "Verify user `%s' with password `%s'",
+ username, password);
+#else
+ _pam_log(LOG_INFO, "Verify user `%s'", username);
+#endif
+ }
+
+ /* Now use the username to look up password */
+ return winbind_auth_request(username, password, ctrl);
+}
+
+PAM_EXTERN
+int pam_sm_setcred(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ return PAM_SUCCESS;
+}
+
+/*
+ * Account management. We want to verify that the account exists
+ * before returning PAM_SUCCESS
+ */
+PAM_EXTERN
+int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ const char *username;
+ int retval = PAM_USER_UNKNOWN;
+
+ /* parse arguments */
+ int ctrl = _pam_parse(argc, argv);
+
+ /* Get the username */
+ retval = pam_get_user(pamh, &username, NULL);
+ if ((retval != PAM_SUCCESS) || (!username)) {
+ if (ctrl & WINBIND_DEBUG_ARG)
+ _pam_log(LOG_DEBUG,"can not get the username");
+ return PAM_SERVICE_ERR;
+ }
+
+ /* Verify the username */
+ retval = valid_user(username);
+ switch (retval) {
+ case -1:
+ /* some sort of system error. The log was already printed */
+ return PAM_SERVICE_ERR;
+ case 1:
+ /* the user does not exist */
+ if (ctrl & WINBIND_DEBUG_ARG)
+ _pam_log(LOG_NOTICE, "user `%s' not found",
+ username);
+ if (ctrl & WINBIND_UNKNOWN_OK_ARG)
+ return PAM_IGNORE;
+ return PAM_USER_UNKNOWN;
+ case 0:
+ /* Otherwise, the authentication looked good */
+ _pam_log(LOG_NOTICE, "user '%s' granted acces", username);
+ return PAM_SUCCESS;
+ default:
+ /* we don't know anything about this return value */
+ _pam_log(LOG_ERR, "internal module error (retval = %d, user = `%s'",
+ retval, username);
+ return PAM_SERVICE_ERR;
+ }
+
+ /* should not be reached */
+ return PAM_IGNORE;
+}
+PAM_EXTERN
+int pam_sm_open_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ /* parse arguments */
+ int ctrl = _pam_parse(argc, argv);
+ if (ctrl & WINBIND_DEBUG_ARG)
+ _pam_log(LOG_DEBUG,"libpam_winbind:pam_sm_open_session handler");
+ return PAM_SUCCESS;
+}
+PAM_EXTERN
+int pam_sm_close_session(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ /* parse arguments */
+ int ctrl = _pam_parse(argc, argv);
+ if (ctrl & WINBIND_DEBUG_ARG)
+ _pam_log(LOG_DEBUG,"libpam_winbind:pam_sm_close_session handler");
+ return PAM_SUCCESS;
+}
+
+
+
+PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
+ int argc, const char **argv)
+{
+ unsigned int lctrl;
+ int retval;
+ unsigned int ctrl = _pam_parse(argc, argv);
+
+ /* <DO NOT free() THESE> */
+ const char *user;
+ char *pass_old, *pass_new;
+ /* </DO NOT free() THESE> */
+
+ char *Announce;
+
+ int retry = 0;
+
+ /*
+ * First get the name of a user
+ */
+ retval = pam_get_user(pamh, &user, "Username: ");
+ if (retval == PAM_SUCCESS) {
+ if (user == NULL) {
+ _pam_log(LOG_ERR, "username was NULL!");
+ return PAM_USER_UNKNOWN;
+ }
+ if (retval == PAM_SUCCESS && on(WINBIND_DEBUG_ARG, ctrl))
+ _pam_log(LOG_DEBUG, "username [%s] obtained",
+ user);
+ } else {
+ if (on(WINBIND_DEBUG_ARG, ctrl))
+ _pam_log(LOG_DEBUG,
+ "password - could not identify user");
+ return retval;
+ }
+
+ /*
+ * obtain and verify the current password (OLDAUTHTOK) for
+ * the user.
+ */
+
+ if (flags & PAM_PRELIM_CHECK) {
+
+ /* instruct user what is happening */
+#define greeting "Changing password for "
+ Announce = (char *) malloc(sizeof(greeting) + strlen(user));
+ if (Announce == NULL) {
+ _pam_log(LOG_CRIT,
+ "password - out of memory");
+ return PAM_BUF_ERR;
+ }
+ (void) strcpy(Announce, greeting);
+ (void) strcpy(Announce + sizeof(greeting) - 1, user);
+#undef greeting
+
+ lctrl = ctrl | WINBIND__OLD_PASSWORD;
+ retval = _winbind_read_password(pamh, lctrl
+ ,Announce
+ ,"(current) NT password: "
+ ,NULL
+ ,(const char **) &pass_old);
+ free(Announce);
+
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_NOTICE
+ ,"password - (old) token not obtained");
+ return retval;
+ }
+ /* verify that this is the password for this user */
+
+ retval = winbind_auth_request(user, pass_old, ctrl);
+
+ if (retval != PAM_ACCT_EXPIRED
+ && retval != PAM_AUTHTOK_EXPIRED
+ && retval != PAM_NEW_AUTHTOK_REQD
+ && retval != PAM_SUCCESS) {
+ pass_old = NULL;
+ return retval;
+ }
+
+ retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
+ pass_old = NULL;
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_CRIT,
+ "failed to set PAM_OLDAUTHTOK");
+ }
+ } else if (flags & PAM_UPDATE_AUTHTOK) {
+
+ /*
+ * obtain the proposed password
+ */
+
+ /*
+ * get the old token back.
+ */
+
+ retval = pam_get_item(pamh, PAM_OLDAUTHTOK
+ ,(const void **) &pass_old);
+
+ if (retval != PAM_SUCCESS) {
+ _pam_log(LOG_NOTICE, "user not authenticated");
+ return retval;
+ }
+
+ lctrl = ctrl;
+
+ if (on(WINBIND_USE_AUTHTOK_ARG, lctrl)) {
+ ctrl = WINBIND_USE_FIRST_PASS_ARG | lctrl;
+ }
+ retry = 0;
+ retval = PAM_AUTHTOK_ERR;
+ while ((retval != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
+ /*
+ * use_authtok is to force the use of a previously entered
+ * password -- needed for pluggable password strength checking
+ */
+
+ retval = _winbind_read_password(pamh, lctrl
+ ,NULL
+ ,"Enter new NT password: "
+ ,"Retype new NT password: "
+ ,(const char **) &pass_new);
+
+ if (retval != PAM_SUCCESS) {
+ if (on(WINBIND_DEBUG_ARG, ctrl)) {
+ _pam_log(LOG_ALERT
+ ,"password - new password not obtained");
+ }
+ pass_old = NULL;/* tidy up */
+ return retval;
+ }
+
+ /*
+ * At this point we know who the user is and what they
+ * propose as their new password. Verify that the new
+ * password is acceptable.
+ */
+
+ if (pass_new[0] == '\0') {/* "\0" password = NULL */
+ pass_new = NULL;
+ }
+ }
+
+ /*
+ * By reaching here we have approved the passwords and must now
+ * rebuild the password database file.
+ */
+
+ retval = winbind_chauthtok_request(user, pass_old, pass_new, ctrl);
+ _pam_overwrite(pass_new);
+ _pam_overwrite(pass_old);
+ pass_old = pass_new = NULL;
+ } else {
+ retval = PAM_SERVICE_ERR;
+ }
+
+ return retval;
+}
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_winbind_modstruct = {
+ MODULE_NAME,
+ pam_sm_authenticate,
+ pam_sm_setcred,
+ pam_sm_acct_mgmt,
+ pam_sm_open_session,
+ pam_sm_close_session,
+ pam_sm_chauthtok
+};
+
+#endif
+
+/*
+ * Copyright (c) Andrew Tridgell <tridge@samba.org> 2000
+ * Copyright (c) Tim Potter <tpot@samba.org> 2000
+ * Copyright (c) Andrew Bartlettt <abartlet@samba.org> 2002
+ * Copyright (c) Jan Rêkorajski 1999.
+ * Copyright (c) Andrew G. Morgan 1996-8.
+ * Copyright (c) Alex O. Yuriev, 1996.
+ * Copyright (c) Cristian Gafton 1996.
+ * Copyright (C) Elliot Lee <sopwith@redhat.com> 1996, Red Hat Software.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/source4/nsswitch/pam_winbind.h b/source4/nsswitch/pam_winbind.h
new file mode 100644
index 0000000000..fae635d806
--- /dev/null
+++ b/source4/nsswitch/pam_winbind.h
@@ -0,0 +1,93 @@
+/* pam_winbind header file
+ (Solaris needs some macros from Linux for common PAM code)
+
+ Shirish Kalele 2000
+*/
+
+#ifdef HAVE_FEATURES_H
+#include <features.h>
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <config.h>
+
+#define MODULE_NAME "pam_winbind"
+#define PAM_SM_AUTH
+#define PAM_SM_ACCOUNT
+#define PAM_SM_PASSWORD
+
+#if defined(SUNOS5) || defined(SUNOS4) || defined(HPUX)
+
+/* Solaris always uses dynamic pam modules */
+#define PAM_EXTERN extern
+#include <security/pam_appl.h>
+
+#define PAM_AUTHTOK_RECOVER_ERR PAM_AUTHTOK_RECOVERY_ERR
+#endif
+
+#ifdef HAVE_SECURITY_PAM_MODULES_H
+#include <security/pam_modules.h>
+#endif
+
+#ifdef HAVE_SECURITY__PAM_MACROS_H
+#include <security/_pam_macros.h>
+#else
+/* Define required macros from (Linux PAM 0.68) security/_pam_macros.h */
+#define _pam_drop_reply(/* struct pam_response * */ reply, /* int */ replies) \
+do { \
+ int reply_i; \
+ \
+ for (reply_i=0; reply_i<replies; ++reply_i) { \
+ if (reply[reply_i].resp) { \
+ _pam_overwrite(reply[reply_i].resp); \
+ free(reply[reply_i].resp); \
+ } \
+ } \
+ if (reply) \
+ free(reply); \
+} while (0)
+
+#define _pam_overwrite(x) \
+do { \
+ register char *__xx__; \
+ if ((__xx__=(x))) \
+ while (*__xx__) \
+ *__xx__++ = '\0'; \
+} while (0)
+
+/*
+ * Don't just free it, forget it too.
+ */
+
+#define _pam_drop(X) SAFE_FREE(X)
+
+#define x_strdup(s) ( (s) ? strdup(s):NULL )
+#endif
+
+#define WINBIND_DEBUG_ARG (1<<0)
+#define WINBIND_USE_AUTHTOK_ARG (1<<1)
+#define WINBIND_UNKNOWN_OK_ARG (1<<2)
+#define WINBIND_TRY_FIRST_PASS_ARG (1<<3)
+#define WINBIND_USE_FIRST_PASS_ARG (1<<4)
+#define WINBIND__OLD_PASSWORD (1<<5)
+
+/*
+ * here is the string to inform the user that the new passwords they
+ * typed were not the same.
+ */
+
+#define MISTYPED_PASS "Sorry, passwords do not match"
+
+#define on(x, y) (x & y)
+#define off(x, y) (!(x & y))
+
+#include "winbind_client.h"
diff --git a/source4/nsswitch/wb_client.c b/source4/nsswitch/wb_client.c
new file mode 100644
index 0000000000..62c9686960
--- /dev/null
+++ b/source4/nsswitch/wb_client.c
@@ -0,0 +1,307 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ winbind client code
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Andrew Tridgell 2000
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "includes.h"
+#include "nsswitch/nss.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+extern DOM_SID global_sid_NULL; /* NULL sid */
+
+NSS_STATUS winbindd_request(int req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response);
+
+/* Call winbindd to convert a name to a sid */
+
+BOOL winbind_lookup_name(const char *dom_name, const char *name, DOM_SID *sid,
+ enum SID_NAME_USE *name_type)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+
+ if (!sid || !name_type)
+ return False;
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ fstrcpy(request.data.name.dom_name, dom_name);
+ fstrcpy(request.data.name.name, name);
+
+ if ((result = winbindd_request(WINBINDD_LOOKUPNAME, &request,
+ &response)) == NSS_STATUS_SUCCESS) {
+ if (!string_to_sid(sid, response.data.sid.sid))
+ return False;
+ *name_type = (enum SID_NAME_USE)response.data.sid.type;
+ }
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/* Call winbindd to convert sid to name */
+
+BOOL winbind_lookup_sid(const DOM_SID *sid,
+ fstring dom_name, fstring name,
+ enum SID_NAME_USE *name_type)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ fstring sid_str;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ sid_to_string(sid_str, sid);
+ fstrcpy(request.data.sid, sid_str);
+
+ /* Make request */
+
+ result = winbindd_request(WINBINDD_LOOKUPSID, &request, &response);
+
+ /* Copy out result */
+
+ if (result == NSS_STATUS_SUCCESS) {
+ fstrcpy(dom_name, response.data.name.dom_name);
+ fstrcpy(name, response.data.name.name);
+ *name_type = (enum SID_NAME_USE)response.data.name.type;
+
+ DEBUG(10, ("winbind_lookup_sid: SUCCESS: SID %s -> %s %s\n",
+ sid_str, dom_name, name));
+ }
+
+ return (result == NSS_STATUS_SUCCESS);
+}
+
+/* Call winbindd to convert SID to uid */
+
+BOOL winbind_sid_to_uid(uid_t *puid, const DOM_SID *sid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int result;
+ fstring sid_str;
+
+ if (!puid)
+ return False;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ sid_to_string(sid_str, sid);
+ fstrcpy(request.data.sid, sid_str);
+
+ /* Make request */
+
+ result = winbindd_request(WINBINDD_SID_TO_UID, &request, &response);
+
+ /* Copy out result */
+
+ if (result == NSS_STATUS_SUCCESS) {
+ *puid = response.data.uid;
+ }
+
+ return (result == NSS_STATUS_SUCCESS);
+}
+
+/* Call winbindd to convert uid to sid */
+
+BOOL winbind_uid_to_sid(DOM_SID *sid, uid_t uid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int result;
+
+ if (!sid)
+ return False;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.data.uid = uid;
+
+ /* Make request */
+
+ result = winbindd_request(WINBINDD_UID_TO_SID, &request, &response);
+
+ /* Copy out result */
+
+ if (result == NSS_STATUS_SUCCESS) {
+ if (!string_to_sid(sid, response.data.sid.sid))
+ return False;
+ } else {
+ sid_copy(sid, &global_sid_NULL);
+ }
+
+ return (result == NSS_STATUS_SUCCESS);
+}
+
+/* Call winbindd to convert SID to gid */
+
+BOOL winbind_sid_to_gid(gid_t *pgid, const DOM_SID *sid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int result;
+ fstring sid_str;
+
+ if (!pgid)
+ return False;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ sid_to_string(sid_str, sid);
+ fstrcpy(request.data.sid, sid_str);
+
+ /* Make request */
+
+ result = winbindd_request(WINBINDD_SID_TO_GID, &request, &response);
+
+ /* Copy out result */
+
+ if (result == NSS_STATUS_SUCCESS) {
+ *pgid = response.data.gid;
+ }
+
+ return (result == NSS_STATUS_SUCCESS);
+}
+
+/* Call winbindd to convert gid to sid */
+
+BOOL winbind_gid_to_sid(DOM_SID *sid, gid_t gid)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int result;
+
+ if (!sid)
+ return False;
+
+ /* Initialise request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.data.gid = gid;
+
+ /* Make request */
+
+ result = winbindd_request(WINBINDD_GID_TO_SID, &request, &response);
+
+ /* Copy out result */
+
+ if (result == NSS_STATUS_SUCCESS) {
+ if (!string_to_sid(sid, response.data.sid.sid))
+ return False;
+ } else {
+ sid_copy(sid, &global_sid_NULL);
+ }
+
+ return (result == NSS_STATUS_SUCCESS);
+}
+
+/* Fetch the list of groups a user is a member of from winbindd. This is
+ used by winbind_getgroups. */
+
+static int wb_getgroups(const char *user, gid_t **groups)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ int result;
+
+ /* Call winbindd */
+
+ fstrcpy(request.data.username, user);
+
+ ZERO_STRUCT(response);
+
+ result = winbindd_request(WINBINDD_GETGROUPS, &request, &response);
+
+ if (result == NSS_STATUS_SUCCESS) {
+
+ /* Return group list. Don't forget to free the group list
+ when finished. */
+
+ *groups = (gid_t *)response.extra_data;
+ return response.data.num_entries;
+ }
+
+ return -1;
+}
+
+/* Return a list of groups the user is a member of. This function is
+ useful for large systems where inverting the group database would be too
+ time consuming. If size is zero, list is not modified and the total
+ number of groups for the user is returned. */
+
+int winbind_getgroups(const char *user, int size, gid_t *list)
+{
+ gid_t *groups = NULL;
+ int result, i;
+
+ /*
+ * Don't do the lookup if the name has no separator _and_ we are not in
+ * 'winbind use default domain' mode.
+ */
+
+ if (!(strchr(user, *lp_winbind_separator()) || lp_winbind_use_default_domain()))
+ return -1;
+
+ /* Fetch list of groups */
+
+ result = wb_getgroups(user, &groups);
+
+ if (size == 0)
+ goto done;
+
+ if (result > size) {
+ result = -1;
+ errno = EINVAL; /* This is what getgroups() does */
+ goto done;
+ }
+
+ /* Copy list of groups across */
+
+ for (i = 0; i < result; i++) {
+ list[i] = groups[i];
+ }
+
+ done:
+ SAFE_FREE(groups);
+ return result;
+}
diff --git a/source4/nsswitch/wb_common.c b/source4/nsswitch/wb_common.c
new file mode 100644
index 0000000000..89c751a4ef
--- /dev/null
+++ b/source4/nsswitch/wb_common.c
@@ -0,0 +1,433 @@
+/*
+ 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 Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "winbind_client.h"
+
+/* Global variables. These are effectively the client state information */
+
+int winbindd_fd = -1; /* fd for winbindd socket */
+
+/* Free a response structure */
+
+void free_response(struct winbindd_response *response)
+{
+ /* Free any allocated extra_data */
+
+ if (response)
+ SAFE_FREE(response->extra_data);
+}
+
+/* Initialise a request structure */
+
+void 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 */
+
+void init_response(struct winbindd_response *response)
+{
+ /* Initialise return value */
+
+ response->result = WINBINDD_ERROR;
+}
+
+/* Close established socket */
+
+void close_sock(void)
+{
+ if (winbindd_fd != -1) {
+ close(winbindd_fd);
+ winbindd_fd = -1;
+ }
+}
+
+/* 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;
+ }
+ /* Parinoia */
+ 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;
+}
+
+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 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 */
+
+int winbind_open_pipe_sock(void)
+{
+#ifdef HAVE_UNIXSOCKET
+ struct sockaddr_un sunaddr;
+ static pid_t our_pid;
+ struct stat st;
+ pstring path;
+ int fd;
+
+ if (our_pid != getpid()) {
+ close_sock();
+ our_pid = getpid();
+ }
+
+ if (winbindd_fd != -1) {
+ return winbindd_fd;
+ }
+
+ /* Check permissions on unix socket directory */
+
+ if (lstat(WINBINDD_SOCKET_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, WINBINDD_SOCKET_DIR, sizeof(path) - 1);
+ path[sizeof(path) - 1] = '\0';
+
+ strncat(path, "/", sizeof(path) - 1);
+ path[sizeof(path) - 1] = '\0';
+
+ strncat(path, WINBINDD_SOCKET_NAME, sizeof(path) - 1);
+ 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;
+ }
+
+ if ((winbindd_fd = make_safe_fd( fd)) == -1) {
+ return winbindd_fd;
+ }
+
+ if (connect(winbindd_fd, (struct sockaddr *)&sunaddr,
+ sizeof(sunaddr)) == -1) {
+ close_sock();
+ return -1;
+ }
+
+ return winbindd_fd;
+#else
+ return -1;
+#endif /* HAVE_UNIXSOCKET */
+}
+
+/* Write data to winbindd socket */
+
+int write_sock(void *buffer, int count)
+{
+ int result, nwritten;
+
+ /* Open connection to winbind daemon */
+
+ restart:
+
+ if (winbind_open_pipe_sock() == -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) {
+ 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 */
+
+ close_sock();
+ return -1;
+ }
+
+ nwritten += result;
+
+ } else {
+
+ /* Pipe has closed on remote end */
+
+ close_sock();
+ goto restart;
+ }
+ }
+
+ return nwritten;
+}
+
+/* Read data from winbindd socket */
+
+static int read_sock(void *buffer, int count)
+{
+ int result = 0, nread = 0;
+
+ /* Read data from socket */
+
+ while(nread < count) {
+
+ 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. */
+
+ close_sock();
+ return -1;
+ }
+
+ nread += result;
+ }
+
+ return result;
+}
+
+/* Read reply */
+
+int read_reply(struct winbindd_response *response)
+{
+ int result1, result2 = 0;
+
+ if (!response) {
+ return -1;
+ }
+
+ /* Read fixed length response */
+
+ if ((result1 = read_sock(response, sizeof(struct winbindd_response)))
+ == -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 = 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 = malloc(extra_data_len))) {
+ return -1;
+ }
+
+ if ((result2 = read_sock(response->extra_data, extra_data_len))
+ == -1) {
+ free_response(response);
+ return -1;
+ }
+ }
+
+ /* Return total amount of data read */
+
+ return result1 + result2;
+}
+
+/*
+ * send simple types of requests
+ */
+
+NSS_STATUS winbindd_send_request(int req_type, struct winbindd_request *request)
+{
+ struct winbindd_request lrequest;
+
+ /* Check for our tricky environment variable */
+
+ if (getenv(WINBINDD_DONT_ENV)) {
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ if (!request) {
+ ZERO_STRUCT(lrequest);
+ request = &lrequest;
+ }
+
+ /* Fill in request and send down pipe */
+
+ init_request(request, req_type);
+
+ if (write_sock(request, sizeof(*request)) == -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 (read_reply(response) == -1) {
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ /* Throw away extra data if client didn't request it */
+ if (response == &lresponse) {
+ 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(int req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response)
+{
+ NSS_STATUS status;
+
+ status = winbindd_send_request(req_type, request);
+ if (status != NSS_STATUS_SUCCESS)
+ return(status);
+ return winbindd_get_response(response);
+}
diff --git a/source4/nsswitch/wbinfo.c b/source4/nsswitch/wbinfo.c
new file mode 100644
index 0000000000..68dc178bcd
--- /dev/null
+++ b/source4/nsswitch/wbinfo.c
@@ -0,0 +1,891 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind status program.
+
+ Copyright (C) Tim Potter 2000-2002
+ Copyright (C) Andrew Bartlett 2002
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "debug.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+extern int winbindd_fd;
+
+static char winbind_separator(void)
+{
+ 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(WINBINDD_INFO, NULL, &response) !=
+ NSS_STATUS_SUCCESS) {
+ d_printf("could not obtain winbind separator!\n");
+ /* HACK: (this module should not call lp_ funtions) */
+ return *lp_winbind_separator();
+ }
+
+ sep = response.data.info.winbind_separator;
+ got_sep = True;
+
+ if (!sep) {
+ d_printf("winbind separator was NULL!\n");
+ /* HACK: (this module should not call lp_ funtions) */
+ sep = *lp_winbind_separator();
+ }
+
+ return sep;
+}
+
+static const char *get_winbind_domain(void)
+{
+ struct winbindd_response response;
+ static fstring winbind_domain;
+
+ ZERO_STRUCT(response);
+
+ /* Send off request */
+
+ if (winbindd_request(WINBINDD_DOMAIN_NAME, NULL, &response) !=
+ NSS_STATUS_SUCCESS) {
+ d_printf("could not obtain winbind domain name!\n");
+
+ /* HACK: (this module should not call lp_ funtions) */
+ return lp_workgroup();
+ }
+
+ 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(domain);
+
+ 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(response);
+
+ /* Send request */
+
+ fstrcpy(request.data.username, user);
+
+ result = winbindd_request(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)[i]);
+
+ SAFE_FREE(response.extra_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(WINBINDD_WINS_BYNAME, &request, &response) !=
+ NSS_STATUS_SUCCESS) {
+ return False;
+ }
+
+ /* Display response */
+
+ 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(WINBINDD_WINS_BYIP, &request, &response) !=
+ NSS_STATUS_SUCCESS) {
+ return False;
+ }
+
+ /* Display response */
+
+ printf("%s\n", response.data.winsresp);
+
+ return True;
+}
+
+/* List trusted domains */
+
+static BOOL wbinfo_list_domains(void)
+{
+ struct winbindd_response response;
+ fstring name;
+
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ if (winbindd_request(WINBINDD_LIST_TRUSTDOM, NULL, &response) !=
+ NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Display response */
+
+ if (response.extra_data) {
+ const char *extra_data = (char *)response.extra_data;
+
+ while(next_token(&extra_data, name, ",", sizeof(fstring)))
+ d_printf("%s\n", name);
+
+ SAFE_FREE(response.extra_data);
+ }
+
+ return True;
+}
+
+
+/* show sequence numbers */
+static BOOL wbinfo_show_sequence(void)
+{
+ struct winbindd_response response;
+
+ ZERO_STRUCT(response);
+
+ /* Send request */
+
+ if (winbindd_request(WINBINDD_SHOW_SEQUENCE, NULL, &response) !=
+ NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Display response */
+
+ if (response.extra_data) {
+ char *extra_data = (char *)response.extra_data;
+ d_printf("%s", extra_data);
+ SAFE_FREE(response.extra_data);
+ }
+
+ 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(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_printf("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(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(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(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(WINBINDD_SID_TO_GID, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Display response */
+
+ d_printf("%d\n", (int)response.data.gid);
+
+ return True;
+}
+
+/* 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(WINBINDD_LOOKUPSID, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Display response */
+
+ d_printf("%s%c%s %d\n", response.data.name.dom_name,
+ winbind_separator(), response.data.name.name,
+ 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(WINBINDD_LOOKUPNAME, &request, &response) !=
+ NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Display response */
+
+ d_printf("%s %d\n", response.data.sid.sid, response.data.sid.type);
+
+ return True;
+}
+
+/* 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(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_printf("error code was %s (0x%x)\n",
+ response.data.auth.nt_status_string,
+ response.data.auth.nt_status);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/* Authenticate a user with a challenge/response */
+
+static BOOL wbinfo_auth_crap(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);
+
+ 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, False);
+
+ SMBencrypt(pass, request.data.auth_crap.chal,
+ (uchar *)request.data.auth_crap.lm_resp);
+ SMBNTencrypt(pass, request.data.auth_crap.chal,
+ (uchar *)request.data.auth_crap.nt_resp);
+
+ request.data.auth_crap.lm_resp_len = 24;
+ request.data.auth_crap.nt_resp_len = 24;
+
+ result = winbindd_request(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_printf("error code was %s (0x%x)\n",
+ response.data.auth.nt_status_string,
+ response.data.auth.nt_status);
+
+ return result == NSS_STATUS_SUCCESS;
+}
+
+/* Print domain users */
+
+static BOOL print_domain_users(void)
+{
+ struct winbindd_response response;
+ const char *extra_data;
+ fstring name;
+
+ /* Send request to winbind daemon */
+
+ ZERO_STRUCT(response);
+
+ if (winbindd_request(WINBINDD_LIST_USERS, NULL, &response) !=
+ NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Look through extra data */
+
+ if (!response.extra_data)
+ return False;
+
+ extra_data = (const char *)response.extra_data;
+
+ while(next_token(&extra_data, name, ",", sizeof(fstring)))
+ d_printf("%s\n", name);
+
+ SAFE_FREE(response.extra_data);
+
+ return True;
+}
+
+/* Print domain groups */
+
+static BOOL print_domain_groups(void)
+{
+ struct winbindd_response response;
+ const char *extra_data;
+ fstring name;
+
+ ZERO_STRUCT(response);
+
+ if (winbindd_request(WINBINDD_LIST_GROUPS, NULL, &response) !=
+ NSS_STATUS_SUCCESS)
+ return False;
+
+ /* Look through extra data */
+
+ if (!response.extra_data)
+ return False;
+
+ extra_data = (const char *)response.extra_data;
+
+ while(next_token(&extra_data, name, ",", sizeof(fstring)))
+ d_printf("%s\n", name);
+
+ SAFE_FREE(response.extra_data);
+
+ return True;
+}
+
+/* Set the authorised user for winbindd access in secrets.tdb */
+
+static BOOL wbinfo_set_auth_user(char *username)
+{
+ char *password;
+ fstring user, domain;
+
+ /* Separate into user and password */
+
+ parse_wbinfo_domain_user(username, domain, user);
+
+ password = strchr(user, '%');
+
+ if (password) {
+ *password = 0;
+ password++;
+ } else
+ password = "";
+
+ /* Store or remove DOMAIN\username%password in secrets.tdb */
+
+ secrets_init();
+
+ if (user[0]) {
+
+ if (!secrets_store(SECRETS_AUTH_USER, user,
+ strlen(user) + 1)) {
+ d_fprintf(stderr, "error storing username\n");
+ return False;
+ }
+
+ /* We always have a domain name added by the
+ parse_wbinfo_domain_user() function. */
+
+ if (!secrets_store(SECRETS_AUTH_DOMAIN, domain,
+ strlen(domain) + 1)) {
+ d_fprintf(stderr, "error storing domain name\n");
+ return False;
+ }
+
+ } else {
+ secrets_delete(SECRETS_AUTH_USER);
+ secrets_delete(SECRETS_AUTH_DOMAIN);
+ }
+
+ if (password[0]) {
+
+ if (!secrets_store(SECRETS_AUTH_PASSWORD, password,
+ strlen(password) + 1)) {
+ d_fprintf(stderr, "error storing password\n");
+ return False;
+ }
+
+ } else
+ secrets_delete(SECRETS_AUTH_PASSWORD);
+
+ return True;
+}
+
+static void wbinfo_get_auth_user(void)
+{
+ char *user, *domain, *password;
+
+ /* Lift data from secrets file */
+
+ secrets_init();
+
+ user = secrets_fetch(SECRETS_AUTH_USER, NULL);
+ domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
+ password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
+
+ if (!user && !domain && !password) {
+ d_printf("No authorised user configured\n");
+ return;
+ }
+
+ /* Pretty print authorised user info */
+
+ d_printf("%s%s%s%s%s\n", domain ? domain : "", domain ? "\\" : "",
+ user, password ? "%" : "", password ? password : "");
+
+ SAFE_FREE(user);
+ SAFE_FREE(domain);
+ SAFE_FREE(password);
+}
+
+static BOOL wbinfo_ping(void)
+{
+ NSS_STATUS result;
+
+ result = winbindd_request(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_SEQUENCE
+};
+
+int main(int argc, char **argv)
+{
+ int opt;
+
+ poptContext pc;
+ static char *string_arg;
+ static int int_arg;
+ BOOL got_command = False;
+ 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-groups", 'g', POPT_ARG_NONE, 0, 'g', "Lists all domain groups" },
+ { "WINS-by-name", 'N', POPT_ARG_STRING, &string_arg, 'N', "Converts NetBIOS name to IP (WINS)", "NETBIOS-NAME" },
+ { "WINS-by-ip", 'I', POPT_ARG_STRING, &string_arg, 'I', "Converts IP address to NetBIOS name (WINS)", "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" },
+ { "sequence", 0, POPT_ARG_NONE, 0, OPT_SEQUENCE, "show sequence numbers of all domains" },
+ { "user-groups", 'r', POPT_ARG_STRING, &string_arg, 'r', "Get user groups", "USER" },
+ { "authenticate", 'a', POPT_ARG_STRING, &string_arg, 'a', "authenticate user", "user%password" },
+ { "set-auth-user", 'A', POPT_ARG_STRING, &string_arg, OPT_SET_AUTH_USER, "Store user and password used by winbindd (root only)", "user%password" },
+ { "get-auth-user", 0, POPT_ARG_NONE, NULL, OPT_GET_AUTH_USER, "Retrieve user and password used by winbindd (root only)", NULL },
+ { "ping", 'p', POPT_ARG_NONE, 0, 'p', "'ping' winbindd to see if it is alive" },
+ { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_version},
+ { 0, 0, 0, 0 }
+ };
+
+ /* Samba client initialisation */
+
+ if (!lp_load(dyn_CONFIGFILE, True, False, False)) {
+ d_fprintf(stderr, "wbinfo: error opening config file %s. Error was %s\n",
+ dyn_CONFIGFILE, strerror(errno));
+ exit(1);
+ }
+
+ if (!init_names())
+ return 1;
+
+ load_interfaces();
+
+ /* 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) {
+ if (got_command) {
+ d_fprintf(stderr, "No more than one command may be specified at once.\n");
+ exit(1);
+ }
+ got_command = True;
+ }
+
+ 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()) {
+ d_printf("Error looking up domain users\n");
+ goto done;
+ }
+ break;
+ case 'g':
+ if (!print_domain_groups()) {
+ d_printf("Error looking up domain groups\n");
+ goto done;
+ }
+ break;
+ case 's':
+ if (!wbinfo_lookupsid(string_arg)) {
+ d_printf("Could not lookup sid %s\n", string_arg);
+ goto done;
+ }
+ break;
+ case 'n':
+ if (!wbinfo_lookupname(string_arg)) {
+ d_printf("Could not lookup name %s\n", string_arg);
+ goto done;
+ }
+ break;
+ case 'N':
+ if (!wbinfo_wins_byname(string_arg)) {
+ d_printf("Could not lookup WINS by name %s\n", string_arg);
+ goto done;
+ }
+ break;
+ case 'I':
+ if (!wbinfo_wins_byip(string_arg)) {
+ d_printf("Could not lookup WINS by IP %s\n", string_arg);
+ goto done;
+ }
+ break;
+ case 'U':
+ if (!wbinfo_uid_to_sid(int_arg)) {
+ d_printf("Could not convert uid %d to sid\n", int_arg);
+ goto done;
+ }
+ break;
+ case 'G':
+ if (!wbinfo_gid_to_sid(int_arg)) {
+ d_printf("Could not convert gid %d to sid\n",
+ int_arg);
+ goto done;
+ }
+ break;
+ case 'S':
+ if (!wbinfo_sid_to_uid(string_arg)) {
+ d_printf("Could not convert sid %s to uid\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case 'Y':
+ if (!wbinfo_sid_to_gid(string_arg)) {
+ d_printf("Could not convert sid %s to gid\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case 't':
+ if (!wbinfo_check_secret()) {
+ d_printf("Could not check secret\n");
+ goto done;
+ }
+ break;
+ case 'm':
+ if (!wbinfo_list_domains()) {
+ d_printf("Could not list trusted domains\n");
+ goto done;
+ }
+ break;
+ case OPT_SEQUENCE:
+ if (!wbinfo_show_sequence()) {
+ d_printf("Could not show sequence numbers\n");
+ goto done;
+ }
+ break;
+ case 'r':
+ if (!wbinfo_get_usergroups(string_arg)) {
+ d_printf("Could not get groups for user %s\n",
+ string_arg);
+ goto done;
+ }
+ break;
+ case 'a': {
+ BOOL got_error = False;
+
+ if (!wbinfo_auth(string_arg)) {
+ d_printf("Could not authenticate user %s with "
+ "plaintext password\n", string_arg);
+ got_error = True;
+ }
+
+ if (!wbinfo_auth_crap(string_arg)) {
+ d_printf("Could not authenticate user %s with "
+ "challenge/response\n", string_arg);
+ got_error = True;
+ }
+
+ if (got_error)
+ goto done;
+ break;
+ }
+ case 'p': {
+ if (!wbinfo_ping()) {
+ d_printf("could not ping winbindd!\n");
+ goto done;
+ }
+ break;
+ }
+ case OPT_SET_AUTH_USER:
+ wbinfo_set_auth_user(string_arg);
+ break;
+ case OPT_GET_AUTH_USER:
+ wbinfo_get_auth_user();
+ 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..4de2d57cc7
--- /dev/null
+++ b/source4/nsswitch/winbind_client.h
@@ -0,0 +1,16 @@
+#include "winbind_nss_config.h"
+#include "winbindd_nss.h"
+
+void init_request(struct winbindd_request *req,int rq_type);
+NSS_STATUS winbindd_send_request(int req_type,
+ struct winbindd_request *request);
+NSS_STATUS winbindd_get_response(struct winbindd_response *response);
+NSS_STATUS winbindd_request(int req_type,
+ struct winbindd_request *request,
+ struct winbindd_response *response);
+int winbind_open_pipe_sock(void);
+int write_sock(void *buffer, int count);
+int read_reply(struct winbindd_response *response);
+void close_sock(void);
+void free_response(struct winbindd_response *response);
+
diff --git a/source4/nsswitch/winbind_nss.c b/source4/nsswitch/winbind_nss.c
new file mode 100644
index 0000000000..0b4c0ce1d0
--- /dev/null
+++ b/source4/nsswitch/winbind_nss.c
@@ -0,0 +1,1341 @@
+/*
+ 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 Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "winbind_client.h"
+
+#ifdef HAVE_NS_API_H
+#undef VOLATILE
+
+#include <ns_daemon.h>
+#endif
+
+#define MAX_GETPWENT_USERS 250
+#define MAX_GETGRENT_USERS 250
+
+/* Prototypes from wb_common.c */
+
+extern int winbindd_fd;
+
+
+#ifdef HAVE_NS_API_H
+/* IRIX version */
+
+static int send_next_request(nsd_file_t *, struct winbindd_request *);
+static int do_list(int state, nsd_file_t *rq);
+
+static nsd_file_t *current_rq = NULL;
+static int current_winbind_xid = 0;
+static int next_winbind_xid = 0;
+
+typedef struct winbind_xid {
+ int xid;
+ nsd_file_t *rq;
+ struct winbindd_request *request;
+ struct winbind_xid *next;
+} winbind_xid_t;
+
+static winbind_xid_t *winbind_xids = (winbind_xid_t *)0;
+
+static int
+winbind_xid_new(int xid, nsd_file_t *rq, struct winbindd_request *request)
+{
+ winbind_xid_t *new;
+
+ nsd_logprintf(NSD_LOG_LOW,
+ "entering winbind_xid_new xid = %d rq = 0x%x, request = 0x%x\n",
+ xid, rq, request);
+ new = (winbind_xid_t *)nsd_calloc(1,sizeof(winbind_xid_t));
+ if (!new) {
+ nsd_logprintf(NSD_LOG_RESOURCE,"winbind_xid_new: failed malloc\n");
+ return NSD_ERROR;
+ }
+
+ new->xid = xid;
+ new->rq = rq;
+ new->request = request;
+ new->next = winbind_xids;
+ winbind_xids = new;
+
+ return NSD_CONTINUE;
+}
+
+/*
+** This routine will look down the xid list and return the request
+** associated with an xid. We remove the record if it is found.
+*/
+nsd_file_t *
+winbind_xid_lookup(int xid, struct winbindd_request **requestp)
+{
+ winbind_xid_t **last, *dx;
+ nsd_file_t *result=0;
+
+ for (last = &winbind_xids, dx = winbind_xids; dx && (dx->xid != xid);
+ last = &dx->next, dx = dx->next);
+ if (dx) {
+ *last = dx->next;
+ result = dx->rq;
+ *requestp = dx->request;
+ SAFE_FREE(dx);
+ }
+ nsd_logprintf(NSD_LOG_LOW,
+ "entering winbind_xid_lookup xid = %d rq = 0x%x, request = 0x%x\n",
+ xid, result, dx->request);
+
+ return result;
+}
+
+static int
+winbind_startnext_timeout(nsd_file_t **rqp, nsd_times_t *to)
+{
+ nsd_file_t *rq;
+ struct winbindd_request *request;
+
+ nsd_logprintf(NSD_LOG_MIN, "timeout (winbind startnext)\n");
+ rq = to->t_file;
+ *rqp = rq;
+ nsd_timeout_remove(rq);
+ request = to->t_clientdata;
+ return(send_next_request(rq, request));
+}
+
+static void
+dequeue_request()
+{
+ nsd_file_t *rq;
+ struct winbindd_request *request;
+
+ /*
+ * Check for queued requests
+ */
+ if (winbind_xids) {
+ nsd_logprintf(NSD_LOG_MIN, "timeout (winbind) unqueue xid %d\n",
+ current_winbind_xid);
+ rq = winbind_xid_lookup(current_winbind_xid++, &request);
+ /* cause a timeout on the queued request so we can send it */
+ nsd_timeout_new(rq,1,winbind_startnext_timeout,request);
+ }
+}
+
+static int
+do_request(nsd_file_t *rq, struct winbindd_request *request)
+{
+ if (winbind_xids == NULL) {
+ /*
+ * No outstanding requests.
+ * Send off the request to winbindd
+ */
+ nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) sending request\n");
+ return(send_next_request(rq, request));
+ } else {
+ /*
+ * Just queue it up for now - previous callout or timout
+ * will start it up
+ */
+ nsd_logprintf(NSD_LOG_MIN,
+ "lookup (winbind): queue request xid = %d\n",
+ next_winbind_xid);
+ return(winbind_xid_new(next_winbind_xid++, rq, request));
+ }
+}
+
+static int
+winbind_callback(nsd_file_t **rqp, int fd)
+{
+ struct winbindd_response response;
+ struct winbindd_pw *pw = &response.data.pw;
+ struct winbindd_gr *gr = &response.data.gr;
+ nsd_file_t *rq;
+ NSS_STATUS status;
+ fstring result;
+ char *members;
+ int i, maxlen;
+
+ dequeue_request();
+
+ nsd_logprintf(NSD_LOG_MIN, "entering callback (winbind)\n");
+
+ rq = current_rq;
+ *rqp = rq;
+
+ nsd_timeout_remove(rq);
+ nsd_callback_remove(fd);
+
+ ZERO_STRUCT(response);
+ status = winbindd_get_response(&response);
+
+ if (status != NSS_STATUS_SUCCESS) {
+ /* free any extra data area in response structure */
+ free_response(&response);
+ nsd_logprintf(NSD_LOG_MIN,
+ "callback (winbind) returning not found, status = %d\n",
+ status);
+ rq->f_status = NS_NOTFOUND;
+ return NSD_NEXT;
+ }
+
+ maxlen = sizeof(result) - 1;
+
+ switch ((int)rq->f_cmd_data) {
+ case WINBINDD_WINS_BYNAME:
+ case WINBINDD_WINS_BYIP:
+ snprintf(result,maxlen,"%s\n",response.data.winsresp);
+ break;
+ case WINBINDD_GETPWUID:
+ case WINBINDD_GETPWNAM:
+ snprintf(result,maxlen,"%s:%s:%d:%d:%s:%s:%s\n",
+ pw->pw_name,
+ pw->pw_passwd,
+ pw->pw_uid,
+ pw->pw_gid,
+ pw->pw_gecos,
+ pw->pw_dir,
+ pw->pw_shell);
+ break;
+ case WINBINDD_GETGRNAM:
+ case WINBINDD_GETGRGID:
+ if (gr->num_gr_mem && response.extra_data)
+ members = response.extra_data;
+ else
+ members = "";
+ snprintf(result,maxlen,"%s:%s:%d:%s\n",
+ gr->gr_name, gr->gr_passwd, gr->gr_gid, members);
+ break;
+ case WINBINDD_SETGRENT:
+ case WINBINDD_SETPWENT:
+ nsd_logprintf(NSD_LOG_MIN, "callback (winbind) - SETPWENT/SETGRENT\n");
+ free_response(&response);
+ return(do_list(1,rq));
+ case WINBINDD_GETGRENT:
+ case WINBINDD_GETGRLST:
+ nsd_logprintf(NSD_LOG_MIN,
+ "callback (winbind) - %d GETGRENT responses\n",
+ response.data.num_entries);
+ if (response.data.num_entries) {
+ gr = (struct winbindd_gr *)response.extra_data;
+ if (! gr ) {
+ nsd_logprintf(NSD_LOG_MIN, " no extra_data\n");
+ free_response(&response);
+ return NSD_ERROR;
+ }
+ members = (char *)response.extra_data +
+ (response.data.num_entries * sizeof(struct winbindd_gr));
+ for (i = 0; i < response.data.num_entries; i++) {
+ snprintf(result,maxlen,"%s:%s:%d:%s\n",
+ gr->gr_name, gr->gr_passwd, gr->gr_gid,
+ &members[gr->gr_mem_ofs]);
+ nsd_logprintf(NSD_LOG_MIN, " GETGRENT %s\n",result);
+ nsd_append_element(rq,NS_SUCCESS,result,strlen(result));
+ gr++;
+ }
+ }
+ i = response.data.num_entries;
+ free_response(&response);
+ if (i < MAX_GETPWENT_USERS)
+ return(do_list(2,rq));
+ else
+ return(do_list(1,rq));
+ case WINBINDD_GETPWENT:
+ nsd_logprintf(NSD_LOG_MIN,
+ "callback (winbind) - %d GETPWENT responses\n",
+ response.data.num_entries);
+ if (response.data.num_entries) {
+ pw = (struct winbindd_pw *)response.extra_data;
+ if (! pw ) {
+ nsd_logprintf(NSD_LOG_MIN, " no extra_data\n");
+ free_response(&response);
+ return NSD_ERROR;
+ }
+ for (i = 0; i < response.data.num_entries; i++) {
+ snprintf(result,maxlen,"%s:%s:%d:%d:%s:%s:%s",
+ pw->pw_name,
+ pw->pw_passwd,
+ pw->pw_uid,
+ pw->pw_gid,
+ pw->pw_gecos,
+ pw->pw_dir,
+ pw->pw_shell);
+ nsd_logprintf(NSD_LOG_MIN, " GETPWENT %s\n",result);
+ nsd_append_element(rq,NS_SUCCESS,result,strlen(result));
+ pw++;
+ }
+ }
+ i = response.data.num_entries;
+ free_response(&response);
+ if (i < MAX_GETPWENT_USERS)
+ return(do_list(2,rq));
+ else
+ return(do_list(1,rq));
+ case WINBINDD_ENDGRENT:
+ case WINBINDD_ENDPWENT:
+ nsd_logprintf(NSD_LOG_MIN, "callback (winbind) - ENDPWENT/ENDGRENT\n");
+ nsd_append_element(rq,NS_SUCCESS,"\n",1);
+ free_response(&response);
+ return NSD_NEXT;
+ default:
+ free_response(&response);
+ nsd_logprintf(NSD_LOG_MIN, "callback (winbind) - no valid command\n");
+ return NSD_NEXT;
+ }
+ nsd_logprintf(NSD_LOG_MIN, "callback (winbind) %s\n", result);
+ /* free any extra data area in response structure */
+ free_response(&response);
+ nsd_set_result(rq,NS_SUCCESS,result,strlen(result),VOLATILE);
+ return NSD_OK;
+}
+
+static int
+winbind_timeout(nsd_file_t **rqp, nsd_times_t *to)
+{
+ nsd_file_t *rq;
+
+ dequeue_request();
+
+ nsd_logprintf(NSD_LOG_MIN, "timeout (winbind)\n");
+
+ rq = to->t_file;
+ *rqp = rq;
+
+ /* Remove the callback and timeout */
+ nsd_callback_remove(winbindd_fd);
+ nsd_timeout_remove(rq);
+
+ rq->f_status = NS_NOTFOUND;
+ return NSD_NEXT;
+}
+
+static int
+send_next_request(nsd_file_t *rq, struct winbindd_request *request)
+{
+ NSS_STATUS status;
+ long timeout;
+
+ timeout = 1000;
+
+ nsd_logprintf(NSD_LOG_MIN, "send_next_request (winbind) %d to = %d\n",
+ rq->f_cmd_data, timeout);
+ status = winbindd_send_request((int)rq->f_cmd_data,request);
+ SAFE_FREE(request);
+
+ if (status != NSS_STATUS_SUCCESS) {
+ nsd_logprintf(NSD_LOG_MIN,
+ "send_next_request (winbind) error status = %d\n",status);
+ rq->f_status = status;
+ return NSD_NEXT;
+ }
+
+ current_rq = rq;
+
+ /*
+ * Set up callback and timeouts
+ */
+ nsd_logprintf(NSD_LOG_MIN, "send_next_request (winbind) fd = %d\n",winbindd_fd);
+ nsd_callback_new(winbindd_fd,winbind_callback,NSD_READ);
+ nsd_timeout_new(rq,timeout,winbind_timeout,(void *)0);
+ return NSD_CONTINUE;
+}
+
+int init(void)
+{
+ nsd_logprintf(NSD_LOG_MIN, "entering init (winbind)\n");
+ return(NSD_OK);
+}
+
+int lookup(nsd_file_t *rq)
+{
+ char *map;
+ char *key;
+ struct winbindd_request *request;
+
+ nsd_logprintf(NSD_LOG_MIN, "entering lookup (winbind)\n");
+ if (! rq)
+ return NSD_ERROR;
+
+ map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
+ key = nsd_attr_fetch_string(rq->f_attrs, "key", (char*)0);
+ if (! map || ! key) {
+ nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) table or key not defined\n");
+ rq->f_status = NS_BADREQ;
+ return NSD_ERROR;
+ }
+
+ nsd_logprintf(NSD_LOG_MIN, "lookup (winbind %s)\n",map);
+
+ request = (struct winbindd_request *)nsd_calloc(1,sizeof(struct winbindd_request));
+ if (! request) {
+ nsd_logprintf(NSD_LOG_RESOURCE,
+ "lookup (winbind): failed malloc\n");
+ return NSD_ERROR;
+ }
+
+ if (strcasecmp(map,"passwd.byuid") == 0) {
+ request->data.uid = atoi(key);
+ rq->f_cmd_data = (void *)WINBINDD_GETPWUID;
+ } else if (strcasecmp(map,"passwd.byname") == 0) {
+ strncpy(request->data.username, key,
+ sizeof(request->data.username) - 1);
+ request->data.username[sizeof(request->data.username) - 1] = '\0';
+ rq->f_cmd_data = (void *)WINBINDD_GETPWNAM;
+ } else if (strcasecmp(map,"group.byname") == 0) {
+ strncpy(request->data.groupname, key,
+ sizeof(request->data.groupname) - 1);
+ request->data.groupname[sizeof(request->data.groupname) - 1] = '\0';
+ rq->f_cmd_data = (void *)WINBINDD_GETGRNAM;
+ } else if (strcasecmp(map,"group.bygid") == 0) {
+ request->data.gid = atoi(key);
+ rq->f_cmd_data = (void *)WINBINDD_GETGRGID;
+ } else if (strcasecmp(map,"hosts.byname") == 0) {
+ strncpy(request->data.winsreq, key, sizeof(request->data.winsreq) - 1);
+ request->data.winsreq[sizeof(request->data.winsreq) - 1] = '\0';
+ rq->f_cmd_data = (void *)WINBINDD_WINS_BYNAME;
+ } else if (strcasecmp(map,"hosts.byaddr") == 0) {
+ strncpy(request->data.winsreq, key, sizeof(request->data.winsreq) - 1);
+ request->data.winsreq[sizeof(request->data.winsreq) - 1] = '\0';
+ rq->f_cmd_data = (void *)WINBINDD_WINS_BYIP;
+ } else {
+ /*
+ * Don't understand this map - just return not found
+ */
+ nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) unknown table\n");
+ SAFE_FREE(request);
+ rq->f_status = NS_NOTFOUND;
+ return NSD_NEXT;
+ }
+
+ return(do_request(rq, request));
+}
+
+int list(nsd_file_t *rq)
+{
+ char *map;
+
+ nsd_logprintf(NSD_LOG_MIN, "entering list (winbind)\n");
+ if (! rq)
+ return NSD_ERROR;
+
+ map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
+ if (! map ) {
+ nsd_logprintf(NSD_LOG_MIN, "list (winbind) table not defined\n");
+ rq->f_status = NS_BADREQ;
+ return NSD_ERROR;
+ }
+
+ nsd_logprintf(NSD_LOG_MIN, "list (winbind %s)\n",map);
+
+ return (do_list(0,rq));
+}
+
+static int
+do_list(int state, nsd_file_t *rq)
+{
+ char *map;
+ struct winbindd_request *request;
+
+ nsd_logprintf(NSD_LOG_MIN, "entering do_list (winbind) state = %d\n",state);
+
+ map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
+ request = (struct winbindd_request *)nsd_calloc(1,sizeof(struct winbindd_request));
+ if (! request) {
+ nsd_logprintf(NSD_LOG_RESOURCE,
+ "do_list (winbind): failed malloc\n");
+ return NSD_ERROR;
+ }
+
+ if (strcasecmp(map,"passwd.byname") == 0) {
+ switch (state) {
+ case 0:
+ rq->f_cmd_data = (void *)WINBINDD_SETPWENT;
+ break;
+ case 1:
+ request->data.num_entries = MAX_GETPWENT_USERS;
+ rq->f_cmd_data = (void *)WINBINDD_GETPWENT;
+ break;
+ case 2:
+ rq->f_cmd_data = (void *)WINBINDD_ENDPWENT;
+ break;
+ default:
+ nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown state\n");
+ SAFE_FREE(request);
+ rq->f_status = NS_NOTFOUND;
+ return NSD_NEXT;
+ }
+ } else if (strcasecmp(map,"group.byname") == 0) {
+ switch (state) {
+ case 0:
+ rq->f_cmd_data = (void *)WINBINDD_SETGRENT;
+ break;
+ case 1:
+ request->data.num_entries = MAX_GETGRENT_USERS;
+ rq->f_cmd_data = (void *)WINBINDD_GETGRENT;
+ break;
+ case 2:
+ rq->f_cmd_data = (void *)WINBINDD_ENDGRENT;
+ break;
+ default:
+ nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown state\n");
+ SAFE_FREE(request);
+ rq->f_status = NS_NOTFOUND;
+ return NSD_NEXT;
+ }
+ } else {
+ /*
+ * Don't understand this map - just return not found
+ */
+ nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown table\n");
+ SAFE_FREE(request);
+ rq->f_status = NS_NOTFOUND;
+ return NSD_NEXT;
+ }
+
+ return(do_request(rq, request));
+}
+
+#else
+
+/* Allocate some space from the nss static buffer. The buffer and buflen
+ are the pointers passed in by the C library to the _nss_ntdom_*
+ functions. */
+
+static char *get_static(char **buffer, int *buflen, int 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. */
+
+BOOL next_token(char **ptr,char *buff,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)
+{
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: setpwent\n", getpid());
+#endif
+
+ if (num_pw_cache > 0) {
+ ndx_pw_cache = num_pw_cache = 0;
+ free_response(&getpwent_response);
+ }
+
+ return winbindd_request(WINBINDD_SETPWENT, NULL, NULL);
+}
+
+/* Close ntdom password database "file pointer" */
+
+NSS_STATUS
+_nss_winbind_endpwent(void)
+{
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: endpwent\n", getpid());
+#endif
+
+ if (num_pw_cache > 0) {
+ ndx_pw_cache = num_pw_cache = 0;
+ free_response(&getpwent_response);
+ }
+
+ return winbindd_request(WINBINDD_ENDPWENT, NULL, NULL);
+}
+
+/* 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) {
+ free_response(&getpwent_response);
+ }
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(getpwent_response);
+
+ request.data.num_entries = MAX_GETPWENT_USERS;
+
+ ret = winbindd_request(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 = getpwent_response.extra_data;
+
+ /* Check data is valid */
+
+ if (pw_cache == NULL) {
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ 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;
+ return ret;
+ }
+
+ *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;
+ free_response(&getpwent_response);
+ }
+ }
+
+ 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;
+
+ /* 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(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;
+ return ret;
+ }
+ }
+
+ } 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;
+ return ret;
+ }
+
+ keep_response = False;
+ *errnop = errno = 0;
+ }
+
+ free_response(&response);
+ 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(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;
+ return ret;
+ }
+ }
+
+ } 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;
+ return ret;
+ }
+
+ keep_response = False;
+ *errnop = errno = 0;
+ }
+
+ free_response(&response);
+ 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)
+{
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: setgrent\n", getpid());
+#endif
+
+ if (num_gr_cache > 0) {
+ ndx_gr_cache = num_gr_cache = 0;
+ free_response(&getgrent_response);
+ }
+
+ return winbindd_request(WINBINDD_SETGRENT, NULL, NULL);
+}
+
+/* Close "file pointer" for ntdom group database */
+
+NSS_STATUS
+_nss_winbind_endgrent(void)
+{
+#ifdef DEBUG_NSS
+ fprintf(stderr, "[%5d]: endgrent\n", getpid());
+#endif
+
+ if (num_gr_cache > 0) {
+ ndx_gr_cache = num_gr_cache = 0;
+ free_response(&getgrent_response);
+ }
+
+ return winbindd_request(WINBINDD_ENDGRENT, NULL, NULL);
+}
+
+/* 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) {
+ free_response(&getgrent_response);
+ }
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(getgrent_response);
+
+ request.data.num_entries = MAX_GETGRENT_USERS;
+
+ ret = winbindd_request(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 = getgrent_response.extra_data;
+
+ /* Check data is valid */
+
+ if (gr_cache == NULL) {
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ /* 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)+mem_ofs,
+ &buffer, &buflen);
+
+ /* Out of memory - try again */
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ called_again = True;
+ *errnop = errno = ERANGE;
+ return ret;
+ }
+
+ *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;
+ free_response(&getgrent_response);
+ }
+ }
+
+ 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(WINBINDD_GETGRNAM, &request, &response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+ ret = fill_grent(result, &response.data.gr,
+ response.extra_data,
+ &buffer, &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = True;
+ *errnop = errno = ERANGE;
+ return ret;
+ }
+ }
+
+ } else {
+
+ /* We've been called again */
+
+ ret = fill_grent(result, &response.data.gr,
+ response.extra_data, &buffer, &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = True;
+ *errnop = errno = ERANGE;
+ return ret;
+ }
+
+ keep_response = False;
+ *errnop = 0;
+ }
+
+ free_response(&response);
+ 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(WINBINDD_GETGRGID, &request, &response);
+
+ if (ret == NSS_STATUS_SUCCESS) {
+
+ ret = fill_grent(result, &response.data.gr,
+ response.extra_data,
+ &buffer, &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = True;
+ *errnop = errno = ERANGE;
+ return ret;
+ }
+ }
+
+ } else {
+
+ /* We've been called again */
+
+ ret = fill_grent(result, &response.data.gr,
+ response.extra_data, &buffer, &buflen);
+
+ if (ret == NSS_STATUS_TRYAGAIN) {
+ keep_response = True;
+ *errnop = errno = ERANGE;
+ return ret;
+ }
+
+ keep_response = False;
+ *errnop = 0;
+ }
+
+ free_response(&response);
+ 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(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;
+
+ /* Copy group list to client */
+
+ for (i = 0; i < num_gids; i++) {
+
+ /* Skip primary group */
+
+ if (gid_list[i] == group) continue;
+
+ /* Add to buffer */
+
+ if (*start == *size && limit <= 0) {
+ (*groups) = realloc(
+ (*groups), (2 * (*size) + 1) * sizeof(**groups));
+ if (! *groups) goto done;
+ *size = 2 * (*size) + 1;
+ }
+
+ if (*start == *size) goto done;
+
+ (*groups)[*start] = gid_list[i];
+ *start += 1;
+
+ /* Filled buffer? */
+
+ if (*start == limit) goto done;
+ }
+ }
+
+ /* Back to your regularly scheduled programming */
+
+ done:
+ return ret;
+}
+
+#endif
diff --git a/source4/nsswitch/winbind_nss_config.h b/source4/nsswitch/winbind_nss_config.h
new file mode 100644
index 0000000000..2faaa30d1b
--- /dev/null
+++ b/source4/nsswitch/winbind_nss_config.h
@@ -0,0 +1,165 @@
+/*
+ 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 Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _WINBIND_NSS_CONFIG_H
+#define _WINBIND_NSS_CONFIG_H
+
+/* Include header files from data in config.h file */
+
+#ifndef NO_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_UNIXSOCKET
+#include <sys/un.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#else
+#ifdef HAVE_SYS_FCNTL_H
+#include <sys/fcntl.h>
+#endif
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <pwd.h>
+#include "nsswitch/nss.h"
+
+/* Declarations for functions in winbind_nss.c
+ needed in winbind_nss_solaris.c (solaris wrapper to nss) */
+
+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);
+
+/* 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
+
+#ifndef _BOOL
+#define _BOOL /* So we don't typedef BOOL again in vfs.h */
+#define False (0)
+#define True (1)
+#define Auto (2)
+typedef int BOOL;
+#endif
+
+#if !defined(uint32)
+#if (SIZEOF_INT == 4)
+#define uint32 unsigned int
+#elif (SIZEOF_LONG == 4)
+#define uint32 unsigned long
+#elif (SIZEOF_SHORT == 4)
+#define uint32 unsigned short
+#endif
+#endif
+
+#if !defined(uint16)
+#if (SIZEOF_SHORT == 4)
+#define uint16 __ERROR___CANNOT_DETERMINE_TYPE_FOR_INT16;
+#else /* SIZEOF_SHORT != 4 */
+#define uint16 unsigned short
+#endif /* SIZEOF_SHORT != 4 */
+#endif
+
+#ifndef uint8
+#define uint8 unsigned char
+#endif
+
+/* zero a structure */
+#ifndef ZERO_STRUCT
+#define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x))
+#endif
+
+/* zero a structure given a pointer to the structure */
+#ifndef ZERO_STRUCTP
+#define ZERO_STRUCTP(x) { if ((x) != NULL) memset((char *)(x), 0, sizeof(*(x))); }
+#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_solaris.c b/source4/nsswitch/winbind_nss_solaris.c
new file mode 100644
index 0000000000..f3bd05b77a
--- /dev/null
+++ b/source4/nsswitch/winbind_nss_solaris.c
@@ -0,0 +1,301 @@
+/*
+ Solaris NSS wrapper for winbind
+ - Shirish Kalele 2000
+
+ Based on Luke Howard's ldap_nss module for Solaris
+ */
+
+/*
+ Copyright (C) 1997-2003 Luke Howard.
+ This file is part of the nss_ldap library.
+
+ The nss_ldap library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The nss_ldap library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the nss_ldap library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <string.h>
+#include <pwd.h>
+#include "includes.h"
+#include <syslog.h>
+#if !defined(HPUX)
+#include <sys/syslog.h>
+#endif /*hpux*/
+#include "winbind_nss_config.h"
+
+#if defined(HAVE_NSS_COMMON_H) || defined(HPUX)
+
+#undef NSS_DEBUG
+
+#ifdef NSS_DEBUG
+#define NSS_DEBUG(str) syslog(LOG_DEBUG, "nss_winbind: %s", str);
+#else
+#define NSS_DEBUG(str) ;
+#endif
+
+#define NSS_ARGS(args) ((nss_XbyY_args_t *)args)
+
+#define make_pwent_str(dest, src) \
+{ \
+ if((dest = get_static(buffer, buflen, strlen(src)+1)) == NULL) \
+ { \
+ *errnop = ERANGE; \
+ NSS_DEBUG("ERANGE error"); \
+ return NSS_STATUS_TRYAGAIN; \
+ } \
+ strcpy(dest, src); \
+}
+
+static NSS_STATUS _nss_winbind_setpwent_solwrap (nss_backend_t* be, void* args)
+{
+ NSS_DEBUG("_nss_winbind_setpwent_solwrap");
+ return _nss_winbind_setpwent();
+}
+
+static NSS_STATUS
+_nss_winbind_endpwent_solwrap (nss_backend_t * be, void *args)
+{
+ NSS_DEBUG("_nss_winbind_endpwent_solwrap");
+ return _nss_winbind_endpwent();
+}
+
+static NSS_STATUS
+_nss_winbind_getpwent_solwrap (nss_backend_t* be, void *args)
+{
+ NSS_STATUS ret;
+ char* buffer = NSS_ARGS(args)->buf.buffer;
+ int buflen = NSS_ARGS(args)->buf.buflen;
+ struct passwd* result = (struct passwd*) NSS_ARGS(args)->buf.result;
+ int* errnop = &NSS_ARGS(args)->erange;
+ char logmsg[80];
+
+ ret = _nss_winbind_getpwent_r(result, buffer,
+ buflen, errnop);
+
+ if(ret == NSS_STATUS_SUCCESS)
+ {
+ snprintf(logmsg, 79, "_nss_winbind_getpwent_solwrap: Returning user: %s\n",
+ result->pw_name);
+ NSS_DEBUG(logmsg);
+ NSS_ARGS(args)->returnval = (void*) result;
+ } else {
+ snprintf(logmsg, 79, "_nss_winbind_getpwent_solwrap: Returning error: %d.\n",ret);
+ NSS_DEBUG(logmsg);
+ }
+
+ return ret;
+}
+
+static NSS_STATUS
+_nss_winbind_getpwnam_solwrap (nss_backend_t* be, void* args)
+{
+ NSS_STATUS ret;
+ struct passwd* result = (struct passwd*) NSS_ARGS(args)->buf.result;
+
+ NSS_DEBUG("_nss_winbind_getpwnam_solwrap");
+
+ ret = _nss_winbind_getpwnam_r (NSS_ARGS(args)->key.name,
+ result,
+ NSS_ARGS(args)->buf.buffer,
+ NSS_ARGS(args)->buf.buflen,
+ &NSS_ARGS(args)->erange);
+ if(ret == NSS_STATUS_SUCCESS)
+ NSS_ARGS(args)->returnval = (void*) result;
+
+ return ret;
+}
+
+static NSS_STATUS
+_nss_winbind_getpwuid_solwrap(nss_backend_t* be, void* args)
+{
+ NSS_STATUS ret;
+ struct passwd* result = (struct passwd*) NSS_ARGS(args)->buf.result;
+
+ NSS_DEBUG("_nss_winbind_getpwuid_solwrap");
+ ret = _nss_winbind_getpwuid_r (NSS_ARGS(args)->key.uid,
+ result,
+ NSS_ARGS(args)->buf.buffer,
+ NSS_ARGS(args)->buf.buflen,
+ &NSS_ARGS(args)->erange);
+ if(ret == NSS_STATUS_SUCCESS)
+ NSS_ARGS(args)->returnval = (void*) result;
+
+ return ret;
+}
+
+static NSS_STATUS _nss_winbind_passwd_destr (nss_backend_t * be, void *args)
+{
+ SAFE_FREE(be);
+ NSS_DEBUG("_nss_winbind_passwd_destr");
+ return NSS_STATUS_SUCCESS;
+}
+
+static nss_backend_op_t passwd_ops[] =
+{
+ _nss_winbind_passwd_destr,
+ _nss_winbind_endpwent_solwrap, /* NSS_DBOP_ENDENT */
+ _nss_winbind_setpwent_solwrap, /* NSS_DBOP_SETENT */
+ _nss_winbind_getpwent_solwrap, /* NSS_DBOP_GETENT */
+ _nss_winbind_getpwnam_solwrap, /* NSS_DBOP_PASSWD_BYNAME */
+ _nss_winbind_getpwuid_solwrap /* NSS_DBOP_PASSWD_BYUID */
+};
+
+nss_backend_t*
+_nss_winbind_passwd_constr (const char* db_name,
+ const char* src_name,
+ const char* cfg_args)
+{
+ nss_backend_t *be;
+
+ if(!(be = (nss_backend_t*) malloc(sizeof(nss_backend_t))) )
+ return NULL;
+
+ be->ops = passwd_ops;
+ be->n_ops = sizeof(passwd_ops) / sizeof(nss_backend_op_t);
+
+ NSS_DEBUG("Initialized nss_winbind passwd backend");
+ return be;
+}
+
+/*****************************************************************
+ GROUP database backend
+ *****************************************************************/
+
+static NSS_STATUS _nss_winbind_setgrent_solwrap (nss_backend_t* be, void* args)
+{
+ NSS_DEBUG("_nss_winbind_setgrent_solwrap");
+ return _nss_winbind_setgrent();
+}
+
+static NSS_STATUS
+_nss_winbind_endgrent_solwrap (nss_backend_t * be, void *args)
+{
+ NSS_DEBUG("_nss_winbind_endgrent_solwrap");
+ return _nss_winbind_endgrent();
+}
+
+static NSS_STATUS
+_nss_winbind_getgrent_solwrap(nss_backend_t* be, void* args)
+{
+ NSS_STATUS ret;
+ char* buffer = NSS_ARGS(args)->buf.buffer;
+ int buflen = NSS_ARGS(args)->buf.buflen;
+ struct group* result = (struct group*) NSS_ARGS(args)->buf.result;
+ int* errnop = &NSS_ARGS(args)->erange;
+ char logmsg[80];
+
+ ret = _nss_winbind_getgrent_r(result, buffer,
+ buflen, errnop);
+
+ if(ret == NSS_STATUS_SUCCESS)
+ {
+ snprintf(logmsg, 79, "_nss_winbind_getgrent_solwrap: Returning group: %s\n", result->gr_name);
+ NSS_DEBUG(logmsg);
+ NSS_ARGS(args)->returnval = (void*) result;
+ } else {
+ snprintf(logmsg, 79, "_nss_winbind_getgrent_solwrap: Returning error: %d.\n", ret);
+ NSS_DEBUG(logmsg);
+ }
+
+ return ret;
+
+}
+
+static NSS_STATUS
+_nss_winbind_getgrnam_solwrap(nss_backend_t* be, void* args)
+{
+ NSS_STATUS ret;
+ struct group* result = (struct group*) NSS_ARGS(args)->buf.result;
+
+ NSS_DEBUG("_nss_winbind_getgrnam_solwrap");
+ ret = _nss_winbind_getgrnam_r(NSS_ARGS(args)->key.name,
+ result,
+ NSS_ARGS(args)->buf.buffer,
+ NSS_ARGS(args)->buf.buflen,
+ &NSS_ARGS(args)->erange);
+
+ if(ret == NSS_STATUS_SUCCESS)
+ NSS_ARGS(args)->returnval = (void*) result;
+
+ return ret;
+}
+
+static NSS_STATUS
+_nss_winbind_getgrgid_solwrap(nss_backend_t* be, void* args)
+{
+ NSS_STATUS ret;
+ struct group* result = (struct group*) NSS_ARGS(args)->buf.result;
+
+ NSS_DEBUG("_nss_winbind_getgrgid_solwrap");
+ ret = _nss_winbind_getgrgid_r (NSS_ARGS(args)->key.gid,
+ result,
+ NSS_ARGS(args)->buf.buffer,
+ NSS_ARGS(args)->buf.buflen,
+ &NSS_ARGS(args)->erange);
+
+ if(ret == NSS_STATUS_SUCCESS)
+ NSS_ARGS(args)->returnval = (void*) result;
+
+ return ret;
+}
+
+static NSS_STATUS
+_nss_winbind_getgroupsbymember_solwrap(nss_backend_t* be, void* args)
+{
+ NSS_DEBUG("_nss_winbind_getgroupsbymember");
+ return NSS_STATUS_NOTFOUND;
+}
+
+static NSS_STATUS
+_nss_winbind_group_destr (nss_backend_t* be, void* args)
+{
+ SAFE_FREE(be);
+ NSS_DEBUG("_nss_winbind_group_destr");
+ return NSS_STATUS_SUCCESS;
+}
+
+static nss_backend_op_t group_ops[] =
+{
+ _nss_winbind_group_destr,
+ _nss_winbind_endgrent_solwrap,
+ _nss_winbind_setgrent_solwrap,
+ _nss_winbind_getgrent_solwrap,
+ _nss_winbind_getgrnam_solwrap,
+ _nss_winbind_getgrgid_solwrap,
+ _nss_winbind_getgroupsbymember_solwrap
+};
+
+nss_backend_t*
+_nss_winbind_group_constr (const char* db_name,
+ const char* src_name,
+ const char* cfg_args)
+{
+ nss_backend_t* be;
+
+ if(!(be = (nss_backend_t*) malloc(sizeof(nss_backend_t))) )
+ return NULL;
+
+ be->ops = group_ops;
+ be->n_ops = sizeof(group_ops) / sizeof(nss_backend_op_t);
+
+ NSS_DEBUG("Initialized nss_winbind group backend");
+ return be;
+}
+
+#endif /* SUN_NSS */
+
+
diff --git a/source4/nsswitch/winbindd.c b/source4/nsswitch/winbindd.c
new file mode 100644
index 0000000000..ad37768c09
--- /dev/null
+++ b/source4/nsswitch/winbindd.c
@@ -0,0 +1,951 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) by Tim Potter 2000-2002
+ Copyright (C) Andrew Tridgell 2002
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "winbindd.h"
+
+BOOL opt_nocache = False;
+BOOL opt_dual_daemon = False;
+
+/* Reload configuration */
+
+static BOOL reload_services_file(BOOL test)
+{
+ BOOL ret;
+ pstring logfile;
+
+ if (lp_loaded()) {
+ pstring fname;
+
+ pstrcpy(fname,lp_configfile());
+ if (file_exist(fname,NULL) && !strcsequal(fname,dyn_CONFIGFILE)) {
+ pstrcpy(dyn_CONFIGFILE,fname);
+ test = False;
+ }
+ }
+
+ snprintf(logfile, sizeof(logfile), "%s/log.winbindd", dyn_LOGFILEBASE);
+ lp_set_logfile(logfile);
+
+ reopen_logs();
+ ret = lp_load(dyn_CONFIGFILE,False,False,True);
+
+ snprintf(logfile, sizeof(logfile), "%s/log.winbindd", dyn_LOGFILEBASE);
+ lp_set_logfile(logfile);
+
+ reopen_logs();
+ load_interfaces();
+
+ return(ret);
+}
+
+/*******************************************************************
+ Print out all talloc memory info.
+********************************************************************/
+
+void return_all_talloc_info(int msg_type, pid_t src_pid, void *buf, size_t len)
+{
+ TALLOC_CTX *ctx = talloc_init("info context");
+ char *info = NULL;
+
+ if (!ctx)
+ return;
+
+ info = talloc_describe_all(ctx);
+ if (info)
+ DEBUG(10,(info));
+ message_send_pid(src_pid, MSG_TALLOC_USAGE, info, info ? strlen(info) + 1: 0, True);
+ talloc_destroy(ctx);
+}
+
+#if DUMP_CORE
+
+/**************************************************************************** **
+ Prepare to dump a core file - carefully!
+ **************************************************************************** */
+
+static BOOL dump_core(void)
+{
+ char *p;
+ pstring dname;
+ pstrcpy( dname, lp_logfile() );
+ if ((p=strrchr(dname,'/')))
+ *p=0;
+ pstrcat( dname, "/corefiles" );
+ mkdir( dname, 0700 );
+ sys_chown( dname, getuid(), getgid() );
+ chmod( dname, 0700 );
+ if ( chdir(dname) )
+ return( False );
+ umask( ~(0700) );
+
+#ifdef HAVE_GETRLIMIT
+#ifdef RLIMIT_CORE
+ {
+ struct rlimit rlp;
+ getrlimit( RLIMIT_CORE, &rlp );
+ rlp.rlim_cur = MAX( 4*1024*1024, rlp.rlim_cur );
+ setrlimit( RLIMIT_CORE, &rlp );
+ getrlimit( RLIMIT_CORE, &rlp );
+ DEBUG( 3, ( "Core limits now %d %d\n", (int)rlp.rlim_cur, (int)rlp.rlim_max ) );
+ }
+#endif
+#endif
+
+ DEBUG(0,("Dumping core in %s\n",dname));
+ abort();
+ return( True );
+} /* dump_core */
+#endif
+
+/**************************************************************************** **
+ Handle a fault..
+ **************************************************************************** */
+
+static void fault_quit(void)
+{
+#if DUMP_CORE
+ dump_core();
+#endif
+}
+
+static void winbindd_status(void)
+{
+ struct winbindd_cli_state *tmp;
+
+ DEBUG(0, ("winbindd status:\n"));
+
+ /* Print client state information */
+
+ DEBUG(0, ("\t%d clients currently active\n", winbindd_num_clients()));
+
+ if (DEBUGLEVEL >= 2 && winbindd_num_clients()) {
+ DEBUG(2, ("\tclient list:\n"));
+ for(tmp = winbindd_client_list(); tmp; tmp = tmp->next) {
+ DEBUG(2, ("\t\tpid %d, sock %d, rbl %d, wbl %d\n",
+ tmp->pid, tmp->sock, tmp->read_buf_len,
+ tmp->write_buf_len));
+ }
+ }
+}
+
+/* Print winbindd status to log file */
+
+static void print_winbindd_status(void)
+{
+ winbindd_status();
+ winbindd_idmap_status();
+ winbindd_cm_status();
+}
+
+/* Flush client cache */
+
+static void flush_caches(void)
+{
+ /* Clear cached user and group enumation info */
+ wcache_flush_cache();
+}
+
+/* Handle the signal by unlinking socket and exiting */
+
+static void terminate(void)
+{
+ pstring path;
+
+ winbindd_idmap_close();
+
+ /* Remove socket file */
+ snprintf(path, sizeof(path), "%s/%s",
+ WINBINDD_SOCKET_DIR, WINBINDD_SOCKET_NAME);
+ unlink(path);
+ exit(0);
+}
+
+static BOOL do_sigterm;
+
+static void termination_handler(int signum)
+{
+ do_sigterm = True;
+ sys_select_signal();
+}
+
+static BOOL do_sigusr2;
+
+static void sigusr2_handler(int signum)
+{
+ do_sigusr2 = True;
+ sys_select_signal();
+}
+
+static BOOL do_sighup;
+
+static void sighup_handler(int signum)
+{
+ do_sighup = True;
+ sys_select_signal();
+}
+
+struct dispatch_table {
+ enum winbindd_cmd cmd;
+ enum winbindd_result (*fn)(struct winbindd_cli_state *state);
+ const char *winbindd_cmd_name;
+};
+
+static struct dispatch_table dispatch_table[] = {
+
+ /* User functions */
+
+ { WINBINDD_GETPWNAM, winbindd_getpwnam, "GETPWNAM" },
+ { WINBINDD_GETPWUID, winbindd_getpwuid, "GETPWUID" },
+
+ { WINBINDD_SETPWENT, winbindd_setpwent, "SETPWENT" },
+ { WINBINDD_ENDPWENT, winbindd_endpwent, "ENDPWENT" },
+ { WINBINDD_GETPWENT, winbindd_getpwent, "GETPWENT" },
+
+ { WINBINDD_GETGROUPS, winbindd_getgroups, "GETGROUPS" },
+
+ /* Group functions */
+
+ { WINBINDD_GETGRNAM, winbindd_getgrnam, "GETGRNAM" },
+ { WINBINDD_GETGRGID, winbindd_getgrgid, "GETGRGID" },
+ { WINBINDD_SETGRENT, winbindd_setgrent, "SETGRENT" },
+ { WINBINDD_ENDGRENT, winbindd_endgrent, "ENDGRENT" },
+ { WINBINDD_GETGRENT, winbindd_getgrent, "GETGRENT" },
+ { WINBINDD_GETGRLST, winbindd_getgrent, "GETGRLST" },
+
+ /* PAM auth functions */
+
+ { WINBINDD_PAM_AUTH, winbindd_pam_auth, "PAM_AUTH" },
+ { WINBINDD_PAM_AUTH_CRAP, winbindd_pam_auth_crap, "AUTH_CRAP" },
+ { WINBINDD_PAM_CHAUTHTOK, winbindd_pam_chauthtok, "CHAUTHTOK" },
+
+ /* Enumeration functions */
+
+ { WINBINDD_LIST_USERS, winbindd_list_users, "LIST_USERS" },
+ { WINBINDD_LIST_GROUPS, winbindd_list_groups, "LIST_GROUPS" },
+ { WINBINDD_LIST_TRUSTDOM, winbindd_list_trusted_domains, "LIST_TRUSTDOM" },
+ { WINBINDD_SHOW_SEQUENCE, winbindd_show_sequence, "SHOW_SEQUENCE" },
+
+ /* SID related functions */
+
+ { WINBINDD_LOOKUPSID, winbindd_lookupsid, "LOOKUPSID" },
+ { WINBINDD_LOOKUPNAME, winbindd_lookupname, "LOOKUPNAME" },
+
+ /* Lookup related functions */
+
+ { WINBINDD_SID_TO_UID, winbindd_sid_to_uid, "SID_TO_UID" },
+ { WINBINDD_SID_TO_GID, winbindd_sid_to_gid, "SID_TO_GID" },
+ { WINBINDD_GID_TO_SID, winbindd_gid_to_sid, "GID_TO_SID" },
+ { WINBINDD_UID_TO_SID, winbindd_uid_to_sid, "UID_TO_SID" },
+
+ /* Miscellaneous */
+
+ { WINBINDD_CHECK_MACHACC, winbindd_check_machine_acct, "CHECK_MACHACC" },
+ { WINBINDD_PING, winbindd_ping, "PING" },
+ { WINBINDD_INFO, winbindd_info, "INFO" },
+ { WINBINDD_INTERFACE_VERSION, winbindd_interface_version, "INTERFACE_VERSION" },
+ { WINBINDD_DOMAIN_NAME, winbindd_domain_name, "DOMAIN_NAME" },
+ { WINBINDD_NETBIOS_NAME, winbindd_netbios_name, "NETBIOS_NAME" },
+
+ /* WINS functions */
+
+ { WINBINDD_WINS_BYNAME, winbindd_wins_byname, "WINS_BYNAME" },
+ { WINBINDD_WINS_BYIP, winbindd_wins_byip, "WINS_BYIP" },
+
+ /* End of list */
+
+ { WINBINDD_NUM_CMDS, NULL, "NONE" }
+};
+
+static void process_request(struct winbindd_cli_state *state)
+{
+ struct dispatch_table *table = dispatch_table;
+
+ /* Free response data - we may be interrupted and receive another
+ command before being able to send this data off. */
+
+ SAFE_FREE(state->response.extra_data);
+
+ ZERO_STRUCT(state->response);
+
+ state->response.result = WINBINDD_ERROR;
+ state->response.length = sizeof(struct winbindd_response);
+
+ /* Process command */
+
+ for (table = dispatch_table; table->fn; table++) {
+ if (state->request.cmd == table->cmd) {
+ DEBUG(10,("process_request: request fn %s\n", table->winbindd_cmd_name ));
+ state->response.result = table->fn(state);
+ break;
+ }
+ }
+
+ if (!table->fn)
+ DEBUG(10,("process_request: unknown request fn number %d\n", (int)state->request.cmd ));
+
+ /* In case extra data pointer is NULL */
+
+ if (!state->response.extra_data)
+ state->response.length = sizeof(struct winbindd_response);
+}
+
+/* Process a new connection by adding it to the client connection list */
+
+static void new_connection(int listen_sock)
+{
+ struct sockaddr_un sunaddr;
+ struct winbindd_cli_state *state;
+ socklen_t len;
+ int sock;
+
+ /* Accept connection */
+
+ len = sizeof(sunaddr);
+
+ do {
+ sock = accept(listen_sock, (struct sockaddr *)&sunaddr, &len);
+ } while (sock == -1 && errno == EINTR);
+
+ if (sock == -1)
+ return;
+
+ DEBUG(6,("accepted socket %d\n", sock));
+
+ /* Create new connection structure */
+
+ if ((state = (struct winbindd_cli_state *)
+ malloc(sizeof(*state))) == NULL)
+ return;
+
+ ZERO_STRUCTP(state);
+ state->sock = sock;
+
+ state->last_access = time(NULL);
+
+ /* Add to connection list */
+
+ winbindd_add_client(state);
+}
+
+/* Remove a client connection from client connection list */
+
+static void remove_client(struct winbindd_cli_state *state)
+{
+ /* It's a dead client - hold a funeral */
+
+ if (state != NULL) {
+
+ /* Close socket */
+
+ close(state->sock);
+
+ /* Free any getent state */
+
+ free_getent_state(state->getpwent_state);
+ free_getent_state(state->getgrent_state);
+
+ /* We may have some extra data that was not freed if the
+ client was killed unexpectedly */
+
+ SAFE_FREE(state->response.extra_data);
+
+ /* Remove from list and free */
+
+ winbindd_remove_client(state);
+ SAFE_FREE(state);
+ }
+}
+
+
+/* Shutdown client connection which has been idle for the longest time */
+
+static BOOL remove_idle_client(void)
+{
+ struct winbindd_cli_state *state, *remove_state = NULL;
+ time_t last_access = 0;
+ int nidle = 0;
+
+ for (state = winbindd_client_list(); state; state = state->next) {
+ if (state->read_buf_len == 0 && state->write_buf_len == 0 &&
+ !state->getpwent_state && !state->getgrent_state) {
+ nidle++;
+ if (!last_access || state->last_access < last_access) {
+ last_access = state->last_access;
+ remove_state = state;
+ }
+ }
+ }
+
+ if (remove_state) {
+ DEBUG(5,("Found %d idle client connections, shutting down sock %d, pid %u\n",
+ nidle, remove_state->sock, (unsigned int)remove_state->pid));
+ remove_client(remove_state);
+ return True;
+ }
+
+ return False;
+}
+
+/* Process a complete received packet from a client */
+
+void winbind_process_packet(struct winbindd_cli_state *state)
+{
+ /* Process request */
+
+ /* Ensure null termination of entire request */
+ state->request.null_term = '\0';
+
+ state->pid = state->request.pid;
+
+ process_request(state);
+
+ /* Update client state */
+
+ state->read_buf_len = 0;
+ state->write_buf_len = sizeof(struct winbindd_response);
+
+ /* we might need to send it to the dual daemon */
+ if (opt_dual_daemon) {
+ dual_send_request(state);
+ }
+}
+
+/* Read some data from a client connection */
+
+void winbind_client_read(struct winbindd_cli_state *state)
+{
+ int n;
+
+ /* Read data */
+
+ n = sys_read(state->sock, state->read_buf_len +
+ (char *)&state->request,
+ sizeof(state->request) - state->read_buf_len);
+
+ DEBUG(10,("client_read: read %d bytes. Need %d more for a full request.\n", n, sizeof(state->request) - n - state->read_buf_len ));
+
+ /* Read failed, kill client */
+
+ if (n == -1 || n == 0) {
+ DEBUG(5,("read failed on sock %d, pid %d: %s\n",
+ state->sock, state->pid,
+ (n == -1) ? strerror(errno) : "EOF"));
+
+ state->finished = True;
+ return;
+ }
+
+ /* Update client state */
+
+ state->read_buf_len += n;
+ state->last_access = time(NULL);
+}
+
+/* Write some data to a client connection */
+
+static void client_write(struct winbindd_cli_state *state)
+{
+ char *data;
+ int num_written;
+
+ /* Write some data */
+
+ if (!state->write_extra_data) {
+
+ /* Write response structure */
+
+ data = (char *)&state->response + sizeof(state->response) -
+ state->write_buf_len;
+
+ } else {
+
+ /* Write extra data */
+
+ data = (char *)state->response.extra_data +
+ state->response.length -
+ sizeof(struct winbindd_response) -
+ state->write_buf_len;
+ }
+
+ num_written = sys_write(state->sock, data, state->write_buf_len);
+
+ DEBUG(10,("client_write: wrote %d bytes.\n", num_written ));
+
+ /* Write failed, kill cilent */
+
+ if (num_written == -1 || num_written == 0) {
+
+ DEBUG(3,("write failed on sock %d, pid %d: %s\n",
+ state->sock, state->pid,
+ (num_written == -1) ? strerror(errno) : "EOF"));
+
+ state->finished = True;
+
+ SAFE_FREE(state->response.extra_data);
+
+ return;
+ }
+
+ /* Update client state */
+
+ state->write_buf_len -= num_written;
+ state->last_access = time(NULL);
+
+ /* Have we written all data? */
+
+ if (state->write_buf_len == 0) {
+
+ /* Take care of extra data */
+
+ if (state->write_extra_data) {
+
+ SAFE_FREE(state->response.extra_data);
+
+ state->write_extra_data = False;
+
+ DEBUG(10,("client_write: client_write: complete response written.\n"));
+
+ } else if (state->response.length >
+ sizeof(struct winbindd_response)) {
+
+ /* Start writing extra data */
+
+ state->write_buf_len =
+ state->response.length -
+ sizeof(struct winbindd_response);
+
+ DEBUG(10,("client_write: need to write %d extra data bytes.\n", (int)state->write_buf_len));
+
+ state->write_extra_data = True;
+ }
+ }
+}
+
+/* Process incoming clients on listen_sock. We use a tricky non-blocking,
+ non-forking, non-threaded model which allows us to handle many
+ simultaneous connections while remaining impervious to many denial of
+ service attacks. */
+
+static void process_loop(void)
+{
+ /* We'll be doing this a lot */
+
+ while (1) {
+ struct winbindd_cli_state *state;
+ fd_set r_fds, w_fds;
+ int maxfd, listen_sock, selret;
+ struct timeval timeout;
+
+ /* Handle messages */
+
+ message_dispatch();
+
+ /* rescan the trusted domains list. This must be done
+ regularly to cope with transitive trusts */
+ rescan_trusted_domains(False);
+
+ /* Free up temporary memory */
+
+ lp_talloc_free();
+ main_loop_talloc_free();
+
+ /* Initialise fd lists for select() */
+
+ listen_sock = open_winbindd_socket();
+
+ if (listen_sock == -1) {
+ perror("open_winbind_socket");
+ exit(1);
+ }
+
+ maxfd = listen_sock;
+
+ FD_ZERO(&r_fds);
+ FD_ZERO(&w_fds);
+ FD_SET(listen_sock, &r_fds);
+
+ timeout.tv_sec = WINBINDD_ESTABLISH_LOOP;
+ timeout.tv_usec = 0;
+
+ if (opt_dual_daemon) {
+ maxfd = dual_select_setup(&w_fds, maxfd);
+ }
+
+ /* Set up client readers and writers */
+
+ state = winbindd_client_list();
+
+ while (state) {
+
+ /* Dispose of client connection if it is marked as
+ finished */
+
+ if (state->finished) {
+ struct winbindd_cli_state *next = state->next;
+
+ remove_client(state);
+ state = next;
+ continue;
+ }
+
+ /* Select requires we know the highest fd used */
+
+ if (state->sock > maxfd)
+ maxfd = state->sock;
+
+ /* Add fd for reading */
+
+ if (state->read_buf_len != sizeof(state->request))
+ FD_SET(state->sock, &r_fds);
+
+ /* Add fd for writing */
+
+ if (state->write_buf_len)
+ FD_SET(state->sock, &w_fds);
+
+ state = state->next;
+ }
+
+ /* Call select */
+
+ selret = sys_select(maxfd + 1, &r_fds, &w_fds, NULL, &timeout);
+
+ if (selret == 0)
+ continue;
+
+ if ((selret == -1 && errno != EINTR) || selret == 0) {
+
+ /* Select error, something is badly wrong */
+
+ perror("select");
+ exit(1);
+ }
+
+ /* Create a new connection if listen_sock readable */
+
+ if (selret > 0) {
+
+ if (opt_dual_daemon) {
+ dual_select(&w_fds);
+ }
+
+ if (FD_ISSET(listen_sock, &r_fds)) {
+ while (winbindd_num_clients() > WINBINDD_MAX_SIMULTANEOUS_CLIENTS - 1) {
+ DEBUG(5,("winbindd: Exceeding %d client connections, removing idle connection.\n",
+ WINBINDD_MAX_SIMULTANEOUS_CLIENTS));
+ if (!remove_idle_client()) {
+ DEBUG(0,("winbindd: Exceeding %d client connections, no idle connection found\n",
+ WINBINDD_MAX_SIMULTANEOUS_CLIENTS));
+ break;
+ }
+ }
+ new_connection(listen_sock);
+ }
+
+ /* Process activity on client connections */
+
+ for (state = winbindd_client_list(); state;
+ state = state->next) {
+
+ /* Data available for reading */
+
+ if (FD_ISSET(state->sock, &r_fds)) {
+
+ /* Read data */
+
+ winbind_client_read(state);
+
+ /*
+ * If we have the start of a
+ * packet, then check the
+ * length field to make sure
+ * the client's not talking
+ * Mock Swedish.
+ */
+
+ if (state->read_buf_len >= sizeof(uint32)
+ && *(uint32 *) &state->request != sizeof(state->request)) {
+ DEBUG(0,("process_loop: Invalid request size from pid %d: %d bytes sent, should be %d\n",
+ state->request.pid, *(uint32 *) &state->request, sizeof(state->request)));
+
+ remove_client(state);
+ break;
+ }
+
+ /* A request packet might be
+ complete */
+
+ if (state->read_buf_len ==
+ sizeof(state->request)) {
+ winbind_process_packet(state);
+ }
+ }
+
+ /* Data available for writing */
+
+ if (FD_ISSET(state->sock, &w_fds))
+ client_write(state);
+ }
+ }
+
+#if 0
+ winbindd_check_cache_size(time(NULL));
+#endif
+
+ /* Check signal handling things */
+
+ if (do_sigterm)
+ terminate();
+
+ if (do_sighup) {
+
+ DEBUG(3, ("got SIGHUP\n"));
+
+ /* Flush various caches */
+
+ flush_caches();
+ reload_services_file(True);
+ do_sighup = False;
+ }
+
+ if (do_sigusr2) {
+ print_winbindd_status();
+ do_sigusr2 = False;
+ }
+ }
+}
+
+
+/*
+ these are split out from the main winbindd for use by the background daemon
+ */
+BOOL winbind_setup_common(void)
+{
+ load_interfaces();
+
+ if (!secrets_init()) {
+
+ DEBUG(0,("Could not initialize domain trust account secrets. Giving up\n"));
+ return False;
+ }
+
+ namecache_enable(); /* Enable netbios namecache */
+
+ /* Check winbindd parameters are valid */
+
+ ZERO_STRUCT(server_state);
+
+ if (!winbindd_param_init())
+ return False;
+
+ /* Winbind daemon initialisation */
+
+ if (!winbindd_idmap_init())
+ return False;
+
+ /* Unblock all signals we are interested in as they may have been
+ blocked by the parent process. */
+
+ BlockSignals(False, SIGINT);
+ BlockSignals(False, SIGQUIT);
+ BlockSignals(False, SIGTERM);
+ BlockSignals(False, SIGUSR1);
+ BlockSignals(False, SIGUSR2);
+ BlockSignals(False, SIGHUP);
+
+ /* Setup signal handlers */
+
+ CatchSignal(SIGINT, termination_handler); /* Exit on these sigs */
+ CatchSignal(SIGQUIT, termination_handler);
+ CatchSignal(SIGTERM, termination_handler);
+
+ CatchSignal(SIGPIPE, SIG_IGN); /* Ignore sigpipe */
+
+ CatchSignal(SIGUSR2, sigusr2_handler); /* Debugging sigs */
+ CatchSignal(SIGHUP, sighup_handler);
+
+ return True;
+}
+
+
+/* Main function */
+
+struct winbindd_state server_state; /* Server state information */
+
+
+static void usage(void)
+{
+ printf("Usage: winbindd [options]\n");
+ printf("\t-F daemon in foreground mode\n");
+ printf("\t-S log to stdout\n");
+ printf("\t-i interactive mode\n");
+ printf("\t-B dual daemon mode\n");
+ printf("\t-n disable cacheing\n");
+ printf("\t-d level set debug level\n");
+ printf("\t-s configfile choose smb.conf location\n");
+ printf("\t-h show this help message\n");
+}
+
+ int main(int argc, char **argv)
+{
+ extern BOOL AllowDebugChange;
+ pstring logfile;
+ BOOL interactive = False;
+ BOOL Fork = True;
+ BOOL log_stdout = False;
+ int opt;
+
+ /* glibc (?) likes to print "User defined signal 1" and exit if a
+ SIGUSR[12] is received before a handler is installed */
+
+ CatchSignal(SIGUSR1, SIG_IGN);
+ CatchSignal(SIGUSR2, SIG_IGN);
+
+ fault_setup((void (*)(void *))fault_quit );
+
+ snprintf(logfile, sizeof(logfile), "%s/log.winbindd", dyn_LOGFILEBASE);
+ lp_set_logfile(logfile);
+
+ /* Initialise for running in non-root mode */
+
+ sec_init();
+
+ /* Set environment variable so we don't recursively call ourselves.
+ This may also be useful interactively. */
+
+ setenv(WINBINDD_DONT_ENV, "1", 1);
+
+ /* Initialise samba/rpc client stuff */
+
+ while ((opt = getopt(argc, argv, "FSid:s:nhB")) != EOF) {
+ switch (opt) {
+
+ case 'F':
+ Fork = False;
+ break;
+ case 'S':
+ log_stdout = True;
+ break;
+ /* Don't become a daemon */
+ case 'i':
+ interactive = True;
+ log_stdout = True;
+ Fork = False;
+ break;
+
+ /* dual daemon system */
+ case 'B':
+ opt_dual_daemon = True;
+ break;
+
+ /* disable cacheing */
+ case 'n':
+ opt_nocache = True;
+ break;
+
+ /* Run with specified debug level */
+ case 'd':
+ DEBUGLEVEL = atoi(optarg);
+ AllowDebugChange = False;
+ break;
+
+ /* Load a different smb.conf file */
+ case 's':
+ pstrcpy(dyn_CONFIGFILE,optarg);
+ break;
+
+ case 'h':
+ usage();
+ exit(0);
+
+ default:
+ printf("Unknown option %c\n", (char)opt);
+ exit(1);
+ }
+ }
+
+ if (log_stdout && Fork) {
+ printf("Can't log to stdout (-S) unless daemon is in foreground +(-F) or interactive (-i)\n");
+ usage();
+ exit(1);
+ }
+
+ snprintf(logfile, sizeof(logfile), "%s/log.winbindd", dyn_LOGFILEBASE);
+ lp_set_logfile(logfile);
+ setup_logging("winbindd", log_stdout);
+ reopen_logs();
+
+ DEBUG(1, ("winbindd version %s started.\n", VERSION ) );
+ DEBUGADD( 1, ( "Copyright The Samba Team 2000-2001\n" ) );
+
+ if (!reload_services_file(False)) {
+ DEBUG(0, ("error opening config file\n"));
+ exit(1);
+ }
+
+ /* Setup names. */
+
+ if (!init_names())
+ exit(1);
+
+ if (!interactive) {
+ become_daemon(Fork);
+ pidfile_create("winbindd");
+ }
+
+
+#if HAVE_SETPGID
+ /*
+ * If we're interactive we want to set our own process group for
+ * signal management.
+ */
+ if (interactive)
+ setpgid( (pid_t)0, (pid_t)0);
+#endif
+
+ if (!winbind_setup_common()) {
+ return 1;
+ }
+
+ if (opt_dual_daemon) {
+ do_dual_daemon();
+ }
+
+ /* Initialise messaging system */
+
+ if (!message_init()) {
+ DEBUG(0, ("unable to initialise messaging system\n"));
+ exit(1);
+ }
+
+ register_msg_pool_usage();
+ message_register(MSG_REQ_TALLOC_USAGE, return_all_talloc_info);
+
+ /* Loop waiting for requests */
+
+ process_loop();
+
+ trustdom_cache_shutdown();
+ uni_group_cache_shutdown();
+ return 0;
+}
diff --git a/source4/nsswitch/winbindd.h b/source4/nsswitch/winbindd.h
new file mode 100644
index 0000000000..42ef209faf
--- /dev/null
+++ b/source4/nsswitch/winbindd.h
@@ -0,0 +1,230 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Anthony Liguori 2003
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _WINBINDD_H
+#define _WINBINDD_H
+
+#include "includes.h"
+#include "nterr.h"
+
+#include "winbindd_nss.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/* Client state structure */
+
+struct winbindd_cli_state {
+ struct winbindd_cli_state *prev, *next; /* Linked list pointers */
+ int sock; /* Open socket from client */
+ pid_t pid; /* pid of client */
+ int read_buf_len, write_buf_len; /* Indexes in request/response */
+ BOOL finished; /* Can delete from list */
+ BOOL write_extra_data; /* Write extra_data field */
+ time_t last_access; /* Time of last access (read or write) */
+ struct winbindd_request request; /* Request from client */
+ struct winbindd_response response; /* Respose to client */
+ struct getent_state *getpwent_state; /* State for getpwent() */
+ struct getent_state *getgrent_state; /* State for getgrent() */
+};
+
+/* State between get{pw,gr}ent() calls */
+
+struct getent_state {
+ struct getent_state *prev, *next;
+ void *sam_entries;
+ uint32 sam_entry_index, num_sam_entries;
+ BOOL got_sam_entries;
+ fstring domain_name;
+};
+
+/* Storage for cached getpwent() user entries */
+
+struct getpwent_user {
+ fstring name; /* Account name */
+ fstring gecos; /* User information */
+ DOM_SID user_sid; /* NT user and primary group SIDs */
+ DOM_SID group_sid;
+};
+
+/* Server state structure */
+
+struct winbindd_state {
+
+ /* User and group id pool */
+
+ uid_t uid_low, uid_high; /* Range of uids to allocate */
+ gid_t gid_low, gid_high; /* Range of gids to allocate */
+};
+
+extern struct winbindd_state server_state; /* Server information */
+
+typedef struct {
+ char *acct_name;
+ char *full_name;
+ DOM_SID *user_sid; /* NT user and primary group SIDs */
+ DOM_SID *group_sid;
+} WINBIND_USERINFO;
+
+/* Structures to hold per domain information */
+
+struct winbindd_domain {
+ fstring name; /* Domain name */
+ fstring alt_name; /* alt Domain name (if any) */
+ DOM_SID sid; /* SID for this domain */
+ BOOL native_mode; /* is this a win2k domain in native mode ? */
+
+ /* Lookup methods for this domain (LDAP or RPC) */
+
+ struct winbindd_methods *methods;
+
+ /* Private data for the backends (used for connection cache) */
+
+ void *private;
+
+ /* Sequence number stuff */
+
+ time_t last_seq_check;
+ uint32 sequence_number;
+
+ /* Linked list info */
+
+ struct winbindd_domain *prev, *next;
+};
+
+/* per-domain methods. This is how LDAP vs RPC is selected
+ */
+struct winbindd_methods {
+ /* does this backend provide a consistent view of the data? (ie. is the primary group
+ always correct) */
+ BOOL consistent;
+
+ /* get a list of users, returning a WINBIND_USERINFO for each one */
+ NTSTATUS (*query_user_list)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ WINBIND_USERINFO **info);
+
+ /* get a list of domain groups */
+ NTSTATUS (*enum_dom_groups)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info);
+
+ /* get a list of domain local groups */
+ NTSTATUS (*enum_local_groups)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info);
+
+ /* convert one user or group name to a sid */
+ NTSTATUS (*name_to_sid)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *name,
+ DOM_SID *sid,
+ enum SID_NAME_USE *type);
+
+ /* convert a sid to a user or group name */
+ NTSTATUS (*sid_to_name)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ DOM_SID *sid,
+ char **name,
+ enum SID_NAME_USE *type);
+
+ /* lookup user info for a given SID */
+ NTSTATUS (*query_user)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ DOM_SID *user_sid,
+ WINBIND_USERINFO *user_info);
+
+ /* lookup all groups that a user is a member of. The backend
+ can also choose to lookup by username or rid for this
+ function */
+ NTSTATUS (*lookup_usergroups)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ DOM_SID *user_sid,
+ uint32 *num_groups, DOM_SID ***user_gids);
+
+ /* find all members of the group with the specified group_rid */
+ NTSTATUS (*lookup_groupmem)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ DOM_SID *group_sid,
+ uint32 *num_names,
+ DOM_SID ***sid_mem, char ***names,
+ uint32 **name_types);
+
+ /* return the current global sequence number */
+ NTSTATUS (*sequence_number)(struct winbindd_domain *domain, uint32 *seq);
+
+ /* enumerate trusted domains */
+ NTSTATUS (*trusted_domains)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_domains,
+ char ***names,
+ char ***alt_names,
+ DOM_SID **dom_sids);
+
+ /* find the domain sid */
+ NTSTATUS (*domain_sid)(struct winbindd_domain *domain,
+ DOM_SID *sid);
+
+ /* setup the list of alternate names for the domain, if any */
+ NTSTATUS (*alternate_name)(struct winbindd_domain *domain);
+};
+
+/* Used to glue a policy handle and cli_state together */
+
+typedef struct {
+ struct cli_state *cli;
+ POLICY_HND pol;
+} CLI_POLICY_HND;
+
+/* Filled out by IDMAP backends */
+struct idmap_methods {
+ /* Called when backend is first loaded */
+ BOOL (*init)(void);
+
+ BOOL (*get_sid_from_uid)(uid_t uid, DOM_SID *sid);
+ BOOL (*get_sid_from_gid)(gid_t gid, DOM_SID *sid);
+
+ BOOL (*get_uid_from_sid)(DOM_SID *sid, uid_t *uid);
+ BOOL (*get_gid_from_sid)(DOM_SID *sid, gid_t *gid);
+
+ /* Called when backend is unloaded */
+ BOOL (*close)(void);
+ /* Called to dump backend status */
+ void (*status)(void);
+};
+
+#include "winbindd_proto.h"
+
+#include "rpc_parse.h"
+#include "rpc_client.h"
+
+#define WINBINDD_ESTABLISH_LOOP 30
+#define WINBINDD_RESCAN_FREQ 300
+
+#define DOM_SEQUENCE_NONE ((uint32)-1)
+
+#endif /* _WINBINDD_H */
diff --git a/source4/nsswitch/winbindd_ads.c b/source4/nsswitch/winbindd_ads.c
new file mode 100644
index 0000000000..de3757aa44
--- /dev/null
+++ b/source4/nsswitch/winbindd_ads.c
@@ -0,0 +1,837 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind ADS backend functions
+
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "winbindd.h"
+
+#ifdef HAVE_ADS
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/* the realm of our primary LDAP server */
+static char *primary_realm;
+
+
+/*
+ return our ads connections structure for a domain. We keep the connection
+ open to make things faster
+*/
+static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS status;
+
+ if (domain->private) {
+ return (ADS_STRUCT *)domain->private;
+ }
+
+ /* we don't want this to affect the users ccache */
+ setenv("KRB5CCNAME", "MEMORY:winbind_ccache", 1);
+
+ ads = ads_init(domain->alt_name, domain->name, NULL);
+ if (!ads) {
+ DEBUG(1,("ads_init for domain %s failed\n", domain->name));
+ return NULL;
+ }
+
+ /* the machine acct password might have change - fetch it every time */
+ SAFE_FREE(ads->auth.password);
+ ads->auth.password = secrets_fetch_machine_password();
+
+ if (primary_realm) {
+ SAFE_FREE(ads->auth.realm);
+ ads->auth.realm = strdup(primary_realm);
+ }
+
+ status = ads_connect(ads);
+ if (!ADS_ERR_OK(status) || !ads->config.realm) {
+ extern struct winbindd_methods msrpc_methods;
+ DEBUG(1,("ads_connect for domain %s failed: %s\n",
+ domain->name, ads_errstr(status)));
+ ads_destroy(&ads);
+
+ /* if we get ECONNREFUSED then it might be a NT4
+ server, fall back to MSRPC */
+ if (status.error_type == ADS_ERROR_SYSTEM &&
+ status.err.rc == ECONNREFUSED) {
+ DEBUG(1,("Trying MSRPC methods\n"));
+ domain->methods = &msrpc_methods;
+ }
+ return NULL;
+ }
+
+ /* remember our primary realm for trusted domain support */
+ if (!primary_realm) {
+ primary_realm = strdup(ads->config.realm);
+ }
+
+ domain->private = (void *)ads;
+ return ads;
+}
+
+
+/* Query display info for a realm. This is the basic user list fn */
+static NTSTATUS query_user_list(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ WINBIND_USERINFO **info)
+{
+ ADS_STRUCT *ads = NULL;
+ const char *attrs[] = {"userPrincipalName",
+ "sAMAccountName",
+ "name", "objectSid", "primaryGroupID",
+ "sAMAccountType", NULL};
+ int i, count;
+ ADS_STATUS rc;
+ void *res = NULL;
+ void *msg = NULL;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+
+ *num_entries = 0;
+
+ DEBUG(3,("ads: query_user_list\n"));
+
+ ads = ads_cached_connection(domain);
+ if (!ads) goto done;
+
+ rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs);
+ if (!ADS_ERR_OK(rc)) {
+ DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
+ goto done;
+ }
+
+ count = ads_count_replies(ads, res);
+ if (count == 0) {
+ DEBUG(1,("query_user_list: No users found\n"));
+ goto done;
+ }
+
+ (*info) = talloc_zero(mem_ctx, count * sizeof(**info));
+ if (!*info) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ i = 0;
+
+ for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
+ char *name, *gecos;
+ DOM_SID sid;
+ DOM_SID *sid2;
+ DOM_SID *group_sid;
+ uint32 group;
+ uint32 atype;
+
+ if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) ||
+ ads_atype_map(atype) != SID_NAME_USER) {
+ DEBUG(1,("Not a user account? atype=0x%x\n", atype));
+ continue;
+ }
+
+ name = ads_pull_username(ads, mem_ctx, msg);
+ gecos = ads_pull_string(ads, mem_ctx, msg, "name");
+ if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
+ DEBUG(1,("No sid for %s !?\n", name));
+ continue;
+ }
+ if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
+ DEBUG(1,("No primary group for %s !?\n", name));
+ continue;
+ }
+
+ sid2 = talloc(mem_ctx, sizeof(*sid2));
+ if (!sid2) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ sid_copy(sid2, &sid);
+
+ group_sid = rid_to_talloced_sid(domain, mem_ctx, group);
+
+ (*info)[i].acct_name = name;
+ (*info)[i].full_name = gecos;
+ (*info)[i].user_sid = sid2;
+ (*info)[i].group_sid = group_sid;
+ i++;
+ }
+
+ (*num_entries) = i;
+ status = NT_STATUS_OK;
+
+ DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries)));
+
+done:
+ if (res) ads_msgfree(ads, res);
+
+ return status;
+}
+
+/* list all domain groups */
+static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info)
+{
+ ADS_STRUCT *ads = NULL;
+ const char *attrs[] = {"userPrincipalName", "sAMAccountName",
+ "name", "objectSid",
+ "sAMAccountType", NULL};
+ int i, count;
+ ADS_STATUS rc;
+ void *res = NULL;
+ void *msg = NULL;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ uint32 group_flags;
+
+ *num_entries = 0;
+
+ DEBUG(3,("ads: enum_dom_groups\n"));
+
+ ads = ads_cached_connection(domain);
+ if (!ads) goto done;
+
+ rc = ads_search_retry(ads, &res, "(objectCategory=group)", attrs);
+ if (!ADS_ERR_OK(rc)) {
+ DEBUG(1,("enum_dom_groups ads_search: %s\n", ads_errstr(rc)));
+ goto done;
+ }
+
+ count = ads_count_replies(ads, res);
+ if (count == 0) {
+ DEBUG(1,("enum_dom_groups: No groups found\n"));
+ goto done;
+ }
+
+ (*info) = talloc_zero(mem_ctx, count * sizeof(**info));
+ if (!*info) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ i = 0;
+
+ group_flags = ATYPE_GLOBAL_GROUP;
+ if ( domain->native_mode )
+ group_flags |= ATYPE_LOCAL_GROUP;
+
+ for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
+ char *name, *gecos;
+ DOM_SID sid;
+ uint32 rid;
+ uint32 account_type;
+
+ if (!ads_pull_uint32(ads, msg, "sAMAccountType", &account_type) || !(account_type & group_flags) )
+ continue;
+
+ name = ads_pull_username(ads, mem_ctx, msg);
+ gecos = ads_pull_string(ads, mem_ctx, msg, "name");
+ if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
+ DEBUG(1,("No sid for %s !?\n", name));
+ continue;
+ }
+
+ if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
+ DEBUG(1,("No rid for %s !?\n", name));
+ continue;
+ }
+
+ fstrcpy((*info)[i].acct_name, name);
+ fstrcpy((*info)[i].acct_desc, gecos);
+ (*info)[i].rid = rid;
+ i++;
+ }
+
+ (*num_entries) = i;
+
+ status = NT_STATUS_OK;
+
+ DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries)));
+
+done:
+ if (res) ads_msgfree(ads, res);
+
+ return status;
+}
+
+/* list all domain local groups */
+static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info)
+{
+ /*
+ * This is a stub function only as we returned the domain
+ * ocal groups in enum_dom_groups() if the domain->native field
+ * was true. This is a simple performance optimization when
+ * using LDAP.
+ *
+ * if we ever need to enumerate domain local groups separately,
+ * then this the optimization in enum_dom_groups() will need
+ * to be split out
+ */
+ *num_entries = 0;
+
+ return NT_STATUS_OK;
+}
+
+/* convert a single name to a sid in a domain */
+static NTSTATUS name_to_sid(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *name,
+ DOM_SID *sid,
+ enum SID_NAME_USE *type)
+{
+ ADS_STRUCT *ads;
+
+ DEBUG(3,("ads: name_to_sid\n"));
+
+ ads = ads_cached_connection(domain);
+ if (!ads)
+ return NT_STATUS_UNSUCCESSFUL;
+
+ return ads_name_to_sid(ads, name, sid, type);
+}
+
+/* convert a sid to a user or group name */
+static NTSTATUS sid_to_name(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ DOM_SID *sid,
+ char **name,
+ enum SID_NAME_USE *type)
+{
+ ADS_STRUCT *ads = NULL;
+ DEBUG(3,("ads: sid_to_name\n"));
+ ads = ads_cached_connection(domain);
+ if (!ads)
+ return NT_STATUS_UNSUCCESSFUL;
+
+ return ads_sid_to_name(ads, mem_ctx, sid, name, type);
+}
+
+
+/* convert a DN to a name, SID and name type
+ this might become a major speed bottleneck if groups have
+ lots of users, in which case we could cache the results
+*/
+static BOOL dn_lookup(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
+ const char *dn,
+ char **name, uint32 *name_type, DOM_SID *sid)
+{
+ char *exp;
+ void *res = NULL;
+ const char *attrs[] = {"userPrincipalName", "sAMAccountName",
+ "objectSid", "sAMAccountType", NULL};
+ ADS_STATUS rc;
+ uint32 atype;
+ char *escaped_dn = escape_ldap_string_alloc(dn);
+
+ if (!escaped_dn) {
+ return False;
+ }
+
+ asprintf(&exp, "(distinguishedName=%s)", dn);
+ rc = ads_search_retry(ads, &res, exp, attrs);
+ SAFE_FREE(exp);
+ SAFE_FREE(escaped_dn);
+
+ if (!ADS_ERR_OK(rc)) {
+ goto failed;
+ }
+
+ (*name) = ads_pull_username(ads, mem_ctx, res);
+
+ if (!ads_pull_uint32(ads, res, "sAMAccountType", &atype)) {
+ goto failed;
+ }
+ (*name_type) = ads_atype_map(atype);
+
+ if (!ads_pull_sid(ads, res, "objectSid", sid)) {
+ goto failed;
+ }
+
+ if (res) ads_msgfree(ads, res);
+ return True;
+
+failed:
+ if (res) ads_msgfree(ads, res);
+ return False;
+}
+
+/* Lookup user information from a rid */
+static NTSTATUS query_user(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ DOM_SID *sid,
+ WINBIND_USERINFO *info)
+{
+ ADS_STRUCT *ads = NULL;
+ const char *attrs[] = {"userPrincipalName",
+ "sAMAccountName",
+ "name",
+ "primaryGroupID", NULL};
+ ADS_STATUS rc;
+ int count;
+ void *msg = NULL;
+ char *exp;
+ char *sidstr;
+ uint32 group_rid;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ DOM_SID *sid2;
+ fstring sid_string;
+
+ DEBUG(3,("ads: query_user\n"));
+
+ ads = ads_cached_connection(domain);
+ if (!ads) goto done;
+
+ sidstr = sid_binstring(sid);
+ asprintf(&exp, "(objectSid=%s)", sidstr);
+ rc = ads_search_retry(ads, &msg, exp, attrs);
+ free(exp);
+ free(sidstr);
+ if (!ADS_ERR_OK(rc)) {
+ DEBUG(1,("query_user(sid=%s) ads_search: %s\n", sid_to_string(sid_string, sid), ads_errstr(rc)));
+ goto done;
+ }
+
+ count = ads_count_replies(ads, msg);
+ if (count != 1) {
+ DEBUG(1,("query_user(sid=%s): Not found\n", sid_to_string(sid_string, sid)));
+ goto done;
+ }
+
+ info->acct_name = ads_pull_username(ads, mem_ctx, msg);
+ info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
+
+ if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group_rid)) {
+ DEBUG(1,("No primary group for %s !?\n", sid_to_string(sid_string, sid)));
+ goto done;
+ }
+
+ sid2 = talloc(mem_ctx, sizeof(*sid2));
+ if (!sid2) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ sid_copy(sid2, sid);
+
+ info->user_sid = sid2;
+
+ info->group_sid = rid_to_talloced_sid(domain, mem_ctx, group_rid);
+
+ status = NT_STATUS_OK;
+
+ DEBUG(3,("ads query_user gave %s\n", info->acct_name));
+done:
+ if (msg) ads_msgfree(ads, msg);
+
+ return status;
+}
+
+/* Lookup groups a user is a member of - alternate method, for when
+ tokenGroups are not available. */
+static NTSTATUS lookup_usergroups_alt(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *user_dn,
+ DOM_SID *primary_group,
+ uint32 *num_groups, DOM_SID ***user_gids)
+{
+ ADS_STATUS rc;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ int count;
+ void *res = NULL;
+ void *msg = NULL;
+ char *exp;
+ ADS_STRUCT *ads;
+ const char *group_attrs[] = {"objectSid", NULL};
+
+ ads = ads_cached_connection(domain);
+ if (!ads) goto done;
+
+ /* buggy server, no tokenGroups. Instead lookup what groups this user
+ is a member of by DN search on member*/
+ if (asprintf(&exp, "(&(member=%s)(objectClass=group))", user_dn) == -1) {
+ DEBUG(1,("lookup_usergroups(dn=%s) asprintf failed!\n", user_dn));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rc = ads_search_retry(ads, &res, exp, group_attrs);
+ free(exp);
+
+ if (!ADS_ERR_OK(rc)) {
+ DEBUG(1,("lookup_usergroups ads_search member=%s: %s\n", user_dn, ads_errstr(rc)));
+ return ads_ntstatus(rc);
+ }
+
+ count = ads_count_replies(ads, res);
+ if (count == 0) {
+ DEBUG(5,("lookup_usergroups: No supp groups found\n"));
+
+ status = ads_ntstatus(rc);
+ goto done;
+ }
+
+ (*user_gids) = talloc_zero(mem_ctx, sizeof(**user_gids) * (count + 1));
+ (*user_gids)[0] = primary_group;
+
+ *num_groups = 1;
+
+ for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
+ DOM_SID group_sid;
+
+ if (!ads_pull_sid(ads, msg, "objectSid", &group_sid)) {
+ DEBUG(1,("No sid for this group ?!?\n"));
+ continue;
+ }
+
+ if (sid_equal(&group_sid, primary_group)) continue;
+
+ (*user_gids)[*num_groups] = talloc(mem_ctx, sizeof(***user_gids));
+ if (!(*user_gids)[*num_groups]) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ sid_copy((*user_gids)[*num_groups], &group_sid);
+
+ (*num_groups)++;
+
+ }
+
+ status = NT_STATUS_OK;
+
+ DEBUG(3,("ads lookup_usergroups (alt) for dn=%s\n", user_dn));
+done:
+ if (res) ads_msgfree(ads, res);
+ if (msg) ads_msgfree(ads, msg);
+
+ return status;
+}
+
+/* Lookup groups a user is a member of. */
+static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ DOM_SID *sid,
+ uint32 *num_groups, DOM_SID ***user_gids)
+{
+ ADS_STRUCT *ads = NULL;
+ const char *attrs[] = {"distinguishedName", NULL};
+ const char *attrs2[] = {"tokenGroups", "primaryGroupID", NULL};
+ ADS_STATUS rc;
+ int count;
+ void *msg = NULL;
+ char *exp;
+ char *user_dn;
+ DOM_SID *sids;
+ int i;
+ DOM_SID *primary_group;
+ uint32 primary_group_rid;
+ char *sidstr;
+ fstring sid_string;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(3,("ads: lookup_usergroups\n"));
+ *num_groups = 0;
+
+ ads = ads_cached_connection(domain);
+ if (!ads) goto done;
+
+ if (!(sidstr = sid_binstring(sid))) {
+ DEBUG(1,("lookup_usergroups(sid=%s) sid_binstring returned NULL\n", sid_to_string(sid_string, sid)));
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ if (asprintf(&exp, "(objectSid=%s)", sidstr) == -1) {
+ free(sidstr);
+ DEBUG(1,("lookup_usergroups(sid=%s) asprintf failed!\n", sid_to_string(sid_string, sid)));
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ rc = ads_search_retry(ads, &msg, exp, attrs);
+ free(exp);
+ free(sidstr);
+
+ if (!ADS_ERR_OK(rc)) {
+ DEBUG(1,("lookup_usergroups(sid=%s) ads_search: %s\n", sid_to_string(sid_string, sid), ads_errstr(rc)));
+ goto done;
+ }
+
+ user_dn = ads_pull_string(ads, mem_ctx, msg, "distinguishedName");
+ if (!user_dn) {
+ DEBUG(1,("lookup_usergroups(sid=%s) ads_search did not return a a distinguishedName!\n", sid_to_string(sid_string, sid)));
+ if (msg) ads_msgfree(ads, msg);
+ goto done;
+ }
+
+ if (msg) ads_msgfree(ads, msg);
+
+ rc = ads_search_retry_dn(ads, &msg, user_dn, attrs2);
+ if (!ADS_ERR_OK(rc)) {
+ DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: %s\n", sid_to_string(sid_string, sid), ads_errstr(rc)));
+ goto done;
+ }
+
+ if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group_rid)) {
+ DEBUG(1,("%s: No primary group for sid=%s !?\n", domain->name, sid_to_string(sid_string, sid)));
+ goto done;
+ }
+
+ primary_group = rid_to_talloced_sid(domain, mem_ctx, primary_group_rid);
+
+ count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids);
+
+ if (msg) ads_msgfree(ads, msg);
+
+ /* there must always be at least one group in the token,
+ unless we are talking to a buggy Win2k server */
+ if (count == 0) {
+ return lookup_usergroups_alt(domain, mem_ctx, user_dn,
+ primary_group,
+ num_groups, user_gids);
+ }
+
+ (*user_gids) = talloc_zero(mem_ctx, sizeof(**user_gids) * (count + 1));
+ (*user_gids)[0] = primary_group;
+
+ *num_groups = 1;
+
+ for (i=0;i<count;i++) {
+ if (sid_equal(&sids[i], primary_group)) continue;
+
+ (*user_gids)[*num_groups] = talloc(mem_ctx, sizeof(***user_gids));
+ if (!(*user_gids)[*num_groups]) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ sid_copy((*user_gids)[*num_groups], &sids[i]);
+ (*num_groups)++;
+ }
+
+ status = NT_STATUS_OK;
+ DEBUG(3,("ads lookup_usergroups for sid=%s\n", sid_to_string(sid_string, sid)));
+done:
+ return status;
+}
+
+/*
+ find the members of a group, given a group rid and domain
+ */
+static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ DOM_SID *group_sid, uint32 *num_names,
+ DOM_SID ***sid_mem, char ***names,
+ uint32 **name_types)
+{
+ ADS_STATUS rc;
+ int count;
+ void *res=NULL;
+ ADS_STRUCT *ads = NULL;
+ char *exp;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ char *sidstr;
+ const char *attrs[] = {"member", NULL};
+ char **members;
+ int i, num_members;
+ fstring sid_string;
+
+ *num_names = 0;
+
+ ads = ads_cached_connection(domain);
+ if (!ads) goto done;
+
+ sidstr = sid_binstring(group_sid);
+
+ /* search for all members of the group */
+ asprintf(&exp, "(objectSid=%s)",sidstr);
+ rc = ads_search_retry(ads, &res, exp, attrs);
+ free(exp);
+ free(sidstr);
+
+ if (!ADS_ERR_OK(rc)) {
+ DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
+ goto done;
+ }
+
+ count = ads_count_replies(ads, res);
+ if (count == 0) {
+ status = NT_STATUS_OK;
+ goto done;
+ }
+
+ members = ads_pull_strings(ads, mem_ctx, res, "member");
+ if (!members) {
+ /* no members? ok ... */
+ status = NT_STATUS_OK;
+ goto done;
+ }
+
+ /* now we need to turn a list of members into rids, names and name types
+ the problem is that the members are in the form of distinguised names
+ */
+ for (i=0;members[i];i++) /* noop */ ;
+ num_members = i;
+
+ (*sid_mem) = talloc_zero(mem_ctx, sizeof(**sid_mem) * num_members);
+ (*name_types) = talloc_zero(mem_ctx, sizeof(**name_types) * num_members);
+ (*names) = talloc_zero(mem_ctx, sizeof(**names) * num_members);
+
+ for (i=0;i<num_members;i++) {
+ uint32 name_type;
+ char *name;
+ DOM_SID sid;
+
+ if (dn_lookup(ads, mem_ctx, members[i], &name, &name_type, &sid)) {
+ (*names)[*num_names] = name;
+ (*name_types)[*num_names] = name_type;
+ (*sid_mem)[*num_names] = talloc(mem_ctx, sizeof(***sid_mem));
+ if (!(*sid_mem)[*num_names]) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ sid_copy((*sid_mem)[*num_names], &sid);
+ (*num_names)++;
+ }
+ }
+
+ status = NT_STATUS_OK;
+ DEBUG(3,("ads lookup_groupmem for sid=%s\n", sid_to_string(sid_string, group_sid)));
+done:
+ if (res) ads_msgfree(ads, res);
+
+ return status;
+}
+
+
+/* find the sequence number for a domain */
+static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
+{
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS rc;
+
+ *seq = DOM_SEQUENCE_NONE;
+
+ ads = ads_cached_connection(domain);
+ if (!ads) return NT_STATUS_UNSUCCESSFUL;
+
+ rc = ads_USN(ads, seq);
+ if (!ADS_ERR_OK(rc)) {
+ /* its a dead connection */
+ ads_destroy(&ads);
+ domain->private = NULL;
+ }
+ return ads_ntstatus(rc);
+}
+
+/* get a list of trusted domains */
+static NTSTATUS trusted_domains(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_domains,
+ char ***names,
+ char ***alt_names,
+ DOM_SID **dom_sids)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+
+ *num_domains = 0;
+ *names = NULL;
+
+ ads = ads_cached_connection(domain);
+ if (!ads) return NT_STATUS_UNSUCCESSFUL;
+
+ rc = ads_trusted_domains(ads, mem_ctx, num_domains, names, alt_names, dom_sids);
+
+ return ads_ntstatus(rc);
+}
+
+/* find the domain sid for a domain */
+static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+
+ ads = ads_cached_connection(domain);
+ if (!ads) return NT_STATUS_UNSUCCESSFUL;
+
+ rc = ads_domain_sid(ads, sid);
+
+ if (!ADS_ERR_OK(rc)) {
+ /* its a dead connection */
+ ads_destroy(&ads);
+ domain->private = NULL;
+ }
+
+ return ads_ntstatus(rc);
+}
+
+
+/* find alternate names list for the domain - for ADS this is the
+ netbios name */
+static NTSTATUS alternate_name(struct winbindd_domain *domain)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ TALLOC_CTX *ctx;
+ char *workgroup;
+
+ ads = ads_cached_connection(domain);
+ if (!ads) return NT_STATUS_UNSUCCESSFUL;
+
+ if (!(ctx = talloc_init("alternate_name"))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rc = ads_workgroup_name(ads, ctx, &workgroup);
+
+ if (ADS_ERR_OK(rc)) {
+ fstrcpy(domain->name, workgroup);
+ fstrcpy(domain->alt_name, ads->config.realm);
+ strupper(domain->alt_name);
+ strupper(domain->name);
+ }
+
+ talloc_destroy(ctx);
+
+ return ads_ntstatus(rc);
+}
+
+/* the ADS backend methods are exposed via this structure */
+struct winbindd_methods ads_methods = {
+ True,
+ query_user_list,
+ enum_dom_groups,
+ enum_local_groups,
+ name_to_sid,
+ sid_to_name,
+ query_user,
+ lookup_usergroups,
+ lookup_groupmem,
+ sequence_number,
+ trusted_domains,
+ domain_sid,
+ alternate_name
+};
+
+#endif
diff --git a/source4/nsswitch/winbindd_cache.c b/source4/nsswitch/winbindd_cache.c
new file mode 100644
index 0000000000..5fb59e7467
--- /dev/null
+++ b/source4/nsswitch/winbindd_cache.c
@@ -0,0 +1,1016 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind cache backend functions
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+struct winbind_cache {
+ struct winbindd_methods *backend;
+ TDB_CONTEXT *tdb;
+};
+
+struct cache_entry {
+ NTSTATUS status;
+ uint32 sequence_number;
+ uint8 *data;
+ uint32 len, ofs;
+};
+
+#define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
+
+static struct winbind_cache *wcache;
+
+/* flush the cache */
+void wcache_flush_cache(void)
+{
+ extern BOOL opt_nocache;
+
+ if (!wcache) return;
+ if (wcache->tdb) {
+ tdb_close(wcache->tdb);
+ wcache->tdb = NULL;
+ }
+ if (opt_nocache) return;
+
+ wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 5000,
+ TDB_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 0600);
+
+ if (!wcache->tdb) {
+ DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
+ }
+}
+
+void winbindd_check_cache_size(time_t t)
+{
+ static time_t last_check_time;
+ struct stat st;
+
+ if (last_check_time == (time_t)0)
+ last_check_time = t;
+
+ if (t - last_check_time < 60 && t - last_check_time > 0)
+ return;
+
+ if (wcache == NULL || wcache->tdb == NULL) {
+ DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
+ return;
+ }
+
+ if (fstat(wcache->tdb->fd, &st) == -1) {
+ DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
+ return;
+ }
+
+ if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
+ DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
+ (unsigned long)st.st_size,
+ (unsigned long)WINBINDD_MAX_CACHE_SIZE));
+ wcache_flush_cache();
+ }
+}
+
+/* get the winbind_cache structure */
+static struct winbind_cache *get_cache(struct winbindd_domain *domain)
+{
+ extern struct winbindd_methods msrpc_methods;
+ struct winbind_cache *ret = wcache;
+
+ if (ret) return ret;
+
+ ret = smb_xmalloc(sizeof(*ret));
+ ZERO_STRUCTP(ret);
+ switch (lp_security()) {
+#ifdef HAVE_ADS
+ case SEC_ADS: {
+ extern struct winbindd_methods ads_methods;
+ ret->backend = &ads_methods;
+ break;
+ }
+#endif
+ default:
+ ret->backend = &msrpc_methods;
+ }
+
+ wcache = ret;
+ wcache_flush_cache();
+
+ return ret;
+}
+
+/*
+ free a centry structure
+*/
+static void centry_free(struct cache_entry *centry)
+{
+ if (!centry) return;
+ SAFE_FREE(centry->data);
+ free(centry);
+}
+
+
+/*
+ pull a uint32 from a cache entry
+*/
+static uint32 centry_uint32(struct cache_entry *centry)
+{
+ uint32 ret;
+ if (centry->len - centry->ofs < 4) {
+ DEBUG(0,("centry corruption? needed 4 bytes, have %d\n",
+ centry->len - centry->ofs));
+ smb_panic("centry_uint32");
+ }
+ ret = IVAL(centry->data, centry->ofs);
+ centry->ofs += 4;
+ return ret;
+}
+
+/*
+ pull a uint8 from a cache entry
+*/
+static uint8 centry_uint8(struct cache_entry *centry)
+{
+ uint8 ret;
+ if (centry->len - centry->ofs < 1) {
+ DEBUG(0,("centry corruption? needed 1 bytes, have %d\n",
+ centry->len - centry->ofs));
+ smb_panic("centry_uint32");
+ }
+ ret = CVAL(centry->data, centry->ofs);
+ centry->ofs += 1;
+ return ret;
+}
+
+/* pull a string from a cache entry, using the supplied
+ talloc context
+*/
+static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
+{
+ uint32 len;
+ char *ret;
+
+ len = centry_uint8(centry);
+
+ if (len == 0xFF) {
+ /* a deliberate NULL string */
+ return NULL;
+ }
+
+ if (centry->len - centry->ofs < len) {
+ DEBUG(0,("centry corruption? needed %d bytes, have %d\n",
+ len, centry->len - centry->ofs));
+ smb_panic("centry_string");
+ }
+
+ ret = talloc(mem_ctx, len+1);
+ if (!ret) {
+ smb_panic("centry_string out of memory\n");
+ }
+ memcpy(ret,centry->data + centry->ofs, len);
+ ret[len] = 0;
+ centry->ofs += len;
+ return ret;
+}
+
+/* pull a string from a cache entry, using the supplied
+ talloc context
+*/
+static DOM_SID *centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
+{
+ DOM_SID *sid;
+ char *sid_string;
+ sid = talloc(mem_ctx, sizeof(*sid));
+ if (!sid) return NULL;
+
+ sid_string = centry_string(centry, mem_ctx);
+ if (!string_to_sid(sid, sid_string)) {
+ return NULL;
+ }
+ return sid;
+}
+
+/* the server is considered down if it can't give us a sequence number */
+static BOOL wcache_server_down(struct winbindd_domain *domain)
+{
+ if (!wcache->tdb) return False;
+ return (domain->sequence_number == DOM_SEQUENCE_NONE);
+}
+
+
+/*
+ refresh the domain sequence number. If force is True
+ then always refresh it, no matter how recently we fetched it
+*/
+static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
+{
+ NTSTATUS status;
+ unsigned time_diff;
+ unsigned cache_time = lp_winbind_cache_time();
+
+ /* trying to reconnect is expensive, don't do it too often */
+ if (domain->sequence_number == DOM_SEQUENCE_NONE) {
+ cache_time *= 8;
+ }
+
+ time_diff = time(NULL) - domain->last_seq_check;
+
+ /* see if we have to refetch the domain sequence number */
+ if (!force && (time_diff < cache_time)) {
+ return;
+ }
+
+ status = wcache->backend->sequence_number(domain, &domain->sequence_number);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ domain->sequence_number = DOM_SEQUENCE_NONE;
+ }
+
+ domain->last_seq_check = time(NULL);
+}
+
+/*
+ decide if a cache entry has expired
+*/
+static BOOL centry_expired(struct winbindd_domain *domain, struct cache_entry *centry)
+{
+ /* if the server is OK and our cache entry came from when it was down then
+ the entry is invalid */
+ if (domain->sequence_number != DOM_SEQUENCE_NONE &&
+ centry->sequence_number == DOM_SEQUENCE_NONE) {
+ return True;
+ }
+
+ /* if the server is down or the cache entry is not older than the
+ current sequence number then it is OK */
+ if (wcache_server_down(domain) ||
+ centry->sequence_number == domain->sequence_number) {
+ return False;
+ }
+
+ /* it's expired */
+ return True;
+}
+
+/*
+ fetch an entry from the cache, with a varargs key. auto-fetch the sequence
+ number and return status
+*/
+static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
+ struct winbindd_domain *domain,
+ const char *format, ...) PRINTF_ATTRIBUTE(3,4);
+static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
+ struct winbindd_domain *domain,
+ const char *format, ...)
+{
+ va_list ap;
+ char *kstr;
+ TDB_DATA data;
+ struct cache_entry *centry;
+ TDB_DATA key;
+
+ refresh_sequence_number(domain, False);
+
+ va_start(ap, format);
+ smb_xvasprintf(&kstr, format, ap);
+ va_end(ap);
+
+ key.dptr = kstr;
+ key.dsize = strlen(kstr);
+ data = tdb_fetch(wcache->tdb, key);
+ free(kstr);
+ if (!data.dptr) {
+ /* a cache miss */
+ return NULL;
+ }
+
+ centry = smb_xmalloc(sizeof(*centry));
+ centry->data = data.dptr;
+ centry->len = data.dsize;
+ centry->ofs = 0;
+
+ if (centry->len < 8) {
+ /* huh? corrupt cache? */
+ centry_free(centry);
+ return NULL;
+ }
+
+ centry->status = NT_STATUS(centry_uint32(centry));
+ centry->sequence_number = centry_uint32(centry);
+
+ if (centry_expired(domain, centry)) {
+ extern BOOL opt_dual_daemon;
+
+ if (opt_dual_daemon) {
+ extern BOOL background_process;
+ background_process = True;
+ } else {
+ centry_free(centry);
+ return NULL;
+ }
+ }
+
+ return centry;
+}
+
+/*
+ make sure we have at least len bytes available in a centry
+*/
+static void centry_expand(struct cache_entry *centry, uint32 len)
+{
+ uint8 *p;
+ if (centry->len - centry->ofs >= len) return;
+ centry->len *= 2;
+ p = realloc(centry->data, centry->len);
+ if (!p) {
+ DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
+ smb_panic("out of memory in centry_expand");
+ }
+ centry->data = p;
+}
+
+/*
+ push a uint32 into a centry
+*/
+static void centry_put_uint32(struct cache_entry *centry, uint32 v)
+{
+ centry_expand(centry, 4);
+ SIVAL(centry->data, centry->ofs, v);
+ centry->ofs += 4;
+}
+
+/*
+ push a uint8 into a centry
+*/
+static void centry_put_uint8(struct cache_entry *centry, uint8 v)
+{
+ centry_expand(centry, 1);
+ SCVAL(centry->data, centry->ofs, v);
+ centry->ofs += 1;
+}
+
+/*
+ push a string into a centry
+ */
+static void centry_put_string(struct cache_entry *centry, const char *s)
+{
+ int len;
+
+ if (!s) {
+ /* null strings are marked as len 0xFFFF */
+ centry_put_uint8(centry, 0xFF);
+ return;
+ }
+
+ len = strlen(s);
+ /* can't handle more than 254 char strings. Truncating is probably best */
+ if (len > 254) len = 254;
+ centry_put_uint8(centry, len);
+ centry_expand(centry, len);
+ memcpy(centry->data + centry->ofs, s, len);
+ centry->ofs += len;
+}
+
+static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
+{
+ int len;
+ fstring sid_string;
+ centry_put_string(centry, sid_to_string(sid_string, sid));
+}
+
+/*
+ start a centry for output. When finished, call centry_end()
+*/
+struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
+{
+ struct cache_entry *centry;
+
+ if (!wcache->tdb) return NULL;
+
+ centry = smb_xmalloc(sizeof(*centry));
+
+ centry->len = 8192; /* reasonable default */
+ centry->data = smb_xmalloc(centry->len);
+ centry->ofs = 0;
+ centry->sequence_number = domain->sequence_number;
+ centry_put_uint32(centry, NT_STATUS_V(status));
+ centry_put_uint32(centry, centry->sequence_number);
+ return centry;
+}
+
+/*
+ finish a centry and write it to the tdb
+*/
+static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
+static void centry_end(struct cache_entry *centry, const char *format, ...)
+{
+ va_list ap;
+ char *kstr;
+ TDB_DATA key, data;
+
+ va_start(ap, format);
+ smb_xvasprintf(&kstr, format, ap);
+ va_end(ap);
+
+ key.dptr = kstr;
+ key.dsize = strlen(kstr);
+ data.dptr = centry->data;
+ data.dsize = centry->ofs;
+
+ tdb_store(wcache->tdb, key, data, TDB_REPLACE);
+ free(kstr);
+}
+
+static void wcache_save_name_to_sid(struct winbindd_domain *domain,
+ NTSTATUS status,
+ const char *name, DOM_SID *sid,
+ enum SID_NAME_USE type)
+{
+ struct cache_entry *centry;
+ uint32 len;
+ fstring uname;
+ fstring sid_string;
+
+ centry = centry_start(domain, status);
+ if (!centry) return;
+ centry_put_sid(centry, sid);
+ fstrcpy(uname, name);
+ strupper(uname);
+ centry_end(centry, "NS/%s", sid_to_string(sid_string, sid));
+ centry_free(centry);
+}
+
+static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
+ DOM_SID *sid, const char *name, enum SID_NAME_USE type)
+{
+ struct cache_entry *centry;
+ fstring sid_string;
+
+ centry = centry_start(domain, status);
+ if (!centry) return;
+ if (NT_STATUS_IS_OK(status)) {
+ centry_put_uint32(centry, type);
+ centry_put_string(centry, name);
+ }
+ centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
+ centry_free(centry);
+}
+
+
+static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
+{
+ struct cache_entry *centry;
+ fstring sid_string;
+
+ centry = centry_start(domain, status);
+ if (!centry) return;
+ centry_put_string(centry, info->acct_name);
+ centry_put_string(centry, info->full_name);
+ centry_put_sid(centry, info->user_sid);
+ centry_put_sid(centry, info->group_sid);
+ centry_end(centry, "U/%s", sid_to_string(sid_string, info->user_sid));
+ centry_free(centry);
+}
+
+
+/* Query display info. This is the basic user list fn */
+static NTSTATUS query_user_list(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ WINBIND_USERINFO **info)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ unsigned int i;
+
+ if (!cache->tdb) goto do_query;
+
+ centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
+ if (!centry) goto do_query;
+
+ *num_entries = centry_uint32(centry);
+
+ if (*num_entries == 0) goto do_cached;
+
+ (*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries));
+ if (! (*info)) smb_panic("query_user_list out of memory");
+ for (i=0; i<(*num_entries); i++) {
+ (*info)[i].acct_name = centry_string(centry, mem_ctx);
+ (*info)[i].full_name = centry_string(centry, mem_ctx);
+ (*info)[i].user_sid = centry_sid(centry, mem_ctx);
+ (*info)[i].group_sid = centry_sid(centry, mem_ctx);
+ }
+
+do_cached:
+ status = centry->status;
+ centry_free(centry);
+ return status;
+
+do_query:
+ *num_entries = 0;
+ *info = NULL;
+
+ if (wcache_server_down(domain)) {
+ return NT_STATUS_SERVER_DISABLED;
+ }
+
+ status = cache->backend->query_user_list(domain, mem_ctx, num_entries, info);
+
+ /* and save it */
+ refresh_sequence_number(domain, True);
+ centry = centry_start(domain, status);
+ if (!centry) goto skip_save;
+ centry_put_uint32(centry, *num_entries);
+ for (i=0; i<(*num_entries); i++) {
+ centry_put_string(centry, (*info)[i].acct_name);
+ centry_put_string(centry, (*info)[i].full_name);
+ centry_put_sid(centry, (*info)[i].user_sid);
+ centry_put_sid(centry, (*info)[i].group_sid);
+ if (cache->backend->consistent) {
+ /* when the backend is consistent we can pre-prime some mappings */
+ wcache_save_name_to_sid(domain, NT_STATUS_OK,
+ (*info)[i].acct_name,
+ (*info)[i].user_sid,
+ SID_NAME_USER);
+ wcache_save_sid_to_name(domain, NT_STATUS_OK,
+ (*info)[i].user_sid,
+ (*info)[i].acct_name,
+ SID_NAME_USER);
+ wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
+ }
+ }
+ centry_end(centry, "UL/%s", domain->name);
+ centry_free(centry);
+
+skip_save:
+ return status;
+}
+
+/* list all domain groups */
+static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ unsigned int i;
+
+ if (!cache->tdb) goto do_query;
+
+ centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
+ if (!centry) goto do_query;
+
+ *num_entries = centry_uint32(centry);
+
+ if (*num_entries == 0) goto do_cached;
+
+ (*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries));
+ if (! (*info)) smb_panic("enum_dom_groups out of memory");
+ for (i=0; i<(*num_entries); i++) {
+ fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
+ fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
+ (*info)[i].rid = centry_uint32(centry);
+ }
+
+do_cached:
+ status = centry->status;
+ centry_free(centry);
+ return status;
+
+do_query:
+ *num_entries = 0;
+ *info = NULL;
+
+ if (wcache_server_down(domain)) {
+ return NT_STATUS_SERVER_DISABLED;
+ }
+
+ status = cache->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
+
+ /* and save it */
+ refresh_sequence_number(domain, True);
+ centry = centry_start(domain, status);
+ if (!centry) goto skip_save;
+ centry_put_uint32(centry, *num_entries);
+ for (i=0; i<(*num_entries); i++) {
+ centry_put_string(centry, (*info)[i].acct_name);
+ centry_put_string(centry, (*info)[i].acct_desc);
+ centry_put_uint32(centry, (*info)[i].rid);
+ }
+ centry_end(centry, "GL/%s/domain", domain->name);
+ centry_free(centry);
+
+skip_save:
+ return status;
+}
+
+/* list all domain groups */
+static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ unsigned int i;
+
+ if (!cache->tdb) goto do_query;
+
+ centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
+ if (!centry) goto do_query;
+
+ *num_entries = centry_uint32(centry);
+
+ if (*num_entries == 0) goto do_cached;
+
+ (*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries));
+ if (! (*info)) smb_panic("enum_dom_groups out of memory");
+ for (i=0; i<(*num_entries); i++) {
+ fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
+ fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
+ (*info)[i].rid = centry_uint32(centry);
+ }
+
+do_cached:
+
+ /* If we are returning cached data and the domain controller
+ is down then we don't know whether the data is up to date
+ or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
+ indicate this. */
+
+ if (wcache_server_down(domain)) {
+ DEBUG(10, ("query_user_list: returning cached user list and server was down\n"));
+ status = NT_STATUS_MORE_PROCESSING_REQUIRED;
+ } else
+ status = centry->status;
+
+ centry_free(centry);
+ return status;
+
+do_query:
+ *num_entries = 0;
+ *info = NULL;
+
+ if (wcache_server_down(domain)) {
+ return NT_STATUS_SERVER_DISABLED;
+ }
+
+ status = cache->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
+
+ /* and save it */
+ refresh_sequence_number(domain, True);
+ centry = centry_start(domain, status);
+ if (!centry) goto skip_save;
+ centry_put_uint32(centry, *num_entries);
+ for (i=0; i<(*num_entries); i++) {
+ centry_put_string(centry, (*info)[i].acct_name);
+ centry_put_string(centry, (*info)[i].acct_desc);
+ centry_put_uint32(centry, (*info)[i].rid);
+ }
+ centry_end(centry, "GL/%s/local", domain->name);
+ centry_free(centry);
+
+skip_save:
+ return status;
+}
+
+/* convert a single name to a sid in a domain */
+static NTSTATUS name_to_sid(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *name,
+ DOM_SID *sid,
+ enum SID_NAME_USE *type)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ fstring uname;
+ DOM_SID *sid2;
+
+ if (!cache->tdb) goto do_query;
+
+ fstrcpy(uname, name);
+ strupper(uname);
+ centry = wcache_fetch(cache, domain, "NS/%s/%s", domain->name, uname);
+ if (!centry) goto do_query;
+ *type = centry_uint32(centry);
+ sid2 = centry_sid(centry, mem_ctx);
+ if (!sid2) {
+ ZERO_STRUCTP(sid);
+ } else {
+ sid_copy(sid, sid2);
+ }
+
+ status = centry->status;
+ centry_free(centry);
+ return status;
+
+do_query:
+ ZERO_STRUCTP(sid);
+
+ if (wcache_server_down(domain)) {
+ return NT_STATUS_SERVER_DISABLED;
+ }
+ status = cache->backend->name_to_sid(domain, mem_ctx, name, sid, type);
+
+ /* and save it */
+ wcache_save_name_to_sid(domain, status, name, sid, *type);
+
+ /* We can't save the sid to name mapping as we don't know the
+ correct case of the name without looking it up */
+
+ return status;
+}
+
+/* convert a sid to a user or group name. The sid is guaranteed to be in the domain
+ given */
+static NTSTATUS sid_to_name(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ DOM_SID *sid,
+ char **name,
+ enum SID_NAME_USE *type)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ fstring sid_string;
+
+ if (!cache->tdb) goto do_query;
+
+ centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
+ if (!centry) goto do_query;
+ if (NT_STATUS_IS_OK(centry->status)) {
+ *type = centry_uint32(centry);
+ *name = centry_string(centry, mem_ctx);
+ }
+ status = centry->status;
+ centry_free(centry);
+ return status;
+
+do_query:
+ *name = NULL;
+
+ if (wcache_server_down(domain)) {
+ return NT_STATUS_SERVER_DISABLED;
+ }
+ status = cache->backend->sid_to_name(domain, mem_ctx, sid, name, type);
+
+ /* and save it */
+ refresh_sequence_number(domain, True);
+ wcache_save_sid_to_name(domain, status, sid, *name, *type);
+ wcache_save_name_to_sid(domain, status, *name, sid, *type);
+
+ return status;
+}
+
+
+/* Lookup user information from a rid */
+static NTSTATUS query_user(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ DOM_SID *user_sid,
+ WINBIND_USERINFO *info)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ fstring sid_string;
+
+ if (!cache->tdb) goto do_query;
+
+ centry = wcache_fetch(cache, domain, "U/%s", sid_to_string(sid_string, user_sid));
+ if (!centry) goto do_query;
+
+ info->acct_name = centry_string(centry, mem_ctx);
+ info->full_name = centry_string(centry, mem_ctx);
+ info->user_sid = centry_sid(centry, mem_ctx);
+ info->group_sid = centry_sid(centry, mem_ctx);
+ status = centry->status;
+ centry_free(centry);
+ return status;
+
+do_query:
+ ZERO_STRUCTP(info);
+
+ if (wcache_server_down(domain)) {
+ return NT_STATUS_SERVER_DISABLED;
+ }
+
+ status = cache->backend->query_user(domain, mem_ctx, user_sid, info);
+
+ /* and save it */
+ refresh_sequence_number(domain, True);
+ wcache_save_user(domain, status, info);
+
+ return status;
+}
+
+
+/* Lookup groups a user is a member of. */
+static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ DOM_SID *user_sid,
+ uint32 *num_groups, DOM_SID ***user_gids)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ unsigned int i;
+ fstring sid_string;
+
+ if (!cache->tdb) goto do_query;
+
+ centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
+ if (!centry) goto do_query;
+
+ *num_groups = centry_uint32(centry);
+
+ if (*num_groups == 0) goto do_cached;
+
+ (*user_gids) = talloc(mem_ctx, sizeof(**user_gids) * (*num_groups));
+ if (! (*user_gids)) smb_panic("lookup_usergroups out of memory");
+ for (i=0; i<(*num_groups); i++) {
+ (*user_gids)[i] = centry_sid(centry, mem_ctx);
+ }
+
+do_cached:
+ status = centry->status;
+ centry_free(centry);
+ return status;
+
+do_query:
+ (*num_groups) = 0;
+ (*user_gids) = NULL;
+
+ if (wcache_server_down(domain)) {
+ return NT_STATUS_SERVER_DISABLED;
+ }
+ status = cache->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
+
+ /* and save it */
+ refresh_sequence_number(domain, True);
+ centry = centry_start(domain, status);
+ if (!centry) goto skip_save;
+ centry_put_uint32(centry, *num_groups);
+ for (i=0; i<(*num_groups); i++) {
+ centry_put_sid(centry, (*user_gids)[i]);
+ }
+ centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
+ centry_free(centry);
+
+skip_save:
+ return status;
+}
+
+
+static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ DOM_SID *group_sid, uint32 *num_names,
+ DOM_SID ***sid_mem, char ***names,
+ uint32 **name_types)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ unsigned int i;
+ fstring sid_string;
+
+ if (!cache->tdb) goto do_query;
+
+ centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
+ if (!centry) goto do_query;
+
+ *num_names = centry_uint32(centry);
+
+ if (*num_names == 0) goto do_cached;
+
+ (*sid_mem) = talloc(mem_ctx, sizeof(**sid_mem) * (*num_names));
+ (*names) = talloc(mem_ctx, sizeof(**names) * (*num_names));
+ (*name_types) = talloc(mem_ctx, sizeof(**name_types) * (*num_names));
+
+ if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
+ smb_panic("lookup_groupmem out of memory");
+ }
+
+ for (i=0; i<(*num_names); i++) {
+ (*sid_mem)[i] = centry_sid(centry, mem_ctx);
+ (*names)[i] = centry_string(centry, mem_ctx);
+ (*name_types)[i] = centry_uint32(centry);
+ }
+
+do_cached:
+ status = centry->status;
+ centry_free(centry);
+ return status;
+
+do_query:
+ (*num_names) = 0;
+ (*sid_mem) = NULL;
+ (*names) = NULL;
+ (*name_types) = NULL;
+
+
+ if (wcache_server_down(domain)) {
+ return NT_STATUS_SERVER_DISABLED;
+ }
+ status = cache->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
+ sid_mem, names, name_types);
+
+ /* and save it */
+ refresh_sequence_number(domain, True);
+ centry = centry_start(domain, status);
+ if (!centry) goto skip_save;
+ centry_put_uint32(centry, *num_names);
+ for (i=0; i<(*num_names); i++) {
+ centry_put_sid(centry, (*sid_mem)[i]);
+ centry_put_string(centry, (*names)[i]);
+ centry_put_uint32(centry, (*name_types)[i]);
+ }
+ centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
+ centry_free(centry);
+
+skip_save:
+ return status;
+}
+
+/* find the sequence number for a domain */
+static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
+{
+ refresh_sequence_number(domain, False);
+
+ *seq = domain->sequence_number;
+
+ return NT_STATUS_OK;
+}
+
+/* enumerate trusted domains */
+static NTSTATUS trusted_domains(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_domains,
+ char ***names,
+ char ***alt_names,
+ DOM_SID **dom_sids)
+{
+ struct winbind_cache *cache = get_cache(domain);
+
+ /* we don't cache this call */
+ return cache->backend->trusted_domains(domain, mem_ctx, num_domains,
+ names, alt_names, dom_sids);
+}
+
+/* find the domain sid */
+static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
+{
+ struct winbind_cache *cache = get_cache(domain);
+
+ /* we don't cache this call */
+ return cache->backend->domain_sid(domain, sid);
+}
+
+/* find the alternate names for the domain, if any */
+static NTSTATUS alternate_name(struct winbindd_domain *domain)
+{
+ struct winbind_cache *cache = get_cache(domain);
+
+ /* we don't cache this call */
+ return cache->backend->alternate_name(domain);
+}
+
+/* the ADS backend methods are exposed via this structure */
+struct winbindd_methods cache_methods = {
+ True,
+ query_user_list,
+ enum_dom_groups,
+ enum_local_groups,
+ name_to_sid,
+ sid_to_name,
+ query_user,
+ lookup_usergroups,
+ lookup_groupmem,
+ sequence_number,
+ trusted_domains,
+ domain_sid,
+ alternate_name
+};
diff --git a/source4/nsswitch/winbindd_cm.c b/source4/nsswitch/winbindd_cm.c
new file mode 100644
index 0000000000..0748a1c534
--- /dev/null
+++ b/source4/nsswitch/winbindd_cm.c
@@ -0,0 +1,954 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon connection manager
+
+ Copyright (C) Tim Potter 2001
+ Copyright (C) Andrew Bartlett 2002
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ We need to manage connections to domain controllers without having to
+ mess up the main winbindd code with other issues. The aim of the
+ connection manager is to:
+
+ - make connections to domain controllers and cache them
+ - re-establish connections when networks or servers go down
+ - centralise the policy on connection timeouts, domain controller
+ selection etc
+ - manage re-entrancy for when winbindd becomes able to handle
+ multiple outstanding rpc requests
+
+ Why not have connection management as part of the rpc layer like tng?
+ Good question. This code may morph into libsmb/rpc_cache.c or something
+ like that but at the moment it's simply staying as part of winbind. I
+ think the TNG architecture of forcing every user of the rpc layer to use
+ the connection caching system is a bad idea. It should be an optional
+ method of using the routines.
+
+ The TNG design is quite good but I disagree with some aspects of the
+ implementation. -tpot
+
+ */
+
+/*
+ TODO:
+
+ - I'm pretty annoyed by all the make_nmb_name() stuff. It should be
+ moved down into another function.
+
+ - There needs to be a utility function in libsmb/namequery.c that does
+ cm_get_dc_name()
+
+ - Take care when destroying cli_structs as they can be shared between
+ various sam handles.
+
+ */
+
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/* Global list of connections. Initially a DLIST but can become a hash
+ table or whatever later. */
+
+struct winbindd_cm_conn {
+ struct winbindd_cm_conn *prev, *next;
+ fstring domain;
+ fstring controller;
+ fstring pipe_name;
+ size_t mutex_ref_count;
+ struct cli_state *cli;
+ POLICY_HND pol;
+};
+
+static struct winbindd_cm_conn *cm_conns = NULL;
+
+/* Get a domain controller name. Cache positive and negative lookups so we
+ don't go to the network too often when something is badly broken. */
+
+#define GET_DC_NAME_CACHE_TIMEOUT 30 /* Seconds between dc lookups */
+
+struct get_dc_name_cache {
+ fstring domain_name;
+ fstring srv_name;
+ time_t lookup_time;
+ struct get_dc_name_cache *prev, *next;
+};
+
+/*
+ find the DC for a domain using methods appropriate for a ADS domain
+*/
+static BOOL cm_ads_find_dc(const char *domain, struct in_addr *dc_ip, fstring srv_name)
+{
+ ADS_STRUCT *ads;
+ const char *realm = domain;
+
+ if (strcasecmp(realm, lp_workgroup()) == 0)
+ realm = lp_realm();
+
+ ads = ads_init(realm, domain, NULL);
+ if (!ads)
+ return False;
+
+ /* we don't need to bind, just connect */
+ ads->auth.flags |= ADS_AUTH_NO_BIND;
+
+ DEBUG(4,("cm_ads_find_dc: domain=%s\n", domain));
+
+#ifdef HAVE_ADS
+ /* a full ads_connect() is actually overkill, as we don't srictly need
+ to do the SASL auth in order to get the info we need, but libads
+ doesn't offer a better way right now */
+ ads_connect(ads);
+#endif
+
+ if (!ads->config.realm)
+ return False;
+
+ fstrcpy(srv_name, ads->config.ldap_server_name);
+ strupper(srv_name);
+ *dc_ip = ads->ldap_ip;
+ ads_destroy(&ads);
+
+ DEBUG(4,("cm_ads_find_dc: using server='%s' IP=%s\n",
+ srv_name, inet_ntoa(*dc_ip)));
+
+ return True;
+}
+
+
+
+static BOOL cm_get_dc_name(const char *domain, fstring srv_name, struct in_addr *ip_out)
+{
+ static struct get_dc_name_cache *get_dc_name_cache;
+ struct get_dc_name_cache *dcc;
+ struct in_addr dc_ip;
+ BOOL ret;
+
+ /* Check the cache for previous lookups */
+
+ for (dcc = get_dc_name_cache; dcc; dcc = dcc->next) {
+
+ if (!strequal(domain, dcc->domain_name))
+ continue; /* Not our domain */
+
+ if ((time(NULL) - dcc->lookup_time) >
+ GET_DC_NAME_CACHE_TIMEOUT) {
+
+ /* Cache entry has expired, delete it */
+
+ DEBUG(10, ("get_dc_name_cache entry expired for %s\n", domain));
+
+ DLIST_REMOVE(get_dc_name_cache, dcc);
+ SAFE_FREE(dcc);
+
+ break;
+ }
+
+ /* Return a positive or negative lookup for this domain */
+
+ if (dcc->srv_name[0]) {
+ DEBUG(10, ("returning positive get_dc_name_cache entry for %s\n", domain));
+ fstrcpy(srv_name, dcc->srv_name);
+ return True;
+ } else {
+ DEBUG(10, ("returning negative get_dc_name_cache entry for %s\n", domain));
+ return False;
+ }
+ }
+
+ /* Add cache entry for this lookup. */
+
+ DEBUG(10, ("Creating get_dc_name_cache entry for %s\n", domain));
+
+ if (!(dcc = (struct get_dc_name_cache *)
+ malloc(sizeof(struct get_dc_name_cache))))
+ return False;
+
+ ZERO_STRUCTP(dcc);
+
+ fstrcpy(dcc->domain_name, domain);
+ dcc->lookup_time = time(NULL);
+
+ DLIST_ADD(get_dc_name_cache, dcc);
+
+ zero_ip(&dc_ip);
+
+ ret = False;
+ if (lp_security() == SEC_ADS)
+ ret = cm_ads_find_dc(domain, &dc_ip, srv_name);
+
+ if (!ret) {
+ /* fall back on rpc methods if the ADS methods fail */
+ ret = rpc_find_dc(domain, srv_name, &dc_ip);
+ }
+
+ if (!ret)
+ return False;
+
+ /* We have a name so make the cache entry positive now */
+ fstrcpy(dcc->srv_name, srv_name);
+
+ DEBUG(3, ("cm_get_dc_name: Returning DC %s (%s) for domain %s\n", srv_name,
+ inet_ntoa(dc_ip), domain));
+
+ *ip_out = dc_ip;
+
+ return True;
+}
+
+/* Choose between anonymous or authenticated connections. We need to use
+ an authenticated connection if DCs have the RestrictAnonymous registry
+ entry set > 0, or the "Additional restrictions for anonymous
+ connections" set in the win2k Local Security Policy.
+
+ Caller to free() result in domain, username, password
+*/
+
+static void cm_get_ipc_userpass(char **username, char **domain, char **password)
+{
+ *username = secrets_fetch(SECRETS_AUTH_USER, NULL);
+ *domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
+ *password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
+
+ if (*username && **username) {
+
+ if (!*domain || !**domain)
+ *domain = smb_xstrdup(lp_workgroup());
+
+ if (!*password || !**password)
+ *password = smb_xstrdup("");
+
+ DEBUG(3, ("IPC$ connections done by user %s\\%s\n",
+ *domain, *username));
+
+ } else {
+ DEBUG(3, ("IPC$ connections done anonymously\n"));
+ *username = smb_xstrdup("");
+ *domain = smb_xstrdup("");
+ *password = smb_xstrdup("");
+ }
+}
+
+/* Open a new smb pipe connection to a DC on a given domain. Cache
+ negative creation attempts so we don't try and connect to broken
+ machines too often. */
+
+#define FAILED_CONNECTION_CACHE_TIMEOUT 30 /* Seconds between attempts */
+
+struct failed_connection_cache {
+ fstring domain_name;
+ fstring controller;
+ time_t lookup_time;
+ NTSTATUS nt_status;
+ struct failed_connection_cache *prev, *next;
+};
+
+static struct failed_connection_cache *failed_connection_cache;
+
+/* Add an entry to the failed conneciton cache */
+
+static void add_failed_connection_entry(struct winbindd_cm_conn *new_conn,
+ NTSTATUS result)
+{
+ struct failed_connection_cache *fcc;
+
+ SMB_ASSERT(!NT_STATUS_IS_OK(result));
+
+ /* Check we already aren't in the cache */
+
+ for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
+ if (strequal(fcc->domain_name, new_conn->domain)) {
+ DEBUG(10, ("domain %s already tried and failed\n",
+ fcc->domain_name));
+ return;
+ }
+ }
+
+ /* Create negative lookup cache entry for this domain and controller */
+
+ if (!(fcc = (struct failed_connection_cache *)
+ malloc(sizeof(struct failed_connection_cache)))) {
+ DEBUG(0, ("malloc failed in add_failed_connection_entry!\n"));
+ return;
+ }
+
+ ZERO_STRUCTP(fcc);
+
+ fstrcpy(fcc->domain_name, new_conn->domain);
+ fstrcpy(fcc->controller, new_conn->controller);
+ fcc->lookup_time = time(NULL);
+ fcc->nt_status = result;
+
+ DLIST_ADD(failed_connection_cache, fcc);
+}
+
+/* Open a connction to the remote server, cache failures for 30 seconds */
+
+static NTSTATUS cm_open_connection(const char *domain, const int pipe_index,
+ struct winbindd_cm_conn *new_conn, BOOL keep_mutex)
+{
+ struct failed_connection_cache *fcc;
+ NTSTATUS result;
+ char *ipc_username, *ipc_domain, *ipc_password;
+ struct in_addr dc_ip;
+ int i;
+ BOOL retry = True;
+ BOOL got_mutex = False;
+
+ ZERO_STRUCT(dc_ip);
+
+ fstrcpy(new_conn->domain, domain);
+ fstrcpy(new_conn->pipe_name, get_pipe_name_from_index(pipe_index));
+
+ /* Look for a domain controller for this domain. Negative results
+ are cached so don't bother applying the caching for this
+ function just yet. */
+
+ if (!cm_get_dc_name(domain, new_conn->controller, &dc_ip)) {
+ result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ add_failed_connection_entry(new_conn, result);
+ return result;
+ }
+
+ /* Return false if we have tried to look up this domain and netbios
+ name before and failed. */
+
+ for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
+
+ if (!(strequal(domain, fcc->domain_name) &&
+ strequal(new_conn->controller, fcc->controller)))
+ continue; /* Not our domain */
+
+ if ((time(NULL) - fcc->lookup_time) >
+ FAILED_CONNECTION_CACHE_TIMEOUT) {
+
+ /* Cache entry has expired, delete it */
+
+ DEBUG(10, ("cm_open_connection cache entry expired for %s, %s\n", domain, new_conn->controller));
+
+ DLIST_REMOVE(failed_connection_cache, fcc);
+ free(fcc);
+
+ break;
+ }
+
+ /* The timeout hasn't expired yet so return false */
+
+ DEBUG(10, ("returning negative open_connection_cache entry for %s, %s\n", domain, new_conn->controller));
+
+ result = fcc->nt_status;
+ SMB_ASSERT(!NT_STATUS_IS_OK(result));
+ return result;
+ }
+
+ /* Initialise SMB connection */
+
+ cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
+
+ DEBUG(5, ("connecting to %s from %s with username [%s]\\[%s]\n",
+ new_conn->controller, lp_netbios_name(), ipc_domain, ipc_username));
+
+ for (i = 0; retry && (i < 3); i++) {
+
+ if (!secrets_named_mutex(new_conn->controller, WINBIND_SERVER_MUTEX_WAIT_TIME, &new_conn->mutex_ref_count)) {
+ DEBUG(0,("cm_open_connection: mutex grab failed for %s\n", new_conn->controller));
+ result = NT_STATUS_POSSIBLE_DEADLOCK;
+ continue;
+ }
+
+ got_mutex = True;
+
+ result = cli_full_connection(&new_conn->cli, lp_netbios_name(), new_conn->controller,
+ &dc_ip, 0, "IPC$", "IPC", ipc_username, ipc_domain,
+ ipc_password, 0, &retry);
+
+ if (NT_STATUS_IS_OK(result))
+ break;
+
+ secrets_named_mutex_release(new_conn->controller, &new_conn->mutex_ref_count);
+ got_mutex = False;
+ }
+
+ SAFE_FREE(ipc_username);
+ SAFE_FREE(ipc_domain);
+ SAFE_FREE(ipc_password);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ if (got_mutex)
+ secrets_named_mutex_release(new_conn->controller, &new_conn->mutex_ref_count);
+ add_failed_connection_entry(new_conn, result);
+ return result;
+ }
+
+ if ( !cli_nt_session_open (new_conn->cli, pipe_index) ) {
+ result = NT_STATUS_PIPE_NOT_AVAILABLE;
+ /*
+ * only cache a failure if we are not trying to open the
+ * **win2k** specific lsarpc UUID. This could be an NT PDC
+ * and therefore a failure is normal. This should probably
+ * be abstracted to a check for 2k specific pipes and wondering
+ * if the PDC is an NT4 box. but since there is only one 2k
+ * specific UUID right now, i'm not going to bother. --jerry
+ */
+ if (got_mutex)
+ secrets_named_mutex_release(new_conn->controller, &new_conn->mutex_ref_count);
+ if ( !is_win2k_pipe(pipe_index) )
+ add_failed_connection_entry(new_conn, result);
+ cli_shutdown(new_conn->cli);
+ return result;
+ }
+
+ if ((got_mutex) && !keep_mutex)
+ secrets_named_mutex_release(new_conn->controller, &new_conn->mutex_ref_count);
+ return NT_STATUS_OK;
+}
+
+/* Return true if a connection is still alive */
+
+static BOOL connection_ok(struct winbindd_cm_conn *conn)
+{
+ if (!conn) {
+ smb_panic("Invalid paramater passed to conneciton_ok(): conn was NULL!\n");
+ return False;
+ }
+
+ if (!conn->cli) {
+ DEBUG(0, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n",
+ conn->controller, conn->domain, conn->pipe_name));
+ smb_panic("connection_ok: conn->cli was null!");
+ return False;
+ }
+
+ if (!conn->cli->initialised) {
+ DEBUG(0, ("Connection to %s for domain %s (pipe %s) was never initialised!\n",
+ conn->controller, conn->domain, conn->pipe_name));
+ smb_panic("connection_ok: conn->cli->initialised is False!");
+ return False;
+ }
+
+ if (conn->cli->fd == -1) {
+ DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n",
+ conn->controller, conn->domain, conn->pipe_name));
+ return False;
+ }
+
+ return True;
+}
+
+/* Get a connection to the remote DC and open the pipe. If there is already a connection, use that */
+
+static NTSTATUS get_connection_from_cache(const char *domain, const char *pipe_name,
+ struct winbindd_cm_conn **conn_out, BOOL keep_mutex)
+{
+ struct winbindd_cm_conn *conn, conn_temp;
+ NTSTATUS result;
+
+ for (conn = cm_conns; conn; conn = conn->next) {
+ if (strequal(conn->domain, domain) &&
+ strequal(conn->pipe_name, pipe_name)) {
+ if (!connection_ok(conn)) {
+ if (conn->cli)
+ cli_shutdown(conn->cli);
+ ZERO_STRUCT(conn_temp);
+ conn_temp.next = conn->next;
+ DLIST_REMOVE(cm_conns, conn);
+ SAFE_FREE(conn);
+ conn = &conn_temp; /* Just to keep the loop moving */
+ } else {
+ if (keep_mutex) {
+ if (!secrets_named_mutex(conn->controller,
+ WINBIND_SERVER_MUTEX_WAIT_TIME, &conn->mutex_ref_count))
+ DEBUG(0,("get_connection_from_cache: mutex grab failed for %s\n",
+ conn->controller));
+ }
+ break;
+ }
+ }
+ }
+
+ if (!conn) {
+ if (!(conn = malloc(sizeof(*conn))))
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(conn);
+
+ if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, get_pipe_index(pipe_name), conn, keep_mutex))) {
+ DEBUG(3, ("Could not open a connection to %s for %s (%s)\n",
+ domain, pipe_name, nt_errstr(result)));
+ SAFE_FREE(conn);
+ return result;
+ }
+ DLIST_ADD(cm_conns, conn);
+ }
+
+ *conn_out = conn;
+ return NT_STATUS_OK;
+}
+
+
+/**********************************************************************************
+**********************************************************************************/
+
+BOOL cm_check_for_native_mode_win2k( const char *domain )
+{
+ NTSTATUS result;
+ struct winbindd_cm_conn conn;
+ DS_DOMINFO_CTR ctr;
+ BOOL ret = False;
+
+ ZERO_STRUCT( conn );
+ ZERO_STRUCT( ctr );
+
+
+ if ( !NT_STATUS_IS_OK(result = cm_open_connection(domain, PI_LSARPC_DS, &conn, False)) ) {
+ DEBUG(5, ("cm_check_for_native_mode_win2k: Could not open a connection to %s for PIPE_LSARPC (%s)\n",
+ domain, nt_errstr(result)));
+ return False;
+ }
+
+ if ( conn.cli ) {
+ if ( !NT_STATUS_IS_OK(cli_ds_getprimarydominfo( conn.cli,
+ conn.cli->mem_ctx, DsRolePrimaryDomainInfoBasic, &ctr)) ) {
+ ret = False;
+ goto done;
+ }
+ }
+
+ if ( (ctr.basic->flags & DSROLE_PRIMARY_DS_RUNNING)
+ && !(ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE) )
+ ret = True;
+
+done:
+ if ( conn.cli )
+ cli_shutdown( conn.cli );
+
+ return ret;
+}
+
+
+
+/* Return a LSA policy handle on a domain */
+
+CLI_POLICY_HND *cm_get_lsa_handle(const char *domain)
+{
+ struct winbindd_cm_conn *conn;
+ uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+ NTSTATUS result;
+ static CLI_POLICY_HND hnd;
+
+ /* Look for existing connections */
+
+ if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn, False)))
+ return NULL;
+
+ /* This *shitty* code needs scrapping ! JRA */
+ if (policy_handle_is_valid(&conn->pol)) {
+ hnd.pol = conn->pol;
+ hnd.cli = conn->cli;
+ return &hnd;
+ }
+
+ result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
+ des_access, &conn->pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ /* Hit the cache code again. This cleans out the old connection and gets a new one */
+ if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
+ if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn, False)))
+ return NULL;
+
+ result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
+ des_access, &conn->pol);
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ cli_shutdown(conn->cli);
+ DLIST_REMOVE(cm_conns, conn);
+ SAFE_FREE(conn);
+ return NULL;
+ }
+ }
+
+ hnd.pol = conn->pol;
+ hnd.cli = conn->cli;
+
+ return &hnd;
+}
+
+/* Return a SAM policy handle on a domain */
+
+CLI_POLICY_HND *cm_get_sam_handle(char *domain)
+{
+ struct winbindd_cm_conn *conn;
+ uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+ NTSTATUS result;
+ static CLI_POLICY_HND hnd;
+
+ /* Look for existing connections */
+
+ if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn, False)))
+ return NULL;
+
+ /* This *shitty* code needs scrapping ! JRA */
+ if (policy_handle_is_valid(&conn->pol)) {
+ hnd.pol = conn->pol;
+ hnd.cli = conn->cli;
+ return &hnd;
+ }
+ result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
+ des_access, &conn->pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ /* Hit the cache code again. This cleans out the old connection and gets a new one */
+ if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
+ if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn, False)))
+ return NULL;
+
+ result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
+ des_access, &conn->pol);
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ cli_shutdown(conn->cli);
+ DLIST_REMOVE(cm_conns, conn);
+ SAFE_FREE(conn);
+ return NULL;
+ }
+ }
+
+ hnd.pol = conn->pol;
+ hnd.cli = conn->cli;
+
+ return &hnd;
+}
+
+#if 0 /* This code now *well* out of date */
+
+/* Return a SAM domain policy handle on a domain */
+
+CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain, DOM_SID *domain_sid)
+{
+ struct winbindd_cm_conn *conn, *basic_conn = NULL;
+ static CLI_POLICY_HND hnd;
+ NTSTATUS result;
+ uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+
+ /* Look for existing connections */
+
+ for (conn = cm_conns; conn; conn = conn->next) {
+ if (strequal(conn->domain, domain) &&
+ strequal(conn->pipe_name, PIPE_SAMR) &&
+ conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM) {
+
+ if (!connection_ok(conn)) {
+ /* Shutdown cli? Free conn? Allow retry of DC? */
+ DLIST_REMOVE(cm_conns, conn);
+ return NULL;
+ }
+
+ goto ok;
+ }
+ }
+
+ /* Create a basic handle to open a domain handle from */
+
+ if (!cm_get_sam_handle(domain))
+ return False;
+
+ for (conn = cm_conns; conn; conn = conn->next) {
+ if (strequal(conn->domain, domain) &&
+ strequal(conn->pipe_name, PIPE_SAMR) &&
+ conn->pipe_data.samr.pipe_type == SAM_PIPE_BASIC)
+ basic_conn = conn;
+ }
+
+ if (!(conn = (struct winbindd_cm_conn *)
+ malloc(sizeof(struct winbindd_cm_conn))))
+ return NULL;
+
+ ZERO_STRUCTP(conn);
+
+ fstrcpy(conn->domain, basic_conn->domain);
+ fstrcpy(conn->controller, basic_conn->controller);
+ fstrcpy(conn->pipe_name, basic_conn->pipe_name);
+
+ conn->pipe_data.samr.pipe_type = SAM_PIPE_DOM;
+ conn->cli = basic_conn->cli;
+
+ result = cli_samr_open_domain(conn->cli, conn->cli->mem_ctx,
+ &basic_conn->pol, des_access,
+ domain_sid, &conn->pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ return NULL;
+
+ /* Add to list */
+
+ DLIST_ADD(cm_conns, conn);
+
+ ok:
+ hnd.pol = conn->pol;
+ hnd.cli = conn->cli;
+
+ return &hnd;
+}
+
+/* Return a SAM policy handle on a domain user */
+
+CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, DOM_SID *domain_sid,
+ uint32 user_rid)
+{
+ struct winbindd_cm_conn *conn, *basic_conn = NULL;
+ static CLI_POLICY_HND hnd;
+ NTSTATUS result;
+ uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+
+ /* Look for existing connections */
+
+ for (conn = cm_conns; conn; conn = conn->next) {
+ if (strequal(conn->domain, domain) &&
+ strequal(conn->pipe_name, PIPE_SAMR) &&
+ conn->pipe_data.samr.pipe_type == SAM_PIPE_USER &&
+ conn->pipe_data.samr.rid == user_rid) {
+
+ if (!connection_ok(conn)) {
+ /* Shutdown cli? Free conn? Allow retry of DC? */
+ DLIST_REMOVE(cm_conns, conn);
+ return NULL;
+ }
+
+ goto ok;
+ }
+ }
+
+ /* Create a domain handle to open a user handle from */
+
+ if (!cm_get_sam_dom_handle(domain, domain_sid))
+ return NULL;
+
+ for (conn = cm_conns; conn; conn = conn->next) {
+ if (strequal(conn->domain, domain) &&
+ strequal(conn->pipe_name, PIPE_SAMR) &&
+ conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
+ basic_conn = conn;
+ }
+
+ if (!basic_conn) {
+ DEBUG(0, ("No domain sam handle was created!\n"));
+ return NULL;
+ }
+
+ if (!(conn = (struct winbindd_cm_conn *)
+ malloc(sizeof(struct winbindd_cm_conn))))
+ return NULL;
+
+ ZERO_STRUCTP(conn);
+
+ fstrcpy(conn->domain, basic_conn->domain);
+ fstrcpy(conn->controller, basic_conn->controller);
+ fstrcpy(conn->pipe_name, basic_conn->pipe_name);
+
+ conn->pipe_data.samr.pipe_type = SAM_PIPE_USER;
+ conn->cli = basic_conn->cli;
+ conn->pipe_data.samr.rid = user_rid;
+
+ result = cli_samr_open_user(conn->cli, conn->cli->mem_ctx,
+ &basic_conn->pol, des_access, user_rid,
+ &conn->pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ return NULL;
+
+ /* Add to list */
+
+ DLIST_ADD(cm_conns, conn);
+
+ ok:
+ hnd.pol = conn->pol;
+ hnd.cli = conn->cli;
+
+ return &hnd;
+}
+
+/* Return a SAM policy handle on a domain group */
+
+CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, DOM_SID *domain_sid,
+ uint32 group_rid)
+{
+ struct winbindd_cm_conn *conn, *basic_conn = NULL;
+ static CLI_POLICY_HND hnd;
+ NTSTATUS result;
+ uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+
+ /* Look for existing connections */
+
+ for (conn = cm_conns; conn; conn = conn->next) {
+ if (strequal(conn->domain, domain) &&
+ strequal(conn->pipe_name, PIPE_SAMR) &&
+ conn->pipe_data.samr.pipe_type == SAM_PIPE_GROUP &&
+ conn->pipe_data.samr.rid == group_rid) {
+
+ if (!connection_ok(conn)) {
+ /* Shutdown cli? Free conn? Allow retry of DC? */
+ DLIST_REMOVE(cm_conns, conn);
+ return NULL;
+ }
+
+ goto ok;
+ }
+ }
+
+ /* Create a domain handle to open a user handle from */
+
+ if (!cm_get_sam_dom_handle(domain, domain_sid))
+ return NULL;
+
+ for (conn = cm_conns; conn; conn = conn->next) {
+ if (strequal(conn->domain, domain) &&
+ strequal(conn->pipe_name, PIPE_SAMR) &&
+ conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
+ basic_conn = conn;
+ }
+
+ if (!basic_conn) {
+ DEBUG(0, ("No domain sam handle was created!\n"));
+ return NULL;
+ }
+
+ if (!(conn = (struct winbindd_cm_conn *)
+ malloc(sizeof(struct winbindd_cm_conn))))
+ return NULL;
+
+ ZERO_STRUCTP(conn);
+
+ fstrcpy(conn->domain, basic_conn->domain);
+ fstrcpy(conn->controller, basic_conn->controller);
+ fstrcpy(conn->pipe_name, basic_conn->pipe_name);
+
+ conn->pipe_data.samr.pipe_type = SAM_PIPE_GROUP;
+ conn->cli = basic_conn->cli;
+ conn->pipe_data.samr.rid = group_rid;
+
+ result = cli_samr_open_group(conn->cli, conn->cli->mem_ctx,
+ &basic_conn->pol, des_access, group_rid,
+ &conn->pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ return NULL;
+
+ /* Add to list */
+
+ DLIST_ADD(cm_conns, conn);
+
+ ok:
+ hnd.pol = conn->pol;
+ hnd.cli = conn->cli;
+
+ return &hnd;
+}
+
+#endif
+
+/* Get a handle on a netlogon pipe. This is a bit of a hack to re-use the
+ netlogon pipe as no handle is returned. */
+
+NTSTATUS cm_get_netlogon_cli(const char *domain, const unsigned char *trust_passwd,
+ struct cli_state **cli)
+{
+ NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ struct winbindd_cm_conn *conn;
+ uint32 neg_flags = 0x000001ff;
+
+ if (!cli)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ /* Open an initial conection - keep the mutex. */
+
+ if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn, True)))
+ return result;
+
+ result = cli_nt_setup_creds(conn->cli, get_sec_chan(), trust_passwd, &neg_flags, 2);
+
+ if (conn->mutex_ref_count)
+ secrets_named_mutex_release(conn->controller, &conn->mutex_ref_count);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0, ("error connecting to domain password server: %s\n",
+ nt_errstr(result)));
+
+ /* Hit the cache code again. This cleans out the old connection and gets a new one */
+ if (conn->cli->fd == -1) {
+
+ if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn, True)))
+ return result;
+
+ /* Try again */
+ result = cli_nt_setup_creds( conn->cli, get_sec_chan(),trust_passwd, &neg_flags, 2);
+
+ if (conn->mutex_ref_count)
+ secrets_named_mutex_release(conn->controller, &conn->mutex_ref_count);
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ cli_shutdown(conn->cli);
+ DLIST_REMOVE(cm_conns, conn);
+ SAFE_FREE(conn);
+ return result;
+ }
+ }
+
+ *cli = conn->cli;
+
+ return result;
+}
+
+/* Dump the current connection status */
+
+static void dump_conn_list(void)
+{
+ struct winbindd_cm_conn *con;
+
+ DEBUG(0, ("\tDomain Controller Pipe\n"));
+
+ for(con = cm_conns; con; con = con->next) {
+ char *msg;
+
+ /* Display pipe info */
+
+ if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
+ DEBUG(0, ("Error: not enough memory!\n"));
+ } else {
+ DEBUG(0, ("%s\n", msg));
+ SAFE_FREE(msg);
+ }
+ }
+}
+
+void winbindd_cm_status(void)
+{
+ /* List open connections */
+
+ DEBUG(0, ("winbindd connection manager status:\n"));
+
+ if (cm_conns)
+ dump_conn_list();
+ else
+ DEBUG(0, ("\tNo active connections\n"));
+}
diff --git a/source4/nsswitch/winbindd_dual.c b/source4/nsswitch/winbindd_dual.c
new file mode 100644
index 0000000000..207757bcea
--- /dev/null
+++ b/source4/nsswitch/winbindd_dual.c
@@ -0,0 +1,210 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind background daemon
+
+ Copyright (C) Andrew Tridgell 2002
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ the idea of the optional dual daemon mode is ot prevent slow domain
+ responses from clagging up the rest of the system. When in dual
+ daemon mode winbindd always responds to requests from cache if the
+ request is in cache, and if the cached answer is stale then it asks
+ the "dual daemon" to update the cache for that request
+
+ */
+
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+extern BOOL opt_dual_daemon;
+BOOL background_process = False;
+int dual_daemon_pipe = -1;
+
+
+/* a list of requests ready to be sent to the dual daemon */
+struct dual_list {
+ struct dual_list *next;
+ char *data;
+ int length;
+ int offset;
+};
+
+static struct dual_list *dual_list;
+static struct dual_list *dual_list_end;
+
+/*
+ setup a select() including the dual daemon pipe
+ */
+int dual_select_setup(fd_set *fds, int maxfd)
+{
+ if (dual_daemon_pipe == -1 ||
+ !dual_list) {
+ return maxfd;
+ }
+
+ FD_SET(dual_daemon_pipe, fds);
+ if (dual_daemon_pipe > maxfd) {
+ maxfd = dual_daemon_pipe;
+ }
+ return maxfd;
+}
+
+
+/*
+ a hook called from the main winbindd select() loop to handle writes
+ to the dual daemon pipe
+*/
+void dual_select(fd_set *fds)
+{
+ int n;
+
+ if (dual_daemon_pipe == -1 ||
+ !dual_list ||
+ !FD_ISSET(dual_daemon_pipe, fds)) {
+ return;
+ }
+
+ n = sys_write(dual_daemon_pipe,
+ &dual_list->data[dual_list->offset],
+ dual_list->length - dual_list->offset);
+
+ if (n <= 0) {
+ /* the pipe is dead! fall back to normal operation */
+ dual_daemon_pipe = -1;
+ return;
+ }
+
+ dual_list->offset += n;
+
+ if (dual_list->offset == dual_list->length) {
+ struct dual_list *next;
+ next = dual_list->next;
+ free(dual_list->data);
+ free(dual_list);
+ dual_list = next;
+ if (!dual_list) {
+ dual_list_end = NULL;
+ }
+ }
+}
+
+/*
+ send a request to the background daemon
+ this is called for stale cached entries
+*/
+void dual_send_request(struct winbindd_cli_state *state)
+{
+ struct dual_list *list;
+
+ if (!background_process) return;
+
+ list = malloc(sizeof(*list));
+ if (!list) return;
+
+ list->next = NULL;
+ list->data = memdup(&state->request, sizeof(state->request));
+ list->length = sizeof(state->request);
+ list->offset = 0;
+
+ if (!dual_list_end) {
+ dual_list = list;
+ dual_list_end = list;
+ } else {
+ dual_list_end->next = list;
+ dual_list_end = list;
+ }
+
+ background_process = False;
+}
+
+
+/*
+the main dual daemon
+*/
+void do_dual_daemon(void)
+{
+ int fdpair[2];
+ struct winbindd_cli_state state;
+
+ if (pipe(fdpair) != 0) {
+ return;
+ }
+
+ ZERO_STRUCT(state);
+ state.pid = getpid();
+
+ dual_daemon_pipe = fdpair[1];
+ state.sock = fdpair[0];
+
+ if (fork() != 0) {
+ close(fdpair[0]);
+ return;
+ }
+ close(fdpair[1]);
+
+ if (!winbind_setup_common())
+ _exit(0);
+
+ dual_daemon_pipe = -1;
+ opt_dual_daemon = False;
+
+ while (1) {
+ /* free up any talloc memory */
+ lp_talloc_free();
+ main_loop_talloc_free();
+
+ /* fetch a request from the main daemon */
+ winbind_client_read(&state);
+
+ if (state.finished) {
+ /* we lost contact with our parent */
+ exit(0);
+ }
+
+ /* process full rquests */
+ if (state.read_buf_len == sizeof(state.request)) {
+ DEBUG(4,("dual daemon request %d\n", (int)state.request.cmd));
+
+ /* special handling for the stateful requests */
+ switch (state.request.cmd) {
+ case WINBINDD_GETPWENT:
+ winbindd_setpwent(&state);
+ break;
+
+ case WINBINDD_GETGRENT:
+ case WINBINDD_GETGRLST:
+ winbindd_setgrent(&state);
+ break;
+ default:
+ break;
+ }
+
+ winbind_process_packet(&state);
+ SAFE_FREE(state.response.extra_data);
+
+ free_getent_state(state.getpwent_state);
+ free_getent_state(state.getgrent_state);
+ state.getpwent_state = NULL;
+ state.getgrent_state = NULL;
+ }
+ }
+}
+
diff --git a/source4/nsswitch/winbindd_group.c b/source4/nsswitch/winbindd_group.c
new file mode 100644
index 0000000000..d06db5943c
--- /dev/null
+++ b/source4/nsswitch/winbindd_group.c
@@ -0,0 +1,896 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jeremy Allison 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/***************************************************************
+ Empty static struct for negative caching.
+****************************************************************/
+
+/* Fill a grent structure from various other information */
+
+static BOOL fill_grent(struct winbindd_gr *gr, const char *dom_name,
+ const char *gr_name, gid_t unix_gid)
+{
+ fstring full_group_name;
+ /* Fill in uid/gid */
+ fill_domain_username(full_group_name, dom_name, gr_name);
+
+ gr->gr_gid = unix_gid;
+
+ /* Group name and password */
+
+ safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
+ safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
+
+ return True;
+}
+
+/* Fill in the group membership field of a NT group given by group_sid */
+
+static BOOL fill_grent_mem(struct winbindd_domain *domain,
+ DOM_SID *group_sid,
+ enum SID_NAME_USE group_name_type,
+ int *num_gr_mem, char **gr_mem, int *gr_mem_len)
+{
+ DOM_SID **sid_mem = NULL;
+ uint32 num_names = 0;
+ uint32 *name_types = NULL;
+ unsigned int buf_len, buf_ndx, i;
+ char **names = NULL, *buf;
+ BOOL result = False;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ fstring sid_string;
+
+ if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
+ return False;
+
+ /* Initialise group membership information */
+
+ DEBUG(10, ("group SID %s\n", sid_to_string(sid_string, group_sid)));
+
+ *num_gr_mem = 0;
+
+ if (group_name_type != SID_NAME_DOM_GRP) {
+ DEBUG(1, ("SID %s in domain %s isn't a domain group\n",
+ sid_to_string(sid_string, group_sid), domain->name));
+ goto done;
+ }
+
+ /* Lookup group members */
+ status = domain->methods->lookup_groupmem(domain, mem_ctx, group_sid, &num_names,
+ &sid_mem, &names, &name_types);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("could not lookup membership for group rid %s in domain %s (error: %s)\n",
+ sid_to_string(sid_string, group_sid), domain->name, nt_errstr(status)));
+
+ goto done;
+ }
+
+ DEBUG(10, ("looked up %d names\n", num_names));
+
+ if (DEBUGLEVEL >= 10) {
+ for (i = 0; i < num_names; i++)
+ DEBUG(10, ("\t%20s %s %d\n", names[i], sid_to_string(sid_string, sid_mem[i]),
+ name_types[i]));
+ }
+
+ /* Add members to list */
+
+ buf = NULL;
+ buf_len = buf_ndx = 0;
+
+ again:
+
+ for (i = 0; i < num_names; i++) {
+ char *the_name;
+ fstring name;
+ int len;
+
+ the_name = names[i];
+
+ DEBUG(10, ("processing name %s\n", the_name));
+
+ /* FIXME: need to cope with groups within groups. These
+ occur in Universal groups on a Windows 2000 native mode
+ server. */
+
+ if (name_types[i] != SID_NAME_USER) {
+ DEBUG(3, ("name %s isn't a domain user\n", the_name));
+ continue;
+ }
+
+ /* Don't bother with machine accounts */
+
+ if (the_name[strlen(the_name) - 1] == '$') {
+ DEBUG(10, ("%s is machine account\n", the_name));
+ continue;
+ }
+
+ /* Append domain name */
+
+ fill_domain_username(name, domain->name, the_name);
+
+ len = strlen(name);
+
+ /* Add to list or calculate buffer length */
+
+ if (!buf) {
+ buf_len += len + 1; /* List is comma separated */
+ (*num_gr_mem)++;
+ DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
+ } else {
+ DEBUG(10, ("appending %s at ndx %d\n", name, len));
+ safe_strcpy(&buf[buf_ndx], name, len);
+ buf_ndx += len;
+ buf[buf_ndx] = ',';
+ buf_ndx++;
+ }
+ }
+
+ /* Allocate buffer */
+
+ if (!buf && buf_len != 0) {
+ if (!(buf = malloc(buf_len))) {
+ DEBUG(1, ("out of memory\n"));
+ result = False;
+ goto done;
+ }
+ memset(buf, 0, buf_len);
+ goto again;
+ }
+
+ if (buf && buf_ndx > 0) {
+ buf[buf_ndx - 1] = '\0';
+ }
+
+ *gr_mem = buf;
+ *gr_mem_len = buf_len;
+
+ DEBUG(10, ("num_mem = %d, len = %d, mem = %s\n", *num_gr_mem,
+ buf_len, *num_gr_mem ? buf : "NULL"));
+ result = True;
+
+done:
+
+ talloc_destroy(mem_ctx);
+
+ DEBUG(10, ("fill_grent_mem returning %d\n", result));
+
+ return result;
+}
+
+/* Return a group structure from a group name */
+
+enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state)
+{
+ DOM_SID group_sid;
+ struct winbindd_domain *domain;
+ enum SID_NAME_USE name_type;
+ fstring name_domain, name_group;
+ char *tmp, *gr_mem;
+ gid_t gid;
+ int gr_mem_len;
+
+ /* Ensure null termination */
+ state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
+
+ DEBUG(3, ("[%5d]: getgrnam %s\n", state->pid,
+ state->request.data.groupname));
+
+ /* Parse domain and groupname */
+
+ memset(name_group, 0, sizeof(fstring));
+
+ tmp = state->request.data.groupname;
+ if (!parse_domain_user(tmp, name_domain, name_group))
+ return WINBINDD_ERROR;
+
+ /* Get info for the domain */
+
+ if ((domain = find_domain_from_name(name_domain)) == NULL) {
+ DEBUG(0, ("could not get domain sid for domain %s\n",
+ name_domain));
+ return WINBINDD_ERROR;
+ }
+
+ /* Get rid and name type from name */
+
+ if (!winbindd_lookup_sid_by_name(domain, name_group, &group_sid,
+ &name_type)) {
+ DEBUG(1, ("group %s in domain %s does not exist\n",
+ name_group, name_domain));
+ return WINBINDD_ERROR;
+ }
+
+ if ((name_type != SID_NAME_ALIAS) && (name_type != SID_NAME_DOM_GRP)) {
+ DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
+ name_group, name_type));
+ return WINBINDD_ERROR;
+ }
+
+ if (!winbindd_idmap_get_gid_from_sid(&group_sid, &gid)) {
+ DEBUG(1, ("error converting unix gid to sid\n"));
+ return WINBINDD_ERROR;
+ }
+
+ if (!fill_grent(&state->response.data.gr, name_domain,
+ name_group, gid) ||
+ !fill_grent_mem(domain, &group_sid, name_type,
+ &state->response.data.gr.num_gr_mem,
+ &gr_mem, &gr_mem_len)) {
+ return WINBINDD_ERROR;
+ }
+
+ /* Group membership lives at start of extra data */
+
+ state->response.data.gr.gr_mem_ofs = 0;
+
+ state->response.length += gr_mem_len;
+ state->response.extra_data = gr_mem;
+
+ return WINBINDD_OK;
+}
+
+/* Return a group structure from a gid number */
+
+enum winbindd_result winbindd_getgrgid(struct winbindd_cli_state *state)
+{
+ struct winbindd_domain *domain;
+ DOM_SID group_sid;
+ enum SID_NAME_USE name_type;
+ fstring dom_name;
+ fstring group_name;
+ int gr_mem_len;
+ char *gr_mem;
+
+ DEBUG(3, ("[%5d]: getgrgid %d\n", state->pid,
+ state->request.data.gid));
+
+ /* Bug out if the gid isn't in the winbind range */
+
+ if ((state->request.data.gid < server_state.gid_low) ||
+ (state->request.data.gid > server_state.gid_high))
+ return WINBINDD_ERROR;
+
+ /* Get rid from gid */
+
+ if (!winbindd_idmap_get_sid_from_gid(state->request.data.gid, &group_sid)) {
+ DEBUG(1, ("could not convert gid %d to rid\n",
+ state->request.data.gid));
+ return WINBINDD_ERROR;
+ }
+
+ /* Get name from sid */
+
+ if (!winbindd_lookup_name_by_sid(&group_sid, dom_name, group_name, &name_type)) {
+ DEBUG(1, ("could not lookup sid\n"));
+ return WINBINDD_ERROR;
+ }
+
+ if (!((name_type == SID_NAME_ALIAS) ||
+ (name_type == SID_NAME_DOM_GRP))) {
+ DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
+ group_name, name_type));
+ return WINBINDD_ERROR;
+ }
+
+ /* Fill in group structure */
+
+ domain = find_domain_from_sid(&group_sid);
+
+ if (!domain) {
+ DEBUG(1,("Can't find domain from sid\n"));
+ return WINBINDD_ERROR;
+ }
+
+ if (!fill_grent(&state->response.data.gr, dom_name, group_name,
+ state->request.data.gid) ||
+ !fill_grent_mem(domain, &group_sid, name_type,
+ &state->response.data.gr.num_gr_mem,
+ &gr_mem, &gr_mem_len))
+ return WINBINDD_ERROR;
+
+ /* Group membership lives at start of extra data */
+
+ state->response.data.gr.gr_mem_ofs = 0;
+
+ state->response.length += gr_mem_len;
+ state->response.extra_data = gr_mem;
+
+ return WINBINDD_OK;
+}
+
+/*
+ * set/get/endgrent functions
+ */
+
+/* "Rewind" file pointer for group database enumeration */
+
+enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state)
+{
+ struct winbindd_domain *domain;
+
+ DEBUG(3, ("[%5d]: setgrent\n", state->pid));
+
+ /* Check user has enabled this */
+
+ if (!lp_winbind_enum_groups())
+ return WINBINDD_ERROR;
+
+ /* Free old static data if it exists */
+
+ if (state->getgrent_state != NULL) {
+ free_getent_state(state->getgrent_state);
+ state->getgrent_state = NULL;
+ }
+
+ /* Create sam pipes for each domain we know about */
+
+ for (domain = domain_list(); domain != NULL; domain = domain->next) {
+ struct getent_state *domain_state;
+
+ /* Create a state record for this domain */
+
+ if ((domain_state = (struct getent_state *)
+ malloc(sizeof(struct getent_state))) == NULL) {
+ DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ ZERO_STRUCTP(domain_state);
+
+ fstrcpy(domain_state->domain_name, domain->name);
+
+ /* Add to list of open domains */
+
+ DLIST_ADD(state->getgrent_state, domain_state);
+ }
+
+ return WINBINDD_OK;
+}
+
+/* Close file pointer to ntdom group database */
+
+enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state)
+{
+ DEBUG(3, ("[%5d]: endgrent\n", state->pid));
+
+ free_getent_state(state->getgrent_state);
+ state->getgrent_state = NULL;
+
+ return WINBINDD_OK;
+}
+
+/* Get the list of domain groups and domain aliases for a domain. We fill in
+ the sam_entries and num_sam_entries fields with domain group information.
+ The dispinfo_ndx field is incremented to the index of the next group to
+ fetch. Return True if some groups were returned, False otherwise. */
+
+#define MAX_FETCH_SAM_ENTRIES 100
+
+static BOOL get_sam_group_entries(struct getent_state *ent)
+{
+ NTSTATUS status;
+ uint32 num_entries;
+ struct acct_info *name_list = NULL, *tmp_name_list = NULL;
+ TALLOC_CTX *mem_ctx;
+ BOOL result = False;
+ struct acct_info *sam_grp_entries = NULL;
+ struct winbindd_domain *domain;
+
+ if (ent->got_sam_entries)
+ return False;
+
+ if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
+ ent->domain_name))) {
+ DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n"));
+ return False;
+ }
+
+ /* Free any existing group info */
+
+ SAFE_FREE(ent->sam_entries);
+ ent->num_sam_entries = 0;
+ ent->got_sam_entries = True;
+
+ /* Enumerate domain groups */
+
+ num_entries = 0;
+
+ if (!(domain = find_domain_from_name(ent->domain_name))) {
+ DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
+ goto done;
+ }
+
+ /* always get the domain global groups */
+
+ status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
+ result = False;
+ goto done;
+ }
+
+ /* Copy entries into return buffer */
+
+ if (num_entries) {
+ if ( !(name_list = malloc(sizeof(struct acct_info) * num_entries)) ) {
+ DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n",
+ num_entries));
+ result = False;
+ goto done;
+ }
+ memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
+ }
+
+ ent->num_sam_entries = num_entries;
+
+ /* get the domain local groups if we are a member of
+ a native win2k domain */
+
+ if ( domain->native_mode && domain->methods->enum_local_groups )
+ {
+ DEBUG(4,("get_sam_group_entries: Native Mode 2k domain; enumerating local groups as well\n"));
+
+ status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
+
+ if ( !NT_STATUS_IS_OK(status) ) {
+ DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n"));
+ num_entries = 0;
+ }
+ else
+ DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
+
+ /* Copy entries into return buffer */
+
+ if ( num_entries ) {
+ if ( !(tmp_name_list = Realloc( name_list, sizeof(struct acct_info) * (ent->num_sam_entries+num_entries))) )
+ {
+ DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n",
+ num_entries));
+ result = False;
+ SAFE_FREE( name_list );
+ goto done;
+ }
+
+ name_list = tmp_name_list;
+
+ memcpy( &name_list[ent->num_sam_entries], sam_grp_entries,
+ num_entries * sizeof(struct acct_info) );
+ }
+
+ ent->num_sam_entries += num_entries;
+ }
+
+
+ /* Fill in remaining fields */
+
+ ent->sam_entries = name_list;
+ ent->sam_entry_index = 0;
+
+ result = (ent->num_sam_entries > 0);
+
+ done:
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
+/* Fetch next group entry from ntdom database */
+
+#define MAX_GETGRENT_GROUPS 500
+
+enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state)
+{
+ struct getent_state *ent;
+ struct winbindd_gr *group_list = NULL;
+ int num_groups, group_list_ndx = 0, i, gr_mem_list_len = 0;
+ char *new_extra_data, *gr_mem_list = NULL;
+
+ DEBUG(3, ("[%5d]: getgrent\n", state->pid));
+
+ /* Check user has enabled this */
+
+ if (!lp_winbind_enum_groups())
+ return WINBINDD_ERROR;
+
+ num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
+
+ if ((state->response.extra_data =
+ malloc(num_groups * sizeof(struct winbindd_gr))) == NULL)
+ return WINBINDD_ERROR;
+
+ state->response.data.num_entries = 0;
+
+ group_list = (struct winbindd_gr *)state->response.extra_data;
+
+ if (!(ent = state->getgrent_state))
+ return WINBINDD_ERROR;
+
+ /* Start sending back groups */
+
+ for (i = 0; i < num_groups; i++) {
+ struct acct_info *name_list = NULL;
+ fstring domain_group_name;
+ uint32 result;
+ gid_t group_gid;
+ int gr_mem_len;
+ char *gr_mem, *new_gr_mem_list;
+ DOM_SID group_sid;
+ struct winbindd_domain *domain;
+
+ /* Do we need to fetch another chunk of groups? */
+
+ tryagain:
+
+ DEBUG(10, ("entry_index = %d, num_entries = %d\n",
+ ent->sam_entry_index, ent->num_sam_entries));
+
+ if (ent->num_sam_entries == ent->sam_entry_index) {
+
+ while(ent && !get_sam_group_entries(ent)) {
+ struct getent_state *next_ent;
+
+ DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name));
+
+ /* Free state information for this domain */
+
+ SAFE_FREE(ent->sam_entries);
+
+ next_ent = ent->next;
+ DLIST_REMOVE(state->getgrent_state, ent);
+
+ SAFE_FREE(ent);
+ ent = next_ent;
+ }
+
+ /* No more domains */
+
+ if (!ent)
+ break;
+ }
+
+ name_list = ent->sam_entries;
+
+ if (!(domain =
+ find_domain_from_name(ent->domain_name))) {
+ DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
+ result = False;
+ goto done;
+ }
+
+ /* Lookup group info */
+
+ sid_copy(&group_sid, &domain->sid);
+ sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
+
+ if (!winbindd_idmap_get_gid_from_sid(
+ &group_sid,
+ &group_gid)) {
+
+ DEBUG(1, ("could not look up gid for group %s\n",
+ name_list[ent->sam_entry_index].acct_name));
+
+ ent->sam_entry_index++;
+ goto tryagain;
+ }
+
+ DEBUG(10, ("got gid %d for group %x\n", group_gid,
+ name_list[ent->sam_entry_index].rid));
+
+ /* Fill in group entry */
+
+ fill_domain_username(domain_group_name, ent->domain_name,
+ name_list[ent->sam_entry_index].acct_name);
+
+ result = fill_grent(&group_list[group_list_ndx],
+ ent->domain_name,
+ name_list[ent->sam_entry_index].acct_name,
+ group_gid);
+
+ /* Fill in group membership entry */
+
+ if (result) {
+ DOM_SID member_sid;
+ group_list[group_list_ndx].num_gr_mem = 0;
+ gr_mem = NULL;
+ gr_mem_len = 0;
+
+ /* Get group membership */
+ if (state->request.cmd == WINBINDD_GETGRLST) {
+ result = True;
+ } else {
+ sid_copy(&member_sid, &domain->sid);
+ sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
+ result = fill_grent_mem(
+ domain,
+ &member_sid,
+ SID_NAME_DOM_GRP,
+ &group_list[group_list_ndx].num_gr_mem,
+ &gr_mem, &gr_mem_len);
+ }
+ }
+
+ if (result) {
+ /* Append to group membership list */
+ new_gr_mem_list = Realloc(
+ gr_mem_list,
+ gr_mem_list_len + gr_mem_len);
+
+ if (!new_gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
+ DEBUG(0, ("out of memory\n"));
+ SAFE_FREE(gr_mem_list);
+ gr_mem_list_len = 0;
+ break;
+ }
+
+ DEBUG(10, ("list_len = %d, mem_len = %d\n",
+ gr_mem_list_len, gr_mem_len));
+
+ gr_mem_list = new_gr_mem_list;
+
+ memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
+ gr_mem_len);
+
+ SAFE_FREE(gr_mem);
+
+ group_list[group_list_ndx].gr_mem_ofs =
+ gr_mem_list_len;
+
+ gr_mem_list_len += gr_mem_len;
+ }
+
+ ent->sam_entry_index++;
+
+ /* Add group to return list */
+
+ if (result) {
+
+ DEBUG(10, ("adding group num_entries = %d\n",
+ state->response.data.num_entries));
+
+ group_list_ndx++;
+ state->response.data.num_entries++;
+
+ state->response.length +=
+ sizeof(struct winbindd_gr);
+
+ } else {
+ DEBUG(0, ("could not lookup domain group %s\n",
+ domain_group_name));
+ }
+ }
+
+ /* Copy the list of group memberships to the end of the extra data */
+
+ if (group_list_ndx == 0)
+ goto done;
+
+ new_extra_data = Realloc(
+ state->response.extra_data,
+ group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
+
+ if (!new_extra_data) {
+ DEBUG(0, ("out of memory\n"));
+ group_list_ndx = 0;
+ SAFE_FREE(state->response.extra_data);
+ SAFE_FREE(gr_mem_list);
+
+ return WINBINDD_ERROR;
+ }
+
+ state->response.extra_data = new_extra_data;
+
+ memcpy(&((char *)state->response.extra_data)
+ [group_list_ndx * sizeof(struct winbindd_gr)],
+ gr_mem_list, gr_mem_list_len);
+
+ SAFE_FREE(gr_mem_list);
+
+ state->response.length += gr_mem_list_len;
+
+ DEBUG(10, ("returning %d groups, length = %d\n",
+ group_list_ndx, gr_mem_list_len));
+
+ /* Out of domains */
+
+ done:
+
+ return (group_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
+}
+
+/* List domain groups without mapping to unix ids */
+
+enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state)
+{
+ uint32 total_entries = 0;
+ struct winbindd_domain *domain;
+ char *extra_data = NULL;
+ char *ted = NULL;
+ unsigned int extra_data_len = 0, i;
+
+ DEBUG(3, ("[%5d]: list groups\n", state->pid));
+
+ /* Enumerate over trusted domains */
+
+ for (domain = domain_list(); domain; domain = domain->next) {
+ struct getent_state groups;
+
+ ZERO_STRUCT(groups);
+
+ /* Get list of sam groups */
+ ZERO_STRUCT(groups);
+ fstrcpy(groups.domain_name, domain->name);
+
+ get_sam_group_entries(&groups);
+
+ if (groups.num_sam_entries == 0) {
+ /* this domain is empty or in an error state */
+ continue;
+ }
+
+ /* keep track the of the total number of groups seen so
+ far over all domains */
+ total_entries += groups.num_sam_entries;
+
+ /* Allocate some memory for extra data. Note that we limit
+ account names to sizeof(fstring) = 128 characters. */
+ ted = Realloc(extra_data, sizeof(fstring) * total_entries);
+
+ if (!ted) {
+ DEBUG(0,("failed to enlarge buffer!\n"));
+ SAFE_FREE(extra_data);
+ return WINBINDD_ERROR;
+ } else
+ extra_data = ted;
+
+ /* Pack group list into extra data fields */
+ for (i = 0; i < groups.num_sam_entries; i++) {
+ char *group_name = ((struct acct_info *)
+ groups.sam_entries)[i].acct_name;
+ fstring name;
+
+ fill_domain_username(name, domain->name, group_name);
+ /* Append to extra data */
+ memcpy(&extra_data[extra_data_len], name,
+ strlen(name));
+ extra_data_len += strlen(name);
+ extra_data[extra_data_len++] = ',';
+ }
+
+ free(groups.sam_entries);
+ }
+
+ /* Assign extra_data fields in response structure */
+ if (extra_data) {
+ extra_data[extra_data_len - 1] = '\0';
+ state->response.extra_data = extra_data;
+ state->response.length += extra_data_len;
+ }
+
+ /* No domains may have responded but that's still OK so don't
+ return an error. */
+
+ return WINBINDD_OK;
+}
+
+/* Get user supplementary groups. This is much quicker than trying to
+ invert the groups database. */
+
+enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state)
+{
+ fstring name_domain, name_user;
+ DOM_SID user_sid;
+ enum SID_NAME_USE name_type;
+ uint32 num_groups, num_gids;
+ NTSTATUS status;
+ DOM_SID **user_gids;
+ struct winbindd_domain *domain;
+ enum winbindd_result result = WINBINDD_ERROR;
+ gid_t *gid_list;
+ unsigned int i;
+ TALLOC_CTX *mem_ctx;
+
+ /* Ensure null termination */
+ state->request.data.username[sizeof(state->request.data.username)-1]='\0';
+
+ DEBUG(3, ("[%5d]: getgroups %s\n", state->pid,
+ state->request.data.username));
+
+ if (!(mem_ctx = talloc_init("winbindd_getgroups(%s)",
+ state->request.data.username)))
+ return WINBINDD_ERROR;
+
+ /* Parse domain and username */
+
+ if (!parse_domain_user(state->request.data.username, name_domain,
+ name_user))
+ goto done;
+
+ /* Get info for the domain */
+
+ if ((domain = find_domain_from_name(name_domain)) == NULL) {
+ DEBUG(0, ("could not find domain entry for domain %s\n",
+ name_domain));
+ goto done;
+ }
+
+ /* Get rid and name type from name. The following costs 1 packet */
+
+ if (!winbindd_lookup_sid_by_name(domain, name_user, &user_sid,
+ &name_type)) {
+ DEBUG(1, ("user '%s' does not exist\n", name_user));
+ goto done;
+ }
+
+ if (name_type != SID_NAME_USER) {
+ DEBUG(1, ("name '%s' is not a user name: %d\n",
+ name_user, name_type));
+ goto done;
+ }
+
+ status = domain->methods->lookup_usergroups(domain, mem_ctx,
+ &user_sid, &num_groups,
+ &user_gids);
+ if (!NT_STATUS_IS_OK(status)) goto done;
+
+ /* Copy data back to client */
+
+ num_gids = 0;
+ gid_list = malloc(sizeof(gid_t) * num_groups);
+
+ if (state->response.extra_data)
+ goto done;
+
+ for (i = 0; i < num_groups; i++) {
+ if (!winbindd_idmap_get_gid_from_sid(
+ user_gids[i],
+ &gid_list[num_gids])) {
+ fstring sid_string;
+
+ DEBUG(1, ("unable to convert group sid %s to gid\n",
+ sid_to_string(sid_string, user_gids[i])));
+ continue;
+ }
+
+ num_gids++;
+ }
+
+ state->response.data.num_entries = num_gids;
+ state->response.extra_data = gid_list;
+ state->response.length += num_gids * sizeof(gid_t);
+
+ result = WINBINDD_OK;
+
+ done:
+
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
diff --git a/source4/nsswitch/winbindd_idmap.c b/source4/nsswitch/winbindd_idmap.c
new file mode 100644
index 0000000000..de547cde41
--- /dev/null
+++ b/source4/nsswitch/winbindd_idmap.c
@@ -0,0 +1,196 @@
+/*
+ Unix SMB/CIFS implementation.
+ Winbind ID Mapping
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Anthony Liguori <aliguor@us.ibm.com> 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "winbindd.h"
+
+static struct {
+ const char *name;
+ /* Function to create a member of the idmap_methods list */
+ BOOL (*reg_meth)(struct idmap_methods **methods);
+ struct idmap_methods *methods;
+} builtin_idmap_functions[] = {
+ { "tdb", winbind_idmap_reg_tdb, NULL },
+ /* { "ldap", winbind_idmap_reg_ldap, NULL },*/
+ { NULL, NULL, NULL }
+};
+
+/* singleton pattern: uberlazy evaluation */
+static struct idmap_methods *impl;
+
+static struct idmap_methods *get_impl(const char *name)
+{
+ int i = 0;
+ struct idmap_methods *ret = NULL;
+
+ while (builtin_idmap_functions[i].name &&
+ strcmp(builtin_idmap_functions[i].name, name)) {
+ i++;
+ }
+
+ if (builtin_idmap_functions[i].name) {
+ if (!builtin_idmap_functions[i].methods) {
+ builtin_idmap_functions[i].reg_meth(&builtin_idmap_functions[i].methods);
+ }
+
+ ret = builtin_idmap_functions[i].methods;
+ }
+
+ return ret;
+}
+
+/* Initialize backend */
+BOOL winbindd_idmap_init(void)
+{
+ BOOL ret = False;
+
+ DEBUG(3, ("winbindd_idmap_init: using '%s' as backend\n",
+ lp_idmap_backend()));
+
+ if (!impl) {
+ impl = get_impl(lp_idmap_backend());
+ if (!impl) {
+ DEBUG(0, ("winbindd_idmap_init: could not load backend '%s'\n",
+ lp_idmap_backend()));
+ }
+ }
+
+ if (impl) {
+ ret = impl->init();
+ }
+
+ DEBUG(3, ("winbind_idmap_init: returning %s\n", ret ? "true" : "false"));
+
+ return ret;
+}
+
+/* Get UID from SID */
+BOOL winbindd_idmap_get_uid_from_sid(DOM_SID *sid, uid_t *uid)
+{
+ BOOL ret = False;
+
+ if (!impl) {
+ impl = get_impl(lp_idmap_backend());
+ if (!impl) {
+ DEBUG(0, ("winbindd_idmap_init: could not load backend '%s'\n",
+ lp_idmap_backend()));
+ }
+ }
+
+ if (impl) {
+ ret = impl->get_uid_from_sid(sid, uid);
+ }
+
+ return ret;
+}
+
+/* Get GID from SID */
+BOOL winbindd_idmap_get_gid_from_sid(DOM_SID *sid, gid_t *gid)
+{
+ BOOL ret = False;
+
+ if (!impl) {
+ impl = get_impl(lp_idmap_backend());
+ if (!impl) {
+ DEBUG(0, ("winbindd_idmap_init: could not load backend '%s'\n",
+ lp_idmap_backend()));
+ }
+ }
+
+ if (impl) {
+ ret = impl->get_gid_from_sid(sid, gid);
+ }
+
+ return ret;
+}
+
+/* Get SID from UID */
+BOOL winbindd_idmap_get_sid_from_uid(uid_t uid, DOM_SID *sid)
+{
+ BOOL ret = False;
+
+ if (!impl) {
+ impl = get_impl(lp_idmap_backend());
+ if (!impl) {
+ DEBUG(0, ("winbindd_idmap_init: could not load backend '%s'\n",
+ lp_idmap_backend()));
+ }
+ }
+
+ if (impl) {
+ ret = impl->get_sid_from_uid(uid, sid);
+ }
+
+ return ret;
+}
+
+/* Get SID from GID */
+BOOL winbindd_idmap_get_sid_from_gid(gid_t gid, DOM_SID *sid)
+{
+ BOOL ret = False;
+
+ if (!impl) {
+ impl = get_impl(lp_idmap_backend());
+ }
+
+ if (impl) {
+ ret = impl->get_sid_from_gid(gid, sid);
+ } else {
+ DEBUG(0, ("winbindd_idmap_init: could not load backend '%s'\n",
+ lp_idmap_backend()));
+ }
+
+ return ret;
+}
+
+/* Close backend */
+BOOL winbindd_idmap_close(void)
+{
+ BOOL ret = False;
+
+ if (!impl) {
+ impl = get_impl(lp_idmap_backend());
+ }
+
+ if (impl) {
+ ret = impl->close();
+ } else {
+ DEBUG(0, ("winbindd_idmap_init: could not load backend '%s'\n",
+ lp_idmap_backend()));
+ }
+
+ return ret;
+}
+
+/* Dump backend status */
+void winbindd_idmap_status(void)
+{
+ if (!impl) {
+ impl = get_impl(lp_idmap_backend());
+ }
+
+ if (impl) {
+ impl->status();
+ } else {
+ DEBUG(0, ("winbindd_idmap_init: could not load backend '%s'\n",
+ lp_idmap_backend()));
+ }
+}
+
diff --git a/source4/nsswitch/winbindd_idmap_tdb.c b/source4/nsswitch/winbindd_idmap_tdb.c
new file mode 100644
index 0000000000..911b3b41d2
--- /dev/null
+++ b/source4/nsswitch/winbindd_idmap_tdb.c
@@ -0,0 +1,441 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon - user related function
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Anthony Liguori 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/* High water mark keys */
+#define HWM_GROUP "GROUP HWM"
+#define HWM_USER "USER HWM"
+
+/* idmap version determines auto-conversion */
+#define IDMAP_VERSION 2
+
+/* Globals */
+static TDB_CONTEXT *idmap_tdb;
+
+/* convert one record to the new format */
+static int tdb_convert_fn(TDB_CONTEXT * tdb, TDB_DATA key, TDB_DATA data,
+ void *ignored)
+{
+ struct winbindd_domain *domain;
+ char *p;
+ DOM_SID sid;
+ uint32 rid;
+ fstring keystr;
+ fstring dom_name;
+ TDB_DATA key2;
+
+ p = strchr(key.dptr, '/');
+ if (!p)
+ return 0;
+
+ *p = 0;
+ fstrcpy(dom_name, key.dptr);
+ *p++ = '/';
+
+ domain = find_domain_from_name(dom_name);
+ if (!domain) {
+ /* We must delete the old record. */
+ DEBUG(0,
+ ("winbindd: tdb_convert_fn : Unable to find domain %s\n",
+ dom_name));
+ DEBUG(0,
+ ("winbindd: tdb_convert_fn : deleting record %s\n",
+ key.dptr));
+ tdb_delete(idmap_tdb, key);
+ return 0;
+ }
+
+ rid = atoi(p);
+
+ sid_copy(&sid, &domain->sid);
+ sid_append_rid(&sid, rid);
+
+ sid_to_string(keystr, &sid);
+ key2.dptr = keystr;
+ key2.dsize = strlen(keystr) + 1;
+
+ if (tdb_store(idmap_tdb, key2, data, TDB_INSERT) != 0) {
+ /* not good! */
+ DEBUG(0,
+ ("winbindd: tdb_convert_fn : Unable to update record %s\n",
+ key2.dptr));
+ DEBUG(0,
+ ("winbindd: tdb_convert_fn : conversion failed - idmap corrupt ?\n"));
+ return -1;
+ }
+
+ if (tdb_store(idmap_tdb, data, key2, TDB_REPLACE) != 0) {
+ /* not good! */
+ DEBUG(0,
+ ("winbindd: tdb_convert_fn : Unable to update record %s\n",
+ data.dptr));
+ DEBUG(0,
+ ("winbindd: tdb_convert_fn : conversion failed - idmap corrupt ?\n"));
+ return -1;
+ }
+
+ tdb_delete(idmap_tdb, key);
+
+ return 0;
+}
+
+/*****************************************************************************
+ Convert the idmap database from an older version.
+*****************************************************************************/
+static BOOL tdb_idmap_convert(const char *idmap_name)
+{
+ int32 vers = tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION");
+ BOOL bigendianheader =
+ (idmap_tdb->flags & TDB_BIGENDIAN) ? True : False;
+
+ if (vers == IDMAP_VERSION)
+ return True;
+
+ if (((vers == -1) && bigendianheader)
+ || (IREV(vers) == IDMAP_VERSION)) {
+ /* Arrggghh ! Bytereversed or old big-endian - make order independent ! */
+ /*
+ * high and low records were created on a
+ * big endian machine and will need byte-reversing.
+ */
+
+ int32 wm;
+
+ wm = tdb_fetch_int32(idmap_tdb, HWM_USER);
+
+ if (wm != -1) {
+ wm = IREV(wm);
+ } else
+ wm = server_state.uid_low;
+
+ if (tdb_store_int32(idmap_tdb, HWM_USER, wm) == -1) {
+ DEBUG(0,
+ ("tdb_idmap_convert: Unable to byteswap user hwm in idmap database\n"));
+ return False;
+ }
+
+ wm = tdb_fetch_int32(idmap_tdb, HWM_GROUP);
+ if (wm != -1) {
+ wm = IREV(wm);
+ } else
+ wm = server_state.gid_low;
+
+ if (tdb_store_int32(idmap_tdb, HWM_GROUP, wm) == -1) {
+ DEBUG(0,
+ ("tdb_idmap_convert: Unable to byteswap group hwm in idmap database\n"));
+ return False;
+ }
+ }
+
+ /* the old format stored as DOMAIN/rid - now we store the SID direct */
+ tdb_traverse(idmap_tdb, tdb_convert_fn, NULL);
+
+ if (tdb_store_int32(idmap_tdb, "IDMAP_VERSION", IDMAP_VERSION) ==
+ -1) {
+ DEBUG(0,
+ ("tdb_idmap_convert: Unable to byteswap group hwm in idmap database\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/* Allocate either a user or group id from the pool */
+static BOOL tdb_allocate_id(uid_t * id, BOOL isgroup)
+{
+ int hwm;
+
+ /* Get current high water mark */
+ if ((hwm = tdb_fetch_int32(idmap_tdb,
+ isgroup ? HWM_GROUP : HWM_USER)) ==
+ -1) {
+ return False;
+ }
+
+ /* Return next available uid in list */
+ if ((isgroup && (hwm > server_state.gid_high)) ||
+ (!isgroup && (hwm > server_state.uid_high))) {
+ DEBUG(0,
+ ("winbind %sid range full!\n", isgroup ? "g" : "u"));
+ return False;
+ }
+
+ if (id) {
+ *id = hwm;
+ }
+
+ hwm++;
+
+ /* Store new high water mark */
+ tdb_store_int32(idmap_tdb, isgroup ? HWM_GROUP : HWM_USER, hwm);
+
+ return True;
+}
+
+/* Get a sid from an id */
+static BOOL tdb_get_sid_from_id(int id, DOM_SID * sid, BOOL isgroup)
+{
+ TDB_DATA key, data;
+ fstring keystr;
+ BOOL result = False;
+
+ slprintf(keystr, sizeof(keystr), "%s %d", isgroup ? "GID" : "UID",
+ id);
+
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ data = tdb_fetch(idmap_tdb, key);
+
+ if (data.dptr) {
+ result = string_to_sid(sid, data.dptr);
+ SAFE_FREE(data.dptr);
+ }
+
+ return result;
+}
+
+/* Get an id from a sid */
+static BOOL tdb_get_id_from_sid(DOM_SID * sid, uid_t * id, BOOL isgroup)
+{
+ TDB_DATA data, key;
+ fstring keystr;
+ BOOL result = False;
+
+ /* Check if sid is present in database */
+ sid_to_string(keystr, sid);
+
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ data = tdb_fetch(idmap_tdb, key);
+
+ if (data.dptr) {
+ fstring scanstr;
+ int the_id;
+
+ /* Parse and return existing uid */
+ fstrcpy(scanstr, isgroup ? "GID" : "UID");
+ fstrcat(scanstr, " %d");
+
+ if (sscanf(data.dptr, scanstr, &the_id) == 1) {
+ /* Store uid */
+ if (id) {
+ *id = the_id;
+ }
+
+ result = True;
+ }
+
+ SAFE_FREE(data.dptr);
+ } else {
+
+ /* Allocate a new id for this sid */
+ if (id && tdb_allocate_id(id, isgroup)) {
+ fstring keystr2;
+
+ /* Store new id */
+ slprintf(keystr2, sizeof(keystr2), "%s %d",
+ isgroup ? "GID" : "UID", *id);
+
+ data.dptr = keystr2;
+ data.dsize = strlen(keystr2) + 1;
+
+ tdb_store(idmap_tdb, key, data, TDB_REPLACE);
+ tdb_store(idmap_tdb, data, key, TDB_REPLACE);
+
+ result = True;
+ }
+ }
+
+ return result;
+}
+
+/*****************************************************************************
+ Initialise idmap database.
+*****************************************************************************/
+static BOOL tdb_idmap_init(void)
+{
+ /* Open tdb cache */
+ if (!(idmap_tdb = tdb_open_log(lock_path("winbindd_idmap.tdb"), 0,
+ TDB_DEFAULT, O_RDWR | O_CREAT,
+ 0600))) {
+ DEBUG(0,
+ ("winbindd_idmap_init: Unable to open idmap database\n"));
+ return False;
+ }
+
+ /* possibly convert from an earlier version */
+ if (!tdb_idmap_convert(lock_path("winbindd_idmap.tdb"))) {
+ DEBUG(0,
+ ("winbindd_idmap_init: Unable to open idmap database\n"));
+ return False;
+ }
+
+ /* Create high water marks for group and user id */
+ if (tdb_fetch_int32(idmap_tdb, HWM_USER) == -1) {
+ if (tdb_store_int32
+ (idmap_tdb, HWM_USER, server_state.uid_low) == -1) {
+ DEBUG(0,
+ ("winbindd_idmap_init: Unable to initialise user hwm in idmap database\n"));
+ return False;
+ }
+ }
+
+ if (tdb_fetch_int32(idmap_tdb, HWM_GROUP) == -1) {
+ if (tdb_store_int32
+ (idmap_tdb, HWM_GROUP, server_state.gid_low) == -1) {
+ DEBUG(0,
+ ("winbindd_idmap_init: Unable to initialise group hwm in idmap database\n"));
+ return False;
+ }
+ }
+
+ return True;
+}
+
+/* Get a sid from a uid */
+static BOOL tdb_get_sid_from_uid(uid_t uid, DOM_SID * sid)
+{
+ return tdb_get_sid_from_id((int) uid, sid, False);
+}
+
+/* Get a sid from a gid */
+static BOOL tdb_get_sid_from_gid(gid_t gid, DOM_SID * sid)
+{
+ return tdb_get_sid_from_id((int) gid, sid, True);
+}
+
+/* Get a uid from a sid */
+static BOOL tdb_get_uid_from_sid(DOM_SID * sid, uid_t * uid)
+{
+ return tdb_get_id_from_sid(sid, uid, False);
+}
+
+/* Get a gid from a group sid */
+static BOOL tdb_get_gid_from_sid(DOM_SID * sid, gid_t * gid)
+{
+ return tdb_get_id_from_sid(sid, gid, True);
+}
+
+/* Close the tdb */
+static BOOL tdb_idmap_close(void)
+{
+ if (idmap_tdb)
+ return (tdb_close(idmap_tdb) == 0);
+ return True;
+}
+
+
+/* Dump status information to log file. Display different stuff based on
+ the debug level:
+
+ Debug Level Information Displayed
+ =================================================================
+ 0 Percentage of [ug]id range allocated
+ 0 High water marks (next allocated ids)
+*/
+
+#define DUMP_INFO 0
+
+static void tdb_idmap_status(void)
+{
+ int user_hwm, group_hwm;
+
+ DEBUG(0, ("winbindd idmap status:\n"));
+
+ /* Get current high water marks */
+
+ if ((user_hwm = tdb_fetch_int32(idmap_tdb, HWM_USER)) == -1) {
+ DEBUG(DUMP_INFO,
+ ("\tCould not get userid high water mark!\n"));
+ }
+
+ if ((group_hwm = tdb_fetch_int32(idmap_tdb, HWM_GROUP)) == -1) {
+ DEBUG(DUMP_INFO,
+ ("\tCould not get groupid high water mark!\n"));
+ }
+
+ /* Display next ids to allocate */
+
+ if (user_hwm != -1) {
+ DEBUG(DUMP_INFO,
+ ("\tNext userid to allocate is %d\n", user_hwm));
+ }
+
+ if (group_hwm != -1) {
+ DEBUG(DUMP_INFO,
+ ("\tNext groupid to allocate is %d\n", group_hwm));
+ }
+
+ /* Display percentage of id range already allocated. */
+
+ if (user_hwm != -1) {
+ int num_users = user_hwm - server_state.uid_low;
+ int total_users =
+ server_state.uid_high - server_state.uid_low;
+
+ DEBUG(DUMP_INFO,
+ ("\tUser id range is %d%% full (%d of %d)\n",
+ num_users * 100 / total_users, num_users,
+ total_users));
+ }
+
+ if (group_hwm != -1) {
+ int num_groups = group_hwm - server_state.gid_low;
+ int total_groups =
+ server_state.gid_high - server_state.gid_low;
+
+ DEBUG(DUMP_INFO,
+ ("\tGroup id range is %d%% full (%d of %d)\n",
+ num_groups * 100 / total_groups, num_groups,
+ total_groups));
+ }
+
+ /* Display complete mapping of users and groups to rids */
+}
+
+struct idmap_methods tdb_idmap_methods = {
+ tdb_idmap_init,
+
+ tdb_get_sid_from_uid,
+ tdb_get_sid_from_gid,
+
+ tdb_get_uid_from_sid,
+ tdb_get_gid_from_sid,
+
+ tdb_idmap_close,
+
+ tdb_idmap_status
+};
+
+BOOL winbind_idmap_reg_tdb(struct idmap_methods **meth)
+{
+ *meth = &tdb_idmap_methods;
+
+ return True;
+}
diff --git a/source4/nsswitch/winbindd_misc.c b/source4/nsswitch/winbindd_misc.c
new file mode 100644
index 0000000000..b85cd0570d
--- /dev/null
+++ b/source4/nsswitch/winbindd_misc.c
@@ -0,0 +1,235 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon - miscellaneous other functions
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Andrew Bartlett 2002
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/* Check the machine account password is valid */
+
+enum winbindd_result winbindd_check_machine_acct(struct winbindd_cli_state *state)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uchar trust_passwd[16];
+ int num_retries = 0;
+ struct cli_state *cli;
+ DEBUG(3, ("[%5d]: check machine account\n", state->pid));
+
+ /* Get trust account password */
+
+ again:
+ if (!secrets_fetch_trust_account_password(
+ lp_workgroup(), trust_passwd, NULL)) {
+ result = NT_STATUS_INTERNAL_ERROR;
+ goto done;
+ }
+
+ /* This call does a cli_nt_setup_creds() which implicitly checks
+ the trust account password. */
+
+ /* Don't shut this down - it belongs to the connection cache code */
+ result = cm_get_netlogon_cli(lp_workgroup(), trust_passwd, &cli);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
+ goto done;
+ }
+
+ /* There is a race condition between fetching the trust account
+ password and the periodic machine password change. So it's
+ possible that the trust account password has been changed on us.
+ We are returned NT_STATUS_ACCESS_DENIED if this happens. */
+
+#define MAX_RETRIES 8
+
+ if ((num_retries < MAX_RETRIES) &&
+ NT_STATUS_V(result) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED)) {
+ num_retries++;
+ goto again;
+ }
+
+ /* Pass back result code - zero for success, other values for
+ specific failures. */
+
+ DEBUG(3, ("secret is %s\n", NT_STATUS_IS_OK(result) ?
+ "good" : "bad"));
+
+ done:
+ state->response.data.auth.nt_status = NT_STATUS_V(result);
+ fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
+ fstrcpy(state->response.data.auth.error_string, nt_errstr(result));
+ state->response.data.auth.pam_error = nt_status_to_pam(result);
+
+ DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Checking the trust account password returned %s\n",
+ state->response.data.auth.nt_status_string));
+
+ return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
+}
+
+enum winbindd_result winbindd_list_trusted_domains(struct winbindd_cli_state
+ *state)
+{
+ struct winbindd_domain *domain;
+ int total_entries = 0, extra_data_len = 0;
+ char *ted, *extra_data = NULL;
+
+ DEBUG(3, ("[%5d]: list trusted domains\n", state->pid));
+
+ /* We need to refresh the trusted domain list as the domains may
+ have changed since we last looked. There may be a sequence
+ number or something we should use but I haven't found it yet. */
+
+ if (!init_domain_list()) {
+ DEBUG(1, ("winbindd_list_trusted_domains: could not "
+ "refresh trusted domain list\n"));
+ return WINBINDD_ERROR;
+ }
+
+ for(domain = domain_list(); domain; domain = domain->next) {
+
+ /* Skip own domain */
+
+ if (strequal(domain->name, lp_workgroup())) continue;
+
+ /* Add domain to list */
+
+ total_entries++;
+ ted = Realloc(extra_data, sizeof(fstring) *
+ total_entries);
+
+ if (!ted) {
+ DEBUG(0,("winbindd_list_trusted_domains: failed to enlarge buffer!\n"));
+ SAFE_FREE(extra_data);
+ return WINBINDD_ERROR;
+ } else
+ extra_data = ted;
+
+ memcpy(&extra_data[extra_data_len], domain->name,
+ strlen(domain->name));
+
+ extra_data_len += strlen(domain->name);
+ extra_data[extra_data_len++] = ',';
+ }
+
+ if (extra_data) {
+ if (extra_data_len > 1)
+ extra_data[extra_data_len - 1] = '\0';
+ state->response.extra_data = extra_data;
+ state->response.length += extra_data_len;
+ }
+
+ return WINBINDD_OK;
+}
+
+
+enum winbindd_result winbindd_show_sequence(struct winbindd_cli_state *state)
+{
+ struct winbindd_domain *domain;
+ char *extra_data = NULL;
+
+ DEBUG(3, ("[%5d]: show sequence\n", state->pid));
+
+ extra_data = strdup("");
+
+ /* this makes for a very simple data format, and is easily parsable as well
+ if that is ever needed */
+ for (domain = domain_list(); domain; domain = domain->next) {
+ char *s;
+
+ domain->methods->sequence_number(domain, &domain->sequence_number);
+
+ if (DOM_SEQUENCE_NONE == (unsigned)domain->sequence_number) {
+ asprintf(&s,"%s%s : DISCONNECTED\n", extra_data,
+ domain->name);
+ } else {
+ asprintf(&s,"%s%s : %u\n", extra_data,
+ domain->name, (unsigned)domain->sequence_number);
+ }
+ free(extra_data);
+ extra_data = s;
+ }
+
+ state->response.extra_data = extra_data;
+ /* must add one to length to copy the 0 for string termination */
+ state->response.length += strlen(extra_data) + 1;
+
+ return WINBINDD_OK;
+}
+
+enum winbindd_result winbindd_ping(struct winbindd_cli_state
+ *state)
+{
+ DEBUG(3, ("[%5d]: ping\n", state->pid));
+
+ return WINBINDD_OK;
+}
+
+/* List various tidbits of information */
+
+enum winbindd_result winbindd_info(struct winbindd_cli_state *state)
+{
+
+ DEBUG(3, ("[%5d]: request misc info\n", state->pid));
+
+ state->response.data.info.winbind_separator = *lp_winbind_separator();
+ fstrcpy(state->response.data.info.samba_version, VERSION);
+
+ return WINBINDD_OK;
+}
+
+/* Tell the client the current interface version */
+
+enum winbindd_result winbindd_interface_version(struct winbindd_cli_state *state)
+{
+
+ DEBUG(3, ("[%5d]: request interface version\n", state->pid));
+
+ state->response.data.interface_version = WINBIND_INTERFACE_VERSION;
+
+ return WINBINDD_OK;
+}
+
+/* What domain are we a member of? */
+
+enum winbindd_result winbindd_domain_name(struct winbindd_cli_state *state)
+{
+
+ DEBUG(3, ("[%5d]: request domain name\n", state->pid));
+
+ fstrcpy(state->response.data.domain_name, lp_workgroup());
+
+ return WINBINDD_OK;
+}
+
+/* What's my name again? */
+
+enum winbindd_result winbindd_netbios_name(struct winbindd_cli_state *state)
+{
+
+ DEBUG(3, ("[%5d]: request netbios name\n", state->pid));
+
+ fstrcpy(state->response.data.netbios_name, lp_netbios_name());
+
+ return WINBINDD_OK;
+}
diff --git a/source4/nsswitch/winbindd_nss.h b/source4/nsswitch/winbindd_nss.h
new file mode 100644
index 0000000000..2c87a77100
--- /dev/null
+++ b/source4/nsswitch/winbindd_nss.h
@@ -0,0 +1,242 @@
+/*
+ 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 Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#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 */
+#define WINBINDD_SOCKET_DIR "/tmp/.winbindd" /* Name of PF_UNIX dir */
+
+#define WINBINDD_DOMAIN_ENV "WINBINDD_DOMAIN" /* Environment variables */
+#define WINBINDD_DONT_ENV "_NO_WINBINDD"
+
+/* Update this when you change the interface. */
+
+#define WINBIND_INTERFACE_VERSION 7
+
+/* 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,
+
+ /* 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,
+
+ /* Lookup functions */
+
+ WINBINDD_SID_TO_UID,
+ WINBINDD_SID_TO_GID,
+ WINBINDD_UID_TO_SID,
+ WINBINDD_GID_TO_SID,
+
+ /* Miscellaneous other stuff */
+
+ 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_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 */
+ /* Placeholder for end of cmd list */
+
+ WINBINDD_NUM_CMDS
+};
+
+#define WINBIND_PAM_INFO3_NDR 0x0001
+#define WINBIND_PAM_INFO3_TEXT 0x0002
+#define WINBIND_PAM_NTKEY 0x0004
+#define WINBIND_PAM_LMKEY 0x0008
+#define WINBIND_PAM_CONTACT_TRUSTDOM 0x0010
+
+/* Winbind request structure */
+
+struct winbindd_request {
+ uint32 length;
+ enum winbindd_cmd cmd; /* Winbindd command to execute */
+ pid_t pid; /* pid of calling process */
+
+ 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;
+ } auth; /* pam_winbind auth module */
+ struct {
+ unsigned char chal[8];
+ fstring user;
+ fstring domain;
+ fstring lm_resp;
+ uint16 lm_resp_len;
+ fstring nt_resp;
+ uint16 nt_resp_len;
+ fstring workstation;
+ uint32 flags;
+ } auth_crap;
+ struct {
+ fstring user;
+ fstring oldpass;
+ fstring newpass;
+ } chauthtok; /* pam_winbind passwd module */
+ fstring sid; /* lookupsid, sid_to_[ug]id */
+ struct {
+ fstring dom_name; /* lookupname */
+ fstring name;
+ } name;
+ uint32 num_entries; /* getpwent, getgrent */
+ } data;
+ char null_term;
+};
+
+/* Response values */
+
+enum winbindd_result {
+ WINBINDD_ERROR,
+ WINBINDD_OK
+};
+
+/* Winbind response structure */
+
+struct winbindd_response {
+
+ /* Header information */
+
+ uint32 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 {
+ fstring pw_name;
+ fstring pw_passwd;
+ uid_t pw_uid;
+ gid_t pw_gid;
+ fstring pw_gecos;
+ fstring pw_dir;
+ fstring pw_shell;
+ } pw;
+
+ /* getgrnam, getgrgid */
+
+ struct winbindd_gr {
+ fstring gr_name;
+ fstring gr_passwd;
+ gid_t gr_gid;
+ int num_gr_mem;
+ int gr_mem_ofs; /* offset to group membership */
+ } gr;
+
+ uint32 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;
+
+ struct auth_reply {
+ uint32 nt_status;
+ fstring nt_status_string;
+ fstring error_string;
+ int pam_error;
+ char nt_session_key[16];
+ char first_8_lm_hash[8];
+ } auth;
+ } data;
+
+ /* Variable length return data */
+
+ void *extra_data; /* getgrnam, getgrgid, getgrent */
+};
+
+#endif
diff --git a/source4/nsswitch/winbindd_pam.c b/source4/nsswitch/winbindd_pam.c
new file mode 100644
index 0000000000..8a0326b42c
--- /dev/null
+++ b/source4/nsswitch/winbindd_pam.c
@@ -0,0 +1,368 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon - pam auth funcions
+
+ Copyright (C) Andrew Tridgell 2000
+ Copyright (C) Tim Potter 2001
+ Copyright (C) Andrew Bartlett 2001-2002
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "winbindd.h"
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+
+static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
+ struct winbindd_cli_state *state,
+ NET_USER_INFO_3 *info3)
+{
+ prs_struct ps;
+ uint32 size;
+ if (!prs_init(&ps, 256 /* Random, non-zero number */, mem_ctx, MARSHALL)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (!net_io_user_info3("", info3, &ps, 1, 3)) {
+ prs_mem_free(&ps);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ size = prs_data_size(&ps);
+ state->response.extra_data = malloc(size);
+ if (!state->response.extra_data) {
+ prs_mem_free(&ps);
+ return NT_STATUS_NO_MEMORY;
+ }
+ prs_copy_all_data_out(state->response.extra_data, &ps);
+ state->response.length += size;
+ prs_mem_free(&ps);
+ return NT_STATUS_OK;
+}
+
+/* Return a password structure from a username. */
+
+enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state)
+{
+ NTSTATUS result;
+ fstring name_domain, name_user;
+ unsigned char trust_passwd[16];
+ time_t last_change_time;
+ uint32 smb_uid_low;
+ NET_USER_INFO_3 info3;
+ struct cli_state *cli = NULL;
+ uchar chal[8];
+ TALLOC_CTX *mem_ctx = NULL;
+ DATA_BLOB lm_resp;
+ DATA_BLOB nt_resp;
+
+ /* Ensure null termination */
+ state->request.data.auth.user[sizeof(state->request.data.auth.user)-1]='\0';
+
+ /* Ensure null termination */
+ state->request.data.auth.pass[sizeof(state->request.data.auth.pass)-1]='\0';
+
+ DEBUG(3, ("[%5d]: pam auth %s\n", state->pid,
+ state->request.data.auth.user));
+
+ if (!(mem_ctx = talloc_init("winbind pam auth for %s", state->request.data.auth.user))) {
+ DEBUG(0, ("winbindd_pam_auth: could not talloc_init()!\n"));
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ /* Parse domain and username */
+
+ if (!parse_domain_user(state->request.data.auth.user, name_domain,
+ name_user)) {
+ DEBUG(5,("no domain separator (%s) in username (%s) - failing auth\n", lp_winbind_separator(), state->request.data.auth.user));
+ result = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ {
+ unsigned char local_lm_response[24];
+ unsigned char local_nt_response[24];
+
+ generate_random_buffer(chal, 8, False);
+ SMBencrypt(state->request.data.auth.pass, chal, local_lm_response);
+
+ SMBNTencrypt(state->request.data.auth.pass, chal, local_nt_response);
+
+ lm_resp = data_blob_talloc(mem_ctx, local_lm_response, sizeof(local_lm_response));
+ nt_resp = data_blob_talloc(mem_ctx, local_nt_response, sizeof(local_nt_response));
+ }
+
+ /*
+ * Get the machine account password for our primary domain
+ */
+
+ if (!secrets_fetch_trust_account_password(
+ lp_workgroup(), trust_passwd, &last_change_time)) {
+ DEBUG(0, ("winbindd_pam_auth: could not fetch trust account "
+ "password for domain %s\n", lp_workgroup()));
+ result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ goto done;
+ }
+
+ /* We really don't care what LUID we give the user. */
+
+ generate_random_buffer( (unsigned char *)&smb_uid_low, 4, False);
+
+ ZERO_STRUCT(info3);
+
+ /* Don't shut this down - it belongs to the connection cache code */
+ result = cm_get_netlogon_cli(lp_workgroup(), trust_passwd, &cli);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
+ goto done;
+ }
+
+ result = cli_netlogon_sam_network_logon(cli, mem_ctx,
+ name_user, name_domain,
+ lp_netbios_name(), chal,
+ lm_resp, nt_resp,
+ &info3);
+
+ uni_group_cache_store_netlogon(mem_ctx, &info3);
+done:
+
+ state->response.data.auth.nt_status = NT_STATUS_V(result);
+ fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
+ fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result));
+ state->response.data.auth.pam_error = nt_status_to_pam(result);
+
+ DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n",
+ state->request.data.auth.user,
+ state->response.data.auth.nt_status_string,
+ state->response.data.auth.pam_error));
+
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+
+ return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
+}
+
+/* Challenge Response Authentication Protocol */
+
+enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state)
+{
+ NTSTATUS result;
+ unsigned char trust_passwd[16];
+ time_t last_change_time;
+ NET_USER_INFO_3 info3;
+ struct cli_state *cli = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+ char *user = NULL;
+ const char *domain = NULL;
+ const char *contact_domain;
+ const char *workstation;
+
+ DATA_BLOB lm_resp, nt_resp;
+
+ /* Ensure null termination */
+ state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]='\0';
+
+ /* Ensure null termination */
+ state->request.data.auth_crap.domain[sizeof(state->request.data.auth_crap.domain)-1]='\0';
+
+ if (!(mem_ctx = talloc_init("winbind pam auth crap for (utf8) %s", state->request.data.auth_crap.user))) {
+ DEBUG(0, ("winbindd_pam_auth_crap: could not talloc_init()!\n"));
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ if (pull_utf8_talloc(mem_ctx, &user, state->request.data.auth_crap.user) == (size_t)-1) {
+ DEBUG(0, ("winbindd_pam_auth_crap: pull_utf8_talloc failed!\n"));
+ }
+
+ if (*state->request.data.auth_crap.domain) {
+ char *dom = NULL;
+ if (pull_utf8_talloc(mem_ctx, &dom, state->request.data.auth_crap.domain) == (size_t)-1) {
+ DEBUG(0, ("winbindd_pam_auth_crap: pull_utf8_talloc failed!\n"));
+ }
+ domain = dom;
+ } else if (lp_winbind_use_default_domain()) {
+ domain = lp_workgroup();
+ } else {
+ DEBUG(5,("no domain specified with username (%s) - failing auth\n",
+ user));
+ result = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ DEBUG(3, ("[%5d]: pam auth crap domain: %s user: %s\n", state->pid,
+ domain, user));
+
+ if (lp_allow_trusted_domains() && (state->request.data.auth_crap.flags & WINBIND_PAM_CONTACT_TRUSTDOM)) {
+ contact_domain = domain;
+ } else {
+ contact_domain = lp_workgroup();
+ }
+
+ if (*state->request.data.auth_crap.workstation) {
+ char *wrk = NULL;
+ if (pull_utf8_talloc(mem_ctx, &wrk, state->request.data.auth_crap.workstation) == (size_t)-1) {
+ DEBUG(0, ("winbindd_pam_auth_crap: pull_utf8_talloc failed!\n"));
+ }
+ workstation = wrk;
+ } else {
+ workstation = lp_netbios_name();
+ }
+
+ if (state->request.data.auth_crap.lm_resp_len > sizeof(state->request.data.auth_crap.lm_resp)
+ || state->request.data.auth_crap.nt_resp_len > sizeof(state->request.data.auth_crap.nt_resp)) {
+ DEBUG(0, ("winbindd_pam_auth_crap: invalid password length %u/%u\n",
+ state->request.data.auth_crap.lm_resp_len,
+ state->request.data.auth_crap.nt_resp_len));
+ result = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ lm_resp = data_blob_talloc(mem_ctx, state->request.data.auth_crap.lm_resp, state->request.data.auth_crap.lm_resp_len);
+ nt_resp = data_blob_talloc(mem_ctx, state->request.data.auth_crap.nt_resp, state->request.data.auth_crap.nt_resp_len);
+
+ /*
+ * Get the machine account password for the domain to contact.
+ * This is either our own domain for a workstation, or possibly
+ * any domain for a PDC with trusted domains.
+ */
+
+ if (!secrets_fetch_trust_account_password (
+ contact_domain, trust_passwd, &last_change_time)) {
+ DEBUG(0, ("winbindd_pam_auth: could not fetch trust account "
+ "password for domain %s\n", contact_domain));
+ result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ goto done;
+ }
+
+ ZERO_STRUCT(info3);
+
+ /* Don't shut this down - it belongs to the connection cache code */
+ result = cm_get_netlogon_cli(contact_domain, trust_passwd, &cli);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n", nt_errstr(result)));
+ goto done;
+ }
+
+ result = cli_netlogon_sam_network_logon(cli, mem_ctx,
+ user, domain,
+ workstation, state->request.data.auth_crap.chal,
+ lm_resp, nt_resp,
+ &info3);
+
+ if (NT_STATUS_IS_OK(result)) {
+ uni_group_cache_store_netlogon(mem_ctx, &info3);
+ if (state->request.data.auth_crap.flags & WINBIND_PAM_INFO3_NDR) {
+ result = append_info3_as_ndr(mem_ctx, state, &info3);
+ }
+
+#if 0
+ /* we don't currently do this stuff right */
+ /* Doing an assert in a daemon is going to be a pretty bad
+ idea. - tpot */
+ if (state->request.data.auth_crap.flags & WINBIND_PAM_NTKEY) {
+ SMB_ASSERT(sizeof(state->response.data.auth.nt_session_key) == sizeof(info3.user_sess_key));
+ memcpy(state->response.data.auth.nt_session_key, info3.user_sess_key, sizeof(state->response.data.auth.nt_session_key) /* 16 */);
+ }
+ if (state->request.data.auth_crap.flags & WINBIND_PAM_LMKEY) {
+ SMB_ASSERT(sizeof(state->response.data.auth.nt_session_key) <= sizeof(info3.user_sess_key));
+ memcpy(state->response.data.auth.first_8_lm_hash, info3.padding, sizeof(state->response.data.auth.nt_session_key) /* 16 */);
+ }
+#endif
+ }
+
+done:
+
+ state->response.data.auth.nt_status = NT_STATUS_V(result);
+ push_utf8_fstring(state->response.data.auth.nt_status_string, nt_errstr(result));
+ push_utf8_fstring(state->response.data.auth.error_string, nt_errstr(result));
+ state->response.data.auth.pam_error = nt_status_to_pam(result);
+
+ DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
+ ("NTLM CRAP authentication for user [%s]\\[%s] returned %s (PAM: %d)\n",
+ domain,
+ user,
+ state->response.data.auth.nt_status_string,
+ state->response.data.auth.pam_error));
+
+ if (mem_ctx)
+ talloc_destroy(mem_ctx);
+
+ return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
+}
+
+/* Change a user password */
+
+enum winbindd_result winbindd_pam_chauthtok(struct winbindd_cli_state *state)
+{
+ NTSTATUS result;
+ char *oldpass, *newpass;
+ fstring domain, user;
+ CLI_POLICY_HND *hnd;
+
+ DEBUG(3, ("[%5d]: pam chauthtok %s\n", state->pid,
+ state->request.data.chauthtok.user));
+
+ /* Setup crap */
+
+ if (state == NULL)
+ return WINBINDD_ERROR;
+
+ if (!parse_domain_user(state->request.data.chauthtok.user, domain,
+ user)) {
+ result = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ /* Change password */
+
+ oldpass = state->request.data.chauthtok.oldpass;
+ newpass = state->request.data.chauthtok.newpass;
+
+ /* Get sam handle */
+
+ if (!(hnd = cm_get_sam_handle(domain))) {
+ DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
+ result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ goto done;
+ }
+
+ if (!cli_oem_change_password(hnd->cli, user, newpass, oldpass)) {
+ DEBUG(1, ("password change failed for user %s/%s\n", domain,
+ user));
+ result = NT_STATUS_WRONG_PASSWORD;
+ } else {
+ result = NT_STATUS_OK;
+ }
+
+done:
+ state->response.data.auth.nt_status = NT_STATUS_V(result);
+ fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
+ fstrcpy(state->response.data.auth.error_string, nt_errstr(result));
+ state->response.data.auth.pam_error = nt_status_to_pam(result);
+
+ DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
+ ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
+ domain,
+ user,
+ state->response.data.auth.nt_status_string,
+ state->response.data.auth.pam_error));
+
+ return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
+}
diff --git a/source4/nsswitch/winbindd_rpc.c b/source4/nsswitch/winbindd_rpc.c
new file mode 100644
index 0000000000..9989f27109
--- /dev/null
+++ b/source4/nsswitch/winbindd_rpc.c
@@ -0,0 +1,776 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind rpc backend functions
+
+ Copyright (C) Tim Potter 2000-2001,2003
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+
+/* Query display info for a domain. This returns enough information plus a
+ bit extra to give an overview of domain users for the User Manager
+ application. */
+static NTSTATUS query_user_list(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ WINBIND_USERINFO **info)
+{
+ CLI_POLICY_HND *hnd;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ POLICY_HND dom_pol;
+ BOOL got_dom_pol = False;
+ uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+ unsigned int i, start_idx, retry;
+
+ DEBUG(3,("rpc: query_user_list\n"));
+
+ *num_entries = 0;
+ *info = NULL;
+
+ retry = 0;
+ do {
+ /* Get sam handle */
+
+ if (!(hnd = cm_get_sam_handle(domain->name)))
+ goto done;
+
+ /* Get domain handle */
+
+ result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol,
+ des_access, &domain->sid, &dom_pol);
+
+ } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_dom_pol = True;
+
+ i = start_idx = 0;
+ do {
+ TALLOC_CTX *ctx2;
+ char **dom_users;
+ uint32 num_dom_users, *dom_rids, j, size = 0xffff;
+ uint16 acb_mask = ACB_NORMAL;
+
+ if (!(ctx2 = talloc_init("winbindd enum_users"))) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ result = cli_samr_enum_dom_users(
+ hnd->cli, ctx2, &dom_pol, &start_idx, acb_mask,
+ size, &dom_users, &dom_rids, &num_dom_users);
+
+ *num_entries += num_dom_users;
+
+ *info = talloc_realloc(
+ mem_ctx, *info,
+ (*num_entries) * sizeof(WINBIND_USERINFO));
+
+ if (!(*info)) {
+ result = NT_STATUS_NO_MEMORY;
+ talloc_destroy(ctx2);
+ goto done;
+ }
+
+ for (j = 0; j < num_dom_users; i++, j++) {
+ (*info)[i].acct_name =
+ talloc_strdup(mem_ctx, dom_users[j]);
+ (*info)[i].full_name = talloc_strdup(mem_ctx, "");
+ (*info)[i].user_sid = rid_to_talloced_sid(domain, mem_ctx, dom_rids[j]);
+ /* For the moment we set the primary group for
+ every user to be the Domain Users group.
+ There are serious problems with determining
+ the actual primary group for large domains.
+ This should really be made into a 'winbind
+ force group' smb.conf parameter or
+ something like that. */
+ (*info)[i].group_sid
+ = rid_to_talloced_sid(domain,
+ mem_ctx,
+ DOMAIN_GROUP_RID_USERS);
+ }
+
+ talloc_destroy(ctx2);
+
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+
+ done:
+
+ if (got_dom_pol)
+ cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+
+ return result;
+}
+
+/* list all domain groups */
+static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info)
+{
+ uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+ CLI_POLICY_HND *hnd;
+ POLICY_HND dom_pol;
+ NTSTATUS status;
+ uint32 start = 0;
+ int retry;
+
+ *num_entries = 0;
+ *info = NULL;
+
+ DEBUG(3,("rpc: enum_dom_groups\n"));
+
+ retry = 0;
+ do {
+ if (!(hnd = cm_get_sam_handle(domain->name)))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ status = cli_samr_open_domain(hnd->cli, mem_ctx,
+ &hnd->pol, des_access, &domain->sid, &dom_pol);
+ } while (!NT_STATUS_IS_OK(status) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+ if (!NT_STATUS_IS_OK(status))
+ return status;
+
+ do {
+ struct acct_info *info2 = NULL;
+ uint32 count = 0;
+ TALLOC_CTX *mem_ctx2;
+
+ mem_ctx2 = talloc_init("enum_dom_groups[rpc]");
+
+ /* start is updated by this call. */
+ status = cli_samr_enum_dom_groups(hnd->cli, mem_ctx2, &dom_pol,
+ &start,
+ 0xFFFF, /* buffer size? */
+ &info2, &count);
+
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
+ talloc_destroy(mem_ctx2);
+ break;
+ }
+
+ (*info) = talloc_realloc(mem_ctx, *info,
+ sizeof(**info) * ((*num_entries) + count));
+ if (! *info) {
+ talloc_destroy(mem_ctx2);
+ cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ memcpy(&(*info)[*num_entries], info2, count*sizeof(*info2));
+ (*num_entries) += count;
+ talloc_destroy(mem_ctx2);
+ } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES));
+
+ cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+
+ return status;
+}
+
+/* List all domain groups */
+
+static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_entries,
+ struct acct_info **info)
+{
+ uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+ CLI_POLICY_HND *hnd;
+ POLICY_HND dom_pol;
+ NTSTATUS result;
+ int retry;
+
+ *num_entries = 0;
+ *info = NULL;
+
+ retry = 0;
+ do {
+ if ( !(hnd = cm_get_sam_handle(domain->name)) )
+ return NT_STATUS_UNSUCCESSFUL;
+
+ result = cli_samr_open_domain( hnd->cli, mem_ctx, &hnd->pol,
+ des_access, &domain->sid, &dom_pol);
+ } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+ if ( !NT_STATUS_IS_OK(result))
+ return result;
+
+ do {
+ struct acct_info *info2 = NULL;
+ uint32 count = 0, start = *num_entries;
+ TALLOC_CTX *mem_ctx2;
+
+ mem_ctx2 = talloc_init("enum_dom_local_groups[rpc]");
+
+ result = cli_samr_enum_als_groups( hnd->cli, mem_ctx2, &dom_pol,
+ &start, 0xFFFF, &info2, &count);
+
+ if ( !NT_STATUS_IS_OK(result)
+ && !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES) )
+ {
+ talloc_destroy(mem_ctx2);
+ break;
+ }
+
+ (*info) = talloc_realloc(mem_ctx, *info,
+ sizeof(**info) * ((*num_entries) + count));
+ if (! *info) {
+ talloc_destroy(mem_ctx2);
+ cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ memcpy(&(*info)[*num_entries], info2, count*sizeof(*info2));
+ (*num_entries) += count;
+ talloc_destroy(mem_ctx2);
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+
+ cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+
+ return result;
+}
+
+/* convert a single name to a sid in a domain */
+static NTSTATUS name_to_sid(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *name,
+ DOM_SID *sid,
+ enum SID_NAME_USE *type)
+{
+ CLI_POLICY_HND *hnd;
+ NTSTATUS status;
+ DOM_SID *sids = NULL;
+ uint32 *types = NULL;
+ const char *full_name;
+ int retry;
+
+ DEBUG(3,("rpc: name_to_sid name=%s\n", name));
+
+ full_name = talloc_asprintf(mem_ctx, "%s\\%s", domain->name, name);
+
+ if (!full_name) {
+ DEBUG(0, ("talloc_asprintf failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ retry = 0;
+ do {
+ if (!(hnd = cm_get_lsa_handle(domain->name))) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ status = cli_lsa_lookup_names(hnd->cli, mem_ctx, &hnd->pol, 1,
+ &full_name, &sids, &types);
+ } while (!NT_STATUS_IS_OK(status) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+ /* Return rid and type if lookup successful */
+
+ if (NT_STATUS_IS_OK(status)) {
+ sid_copy(sid, &sids[0]);
+ *type = types[0];
+ }
+
+ return status;
+}
+
+/*
+ convert a domain SID to a user or group name
+*/
+static NTSTATUS sid_to_name(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ DOM_SID *sid,
+ char **name,
+ enum SID_NAME_USE *type)
+{
+ CLI_POLICY_HND *hnd;
+ char **domains;
+ char **names;
+ uint32 *types;
+ NTSTATUS status;
+ int retry;
+
+ DEBUG(3,("rpc: sid_to_name\n"));
+
+ retry = 0;
+ do {
+ if (!(hnd = cm_get_lsa_handle(domain->name)))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ status = cli_lsa_lookup_sids(hnd->cli, mem_ctx, &hnd->pol,
+ 1, sid, &domains, &names, &types);
+ } while (!NT_STATUS_IS_OK(status) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+ if (NT_STATUS_IS_OK(status)) {
+ *type = types[0];
+ *name = names[0];
+ DEBUG(5,("Mapped sid to [%s]\\[%s]\n", domains[0], *name));
+
+ /* Paranoia */
+ if (strcasecmp(domain->name, domains[0]) != 0) {
+ DEBUG(1, ("domain name from domain param and PDC lookup return differ! (%s vs %s)\n", domain->name, domains[0]));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+ return status;
+}
+
+/* Lookup user information from a rid or username. */
+static NTSTATUS query_user(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ DOM_SID *user_sid,
+ WINBIND_USERINFO *user_info)
+{
+ CLI_POLICY_HND *hnd;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ POLICY_HND dom_pol, user_pol;
+ BOOL got_dom_pol = False, got_user_pol = False;
+ SAM_USERINFO_CTR *ctr;
+ int retry;
+ fstring sid_string;
+ uint32 user_rid;
+
+ DEBUG(3,("rpc: query_user rid=%s\n", sid_to_string(sid_string, user_sid)));
+ if (!sid_peek_check_rid(&domain->sid, user_sid, &user_rid)) {
+ goto done;
+ }
+
+ retry = 0;
+ do {
+ /* Get sam handle */
+ if (!(hnd = cm_get_sam_handle(domain->name)))
+ goto done;
+
+ /* Get domain handle */
+
+ result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &domain->sid, &dom_pol);
+ } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_dom_pol = True;
+
+ /* Get user handle */
+ result = cli_samr_open_user(hnd->cli, mem_ctx, &dom_pol,
+ SEC_RIGHTS_MAXIMUM_ALLOWED, user_rid, &user_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_user_pol = True;
+
+ /* Get user info */
+ result = cli_samr_query_userinfo(hnd->cli, mem_ctx, &user_pol,
+ 0x15, &ctr);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ cli_samr_close(hnd->cli, mem_ctx, &user_pol);
+ got_user_pol = False;
+
+ user_info->user_sid = rid_to_talloced_sid(domain, mem_ctx, user_rid);
+ user_info->group_sid = rid_to_talloced_sid(domain, mem_ctx, ctr->info.id21->group_rid);
+ user_info->acct_name = unistr2_tdup(mem_ctx,
+ &ctr->info.id21->uni_user_name);
+ user_info->full_name = unistr2_tdup(mem_ctx,
+ &ctr->info.id21->uni_full_name);
+
+ done:
+ /* Clean up policy handles */
+ if (got_user_pol)
+ cli_samr_close(hnd->cli, mem_ctx, &user_pol);
+
+ if (got_dom_pol)
+ cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+
+ return result;
+}
+
+/* Lookup groups a user is a member of. I wish Unix had a call like this! */
+static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ DOM_SID *user_sid,
+ uint32 *num_groups, DOM_SID ***user_gids)
+{
+ CLI_POLICY_HND *hnd;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ POLICY_HND dom_pol, user_pol;
+ uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+ BOOL got_dom_pol = False, got_user_pol = False;
+ DOM_GID *user_groups;
+ unsigned int i;
+ unsigned int retry;
+ fstring sid_string;
+ uint32 user_rid;
+
+ DEBUG(3,("rpc: lookup_usergroups sid=%s\n", sid_to_string(sid_string, user_sid)));
+
+ *num_groups = 0;
+
+ /* First try cached universal groups from logon */
+ *user_gids = uni_group_cache_fetch(&domain->sid, user_sid, mem_ctx, num_groups);
+ if((*num_groups > 0) && *user_gids) {
+ return NT_STATUS_OK;
+ } else {
+ *user_gids = NULL;
+ *num_groups = 0;
+ }
+
+ retry = 0;
+ do {
+ /* Get sam handle */
+ if (!(hnd = cm_get_sam_handle(domain->name)))
+ goto done;
+
+ /* Get domain handle */
+ result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol,
+ des_access, &domain->sid, &dom_pol);
+ } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_dom_pol = True;
+
+
+ if (!sid_peek_check_rid(&domain->sid, user_sid, &user_rid)) {
+ goto done;
+ }
+
+ /* Get user handle */
+ result = cli_samr_open_user(hnd->cli, mem_ctx, &dom_pol,
+ des_access, user_rid, &user_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_user_pol = True;
+
+ /* Query user rids */
+ result = cli_samr_query_usergroups(hnd->cli, mem_ctx, &user_pol,
+ num_groups, &user_groups);
+
+ if (!NT_STATUS_IS_OK(result) || (*num_groups) == 0)
+ goto done;
+
+ (*user_gids) = talloc(mem_ctx, sizeof(uint32) * (*num_groups));
+ if (!(*user_gids)) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ for (i=0;i<(*num_groups);i++) {
+ (*user_gids)[i] = rid_to_talloced_sid(domain, mem_ctx, user_groups[i].g_rid);
+ }
+
+ done:
+ /* Clean up policy handles */
+ if (got_user_pol)
+ cli_samr_close(hnd->cli, mem_ctx, &user_pol);
+
+ if (got_dom_pol)
+ cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+
+ return result;
+}
+
+
+/* Lookup group membership given a rid. */
+static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ DOM_SID *group_sid, uint32 *num_names,
+ DOM_SID ***sid_mem, char ***names,
+ uint32 **name_types)
+{
+ CLI_POLICY_HND *hnd;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 i, total_names = 0;
+ POLICY_HND dom_pol, group_pol;
+ uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+ BOOL got_dom_pol = False, got_group_pol = False;
+ uint32 *rid_mem = NULL;
+ uint32 group_rid;
+ int retry;
+ unsigned int j;
+ fstring sid_string;
+
+ DEBUG(10,("rpc: lookup_groupmem %s sid=%s\n", domain->name, sid_to_string(sid_string, group_sid)));
+
+ if (!sid_peek_check_rid(&domain->sid, group_sid, &group_rid)) {
+ goto done;
+ }
+
+ *num_names = 0;
+
+ retry = 0;
+ do {
+ /* Get sam handle */
+ if (!(hnd = cm_get_sam_handle(domain->name)))
+ goto done;
+
+ /* Get domain handle */
+
+ result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol,
+ des_access, &domain->sid, &dom_pol);
+ } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_dom_pol = True;
+
+ /* Get group handle */
+
+ result = cli_samr_open_group(hnd->cli, mem_ctx, &dom_pol,
+ des_access, group_rid, &group_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_group_pol = True;
+
+ /* Step #1: Get a list of user rids that are the members of the
+ group. */
+
+ result = cli_samr_query_groupmem(hnd->cli, mem_ctx,
+ &group_pol, num_names, &rid_mem,
+ name_types);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Step #2: Convert list of rids into list of usernames. Do this
+ in bunches of ~1000 to avoid crashing NT4. It looks like there
+ is a buffer overflow or something like that lurking around
+ somewhere. */
+
+#define MAX_LOOKUP_RIDS 900
+
+ *names = talloc_zero(mem_ctx, *num_names * sizeof(char *));
+ *name_types = talloc_zero(mem_ctx, *num_names * sizeof(uint32));
+ *sid_mem = talloc_zero(mem_ctx, *num_names * sizeof(DOM_SID *));
+
+ for (j=0;j<(*num_names);j++) {
+ (*sid_mem)[j] = rid_to_talloced_sid(domain, mem_ctx, (rid_mem)[j]);
+ }
+
+ if (!*names || !*name_types) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ for (i = 0; i < *num_names; i += MAX_LOOKUP_RIDS) {
+ int num_lookup_rids = MIN(*num_names - i, MAX_LOOKUP_RIDS);
+ uint32 tmp_num_names = 0;
+ char **tmp_names = NULL;
+ uint32 *tmp_types = NULL;
+
+ /* Lookup a chunk of rids */
+
+ result = cli_samr_lookup_rids(hnd->cli, mem_ctx,
+ &dom_pol, 1000, /* flags */
+ num_lookup_rids,
+ &rid_mem[i],
+ &tmp_num_names,
+ &tmp_names, &tmp_types);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ /* Copy result into array. The talloc system will take
+ care of freeing the temporary arrays later on. */
+
+ memcpy(&(*names)[i], tmp_names, sizeof(char *) *
+ tmp_num_names);
+
+ memcpy(&(*name_types)[i], tmp_types, sizeof(uint32) *
+ tmp_num_names);
+
+ total_names += tmp_num_names;
+ }
+
+ *num_names = total_names;
+
+ done:
+ if (got_group_pol)
+ cli_samr_close(hnd->cli, mem_ctx, &group_pol);
+
+ if (got_dom_pol)
+ cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+
+ return result;
+}
+
+/* find the sequence number for a domain */
+static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
+{
+ TALLOC_CTX *mem_ctx;
+ CLI_POLICY_HND *hnd;
+ SAM_UNK_CTR ctr;
+ uint16 switch_value = 2;
+ NTSTATUS result;
+ uint32 seqnum = DOM_SEQUENCE_NONE;
+ POLICY_HND dom_pol;
+ BOOL got_dom_pol = False;
+ uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+ int retry;
+
+ DEBUG(10,("rpc: fetch sequence_number for %s\n", domain->name));
+
+ *seq = DOM_SEQUENCE_NONE;
+
+ if (!(mem_ctx = talloc_init("sequence_number[rpc]")))
+ return NT_STATUS_NO_MEMORY;
+
+ retry = 0;
+ do {
+ /* Get sam handle */
+ if (!(hnd = cm_get_sam_handle(domain->name)))
+ goto done;
+
+ /* Get domain handle */
+ result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol,
+ des_access, &domain->sid, &dom_pol);
+ } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ got_dom_pol = True;
+
+ /* Query domain info */
+
+ result = cli_samr_query_dom_info(hnd->cli, mem_ctx, &dom_pol,
+ switch_value, &ctr);
+
+ if (NT_STATUS_IS_OK(result)) {
+ seqnum = ctr.info.inf2.seq_num;
+ DEBUG(10,("domain_sequence_number: for domain %s is %u\n", domain->name, (unsigned)seqnum ));
+ } else {
+ DEBUG(10,("domain_sequence_number: failed to get sequence number (%u) for domain %s\n",
+ (unsigned)seqnum, domain->name ));
+ }
+
+ done:
+
+ if (got_dom_pol)
+ cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
+
+ talloc_destroy(mem_ctx);
+
+ *seq = seqnum;
+
+ return result;
+}
+
+/* get a list of trusted domains */
+static NTSTATUS trusted_domains(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 *num_domains,
+ char ***names,
+ char ***alt_names,
+ DOM_SID **dom_sids)
+{
+ CLI_POLICY_HND *hnd;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 enum_ctx = 0;
+ int retry;
+
+ DEBUG(3,("rpc: trusted_domains\n"));
+
+ *num_domains = 0;
+ *alt_names = NULL;
+
+ retry = 0;
+ do {
+ if (!(hnd = cm_get_lsa_handle(lp_workgroup())))
+ goto done;
+
+ result = cli_lsa_enum_trust_dom(hnd->cli, mem_ctx,
+ &hnd->pol, &enum_ctx,
+ num_domains, names, dom_sids);
+ } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+done:
+ return result;
+}
+
+/* find the domain sid for a domain */
+static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
+{
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ TALLOC_CTX *mem_ctx;
+ CLI_POLICY_HND *hnd;
+ fstring level5_dom;
+ int retry;
+
+ DEBUG(3,("rpc: domain_sid\n"));
+
+ if (!(mem_ctx = talloc_init("domain_sid[rpc]")))
+ return NT_STATUS_NO_MEMORY;
+
+ retry = 0;
+ do {
+ /* Get sam handle */
+ if (!(hnd = cm_get_lsa_handle(domain->name)))
+ goto done;
+
+ status = cli_lsa_query_info_policy(hnd->cli, mem_ctx,
+ &hnd->pol, 0x05, level5_dom, sid);
+ } while (!NT_STATUS_IS_OK(status) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1);
+
+done:
+ talloc_destroy(mem_ctx);
+ return status;
+}
+
+/* find alternate names list for the domain - none for rpc */
+static NTSTATUS alternate_name(struct winbindd_domain *domain)
+{
+ return NT_STATUS_OK;
+}
+
+
+/* the rpc backend methods are exposed via this structure */
+struct winbindd_methods msrpc_methods = {
+ False,
+ query_user_list,
+ enum_dom_groups,
+ enum_local_groups,
+ name_to_sid,
+ sid_to_name,
+ query_user,
+ lookup_usergroups,
+ lookup_groupmem,
+ sequence_number,
+ trusted_domains,
+ domain_sid,
+ alternate_name
+};
diff --git a/source4/nsswitch/winbindd_sid.c b/source4/nsswitch/winbindd_sid.c
new file mode 100644
index 0000000000..6ab2eaa646
--- /dev/null
+++ b/source4/nsswitch/winbindd_sid.c
@@ -0,0 +1,235 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon - sid related functions
+
+ Copyright (C) Tim Potter 2000
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/* Convert a string */
+
+enum winbindd_result winbindd_lookupsid(struct winbindd_cli_state *state)
+{
+ extern DOM_SID global_sid_Builtin;
+ enum SID_NAME_USE type;
+ DOM_SID sid, tmp_sid;
+ uint32 rid;
+ fstring name;
+ fstring dom_name;
+
+ /* Ensure null termination */
+ state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
+
+ DEBUG(3, ("[%5d]: lookupsid %s\n", state->pid,
+ state->request.data.sid));
+
+ /* Lookup sid from PDC using lsa_lookup_sids() */
+
+ if (!string_to_sid(&sid, state->request.data.sid)) {
+ DEBUG(5, ("%s not a SID\n", state->request.data.sid));
+ return WINBINDD_ERROR;
+ }
+
+ /* Don't look up BUILTIN sids */
+
+ sid_copy(&tmp_sid, &sid);
+ sid_split_rid(&tmp_sid, &rid);
+
+ if (sid_equal(&tmp_sid, &global_sid_Builtin)) {
+ return WINBINDD_ERROR;
+ }
+
+ /* Lookup the sid */
+
+ if (!winbindd_lookup_name_by_sid(&sid, dom_name, name, &type)) {
+ return WINBINDD_ERROR;
+ }
+
+ fstrcpy(state->response.data.name.dom_name, dom_name);
+ fstrcpy(state->response.data.name.name, name);
+
+ state->response.data.name.type = type;
+
+ return WINBINDD_OK;
+}
+
+
+/**
+ * Look up the SID for a qualified name.
+ **/
+enum winbindd_result winbindd_lookupname(struct winbindd_cli_state *state)
+{
+ enum SID_NAME_USE type;
+ fstring sid_str;
+ char *name_domain, *name_user;
+ DOM_SID sid;
+ struct winbindd_domain *domain;
+
+ /* Ensure null termination */
+ state->request.data.sid[sizeof(state->request.data.name.dom_name)-1]='\0';
+
+ /* Ensure null termination */
+ state->request.data.sid[sizeof(state->request.data.name.name)-1]='\0';
+
+ DEBUG(3, ("[%5d]: lookupname %s%s%s\n", state->pid,
+ state->request.data.name.dom_name,
+ lp_winbind_separator(),
+ state->request.data.name.name));
+
+ name_domain = state->request.data.name.dom_name;
+ name_user = state->request.data.name.name;
+
+ if ((domain = find_domain_from_name(name_domain)) == NULL) {
+ DEBUG(0, ("could not find domain entry for domain %s\n",
+ name_domain));
+ return WINBINDD_ERROR;
+ }
+
+ /* Lookup name from PDC using lsa_lookup_names() */
+ if (!winbindd_lookup_sid_by_name(domain, name_user, &sid, &type)) {
+ return WINBINDD_ERROR;
+ }
+
+ sid_to_string(sid_str, &sid);
+ fstrcpy(state->response.data.sid.sid, sid_str);
+ state->response.data.sid.type = type;
+
+ return WINBINDD_OK;
+}
+
+/* Convert a sid to a uid. We assume we only have one rid attached to the
+ sid. */
+
+enum winbindd_result winbindd_sid_to_uid(struct winbindd_cli_state *state)
+{
+ DOM_SID sid;
+
+ /* Ensure null termination */
+ state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
+
+ DEBUG(3, ("[%5d]: sid to uid %s\n", state->pid,
+ state->request.data.sid));
+
+ /* Split sid into domain sid and user rid */
+ if (!string_to_sid(&sid, state->request.data.sid)) {
+ DEBUG(1, ("Could not get convert sid %s from string\n",
+ state->request.data.sid));
+ return WINBINDD_ERROR;
+ }
+
+ /* Find uid for this sid and return it */
+ if (!winbindd_idmap_get_uid_from_sid(&sid, &state->response.data.uid)) {
+ DEBUG(1, ("Could not get uid for sid %s\n",
+ state->request.data.sid));
+ return WINBINDD_ERROR;
+ }
+
+ return WINBINDD_OK;
+}
+
+/* Convert a sid to a gid. We assume we only have one rid attached to the
+ sid.*/
+
+enum winbindd_result winbindd_sid_to_gid(struct winbindd_cli_state *state)
+{
+ DOM_SID sid;
+
+ /* Ensure null termination */
+ state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
+
+ DEBUG(3, ("[%5d]: sid to gid %s\n", state->pid,
+ state->request.data.sid));
+
+ if (!string_to_sid(&sid, state->request.data.sid)) {
+ DEBUG(1, ("Could not cvt string to sid %s\n",
+ state->request.data.sid));
+ return WINBINDD_ERROR;
+ }
+
+ /* Find gid for this sid and return it */
+ if (!winbindd_idmap_get_gid_from_sid(&sid, &state->response.data.gid)) {
+ DEBUG(1, ("Could not get gid for sid %s\n",
+ state->request.data.sid));
+ return WINBINDD_ERROR;
+ }
+
+ return WINBINDD_OK;
+}
+
+/* Convert a uid to a sid */
+
+enum winbindd_result winbindd_uid_to_sid(struct winbindd_cli_state *state)
+{
+ DOM_SID sid;
+
+ /* Bug out if the uid isn't in the winbind range */
+
+ if ((state->request.data.uid < server_state.uid_low ) ||
+ (state->request.data.uid > server_state.uid_high)) {
+ return WINBINDD_ERROR;
+ }
+
+ DEBUG(3, ("[%5d]: uid to sid %d\n", state->pid,
+ state->request.data.uid));
+
+ /* Lookup rid for this uid */
+ if (!winbindd_idmap_get_sid_from_uid(state->request.data.uid, &sid)) {
+ DEBUG(1, ("Could not convert uid %d to rid\n",
+ state->request.data.uid));
+ return WINBINDD_ERROR;
+ }
+
+ sid_to_string(state->response.data.sid.sid, &sid);
+ state->response.data.sid.type = SID_NAME_USER;
+
+ return WINBINDD_OK;
+}
+
+/* Convert a gid to a sid */
+
+enum winbindd_result winbindd_gid_to_sid(struct winbindd_cli_state *state)
+{
+ DOM_SID sid;
+
+ /* Bug out if the gid isn't in the winbind range */
+
+ if ((state->request.data.gid < server_state.gid_low) ||
+ (state->request.data.gid > server_state.gid_high)) {
+ return WINBINDD_ERROR;
+ }
+
+ DEBUG(3, ("[%5d]: gid to sid %d\n", state->pid,
+ state->request.data.gid));
+
+ /* Lookup sid for this uid */
+ if (!winbindd_idmap_get_sid_from_gid(state->request.data.gid, &sid)) {
+ DEBUG(1, ("Could not convert gid %d to sid\n",
+ state->request.data.gid));
+ return WINBINDD_ERROR;
+ }
+
+ /* Construct sid and return it */
+ sid_to_string(state->response.data.sid.sid, &sid);
+ state->response.data.sid.type = SID_NAME_DOM_GRP;
+
+ return WINBINDD_OK;
+}
diff --git a/source4/nsswitch/winbindd_user.c b/source4/nsswitch/winbindd_user.c
new file mode 100644
index 0000000000..ee05543d30
--- /dev/null
+++ b/source4/nsswitch/winbindd_user.c
@@ -0,0 +1,607 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon - user related functions
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jeremy Allison 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/* Fill a pwent structure with information we have obtained */
+
+static BOOL winbindd_fill_pwent(char *dom_name, char *user_name,
+ DOM_SID *user_sid, DOM_SID *group_sid,
+ char *full_name, struct winbindd_pw *pw)
+{
+ extern userdom_struct current_user_info;
+ fstring output_username;
+ pstring homedir;
+ fstring sid_string;
+
+ if (!pw || !dom_name || !user_name)
+ return False;
+
+ /* Resolve the uid number */
+
+ if (!winbindd_idmap_get_uid_from_sid(user_sid,
+ &pw->pw_uid)) {
+ DEBUG(1, ("error getting user id for sid %s\n", sid_to_string(sid_string, user_sid)));
+ return False;
+ }
+
+ /* Resolve the gid number */
+
+ if (!winbindd_idmap_get_gid_from_sid(group_sid,
+ &pw->pw_gid)) {
+ DEBUG(1, ("error getting group id for sid %s\n", sid_to_string(sid_string, group_sid)));
+ return False;
+ }
+
+ /* Username */
+
+ fill_domain_username(output_username, dom_name, user_name);
+
+ safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
+
+ /* Full name (gecos) */
+
+ safe_strcpy(pw->pw_gecos, full_name, sizeof(pw->pw_gecos) - 1);
+
+ /* Home directory and shell - use template config parameters. The
+ defaults are /tmp for the home directory and /bin/false for
+ shell. */
+
+ /* The substitution of %U and %D in the 'template homedir' is done
+ by lp_string() calling standard_sub_basic(). */
+
+ fstrcpy(current_user_info.smb_name, user_name);
+ sub_set_smb_name(user_name);
+ fstrcpy(current_user_info.domain, dom_name);
+
+ pstrcpy(homedir, lp_template_homedir());
+
+ safe_strcpy(pw->pw_dir, homedir, sizeof(pw->pw_dir) - 1);
+
+ safe_strcpy(pw->pw_shell, lp_template_shell(),
+ sizeof(pw->pw_shell) - 1);
+
+ /* Password - set to "x" as we can't generate anything useful here.
+ Authentication can be done using the pam_winbind module. */
+
+ safe_strcpy(pw->pw_passwd, "x", sizeof(pw->pw_passwd) - 1);
+
+ return True;
+}
+
+/* Return a password structure from a username. */
+
+enum winbindd_result winbindd_getpwnam(struct winbindd_cli_state *state)
+{
+ WINBIND_USERINFO user_info;
+ DOM_SID user_sid;
+ NTSTATUS status;
+ fstring name_domain, name_user;
+ enum SID_NAME_USE name_type;
+ struct winbindd_domain *domain;
+ TALLOC_CTX *mem_ctx;
+
+ /* Ensure null termination */
+ state->request.data.username[sizeof(state->request.data.username)-1]='\0';
+
+ DEBUG(3, ("[%5d]: getpwnam %s\n", state->pid,
+ state->request.data.username));
+
+ /* Parse domain and username */
+
+ if (!parse_domain_user(state->request.data.username, name_domain,
+ name_user))
+ return WINBINDD_ERROR;
+
+ if ((domain = find_domain_from_name(name_domain)) == NULL) {
+ DEBUG(5, ("no such domain: %s\n", name_domain));
+ return WINBINDD_ERROR;
+ }
+
+ /* Get rid and name type from name */
+
+ if (!winbindd_lookup_sid_by_name(domain, name_user, &user_sid, &name_type)) {
+ DEBUG(1, ("user '%s' does not exist\n", name_user));
+ return WINBINDD_ERROR;
+ }
+
+ if (name_type != SID_NAME_USER) {
+ DEBUG(1, ("name '%s' is not a user name: %d\n", name_user,
+ name_type));
+ return WINBINDD_ERROR;
+ }
+
+ /* Get some user info. Split the user rid from the sid obtained
+ from the winbind_lookup_by_name() call and use it in a
+ winbind_lookup_userinfo() */
+
+ if (!(mem_ctx = talloc_init("winbindd_getpwnam([%s]\\[%s])",
+ name_domain, name_user))) {
+ DEBUG(1, ("out of memory\n"));
+ return WINBINDD_ERROR;
+ }
+
+ status = domain->methods->query_user(domain, mem_ctx, &user_sid,
+ &user_info);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("error getting user info for user '[%s]\\[%s]'\n",
+ name_domain, name_user));
+ talloc_destroy(mem_ctx);
+ return WINBINDD_ERROR;
+ }
+
+ /* Now take all this information and fill in a passwd structure */
+ if (!winbindd_fill_pwent(name_domain, name_user,
+ user_info.user_sid, user_info.group_sid,
+ user_info.full_name,
+ &state->response.data.pw)) {
+ talloc_destroy(mem_ctx);
+ return WINBINDD_ERROR;
+ }
+
+ talloc_destroy(mem_ctx);
+
+ return WINBINDD_OK;
+}
+
+/* Return a password structure given a uid number */
+
+enum winbindd_result winbindd_getpwuid(struct winbindd_cli_state *state)
+{
+ DOM_SID user_sid;
+ struct winbindd_domain *domain;
+ fstring dom_name;
+ fstring user_name;
+ enum SID_NAME_USE name_type;
+ WINBIND_USERINFO user_info;
+ gid_t gid;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+
+ /* Bug out if the uid isn't in the winbind range */
+
+ if ((state->request.data.uid < server_state.uid_low ) ||
+ (state->request.data.uid > server_state.uid_high))
+ return WINBINDD_ERROR;
+
+ DEBUG(3, ("[%5d]: getpwuid %d\n", state->pid,
+ state->request.data.uid));
+
+ /* Get rid from uid */
+
+ if (!winbindd_idmap_get_sid_from_uid(state->request.data.uid,
+ &user_sid)) {
+ DEBUG(1, ("could not convert uid %d to SID\n",
+ state->request.data.uid));
+ return WINBINDD_ERROR;
+ }
+
+ /* Get name and name type from rid */
+
+ if (!winbindd_lookup_name_by_sid(&user_sid, dom_name, user_name, &name_type)) {
+ fstring temp;
+
+ sid_to_string(temp, &user_sid);
+ DEBUG(1, ("could not lookup sid %s\n", temp));
+ return WINBINDD_ERROR;
+ }
+
+ domain = find_domain_from_sid(&user_sid);
+
+ if (!domain) {
+ DEBUG(1,("Can't find domain from sid\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* Get some user info */
+
+ if (!(mem_ctx = talloc_init("winbind_getpwuid(%d)",
+ state->request.data.uid))) {
+
+ DEBUG(1, ("out of memory\n"));
+ return WINBINDD_ERROR;
+ }
+
+ status = domain->methods->query_user(domain, mem_ctx, &user_sid,
+ &user_info);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("error getting user info for user '%s'\n",
+ user_name));
+ talloc_destroy(mem_ctx);
+ return WINBINDD_ERROR;
+ }
+
+ /* Resolve gid number */
+
+ if (!winbindd_idmap_get_gid_from_sid(user_info.group_sid, &gid)) {
+ DEBUG(1, ("error getting group id for user %s\n", user_name));
+ talloc_destroy(mem_ctx);
+ return WINBINDD_ERROR;
+ }
+
+ /* Fill in password structure */
+
+ if (!winbindd_fill_pwent(domain->name, user_name, user_info.user_sid,
+ user_info.group_sid,
+ user_info.full_name, &state->response.data.pw)) {
+ talloc_destroy(mem_ctx);
+ return WINBINDD_ERROR;
+ }
+
+ talloc_destroy(mem_ctx);
+
+ return WINBINDD_OK;
+}
+
+/*
+ * set/get/endpwent functions
+ */
+
+/* Rewind file pointer for ntdom passwd database */
+
+enum winbindd_result winbindd_setpwent(struct winbindd_cli_state *state)
+{
+ struct winbindd_domain *domain;
+
+ DEBUG(3, ("[%5d]: setpwent\n", state->pid));
+
+ /* Check user has enabled this */
+
+ if (!lp_winbind_enum_users())
+ return WINBINDD_ERROR;
+
+ /* Free old static data if it exists */
+
+ if (state->getpwent_state != NULL) {
+ free_getent_state(state->getpwent_state);
+ state->getpwent_state = NULL;
+ }
+
+ /* Create sam pipes for each domain we know about */
+
+ for(domain = domain_list(); domain != NULL; domain = domain->next) {
+ struct getent_state *domain_state;
+
+ /* Create a state record for this domain */
+
+ if ((domain_state = (struct getent_state *)
+ malloc(sizeof(struct getent_state))) == NULL)
+ return WINBINDD_ERROR;
+
+ ZERO_STRUCTP(domain_state);
+
+ fstrcpy(domain_state->domain_name, domain->name);
+
+ /* Add to list of open domains */
+
+ DLIST_ADD(state->getpwent_state, domain_state);
+ }
+
+ return WINBINDD_OK;
+}
+
+/* Close file pointer to ntdom passwd database */
+
+enum winbindd_result winbindd_endpwent(struct winbindd_cli_state *state)
+{
+ DEBUG(3, ("[%5d]: endpwent\n", state->pid));
+
+ free_getent_state(state->getpwent_state);
+ state->getpwent_state = NULL;
+
+ return WINBINDD_OK;
+}
+
+/* Get partial list of domain users for a domain. We fill in the sam_entries,
+ and num_sam_entries fields with domain user information. The dispinfo_ndx
+ field is incremented to the index of the next user to fetch. Return True if
+ some users were returned, False otherwise. */
+
+#define MAX_FETCH_SAM_ENTRIES 100
+
+static BOOL get_sam_user_entries(struct getent_state *ent)
+{
+ NTSTATUS status;
+ uint32 num_entries;
+ WINBIND_USERINFO *info;
+ struct getpwent_user *name_list = NULL;
+ BOOL result = False;
+ TALLOC_CTX *mem_ctx;
+ struct winbindd_domain *domain;
+ struct winbindd_methods *methods;
+ unsigned int i;
+
+ if (ent->num_sam_entries)
+ return False;
+
+ if (!(mem_ctx = talloc_init("get_sam_user_entries(%s)",
+ ent->domain_name)))
+ return False;
+
+ if (!(domain = find_domain_from_name(ent->domain_name))) {
+ DEBUG(3, ("no such domain %s in get_sam_user_entries\n",
+ ent->domain_name));
+ return False;
+ }
+
+ methods = domain->methods;
+
+ /* Free any existing user info */
+
+ SAFE_FREE(ent->sam_entries);
+ ent->num_sam_entries = 0;
+
+ /* Call query_user_list to get a list of usernames and user rids */
+
+ num_entries = 0;
+
+ status = methods->query_user_list(domain, mem_ctx, &num_entries,
+ &info);
+
+ if (num_entries) {
+ struct getpwent_user *tnl;
+
+ tnl = (struct getpwent_user *)Realloc(name_list,
+ sizeof(struct getpwent_user) *
+ (ent->num_sam_entries +
+ num_entries));
+
+ if (!tnl) {
+ DEBUG(0,("get_sam_user_entries realloc failed.\n"));
+ SAFE_FREE(name_list);
+ goto done;
+ } else
+ name_list = tnl;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ /* Store account name and gecos */
+ if (!info[i].acct_name) {
+ fstrcpy(name_list[ent->num_sam_entries + i].name, "");
+ } else {
+ fstrcpy(name_list[ent->num_sam_entries + i].name,
+ info[i].acct_name);
+ }
+ if (!info[i].full_name) {
+ fstrcpy(name_list[ent->num_sam_entries + i].gecos, "");
+ } else {
+ fstrcpy(name_list[ent->num_sam_entries + i].gecos,
+ info[i].full_name);
+ }
+
+ /* User and group ids */
+ sid_copy(&name_list[ent->num_sam_entries+i].user_sid, info[i].user_sid);
+ sid_copy(&name_list[ent->num_sam_entries+i].group_sid, info[i].group_sid);
+ }
+
+ ent->num_sam_entries += num_entries;
+
+ /* Fill in remaining fields */
+
+ ent->sam_entries = name_list;
+ ent->sam_entry_index = 0;
+ result = ent->num_sam_entries > 0;
+
+ done:
+
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
+
+/* Fetch next passwd entry from ntdom database */
+
+#define MAX_GETPWENT_USERS 500
+
+enum winbindd_result winbindd_getpwent(struct winbindd_cli_state *state)
+{
+ struct getent_state *ent;
+ struct winbindd_pw *user_list;
+ int num_users, user_list_ndx = 0, i;
+
+ DEBUG(3, ("[%5d]: getpwent\n", state->pid));
+
+ /* Check user has enabled this */
+
+ if (!lp_winbind_enum_users())
+ return WINBINDD_ERROR;
+
+ /* Allocate space for returning a chunk of users */
+
+ num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries);
+
+ if ((state->response.extra_data =
+ malloc(num_users * sizeof(struct winbindd_pw))) == NULL)
+ return WINBINDD_ERROR;
+
+ memset(state->response.extra_data, 0, num_users *
+ sizeof(struct winbindd_pw));
+
+ user_list = (struct winbindd_pw *)state->response.extra_data;
+
+ if (!(ent = state->getpwent_state))
+ return WINBINDD_ERROR;
+
+ /* Start sending back users */
+
+ for (i = 0; i < num_users; i++) {
+ struct getpwent_user *name_list = NULL;
+ fstring domain_user_name;
+ uint32 result;
+
+ /* Do we need to fetch another chunk of users? */
+
+ if (ent->num_sam_entries == ent->sam_entry_index) {
+
+ while(ent && !get_sam_user_entries(ent)) {
+ struct getent_state *next_ent;
+
+ /* Free state information for this domain */
+
+ SAFE_FREE(ent->sam_entries);
+
+ next_ent = ent->next;
+ DLIST_REMOVE(state->getpwent_state, ent);
+
+ SAFE_FREE(ent);
+ ent = next_ent;
+ }
+
+ /* No more domains */
+
+ if (!ent)
+ break;
+ }
+
+ name_list = ent->sam_entries;
+
+ /* Skip machine accounts */
+
+ if (name_list[ent->sam_entry_index].
+ name[strlen(name_list[ent->sam_entry_index].name) - 1]
+ == '$') {
+ ent->sam_entry_index++;
+ continue;
+ }
+
+ /* Lookup user info */
+
+ result = winbindd_fill_pwent(
+ ent->domain_name,
+ name_list[ent->sam_entry_index].name,
+ &name_list[ent->sam_entry_index].user_sid,
+ &name_list[ent->sam_entry_index].group_sid,
+ name_list[ent->sam_entry_index].gecos,
+ &user_list[user_list_ndx]);
+
+ ent->sam_entry_index++;
+
+ /* Add user to return list */
+
+ if (result) {
+
+ user_list_ndx++;
+ state->response.data.num_entries++;
+ state->response.length +=
+ sizeof(struct winbindd_pw);
+
+ } else
+ DEBUG(1, ("could not lookup domain user %s\n",
+ domain_user_name));
+ }
+
+ /* Out of domains */
+
+ return (user_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
+}
+
+/* List domain users without mapping to unix ids */
+
+enum winbindd_result winbindd_list_users(struct winbindd_cli_state *state)
+{
+ struct winbindd_domain *domain;
+ WINBIND_USERINFO *info;
+ uint32 num_entries = 0, total_entries = 0;
+ char *ted, *extra_data = NULL;
+ int extra_data_len = 0;
+ TALLOC_CTX *mem_ctx;
+ enum winbindd_result rv = WINBINDD_ERROR;
+
+ DEBUG(3, ("[%5d]: list users\n", state->pid));
+
+ if (!(mem_ctx = talloc_init("winbindd_list_users")))
+ return WINBINDD_ERROR;
+
+ /* Enumerate over trusted domains */
+
+ for (domain = domain_list(); domain; domain = domain->next) {
+ NTSTATUS status;
+ struct winbindd_methods *methods;
+ unsigned int i;
+
+ methods = domain->methods;
+
+ /* Query display info */
+ status = methods->query_user_list(domain, mem_ctx,
+ &num_entries, &info);
+
+ if (num_entries == 0)
+ continue;
+
+ /* Allocate some memory for extra data */
+ total_entries += num_entries;
+
+ ted = Realloc(extra_data, sizeof(fstring) * total_entries);
+
+ if (!ted) {
+ DEBUG(0,("failed to enlarge buffer!\n"));
+ SAFE_FREE(extra_data);
+ goto done;
+ } else
+ extra_data = ted;
+
+ /* Pack user list into extra data fields */
+
+ for (i = 0; i < num_entries; i++) {
+ fstring acct_name, name;
+
+ if (!info[i].acct_name) {
+ fstrcpy(acct_name, "");
+ } else {
+ fstrcpy(acct_name, info[i].acct_name);
+ }
+
+ fill_domain_username(name, domain->name, acct_name);
+
+ /* Append to extra data */
+ memcpy(&extra_data[extra_data_len], name,
+ strlen(name));
+ extra_data_len += strlen(name);
+ extra_data[extra_data_len++] = ',';
+ }
+ }
+
+ /* Assign extra_data fields in response structure */
+
+ if (extra_data) {
+ extra_data[extra_data_len - 1] = '\0';
+ state->response.extra_data = extra_data;
+ state->response.length += extra_data_len;
+ }
+
+ /* No domains responded but that's still OK so don't return an
+ error. */
+
+ rv = WINBINDD_OK;
+
+ done:
+
+ talloc_destroy(mem_ctx);
+
+ return rv;
+}
diff --git a/source4/nsswitch/winbindd_util.c b/source4/nsswitch/winbindd_util.c
new file mode 100644
index 0000000000..7ccf032041
--- /dev/null
+++ b/source4/nsswitch/winbindd_util.c
@@ -0,0 +1,553 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000-2001
+ Copyright (C) 2001 by Martin Pool <mbp@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/**
+ * @file winbindd_util.c
+ *
+ * Winbind daemon for NT domain authentication nss module.
+ **/
+
+
+/**
+ * Used to clobber name fields that have an undefined value.
+ *
+ * Correct code should never look at a field that has this value.
+ **/
+
+static const fstring name_deadbeef = "<deadbeef>";
+
+/* The list of trusted domains. Note that the list can be deleted and
+ recreated using the init_domain_list() function so pointers to
+ individual winbindd_domain structures cannot be made. Keep a copy of
+ the domain name instead. */
+
+static struct winbindd_domain *_domain_list;
+
+struct winbindd_domain *domain_list(void)
+{
+ /* Initialise list */
+
+ if (!_domain_list)
+ init_domain_list();
+
+ return _domain_list;
+}
+
+/* Free all entries in the trusted domain list */
+
+void free_domain_list(void)
+{
+ struct winbindd_domain *domain = _domain_list;
+
+ while(domain) {
+ struct winbindd_domain *next = domain->next;
+
+ DLIST_REMOVE(_domain_list, domain);
+ SAFE_FREE(domain);
+ domain = next;
+ }
+}
+
+
+/* Add a trusted domain to our list of domains */
+static struct winbindd_domain *add_trusted_domain(const char *domain_name, const char *alt_name,
+ struct winbindd_methods *methods,
+ DOM_SID *sid)
+{
+ struct winbindd_domain *domain;
+
+ /* We can't call domain_list() as this function is called from
+ init_domain_list() and we'll get stuck in a loop. */
+ for (domain = _domain_list; domain; domain = domain->next) {
+ if (strcasecmp(domain_name, domain->name) == 0 ||
+ strcasecmp(domain_name, domain->alt_name) == 0) {
+ return domain;
+ }
+ if (alt_name && *alt_name) {
+ if (strcasecmp(alt_name, domain->name) == 0 ||
+ strcasecmp(alt_name, domain->alt_name) == 0) {
+ return domain;
+ }
+ }
+ }
+
+ /* Create new domain entry */
+
+ if ((domain = (struct winbindd_domain *)
+ malloc(sizeof(*domain))) == NULL)
+ return NULL;
+
+ /* Fill in fields */
+
+ ZERO_STRUCTP(domain);
+
+ /* prioritise the short name */
+ if (strchr_m(domain_name, '.') && alt_name && *alt_name) {
+ fstrcpy(domain->name, alt_name);
+ fstrcpy(domain->alt_name, domain_name);
+ } else {
+ fstrcpy(domain->name, domain_name);
+ if (alt_name) {
+ fstrcpy(domain->alt_name, alt_name);
+ }
+ }
+
+ domain->methods = methods;
+ domain->sequence_number = DOM_SEQUENCE_NONE;
+ domain->last_seq_check = 0;
+ if (sid) {
+ sid_copy(&domain->sid, sid);
+ }
+
+ /* see if this is a native mode win2k domain, but only for our own domain */
+
+ if ( strequal( lp_workgroup(), domain_name) ) {
+ domain->native_mode = cm_check_for_native_mode_win2k( domain_name );
+ DEBUG(3,("add_trusted_domain: %s is a %s mode domain\n", domain_name,
+ domain->native_mode ? "native" : "mixed" ));
+ }
+
+ /* Link to domain list */
+ DLIST_ADD(_domain_list, domain);
+
+ DEBUG(1,("Added domain %s %s %s\n",
+ domain->name, domain->alt_name,
+ sid?sid_string_static(&domain->sid):""));
+
+ return domain;
+}
+
+
+/*
+ rescan our domains looking for new trusted domains
+ */
+void rescan_trusted_domains(BOOL force)
+{
+ struct winbindd_domain *domain;
+ TALLOC_CTX *mem_ctx;
+ static time_t last_scan;
+ time_t t = time(NULL);
+
+ /* trusted domains might be disabled */
+ if (!lp_allow_trusted_domains()) {
+ return;
+ }
+
+ /* Only rescan every few minutes but force if necessary */
+
+ if (((unsigned)(t - last_scan) < WINBINDD_RESCAN_FREQ) && !force)
+ return;
+
+ last_scan = t;
+
+ DEBUG(1, ("scanning trusted domain list\n"));
+
+ if (!(mem_ctx = talloc_init("init_domain_list")))
+ return;
+
+ for (domain = _domain_list; domain; domain = domain->next) {
+ NTSTATUS result;
+ char **names;
+ char **alt_names;
+ int num_domains = 0;
+ DOM_SID *dom_sids;
+ int i;
+
+ result = domain->methods->trusted_domains(domain, mem_ctx, &num_domains,
+ &names, &alt_names, &dom_sids);
+ if (!NT_STATUS_IS_OK(result)) {
+ continue;
+ }
+
+ /* Add each domain to the trusted domain list. Each domain inherits
+ the access methods of its parent */
+ for(i = 0; i < num_domains; i++) {
+ DEBUG(10,("Found domain %s\n", names[i]));
+ add_trusted_domain(names[i], alt_names?alt_names[i]:NULL,
+ domain->methods, &dom_sids[i]);
+
+ /* store trusted domain in the cache */
+ trustdom_cache_store(mem_ctx, names[i], alt_names ? alt_names[i] : NULL,
+ &dom_sids[i], t + WINBINDD_RESCAN_FREQ);
+ }
+ }
+
+ talloc_destroy(mem_ctx);
+}
+
+/* Look up global info for the winbind daemon */
+BOOL init_domain_list(void)
+{
+ extern struct winbindd_methods cache_methods;
+ struct winbindd_domain *domain;
+
+ /* Free existing list */
+ free_domain_list();
+
+ /* Add ourselves as the first entry */
+ domain = add_trusted_domain(lp_workgroup(), NULL, &cache_methods, NULL);
+ if (!secrets_fetch_domain_sid(domain->name, &domain->sid)) {
+ DEBUG(1, ("Could not fetch sid for our domain %s\n",
+ domain->name));
+ return False;
+ }
+
+ /* get any alternate name for the primary domain */
+ cache_methods.alternate_name(domain);
+
+ /* do an initial scan for trusted domains */
+ rescan_trusted_domains(True);
+
+ return True;
+}
+
+/* Given a domain name, return the struct winbindd domain info for it
+ if it is actually working. */
+
+struct winbindd_domain *find_domain_from_name(const char *domain_name)
+{
+ struct winbindd_domain *domain;
+
+ /* Search through list */
+
+ for (domain = domain_list(); domain != NULL; domain = domain->next) {
+ if (strequal(domain_name, domain->name) ||
+ (domain->alt_name[0] && strequal(domain_name, domain->alt_name)))
+ return domain;
+ }
+
+ /* Not found */
+
+ return NULL;
+}
+
+/* Given a domain sid, return the struct winbindd domain info for it */
+
+struct winbindd_domain *find_domain_from_sid(DOM_SID *sid)
+{
+ struct winbindd_domain *domain;
+
+ /* Search through list */
+
+ for (domain = domain_list(); domain != NULL; domain = domain->next) {
+ if (sid_compare_domain(sid, &domain->sid) == 0)
+ return domain;
+ }
+
+ /* Not found */
+
+ return NULL;
+}
+
+/* Lookup a sid in a domain from a name */
+
+BOOL winbindd_lookup_sid_by_name(struct winbindd_domain *domain,
+ const char *name, DOM_SID *sid,
+ enum SID_NAME_USE *type)
+{
+ NTSTATUS result;
+ TALLOC_CTX *mem_ctx;
+ /* Don't bother with machine accounts */
+
+ if (name[strlen(name) - 1] == '$')
+ return False;
+
+ mem_ctx = talloc_init("lookup_sid_by_name for %s\n", name);
+ if (!mem_ctx)
+ return False;
+
+ /* Lookup name */
+ result = domain->methods->name_to_sid(domain, mem_ctx, name, sid, type);
+
+ talloc_destroy(mem_ctx);
+
+ /* Return rid and type if lookup successful */
+ if (!NT_STATUS_IS_OK(result)) {
+ *type = SID_NAME_UNKNOWN;
+ }
+
+ return NT_STATUS_IS_OK(result);
+}
+
+/**
+ * @brief Lookup a name in a domain from a sid.
+ *
+ * @param sid Security ID you want to look up.
+ *
+ * @param name On success, set to the name corresponding to @p sid.
+ *
+ * @param dom_name On success, set to the 'domain name' corresponding to @p sid.
+ *
+ * @param type On success, contains the type of name: alias, group or
+ * user.
+ *
+ * @retval True if the name exists, in which case @p name and @p type
+ * are set, otherwise False.
+ **/
+BOOL winbindd_lookup_name_by_sid(DOM_SID *sid,
+ fstring dom_name,
+ fstring name,
+ enum SID_NAME_USE *type)
+{
+ char *names;
+ NTSTATUS result;
+ TALLOC_CTX *mem_ctx;
+ BOOL rv = False;
+ struct winbindd_domain *domain;
+
+ domain = find_domain_from_sid(sid);
+
+ if (!domain) {
+ DEBUG(1,("Can't find domain from sid\n"));
+ return False;
+ }
+
+ /* Lookup name */
+
+ if (!(mem_ctx = talloc_init("winbindd_lookup_name_by_sid")))
+ return False;
+
+ result = domain->methods->sid_to_name(domain, mem_ctx, sid, &names, type);
+
+ /* Return name and type if successful */
+
+ if ((rv = NT_STATUS_IS_OK(result))) {
+ fstrcpy(dom_name, domain->name);
+ fstrcpy(name, names);
+ } else {
+ *type = SID_NAME_UNKNOWN;
+ fstrcpy(name, name_deadbeef);
+ }
+
+ talloc_destroy(mem_ctx);
+
+ return rv;
+}
+
+
+/* Free state information held for {set,get,end}{pw,gr}ent() functions */
+
+void free_getent_state(struct getent_state *state)
+{
+ struct getent_state *temp;
+
+ /* Iterate over state list */
+
+ temp = state;
+
+ while(temp != NULL) {
+ struct getent_state *next;
+
+ /* Free sam entries then list entry */
+
+ SAFE_FREE(state->sam_entries);
+ DLIST_REMOVE(state, state);
+ next = temp->next;
+
+ SAFE_FREE(temp);
+ temp = next;
+ }
+}
+
+/* Parse winbindd related parameters */
+
+BOOL winbindd_param_init(void)
+{
+ /* Parse winbind uid and winbind_gid parameters */
+
+ if (!lp_winbind_uid(&server_state.uid_low, &server_state.uid_high)) {
+ DEBUG(0, ("winbind uid range missing or invalid\n"));
+ return False;
+ }
+
+ if (!lp_winbind_gid(&server_state.gid_low, &server_state.gid_high)) {
+ DEBUG(0, ("winbind gid range missing or invalid\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/* Check if a domain is present in a comma-separated list of domains */
+
+BOOL check_domain_env(char *domain_env, char *domain)
+{
+ fstring name;
+ const char *tmp = domain_env;
+
+ while(next_token(&tmp, name, ",", sizeof(fstring))) {
+ if (strequal(name, domain))
+ return True;
+ }
+
+ return False;
+}
+
+/* Parse a string of the form DOMAIN/user into a domain and a user */
+
+BOOL parse_domain_user(const char *domuser, fstring domain, fstring user)
+{
+ char *p = strchr(domuser,*lp_winbind_separator());
+
+ if (!(p || lp_winbind_use_default_domain()))
+ return False;
+
+ if(!p && lp_winbind_use_default_domain()) {
+ fstrcpy(user, domuser);
+ fstrcpy(domain, lp_workgroup());
+ } else {
+ fstrcpy(user, p+1);
+ fstrcpy(domain, domuser);
+ domain[PTR_DIFF(p, domuser)] = 0;
+ }
+ strupper(domain);
+ return True;
+}
+
+/*
+ Fill DOMAIN\\USERNAME entry accounting 'winbind use default domain' and
+ 'winbind separator' options.
+ This means:
+ - omit DOMAIN when 'winbind use default domain = true' and DOMAIN is
+ lp_workgroup
+
+*/
+void fill_domain_username(fstring name, const char *domain, const char *user)
+{
+ if(lp_winbind_use_default_domain() &&
+ !strcmp(lp_workgroup(), domain)) {
+ strlcpy(name, user, sizeof(fstring));
+ } else {
+ slprintf(name, sizeof(fstring) - 1, "%s%s%s",
+ domain, lp_winbind_separator(),
+ user);
+ }
+}
+
+/*
+ * Winbindd socket accessor functions
+ */
+
+/* Open the winbindd socket */
+
+static int _winbindd_socket = -1;
+
+int open_winbindd_socket(void)
+{
+ if (_winbindd_socket == -1) {
+ _winbindd_socket = create_pipe_sock(
+ WINBINDD_SOCKET_DIR, WINBINDD_SOCKET_NAME, 0755);
+ DEBUG(10, ("open_winbindd_socket: opened socket fd %d\n",
+ _winbindd_socket));
+ }
+
+ return _winbindd_socket;
+}
+
+/* Close the winbindd socket */
+
+void close_winbindd_socket(void)
+{
+ if (_winbindd_socket != -1) {
+ DEBUG(10, ("close_winbindd_socket: closing socket fd %d\n",
+ _winbindd_socket));
+ close(_winbindd_socket);
+ _winbindd_socket = -1;
+ }
+}
+
+/*
+ * Client list accessor functions
+ */
+
+static struct winbindd_cli_state *_client_list;
+static int _num_clients;
+
+/* Return list of all connected clients */
+
+struct winbindd_cli_state *winbindd_client_list(void)
+{
+ return _client_list;
+}
+
+/* Add a connection to the list */
+
+void winbindd_add_client(struct winbindd_cli_state *cli)
+{
+ DLIST_ADD(_client_list, cli);
+ _num_clients++;
+}
+
+/* Remove a client from the list */
+
+void winbindd_remove_client(struct winbindd_cli_state *cli)
+{
+ DLIST_REMOVE(_client_list, cli);
+ _num_clients--;
+}
+
+/* Close all open clients */
+
+void winbindd_kill_all_clients(void)
+{
+ struct winbindd_cli_state *cl = winbindd_client_list();
+
+ DEBUG(10, ("winbindd_kill_all_clients: going postal\n"));
+
+ while (cl) {
+ struct winbindd_cli_state *next;
+
+ next = cl->next;
+ winbindd_remove_client(cl);
+ cl = next;
+ }
+}
+
+/* Return number of open clients */
+
+int winbindd_num_clients(void)
+{
+ return _num_clients;
+}
+
+/* Help with RID -> SID conversion */
+
+DOM_SID *rid_to_talloced_sid(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32 rid)
+{
+ DOM_SID *sid;
+ sid = talloc(mem_ctx, sizeof(*sid));
+ if (!sid) {
+ smb_panic("rid_to_to_talloced_sid: talloc for DOM_SID failed!\n");
+ }
+ sid_copy(sid, &domain->sid);
+ sid_append_rid(sid, rid);
+ return sid;
+}
+
diff --git a/source4/nsswitch/winbindd_wins.c b/source4/nsswitch/winbindd_wins.c
new file mode 100644
index 0000000000..8ddd5dc10d
--- /dev/null
+++ b/source4/nsswitch/winbindd_wins.c
@@ -0,0 +1,210 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon - WINS related functions
+
+ Copyright (C) Andrew Tridgell 1999
+ Copyright (C) Herb Lewis 2002
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/* Use our own create socket code so we don't recurse.... */
+
+static int wins_lookup_open_socket_in(void)
+{
+ struct sockaddr_in sock;
+ int val=1;
+ int res;
+
+ memset((char *)&sock,'\0',sizeof(sock));
+
+#ifdef HAVE_SOCK_SIN_LEN
+ sock.sin_len = sizeof(sock);
+#endif
+ sock.sin_port = 0;
+ sock.sin_family = AF_INET;
+ sock.sin_addr.s_addr = interpret_addr("0.0.0.0");
+ res = socket(AF_INET, SOCK_DGRAM, 0);
+ if (res == -1)
+ return -1;
+
+ setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val));
+#ifdef SO_REUSEPORT
+ setsockopt(res,SOL_SOCKET,SO_REUSEPORT,(char *)&val,sizeof(val));
+#endif /* SO_REUSEPORT */
+
+ /* now we've got a socket - we need to bind it */
+
+ if (bind(res, (struct sockaddr * ) &sock,sizeof(sock)) < 0) {
+ close(res);
+ return(-1);
+ }
+
+ set_socket_options(res,"SO_BROADCAST");
+
+ return res;
+}
+
+
+static struct node_status *lookup_byaddr_backend(char *addr, int *count)
+{
+ int fd;
+ struct in_addr ip;
+ struct nmb_name nname;
+ struct node_status *status;
+
+ fd = wins_lookup_open_socket_in();
+ if (fd == -1)
+ return NULL;
+
+ make_nmb_name(&nname, "*", 0);
+ ip = *interpret_addr2(addr);
+ status = node_status_query(fd,&nname,ip, count);
+
+ close(fd);
+ return status;
+}
+
+static struct in_addr *lookup_byname_backend(const char *name, int *count)
+{
+ int fd;
+ struct in_addr *ret = NULL;
+ int j, flags = 0;
+
+ *count = 0;
+
+ /* always try with wins first */
+ if (resolve_wins(name,0x20,&ret,count)) {
+ return ret;
+ }
+
+ fd = wins_lookup_open_socket_in();
+ if (fd == -1) {
+ return NULL;
+ }
+
+ /* uggh, we have to broadcast to each interface in turn */
+ for (j=iface_count() - 1;
+ j >= 0;
+ j--) {
+ struct in_addr *bcast = iface_n_bcast(j);
+ ret = name_query(fd,name,0x20,True,True,*bcast,count, &flags, NULL);
+ if (ret) break;
+ }
+
+ close(fd);
+ return ret;
+}
+
+/* Get hostname from IP */
+
+enum winbindd_result winbindd_wins_byip(struct winbindd_cli_state *state)
+{
+ fstring response;
+ int i, count, maxlen, size;
+ struct node_status *status;
+
+ /* Ensure null termination */
+ state->request.data.winsreq[sizeof(state->request.data.winsreq)-1]='\0';
+
+ DEBUG(3, ("[%5d]: wins_byip %s\n", state->pid,
+ state->request.data.winsreq));
+
+ *response = '\0';
+ maxlen = sizeof(response) - 1;
+
+ if ((status = lookup_byaddr_backend(state->request.data.winsreq, &count))){
+ size = strlen(state->request.data.winsreq);
+ if (size > maxlen) {
+ SAFE_FREE(status);
+ return WINBINDD_ERROR;
+ }
+ safe_strcat(response,state->request.data.winsreq,maxlen);
+ safe_strcat(response,"\t",maxlen);
+ for (i = 0; i < count; i++) {
+ /* ignore group names */
+ if (status[i].flags & 0x80) continue;
+ if (status[i].type == 0x20) {
+ size = sizeof(status[i].name) + strlen(response);
+ if (size > maxlen) {
+ SAFE_FREE(status);
+ return WINBINDD_ERROR;
+ }
+ safe_strcat(response, status[i].name, maxlen);
+ safe_strcat(response, " ", maxlen);
+ }
+ }
+ /* make last character a newline */
+ response[strlen(response)-1] = '\n';
+ SAFE_FREE(status);
+ }
+ fstrcpy(state->response.data.winsresp,response);
+ return WINBINDD_OK;
+}
+
+/* Get IP from hostname */
+
+enum winbindd_result winbindd_wins_byname(struct winbindd_cli_state *state)
+{
+ struct in_addr *ip_list;
+ int i, count, maxlen, size;
+ fstring response;
+ char * addr;
+
+ /* Ensure null termination */
+ state->request.data.winsreq[sizeof(state->request.data.winsreq)-1]='\0';
+
+ DEBUG(3, ("[%5d]: wins_byname %s\n", state->pid,
+ state->request.data.winsreq));
+
+ *response = '\0';
+ maxlen = sizeof(response) - 1;
+
+ if ((ip_list = lookup_byname_backend(state->request.data.winsreq,&count))){
+ for (i = count; i ; i--) {
+ addr = inet_ntoa(ip_list[i-1]);
+ size = strlen(addr);
+ if (size > maxlen) {
+ SAFE_FREE(ip_list);
+ return WINBINDD_ERROR;
+ }
+ if (i != 0) {
+ /* Clear out the newline character */
+ response[strlen(response)-1] = ' ';
+ }
+ safe_strcat(response,addr,maxlen);
+ safe_strcat(response,"\t",maxlen);
+ }
+ size = strlen(state->request.data.winsreq) + strlen(response);
+ if (size > maxlen) {
+ SAFE_FREE(ip_list);
+ return WINBINDD_ERROR;
+ }
+ safe_strcat(response,state->request.data.winsreq,maxlen);
+ safe_strcat(response,"\n",maxlen);
+ SAFE_FREE(ip_list);
+ } else
+ return WINBINDD_ERROR;
+
+ fstrcpy(state->response.data.winsresp,response);
+
+ return WINBINDD_OK;
+}
diff --git a/source4/nsswitch/wins.c b/source4/nsswitch/wins.c
new file mode 100644
index 0000000000..187748d285
--- /dev/null
+++ b/source4/nsswitch/wins.c
@@ -0,0 +1,322 @@
+/*
+ Unix SMB/CIFS implementation.
+ a WINS nsswitch module
+ Copyright (C) Andrew Tridgell 1999
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "includes.h"
+#ifdef HAVE_NS_API_H
+#undef VOLATILE
+
+#include <ns_daemon.h>
+#endif
+
+#ifndef INADDRSZ
+#define INADDRSZ 4
+#endif
+
+static int initialised;
+
+extern BOOL AllowDebugChange;
+
+/* Use our own create socket code so we don't recurse.... */
+
+static int wins_lookup_open_socket_in(void)
+{
+ struct sockaddr_in sock;
+ int val=1;
+ int res;
+
+ memset((char *)&sock,'\0',sizeof(sock));
+
+#ifdef HAVE_SOCK_SIN_LEN
+ sock.sin_len = sizeof(sock);
+#endif
+ sock.sin_port = 0;
+ sock.sin_family = AF_INET;
+ sock.sin_addr.s_addr = interpret_addr("0.0.0.0");
+ res = socket(AF_INET, SOCK_DGRAM, 0);
+ if (res == -1)
+ return -1;
+
+ setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val));
+#ifdef SO_REUSEPORT
+ setsockopt(res,SOL_SOCKET,SO_REUSEPORT,(char *)&val,sizeof(val));
+#endif /* SO_REUSEPORT */
+
+ /* now we've got a socket - we need to bind it */
+
+ if (bind(res, (struct sockaddr * ) &sock,sizeof(sock)) < 0) {
+ close(res);
+ return(-1);
+ }
+
+ set_socket_options(res,"SO_BROADCAST");
+
+ return res;
+}
+
+
+static void nss_wins_init(void)
+{
+ initialised = 1;
+ DEBUGLEVEL = 0;
+ AllowDebugChange = False;
+
+ TimeInit();
+ setup_logging("nss_wins",False);
+ lp_load(dyn_CONFIGFILE,True,False,False);
+ load_interfaces();
+}
+
+static struct node_status *lookup_byaddr_backend(char *addr, int *count)
+{
+ int fd;
+ struct in_addr ip;
+ struct nmb_name nname;
+ struct node_status *status;
+
+ if (!initialised) {
+ nss_wins_init();
+ }
+
+ fd = wins_lookup_open_socket_in();
+ if (fd == -1)
+ return NULL;
+
+ make_nmb_name(&nname, "*", 0);
+ ip = *interpret_addr2(addr);
+ status = node_status_query(fd,&nname,ip, count);
+
+ close(fd);
+ return status;
+}
+
+static struct in_addr *lookup_byname_backend(const char *name, int *count)
+{
+ int fd = -1;
+ struct in_addr *ret = NULL;
+ struct in_addr p;
+ int j, flags = 0;
+
+ if (!initialised) {
+ nss_wins_init();
+ }
+
+ *count = 0;
+
+ /* always try with wins first */
+ if (resolve_wins(name,0x20,&ret,count)) {
+ return ret;
+ }
+
+ fd = wins_lookup_open_socket_in();
+ if (fd == -1) {
+ return NULL;
+ }
+
+ /* uggh, we have to broadcast to each interface in turn */
+ for (j=iface_count() - 1;j >= 0;j--) {
+ struct in_addr *bcast = iface_n_bcast(j);
+ ret = name_query(fd,name,0x20,True,True,*bcast,count, &flags, NULL);
+ if (ret) break;
+ }
+
+out:
+ close(fd);
+ return ret;
+}
+
+
+#ifdef HAVE_NS_API_H
+/* IRIX version */
+
+int init(void)
+{
+ nsd_logprintf(NSD_LOG_MIN, "entering init (wins)\n");
+ nss_wins_init();
+ return NSD_OK;
+}
+
+int lookup(nsd_file_t *rq)
+{
+ char *map;
+ char *key;
+ char *addr;
+ struct in_addr *ip_list;
+ struct node_status *status;
+ int i, count, len, size;
+ char response[1024];
+ BOOL found = False;
+
+ nsd_logprintf(NSD_LOG_MIN, "entering lookup (wins)\n");
+ if (! rq)
+ return NSD_ERROR;
+
+ map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
+ if (! map) {
+ rq->f_status = NS_FATAL;
+ return NSD_ERROR;
+ }
+
+ key = nsd_attr_fetch_string(rq->f_attrs, "key", (char*)0);
+ if (! key || ! *key) {
+ rq->f_status = NS_FATAL;
+ return NSD_ERROR;
+ }
+
+ response[0] = '\0';
+ len = sizeof(response) - 2;
+
+ /*
+ * response needs to be a string of the following format
+ * ip_address[ ip_address]*\tname[ alias]*
+ */
+ if (strcasecmp(map,"hosts.byaddr") == 0) {
+ if ( status = lookup_byaddr_backend(key, &count)) {
+ size = strlen(key) + 1;
+ if (size > len) {
+ free(status);
+ return NSD_ERROR;
+ }
+ len -= size;
+ strncat(response,key,size);
+ strncat(response,"\t",1);
+ for (i = 0; i < count; i++) {
+ /* ignore group names */
+ if (status[i].flags & 0x80) continue;
+ if (status[i].type == 0x20) {
+ size = sizeof(status[i].name) + 1;
+ if (size > len) {
+ free(status);
+ return NSD_ERROR;
+ }
+ len -= size;
+ strncat(response, status[i].name, size);
+ strncat(response, " ", 1);
+ found = True;
+ }
+ }
+ response[strlen(response)-1] = '\n';
+ free(status);
+ }
+ } else if (strcasecmp(map,"hosts.byname") == 0) {
+ if (ip_list = lookup_byname_backend(key, &count)) {
+ for (i = count; i ; i--) {
+ addr = inet_ntoa(ip_list[i-1]);
+ size = strlen(addr) + 1;
+ if (size > len) {
+ free(ip_list);
+ return NSD_ERROR;
+ }
+ len -= size;
+ if (i != 0)
+ response[strlen(response)-1] = ' ';
+ strncat(response,addr,size);
+ strncat(response,"\t",1);
+ }
+ size = strlen(key) + 1;
+ if (size > len) {
+ free(ip_list);
+ return NSD_ERROR;
+ }
+ strncat(response,key,size);
+ strncat(response,"\n",1);
+ found = True;
+ free(ip_list);
+ }
+ }
+
+ if (found) {
+ nsd_logprintf(NSD_LOG_LOW, "lookup (wins %s) %s\n",map,response);
+ nsd_set_result(rq,NS_SUCCESS,response,strlen(response),VOLATILE);
+ return NSD_OK;
+ }
+ nsd_logprintf(NSD_LOG_LOW, "lookup (wins) not found\n");
+ rq->f_status = NS_NOTFOUND;
+ return NSD_NEXT;
+}
+
+#else
+/****************************************************************************
+gethostbyname() - we ignore any domain portion of the name and only
+handle names that are at most 15 characters long
+ **************************************************************************/
+NSS_STATUS
+_nss_wins_gethostbyname_r(const char *name, struct hostent *he,
+ char *buffer, size_t buflen, int *errnop,
+ int *h_errnop)
+{
+ char **host_addresses;
+ struct in_addr *ip_list;
+ int i, count;
+ size_t namelen = strlen(name) + 1;
+
+ memset(he, '\0', sizeof(*he));
+
+ ip_list = lookup_byname_backend(name, &count);
+ if (!ip_list) {
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ if (buflen < namelen + (2*count+1)*INADDRSZ) {
+ /* no ENOMEM error type?! */
+ return NSS_STATUS_NOTFOUND;
+ }
+
+
+ host_addresses = (char **)buffer;
+ he->h_addr_list = host_addresses;
+ host_addresses[count] = NULL;
+ buffer += (count + 1) * INADDRSZ;
+ buflen += (count + 1) * INADDRSZ;
+ he->h_addrtype = AF_INET;
+ he->h_length = INADDRSZ;
+
+ for (i=0;i<count;i++) {
+ memcpy(buffer, &ip_list[i].s_addr, INADDRSZ);
+ *host_addresses = buffer;
+ buffer += INADDRSZ;
+ buflen -= INADDRSZ;
+ host_addresses++;
+ }
+
+ if (ip_list)
+ free(ip_list);
+
+ memcpy(buffer, name, namelen);
+ he->h_name = buffer;
+
+ return NSS_STATUS_SUCCESS;
+}
+
+
+NSS_STATUS
+_nss_wins_gethostbyname2_r(const char *name, int af, struct hostent *he,
+ char *buffer, size_t buflen, int *errnop,
+ int *h_errnop)
+{
+ if(af!=AF_INET) {
+ *h_errnop = NO_DATA;
+ *errnop = EAFNOSUPPORT;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ return _nss_wins_gethostbyname_r(name,he,buffer,buflen,errnop,h_errnop);
+}
+#endif