brought the winbindd code into head
authorAndrew Tridgell <tridge@samba.org>
Tue, 9 May 2000 11:43:00 +0000 (11:43 +0000)
committerAndrew Tridgell <tridge@samba.org>
Tue, 9 May 2000 11:43:00 +0000 (11:43 +0000)
this does not yet compile, but I'm working on that.

14 files changed:
source/nsswitch/common.c [new file with mode: 0644]
source/nsswitch/pam_winbind.c [new file with mode: 0644]
source/nsswitch/winbind_nss.c [new file with mode: 0644]
source/nsswitch/winbind_nss_config.h [new file with mode: 0644]
source/nsswitch/winbindd.c [new file with mode: 0644]
source/nsswitch/winbindd.h [new file with mode: 0644]
source/nsswitch/winbindd_cache.c [new file with mode: 0644]
source/nsswitch/winbindd_group.c [new file with mode: 0644]
source/nsswitch/winbindd_idmap.c [new file with mode: 0644]
source/nsswitch/winbindd_nss.h [new file with mode: 0644]
source/nsswitch/winbindd_pam.c [new file with mode: 0644]
source/nsswitch/winbindd_proto.h [new file with mode: 0644]
source/nsswitch/winbindd_user.c [new file with mode: 0644]
source/nsswitch/winbindd_util.c [new file with mode: 0644]

diff --git a/source/nsswitch/common.c b/source/nsswitch/common.c
new file mode 100644 (file)
index 0000000..be0b24c
--- /dev/null
@@ -0,0 +1,282 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 2.0
+
+   winbind client common 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 "ntdom_config.h"
+#include "winbindd_ntdom.h"
+
+/* Global variables.  These are effectively the client state information */
+
+static int established_socket = -1;           /* fd for winbindd socket */
+
+/*
+ * Utility and helper functions
+ */
+
+void init_request(struct winbindd_request *req,int rq_type)
+{
+        static char *domain_env;
+        static BOOL initialised;
+
+       req->cmd = rq_type;
+       req->pid = getpid();
+       req->domain[0] = '\0';
+
+       if (!initialised) {
+               initialised = True;
+               domain_env = getenv(WINBINDD_DOMAIN_ENV);
+       }
+
+       if (domain_env) {
+               strncpy(req->domain, domain_env,
+                       sizeof(req->domain) - 1);
+               req->domain[sizeof(req->domain) - 1] = '\0';
+       }
+}
+
+/* Close established socket */
+
+void close_sock(void)
+{
+    if (established_socket != -1) {
+           close(established_socket);
+           established_socket = -1;
+    }
+}
+
+/* Connect to winbindd socket */
+
+static int open_pipe_sock(void)
+{
+    struct sockaddr_un sunaddr;
+    static pid_t our_pid;
+    struct stat st;
+    pstring path;
+
+    if (our_pid != getpid()) {
+        if (established_socket != -1) {
+            close(established_socket);
+        }
+        established_socket = -1;
+        our_pid = getpid();
+    }
+
+    if (established_socket != -1) {
+        return established_socket;
+    }
+
+    /* 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)) {
+        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)) {
+        return -1;
+    }
+
+    /* Connect to socket */
+
+    if ((established_socket = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+        return -1;
+    }
+
+    if (connect(established_socket, (struct sockaddr *)&sunaddr, 
+                sizeof(sunaddr)) == -1) {
+        close_sock();
+        return -1;
+    }
+        
+    return established_socket;
+}
+
+/* Write data to winbindd socket with timeout */
+
+int write_sock(void *buffer, int count)
+{
+    int result, nwritten;
+
+    /* Open connection to winbind daemon */
+
+ restart:
+
+    if (open_pipe_sock() == -1) {
+        return -1;
+    }
+
+    /* Write data to socket */
+
+    nwritten = 0;
+
+    while(nwritten < count) {
+        struct timeval tv;
+        fd_set r_fds;
+        int selret;
+
+        /* Catch pipe close on other end by checking if a read() call would 
+           not block by calling select(). */
+
+        FD_ZERO(&r_fds);
+        FD_SET(established_socket, &r_fds);
+        ZERO_STRUCT(tv);
+
+        if ((selret = select(established_socket + 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(established_socket, &r_fds)) {
+
+            /* Do the write */
+
+            result = write(established_socket, (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 with timeout */
+
+static int read_sock(void *buffer, int count)
+{
+    int result, nread;
+
+    /* Read data from socket */
+
+    nread = 0;
+
+    while(nread < count) {
+
+        result = read(established_socket, (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;
+
+    if (!response) {
+        return -1;
+    }
+
+    /* Read fixed length response */
+
+    if ((result1 = read_sock(response, sizeof(struct winbindd_response)))
+         == -1) {
+
+        return -1;
+    }
+
+    /* Read variable length response */
+
+    if (response->length > sizeof(struct winbindd_response)) {
+        int extra_data_len = response->length - 
+            sizeof(struct winbindd_response);
+
+        /* Mallocate memory for extra data */
+
+        if (!(response->extra_data = malloc(extra_data_len))) {
+            return -1;
+        }
+
+        if ((result2 = read_sock(response->extra_data, extra_data_len))
+            == -1) {
+
+            return -1;
+        }
+    }
+
+    /* Return total amount of data read */
+
+    return result1 + result2;
+}
+
diff --git a/source/nsswitch/pam_winbind.c b/source/nsswitch/pam_winbind.c
new file mode 100644 (file)
index 0000000..549d8c9
--- /dev/null
@@ -0,0 +1,388 @@
+/* pam_winbind module
+
+   Copyright Andrew Tridgell <tridge@samba.org> 2000
+
+   largely based on pam_userdb by Christian Gafton <gafton@redhat.com> 
+*/
+
+#include <features.h>
+#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>
+
+#define MODULE_NAME "pam_winbind"
+#define PAM_SM_AUTH
+#define PAM_SM_ACCOUNT
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+#define PAM_DEBUG_ARG (1<<0)
+#define PAM_USE_AUTHTOK_ARG (1<<1)
+#define PAM_UNKNOWN_OK_ARG (1<<2)
+
+#include "ntdom_config.h"
+#include "winbindd_ntdom.h"
+
+/* prototypes from common.c */
+void init_request(struct winbindd_request *req,int rq_type);
+int write_sock(void *buffer, int count);
+int read_reply(struct winbindd_response *response);
+
+/* 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 ctrl         = 0;
+
+static int _pam_parse(int argc, const char **argv)
+{
+     /* step through arguments */
+     for (ctrl = 0; argc-- > 0; ++argv) {
+
+          /* generic options */
+
+          if (!strcmp(*argv,"debug"))
+               ctrl |= PAM_DEBUG_ARG;
+         else if (!strcasecmp(*argv, "use_authtok"))
+             ctrl |= PAM_USE_AUTHTOK_ARG;
+         else if (!strcasecmp(*argv, "unknown_ok"))
+             ctrl |= PAM_UNKNOWN_OK_ARG;
+         else {
+               _pam_log(LOG_ERR, "pam_parse: unknown option; %s", *argv);
+          }
+     }
+
+     return ctrl;
+}
+
+
+/* talk to winbindd */
+static int winbind_request(int req_type, const char *user, const char *pass)
+{
+       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);
+       
+       /* Fill in request and send down pipe */
+       init_request(&request, req_type);
+       
+       if (write_sock(&request, sizeof(request)) == -1) {
+               return -2;
+       }
+       
+       /* Wait for reply */
+       if (read_reply(&response) == -1) {
+               return -2;
+       }
+
+       /* Copy reply data from socket */
+       if (response.result != WINBINDD_OK) {
+               return 1;
+       }
+       
+       return 0;
+}
+
+/*
+ * Looks up an user name and checks the password
+ *
+ * return values:
+ *      1  = User not found
+ *      0  = OK
+ *     -1  = Password incorrect
+ *     -2  = System error
+ */
+static int user_lookup(const char *user, const char *pass)
+{
+       return winbind_request(WINBINDD_PAM_AUTH, user, pass);
+}
+
+/*
+ * 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;
+}
+
+/* --- authentication management functions --- */
+
+/*
+ * dummy conversation function sending exactly one prompt
+ * and expecting exactly one response from the other party
+ */
+static int converse(pam_handle_t *pamh,
+                   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(1, (const struct pam_message **)message,
+                           response, conv->appdata_ptr);
+       
+    return retval; /* propagate error status */
+}
+
+
+static char *_pam_delete(register char *xx)
+{
+    _pam_overwrite(xx);
+    _pam_drop(xx);
+    return NULL;
+}
+
+/*
+ * This is a conversation function to obtain the user's password
+ */
+static int conversation(pam_handle_t *pamh)
+{
+    struct pam_message msg[2],*pmsg[2];
+    struct pam_response *resp;
+    int retval;
+    char * token;
+    
+    pmsg[0] = &msg[0];
+    msg[0].msg_style = PAM_PROMPT_ECHO_OFF;
+    msg[0].msg = "Password: ";
+
+    /* so call the conversation expecting i responses */
+    resp = NULL;
+    retval = converse(pamh, pmsg, &resp);
+
+    if (resp != NULL) {
+       char * const item;
+       /* interpret the response */
+       if (retval == PAM_SUCCESS) {     /* a good conversation */
+           token = x_strdup(resp[0].resp);
+           if (token == NULL) {
+               return PAM_AUTHTOK_RECOVER_ERR;
+           }
+       }
+
+       /* set the auth token */
+       retval = pam_set_item(pamh, PAM_AUTHTOK, token);
+       token = _pam_delete(token);   /* clean it up */
+       if ( (retval != PAM_SUCCESS) ||
+            (retval = pam_get_item(pamh, PAM_AUTHTOK, (const void **) &item)) != PAM_SUCCESS ) {
+           return retval;
+       }
+       
+       _pam_drop_reply(resp, 1);
+    } else {
+       retval = (retval == PAM_SUCCESS)
+           ? PAM_AUTHTOK_RECOVER_ERR:retval ;
+    }
+
+    return retval;
+}
+
+
+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 */
+     ctrl = _pam_parse(argc, argv);
+
+     /* Get the username */
+     retval = pam_get_user(pamh, &username, NULL);
+     if ((retval != PAM_SUCCESS) || (!username)) {
+        if (ctrl & PAM_DEBUG_ARG)
+            _pam_log(LOG_DEBUG,"can not get the username");
+        return PAM_SERVICE_ERR;
+     }
+     
+     if ((ctrl & PAM_USE_AUTHTOK_ARG) == 0) {
+        /* Converse just to be sure we have the password */
+        retval = conversation(pamh);
+        if (retval != PAM_SUCCESS) {
+            _pam_log(LOG_ERR, "could not obtain password for `%s'",
+                     username);
+            return PAM_CONV_ERR;
+        }
+     }
+     
+     /* Get the password */
+     retval = pam_get_item(pamh, PAM_AUTHTOK, (const void **) &password);
+     if (retval != PAM_SUCCESS) {
+        _pam_log(LOG_ERR, "Could not retrive user's password");
+        return PAM_AUTHTOK_ERR;
+     }
+     
+     if (ctrl & PAM_DEBUG_ARG)
+        _pam_log(LOG_INFO, "Verify user `%s' with password `%s'",
+                 username, password);
+     
+     /* Now use the username to look up password */
+     retval = user_lookup(username, password);
+     switch (retval) {
+        case -2:
+            /* some sort of system error. The log was already printed */
+            return PAM_SERVICE_ERR;    
+        case -1:
+            /* incorrect password */
+            _pam_log(LOG_WARNING, "user `%s' denied access (incorrect password)", username);
+            return PAM_AUTH_ERR;
+        case 1:
+                /* the user does not exist */
+            if (ctrl & PAM_DEBUG_ARG)
+                _pam_log(LOG_NOTICE, "user `%s' not found",
+                         username);
+            if (ctrl & PAM_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_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 */
+    ctrl = _pam_parse(argc, argv);
+
+    /* Get the username */
+    retval = pam_get_user(pamh, &username, NULL);
+    if ((retval != PAM_SUCCESS) || (!username)) {
+       if (ctrl & PAM_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 & PAM_DEBUG_ARG)
+               _pam_log(LOG_NOTICE, "user `%s' not found",
+                        username);
+           if (ctrl & PAM_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;
+}
+
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_userdb_modstruct = {
+     MODULE_NAME,
+     pam_sm_authenticate,
+     pam_sm_setcred,
+     pam_sm_acct_mgmt,
+     NULL,
+     NULL,
+     NULL,
+};
+
+#endif
+
+/*
+ * Copyright (c) Andrew Tridgell <tridge@samba.org> 2000
+ *
+ * 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/source/nsswitch/winbind_nss.c b/source/nsswitch/winbind_nss.c
new file mode 100644 (file)
index 0000000..48dc188
--- /dev/null
@@ -0,0 +1,465 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 2.0
+
+   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 "ntdom_config.h"
+#include "winbindd_ntdom.h"
+
+/* prototypes from common.c */
+void init_request(struct winbindd_request *req,int rq_type);
+int write_sock(void *buffer, int count);
+int read_reply(struct winbindd_response *response);
+
+
+/* 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. */
+
+#ifdef strchr /* Aargh! This points at multibyte_strchr(). )-: */
+#undef strchr
+#endif
+
+static char *last_ptr = NULL;
+
+BOOL next_token(char **ptr, char *buff, char *sep, size_t bufsize)
+{
+    char *s;
+    BOOL quoted;
+    size_t len=1;
+    
+    if (!ptr) ptr = &last_ptr;
+    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;
+    last_ptr = *ptr;
+  
+    return(True);
+}
+
+
+/* handle simple types of requests */
+static enum nss_status generic_request(int req_type, 
+                                      struct winbindd_request *request,
+                                      struct winbindd_response *response)
+{
+       struct winbindd_request lrequest;
+       struct winbindd_response lresponse;
+
+       if (!response) response = &lresponse;
+       if (!request) 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;
+       }
+       
+       /* Wait for reply */
+       if (read_reply(response) == -1) {
+               return NSS_STATUS_UNAVAIL;
+       }
+
+       /* Copy reply data from socket */
+       if (response->result != WINBINDD_OK) {
+               return NSS_STATUS_NOTFOUND;
+       }
+       
+       return NSS_STATUS_SUCCESS;
+}
+
+/* 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 errno = ERANGE and NSS_STATUS_TRYAGAIN if we run out of
+   memory. */
+
+static enum nss_status fill_pwent(struct passwd *result,
+                                 struct winbindd_response *response,
+                                 char **buffer, int *buflen, int *errnop)
+{
+    struct winbindd_pw *pw = &response->data.pw;
+
+    /* User name */
+
+    if ((result->pw_name = 
+         get_static(buffer, buflen, strlen(pw->pw_name) + 1)) == NULL) {
+
+        /* Out of memory */
+
+        *errnop = ERANGE;
+        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 */
+
+        *errnop = ERANGE;
+        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 */
+
+        *errnop = ERANGE;
+        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 */
+
+        *errnop = ERANGE;
+        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 */
+
+        *errnop = ERANGE;
+        return NSS_STATUS_TRYAGAIN;
+    }
+
+    strcpy(result->pw_shell, pw->pw_shell);
+
+    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 errno = ERANGE and NSS_STATUS_TRYAGAIN if we run out of
+   memory. */
+
+static int fill_grent(struct group *result, 
+                      struct winbindd_response *response,
+                      char **buffer, int *buflen, int *errnop)
+{
+    struct winbindd_gr *gr = &response->data.gr;
+    fstring name;
+    int i;
+
+    /* Group name */
+
+    if ((result->gr_name =
+         get_static(buffer, buflen, strlen(gr->gr_name) + 1)) == NULL) {
+
+        /* Out of memory */
+
+        *errnop = ERANGE;
+        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 */
+
+        *errnop = ERANGE;
+        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) || !response->extra_data) {
+        gr->num_gr_mem = 0;
+    }
+
+    if ((result->gr_mem = 
+         (char **)get_static(buffer, buflen, (gr->num_gr_mem + 1) * 
+                             sizeof(char *))) == NULL) {
+
+        /* Out of memory */
+
+        *errnop = ERANGE;
+        return NSS_STATUS_TRYAGAIN;
+    }
+
+    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(&response->extra_data, name, ",", sizeof(fstring))) {
+        
+        /* Allocate space for member */
+        
+        if (((result->gr_mem)[i] = 
+             get_static(buffer, buflen, strlen(name) + 1)) == NULL) {
+            
+            /* Out of memory */
+            
+            *errnop = ERANGE;
+            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
+ */
+
+/* Rewind "file pointer" to start of ntdom password database */
+
+enum nss_status
+_nss_ntdom_setpwent(void)
+{
+       return generic_request(WINBINDD_SETPWENT, NULL, NULL);
+}
+
+/* Close ntdom password database "file pointer" */
+
+enum nss_status
+_nss_ntdom_endpwent(void)
+{
+       return generic_request(WINBINDD_ENDPWENT, NULL, NULL);
+}
+
+/* Fetch the next password entry from ntdom password database */
+
+enum nss_status
+_nss_ntdom_getpwent_r(struct passwd *result, char *buffer, 
+                      size_t buflen, int *errnop)
+{
+       enum nss_status ret;
+       struct winbindd_response response;
+
+       ret = generic_request(WINBINDD_GETPWENT, NULL, &response);
+       if (ret != NSS_STATUS_SUCCESS) return ret;
+
+       return fill_pwent(result, &response, &buffer, &buflen, errnop);
+}
+
+/* Return passwd struct from uid */
+
+enum nss_status
+_nss_ntdom_getpwuid_r(uid_t uid, struct passwd *result, char *buffer,
+                      size_t buflen, int *errnop)
+{
+       enum nss_status ret;
+       struct winbindd_response response;
+       struct winbindd_request request;
+
+       request.data.uid = uid;
+
+       ret = generic_request(WINBINDD_GETPWNAM_FROM_UID, &request, &response);
+       if (ret != NSS_STATUS_SUCCESS) return ret;
+
+       return fill_pwent(result, &response, &buffer, &buflen, errnop);
+}
+
+/* Return passwd struct from username */
+
+enum nss_status
+_nss_ntdom_getpwnam_r(const char *name, struct passwd *result, char *buffer,
+                      size_t buflen, int *errnop)
+{
+       enum nss_status ret;
+       struct winbindd_response response;
+       struct winbindd_request request;
+
+       strncpy(request.data.username, name, sizeof(request.data.username) - 1);
+       request.data.username[sizeof(request.data.username) - 1] = '\0';
+
+       ret = generic_request(WINBINDD_GETPWNAM_FROM_USER, &request, &response);
+       if (ret != NSS_STATUS_SUCCESS) return ret;
+
+       return fill_pwent(result, &response, &buffer, &buflen, errnop);
+}
+
+/*
+ * NSS group functions
+ */
+
+/* Rewind "file pointer" to start of ntdom group database */
+
+enum nss_status
+_nss_ntdom_setgrent(void)
+{
+       return generic_request(WINBINDD_SETGRENT, NULL, NULL);
+}
+
+/* Close "file pointer" for ntdom group database */
+
+enum nss_status
+_nss_ntdom_endgrent(void)
+{
+       return generic_request(WINBINDD_ENDGRENT, NULL, NULL);
+}
+
+
+
+/* Get next entry from ntdom group database */
+
+enum nss_status
+_nss_ntdom_getgrent_r(struct group *result,
+                      char *buffer, size_t buflen, int *errnop)
+{
+       enum nss_status ret;
+       struct winbindd_response response;
+
+       ret = generic_request(WINBINDD_GETGRENT, NULL, &response);
+       if (ret != NSS_STATUS_SUCCESS) return ret;
+
+       return fill_grent(result, &response, &buffer, &buflen, errnop);
+}
+
+/* Return group struct from group name */
+
+enum nss_status
+_nss_ntdom_getgrnam_r(const char *name,
+                      struct group *result, char *buffer,
+                      size_t buflen, int *errnop)
+{
+       enum nss_status ret;
+       struct winbindd_response response;
+       struct winbindd_request request;
+
+       strncpy(request.data.groupname, name, sizeof(request.data.groupname));
+       request.data.groupname[sizeof(request.data.groupname) - 1] = '\0';
+
+       ret = generic_request(WINBINDD_GETGRNAM_FROM_GROUP, &request, &response);
+       if (ret != NSS_STATUS_SUCCESS) return ret;
+
+       return fill_grent(result, &response, &buffer, &buflen, errnop);
+}
+
+/* Return group struct from gid */
+
+enum nss_status
+_nss_ntdom_getgrgid_r(gid_t gid,
+                      struct group *result, char *buffer,
+                      size_t buflen, int *errnop)
+{
+       enum nss_status ret;
+       struct winbindd_response response;
+       struct winbindd_request request;
+
+       request.data.gid = gid;
+
+       ret = generic_request(WINBINDD_GETGRNAM_FROM_GID, &request, &response);
+       if (ret != NSS_STATUS_SUCCESS) return ret;
+
+       return fill_grent(result, &response, &buffer, &buflen, errnop);
+}
diff --git a/source/nsswitch/winbind_nss_config.h b/source/nsswitch/winbind_nss_config.h
new file mode 100644 (file)
index 0000000..0aac5a7
--- /dev/null
@@ -0,0 +1,111 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 2.0
+
+   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 _NTDOM_CONFIG_H
+#define _NTDOM_CONFIG_H
+
+/* Include header files from data in config.h file */
+
+#include <config.h>
+
+#include <stdio.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <pwd.h>
+#include <nss.h>
+
+/* I'm trying really hard not to include anything from smb.h with the
+   result of some silly looking redeclaration of structures. */
+
+#ifndef _PSTRING
+#define _PSTRING
+#define PSTRING_LEN 1024
+#define FSTRING_LEN 128
+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 */
+#define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x))
+
+/* zero a structure given a pointer to the structure */
+#define ZERO_STRUCTP(x) { if ((x) != NULL) memset((char *)(x), 0, sizeof(*(x))); }
+
+#endif
diff --git a/source/nsswitch/winbindd.c b/source/nsswitch/winbindd.c
new file mode 100644 (file)
index 0000000..e05166e
--- /dev/null
@@ -0,0 +1,663 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 2.0
+
+   Winbind daemon for ntdom nss module
+
+   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"
+
+/* List of all connected clients */
+
+static struct winbindd_cli_state *client_list;
+
+/* Reload configuration */
+
+static BOOL reload_services_file(void)
+{
+        pstring servicesf = CONFIGFILE;
+       BOOL ret;
+
+       reopen_logs();
+       ret = lp_load(servicesf,False,False,True);
+
+       reopen_logs();
+       load_interfaces();
+
+       return(ret);
+}
+
+/* Print client information */
+
+static void do_print_client_info(void)
+{
+    struct winbindd_cli_state *client;
+    int i;
+
+    if (client_list == NULL) {
+        DEBUG(0, ("no clients in list\n"));
+        return;
+    }
+
+    DEBUG(0, ("client list is:\n"));
+
+    for (client = client_list, i = 0; client; client = client->next) {
+        DEBUG(0, ("client %3d: pid = %5d fd = %d read = %4d write = %4d\n", 
+                  i, client->pid, client->sock, client->read_buf_len, 
+                  client->write_buf_len));
+        i++;
+    }
+}
+
+/* Flush client cache */
+
+static void do_flush_caches(void)
+{
+    /* Clear cached user and group enumation info */
+
+    winbindd_flush_cache();
+}
+
+/* Handle the signal by unlinking socket and exiting */
+
+static void termination_handler(int signum)
+{
+    pstring path;
+
+    /* Remove socket file */
+
+    slprintf(path, sizeof(path), "%s/%s", 
+            WINBINDD_SOCKET_DIR, WINBINDD_SOCKET_NAME);
+    unlink(path);
+
+    exit(0);
+}
+
+static BOOL print_client_info;
+
+static void sigusr1_handler(int signum)
+{
+    BlockSignals(True, SIGUSR1);
+    print_client_info = True;
+    BlockSignals(False, SIGUSR1);
+}
+
+static BOOL flush_cache;
+
+static void sighup_handler(int signum)
+{
+    BlockSignals(True, SIGHUP);
+    flush_cache = True;
+    BlockSignals(False, SIGHUP);
+}
+
+/* Create winbindd socket */
+
+static int create_sock(void)
+{
+    struct sockaddr_un sunaddr;
+    struct stat st;
+    int sock;
+    mode_t old_umask;
+    pstring path;
+
+    /* Create the socket directory or reuse the existing one */
+
+    if (lstat(WINBINDD_SOCKET_DIR, &st) == -1) {
+
+        if (errno == ENOENT) {
+
+            /* Create directory */
+
+            if (mkdir(WINBINDD_SOCKET_DIR, 0755) == -1) {
+                DEBUG(0, ("error creating socket directory %s: %s\n",
+                          WINBINDD_SOCKET_DIR, sys_errlist[errno]));
+                return -1;
+            }
+
+        } else {
+
+            DEBUG(0, ("lstat failed on socket directory %s: %s\n",
+                      WINBINDD_SOCKET_DIR, sys_errlist[errno]));
+            return -1;
+        }
+
+    } else {
+
+        /* Check ownership and permission on existing directory */
+        
+        if (!S_ISDIR(st.st_mode)) {
+            DEBUG(0, ("socket directory %s isn't a directory\n",
+                      WINBINDD_SOCKET_DIR));
+            return -1;
+        }
+        
+        if ((st.st_uid != 0) || ((st.st_mode & 0777) != 0755)) {
+            DEBUG(0, ("invalid permissions on socket directory %s\n",
+                      WINBINDD_SOCKET_DIR));
+            return -1;
+        }
+    }
+
+    /* Create the socket file */
+
+    old_umask = umask(0);
+
+    sock = socket(AF_UNIX, SOCK_STREAM, 0);
+    
+    if (sock == -1) {
+        perror("socket");
+        return -1;
+    }
+
+    slprintf(path, sizeof(path), "%s/%s", 
+            WINBINDD_SOCKET_DIR, WINBINDD_SOCKET_NAME);
+
+    unlink(path);
+    memset(&sunaddr, 0, sizeof(sunaddr));
+    sunaddr.sun_family = AF_UNIX;
+    safe_strcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path)-1);
+    
+    if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) == -1) {
+        DEBUG(0, ("bind failed on winbind socket %s: %s\n",
+                  path,
+                  sys_errlist[errno]));
+        close(sock);
+        return -1;
+    }
+    
+    if (listen(sock, 5) == -1) {
+        DEBUG(0, ("listen failed on winbind socket %s: %s\n",
+                  path,
+                  sys_errlist[errno]));
+        close(sock);
+        return -1;
+    }
+    
+    umask(old_umask);
+    
+    /* Success! */
+    
+    return sock;
+}
+
+static void process_request(struct winbindd_cli_state *state)
+{
+    /* Process command */
+
+    state->response.result = WINBINDD_ERROR;
+    state->response.length = sizeof(struct winbindd_response);
+
+    DEBUG(3,("processing command %s from pid %d\n", 
+            winbindd_cmd_to_string(state->request.cmd), state->pid));
+
+    if (!server_state.lsa_handle_open) return;
+
+    switch(state->request.cmd) {
+        
+        /* User functions */
+        
+    case WINBINDD_GETPWNAM_FROM_USER: 
+        state->response.result = winbindd_getpwnam_from_user(state);
+        break;
+        
+    case WINBINDD_GETPWNAM_FROM_UID:
+        state->response.result = winbindd_getpwnam_from_uid(state);
+        break;
+        
+    case WINBINDD_SETPWENT:
+        state->response.result = winbindd_setpwent(state);
+        break;
+        
+    case WINBINDD_ENDPWENT:
+        state->response.result = winbindd_endpwent(state);
+        break;
+        
+    case WINBINDD_GETPWENT:
+        state->response.result = winbindd_getpwent(state);
+        break;
+
+        /* Group functions */
+        
+    case WINBINDD_GETGRNAM_FROM_GROUP:
+        state->response.result = winbindd_getgrnam_from_group(state);
+        break;
+        
+    case WINBINDD_GETGRNAM_FROM_GID:
+        state->response.result = winbindd_getgrnam_from_gid(state);
+        break;
+        
+    case WINBINDD_SETGRENT:
+        state->response.result = winbindd_setgrent(state);
+        break;
+        
+    case WINBINDD_ENDGRENT:
+        state->response.result = winbindd_endgrent(state);
+        break;
+        
+    case WINBINDD_GETGRENT:
+        state->response.result = winbindd_getgrent(state);
+        break;
+
+       /* pam auth functions */
+    case WINBINDD_PAM_AUTH:
+        state->response.result = winbindd_pam_auth(state);
+        break;
+
+        /* Oops */
+        
+    default:
+        DEBUG(0, ("oops - unknown winbindd command %d\n", state->request.cmd));
+        break;
+    }
+}
+
+/* Process a new connection by adding it to the client connection list */
+
+static void new_connection(int accept_sock)
+{
+    struct sockaddr_un sunaddr;
+    struct winbindd_cli_state *state;
+    int len, sock;
+    
+    /* Accept connection */
+    
+    len = sizeof(sunaddr);
+    if ((sock = accept(accept_sock, (struct sockaddr *)&sunaddr, &len)) 
+        == -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;
+
+    /* Add to connection list */
+
+    DLIST_ADD(client_list, 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);
+
+        /* Free any extra data */
+
+        safe_free(state->response.extra_data);
+
+        /* Remove from list and free */
+
+        DLIST_REMOVE(client_list, state);
+        free(state);
+    }
+}
+
+/* Process a complete received packet from a client */
+
+static void process_packet(struct winbindd_cli_state *state)
+{
+    /* Process request */
+
+    state->pid = state->request.pid;
+
+    process_request(state);
+
+    /* Update client state */
+
+    state->read_buf_len = 0;
+    state->write_buf_len = sizeof(state->response);
+}
+
+/* Read some data from a client connection */
+
+static void client_read(struct winbindd_cli_state *state)
+{
+    int n;
+    
+    /* Read data */
+
+    n = read(state->sock, state->read_buf_len + (char *)&state->request, 
+             sizeof(state->request) - 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) ? sys_errlist[errno] : "EOF"));
+
+        state->finished = True;
+        return;
+    }
+
+    /* Update client state */
+
+    state->read_buf_len += n;
+}
+
+/* Write some data to a client connection */
+
+static void client_write(struct winbindd_cli_state *state)
+{
+    char *data;
+    int n;
+
+    /* Write data */
+
+    if (state->write_extra_data) {
+
+        /* Write extra data */
+
+        data = (char *)state->response.extra_data + 
+            state->response.length - sizeof(struct winbindd_response) - 
+            state->write_buf_len;
+
+    } else {
+
+        /* Write response structure */
+
+        data = (char *)&state->response + sizeof(state->response) - 
+            state->write_buf_len;
+    }
+
+    n = write(state->sock, data, state->write_buf_len);
+
+    /* Write failed, kill cilent */
+
+    if (n == -1 || n == 0) {
+
+           DEBUG(3,("write failed on sock %d, pid %d: %s\n",
+                    state->sock, state->pid, 
+                    (n == -1) ? sys_errlist[errno] : "EOF"));
+
+        state->finished = True;
+        return;
+    }
+
+    /* Update client state */
+    
+    state->write_buf_len -= n;
+
+    /* Have we written all data? */
+
+    if (state->write_buf_len == 0) {
+
+        /* Take care of extra data */
+
+        if (state->response.length > sizeof(struct winbindd_response)) {
+
+            if (state->write_extra_data) {
+
+                /* Already written extra data - free it */
+
+                safe_free(state->response.extra_data);
+                state->response.extra_data = NULL;
+                state->write_extra_data = False;
+
+            } else {
+
+                /* Start writing extra data */
+
+                state->write_buf_len = state->response.length -
+                    sizeof(struct winbindd_response);
+                state->write_extra_data = True;
+            }
+        }
+    }
+}
+
+/* Process incoming clients on accept_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(int accept_sock)
+{
+    /* We'll be doing this a lot */
+
+    while (1) {
+        struct winbindd_cli_state *state;
+        fd_set r_fds, w_fds;
+        int maxfd = accept_sock, selret;
+       struct timeval timeout;
+
+       /* do any connection establishment that is needed */
+       establish_connections();            
+
+        /* Initialise fd lists for select() */
+
+        FD_ZERO(&r_fds);
+        FD_ZERO(&w_fds);
+        FD_SET(accept_sock, &r_fds);
+
+       timeout.tv_sec = WINBINDD_ESTABLISH_LOOP;
+       timeout.tv_usec = 0;
+
+        /* Set up client readers and writers */
+
+        state = 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;
+        }
+
+        /* Check signal handling things */
+
+        if (flush_cache) {
+            do_flush_caches();
+            reload_services_file();
+            flush_cache = False;
+        }
+
+        if (print_client_info) {
+            do_print_client_info();
+            print_client_info = False;
+        }
+
+        /* Call select */
+        
+        selret = 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 accept_sock readable */
+
+        if (selret > 0) {
+
+            if (FD_ISSET(accept_sock, &r_fds)) {
+                new_connection(accept_sock);
+            }
+            
+            /* Process activity on client connections */
+            
+            for (state = client_list; state ; state = state->next) {
+                
+                /* Data available for reading */
+                
+                if (FD_ISSET(state->sock, &r_fds)) {
+                    
+                    /* Read data */
+                    
+                    client_read(state);
+                    
+                    /* A request packet might be complete */
+                    
+                    if (state->read_buf_len == sizeof(state->request)) {
+                        process_packet(state);
+                    }
+                }
+                
+                /* Data available for writing */
+                
+                if (FD_ISSET(state->sock, &w_fds)) {
+                       client_write(state);
+                }
+            }
+        }
+    }
+}
+
+/* Main function */
+
+struct winbindd_state server_state;   /* Server state information */
+
+int main(int argc, char **argv)
+{
+    extern pstring global_myname;
+    extern pstring debugf;
+    int accept_sock;
+    BOOL interactive = False;
+    int opt;
+
+    while ((opt = getopt(argc, argv, "i")) != EOF) {
+           switch (opt) {
+               case 'i':
+                       interactive = True;
+                       break;
+               default:
+                       printf("Unknown option %c (%d)\n", (char)opt, opt);
+                       exit(1);
+               }
+    }
+
+    /* Initialise samba/rpc client stuff */
+    slprintf(debugf, sizeof(debugf), "%s/log.winbindd", LOGFILEBASE);
+    setup_logging("winbindd", interactive);
+    reopen_logs();
+
+    if (!*global_myname) {
+        char *p;
+
+        fstrcpy(global_myname, myhostname());
+        p = strchr(global_myname, '.');
+        if (p) {
+            *p = 0;
+        }
+    }
+
+    TimeInit();
+    charset_initialise();
+    codepage_initialise(lp_client_code_page());
+
+    if (!lp_load(CONFIGFILE, True, False, False)) {
+        DEBUG(0, ("error opening config file\n"));
+        exit(1);
+    }
+
+    if (!interactive) {
+           become_daemon();
+    }
+    load_interfaces();
+
+    secrets_init();
+
+    ZERO_STRUCT(server_state);
+
+    /* Winbind daemon initialisation */
+    if (!winbindd_param_init()) {
+           return 1;
+    }
+
+    if (!winbindd_idmap_init()) {
+        return 1;
+    }
+
+    winbindd_cache_init();
+
+    /* 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(SIGUSR1, sigusr1_handler);            /* Debugging sigs */
+    CatchSignal(SIGHUP, sighup_handler);
+
+    /* Create UNIX domain socket */
+
+    if ((accept_sock = create_sock()) == -1) {
+        DEBUG(0, ("failed to create socket\n"));
+        return 1;
+    }
+
+    /* Loop waiting for requests */
+
+    process_loop(accept_sock);
+
+    return 0;
+}
diff --git a/source/nsswitch/winbindd.h b/source/nsswitch/winbindd.h
new file mode 100644 (file)
index 0000000..706a1fc
--- /dev/null
@@ -0,0 +1,106 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 2.0
+
+   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 _WINBINDD_H
+#define _WINBINDD_H
+
+#include "includes.h"
+#include "nterr.h"
+
+#include "winbindd_ntdom.h"
+
+/* Naughty global stuff */
+
+extern int DEBUGLEVEL;
+
+/* 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 */
+    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() */
+};
+
+struct getent_state {
+    struct getent_state *prev, *next;
+    struct acct_info *sam_entries;
+    uint32 sam_entry_index, num_sam_entries;  
+    struct winbindd_domain *domain;
+    BOOL got_sam_entries;
+};
+
+/* Server state structure */
+
+struct winbindd_state {
+       /* Netbios name of PDC */
+       fstring controller;
+       
+       /* 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 */
+       
+       /* Cached handle to lsa pipe */
+       POLICY_HND lsa_handle;
+       BOOL lsa_handle_open;
+       BOOL pwdb_initialised;
+};
+
+extern struct winbindd_state server_state;  /* Server information */
+
+/* Structures to hold per domain information */
+
+struct winbindd_domain {
+
+    /* Domain information */
+
+    fstring name;                          /* Domain name */
+    fstring controller;                    /* NetBIOS name of DC */
+
+    DOM_SID sid;                           /* SID for this domain */
+    BOOL got_domain_info;                  /* Got controller and sid */
+
+    /* Cached handles to samr pipe */
+    POLICY_HND sam_handle, sam_dom_handle;
+    BOOL sam_handle_open, sam_dom_handle_open;
+
+    struct winbindd_domain *prev, *next;   /* Linked list info */
+};
+
+extern struct winbindd_domain *domain_list;  /* List of domains we know */
+
+#include "winbindd_proto.h"
+
+#include "rpc_parse.h"
+
+#define WINBINDD_ESTABLISH_LOOP 30
+#define DOM_SEQUENCE_NONE ((uint32)-1)
+
+#endif /* _WINBINDD_H */
diff --git a/source/nsswitch/winbindd_cache.c b/source/nsswitch/winbindd_cache.c
new file mode 100644 (file)
index 0000000..adfcadf
--- /dev/null
@@ -0,0 +1,416 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 2.0
+
+   Winbind daemon - caching 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"
+
+#define CACHE_TYPE_USER "USR"
+#define CACHE_TYPE_GROUP "GRP"
+
+/* Initialise caching system */
+
+static TDB_CONTEXT *cache_tdb;
+
+struct cache_rec {
+       uint32 seq_num;
+       time_t mod_time;
+};
+
+void winbindd_cache_init(void)
+{
+       /* Open tdb cache */
+       unlink(lock_path("winbindd_cache.tdb"));
+       if (!(cache_tdb = tdb_open(lock_path("winbindd_cache.tdb"), 0, 
+                                  TDB_NOLOCK,
+                                  O_RDWR | O_CREAT, 0600))) {
+               DEBUG(0, ("Unable to open tdb cache - user and group caching "
+                         "disabled\n"));
+       }
+}
+
+/* get the domain sequence number, possibly re-fetching */
+static uint32 cached_sequence_number(char *domain_name)
+{
+       fstring keystr;
+       TDB_DATA dbuf;
+       struct cache_rec rec;
+       time_t t = time(NULL);
+
+       slprintf(keystr, sizeof(keystr), "CACHESEQ/%s", domain_name);
+       dbuf = tdb_fetch_by_string(cache_tdb, keystr);
+       if (!dbuf.dptr || dbuf.dsize != sizeof(rec)) {
+               goto refetch;
+       }
+       memcpy(&rec, dbuf.dptr, sizeof(rec));
+       free(dbuf.dptr);
+
+       if (t < (rec.mod_time + lp_winbind_cache_time())) {
+               DEBUG(4,("cached sequence number for %s is %u\n",
+                        domain_name, (unsigned)rec.seq_num));
+               return rec.seq_num;
+       }
+
+ refetch:      
+       rec.seq_num = domain_sequence_number(domain_name);
+       rec.mod_time = t;
+       tdb_store_by_string(cache_tdb, keystr, &rec, sizeof(rec));
+
+       return rec.seq_num;
+}
+
+/* Check whether a seq_num for a cached item has expired */
+static BOOL cache_domain_expired(char *domain_name, uint32 seq_num)
+{
+       if (cached_sequence_number(domain_name) != seq_num) {
+               DEBUG(4,("seq %u for %s has expired\n", (unsigned)seq_num, domain_name));
+               return True;
+       }
+       return False;
+}
+
+static void set_cache_sequence_number(char *domain_name, char *cache_type, char *subkey)
+{
+       fstring keystr;
+       slprintf(keystr,sizeof(keystr),"CACHESEQ %s/%s/%s",
+                domain_name, cache_type, subkey?subkey:"");
+       tdb_store_int(cache_tdb, keystr, cached_sequence_number(domain_name));
+}
+
+static uint32 get_cache_sequence_number(char *domain_name, char *cache_type, char *subkey)
+{
+       fstring keystr;
+       uint32 seq_num;
+       slprintf(keystr,sizeof(keystr),"CACHESEQ %s/%s/%s",
+                domain_name, cache_type, subkey?subkey:"");
+       seq_num = (uint32)tdb_get_int(cache_tdb, keystr);
+       DEBUG(4,("%s is %u\n", keystr, (unsigned)seq_num));
+       return seq_num;
+}
+
+/* Fill the user or group cache with supplied data */
+static void fill_cache(char *domain_name, char *cache_type,
+                       struct acct_info *sam_entries,
+                       int num_sam_entries)
+{
+       fstring keystr;
+
+       if (lp_winbind_cache_time() == 0) return;
+
+       /* Error check */
+       if (!sam_entries || (num_sam_entries == 0)) return;
+
+       DEBUG(4, ("filling %s cache for domain %s with %d entries\n",
+                 cache_type, domain_name, num_sam_entries));
+
+       /* Store data as a mega-huge chunk in the tdb */
+       slprintf(keystr, sizeof(keystr), "%s CACHE DATA/%s", cache_type,
+                domain_name);
+
+       tdb_store_by_string(cache_tdb, keystr, 
+                           sam_entries, sizeof(struct acct_info) * num_sam_entries);
+
+       /* Stamp cache with current seq number */
+       set_cache_sequence_number(domain_name, cache_type, NULL);
+}
+
+/* Fill the user cache with supplied data */
+
+void winbindd_fill_user_cache(char *domain_name, 
+                              struct acct_info *sam_entries,
+                              int num_sam_entries)
+{
+       fill_cache(domain_name, CACHE_TYPE_USER, sam_entries, num_sam_entries);
+}
+
+/* Fill the group cache with supplied data */
+
+void winbindd_fill_group_cache(char *domain_name,
+                               struct acct_info *sam_entries,
+                               int num_sam_entries)
+{
+       fill_cache(domain_name, CACHE_TYPE_GROUP, sam_entries, num_sam_entries);
+}
+
+static void fill_cache_entry(char *domain, char *name, void *buf, int len)
+{
+       fstring keystr;
+
+       /* Create key for store */
+       slprintf(keystr, sizeof(keystr), "%s/%s", domain, name);
+
+       DEBUG(4, ("filling cache entry %s\n", keystr));
+
+       /* Store it */
+       tdb_store_by_string(cache_tdb, keystr, buf, len);
+}
+
+/* Fill a user info cache entry */
+void winbindd_fill_user_cache_entry(char *domain, char *user_name, 
+                                    struct winbindd_pw *pw)
+{
+        if (lp_winbind_cache_time() == 0) return;
+
+        fill_cache_entry(domain, user_name, pw, sizeof(struct winbindd_pw));
+       set_cache_sequence_number(domain, CACHE_TYPE_USER, user_name);
+}
+
+/* Fill a user uid cache entry */
+void winbindd_fill_uid_cache_entry(char *domain, uid_t uid, 
+                                    struct winbindd_pw *pw)
+{
+        fstring uidstr;
+
+        if (lp_winbind_cache_time() == 0) return;
+
+        slprintf(uidstr, sizeof(uidstr), "#%u", (unsigned)uid);
+        fill_cache_entry(domain, uidstr, pw, sizeof(struct winbindd_pw));
+        set_cache_sequence_number(domain, CACHE_TYPE_USER, uidstr);
+}
+
+/* Fill a group info cache entry */
+void winbindd_fill_group_cache_entry(char *domain, char *group_name, 
+                                     struct winbindd_gr *gr, void *extra_data,
+                                     int extra_data_len)
+{
+        fstring keystr;
+
+        if (lp_winbind_cache_time() == 0) return;
+
+        /* Fill group data */
+        fill_cache_entry(domain, group_name, gr, sizeof(struct winbindd_gr));
+
+        /* Fill extra data */
+        slprintf(keystr, sizeof(keystr), "%s/%s DATA", domain, group_name);
+        tdb_store_by_string(cache_tdb, keystr, extra_data, extra_data_len);
+
+       set_cache_sequence_number(domain, CACHE_TYPE_GROUP, group_name);
+}
+
+/* Fill a group info cache entry */
+void winbindd_fill_gid_cache_entry(char *domain, gid_t gid, 
+                                     struct winbindd_gr *gr, void *extra_data,
+                                     int extra_data_len)
+{
+        fstring keystr;
+       fstring gidstr;
+
+       slprintf(gidstr, sizeof(gidstr), "#%u", (unsigned)gid);
+
+        if (lp_winbind_cache_time() == 0) return;
+
+        /* Fill group data */
+        fill_cache_entry(domain, gidstr, gr, sizeof(struct winbindd_gr));
+
+        /* Fill extra data */
+        slprintf(keystr, sizeof(keystr), "%s/%s DATA", domain, gidstr);
+        tdb_store_by_string(cache_tdb, keystr, extra_data, extra_data_len);
+
+       set_cache_sequence_number(domain, CACHE_TYPE_GROUP, gidstr);
+}
+
+/* Fetch some cached user or group data */
+static BOOL fetch_cache(char *domain_name, char *cache_type,
+                        struct acct_info **sam_entries, int *num_sam_entries)
+{
+        TDB_DATA data;
+        fstring keystr;
+
+        if (lp_winbind_cache_time() == 0) return False;
+
+       /* Parameter check */
+       if (!sam_entries || !num_sam_entries) {
+               return False;
+       }
+
+       /* Check cache data is current */
+       if (cache_domain_expired(domain_name, 
+                                get_cache_sequence_number(domain_name, cache_type, NULL))) {
+               return False;
+       }
+       
+        /* Create key */        
+        slprintf(keystr, sizeof(keystr), "%s CACHE DATA/%s", cache_type,
+                 domain_name);
+       
+        /* Fetch cache information */
+       data = tdb_fetch_by_string(cache_tdb, keystr);
+       
+        if (!data.dptr) return False;
+
+       /* Copy across cached data.  We can save a memcpy() by directly
+          assigning the data.dptr to the sam_entries pointer.  It will
+          be freed by the end{pw,gr}ent() function. */
+       
+       *sam_entries = (struct acct_info *)data.dptr;
+       *num_sam_entries = data.dsize / sizeof(struct acct_info);
+       
+       DEBUG(4, ("fetched %d cached %s entries for domain %s\n",
+                 *num_sam_entries, cache_type, domain_name));
+       
+       return True;
+}
+
+/* Return cached entries for a domain.  Return false if there are no cached
+   entries, or the cached information has expired for the domain. */
+
+BOOL winbindd_fetch_user_cache(char *domain_name,
+                               struct acct_info **sam_entries,
+                               int *num_entries)
+{
+       return fetch_cache(domain_name, CACHE_TYPE_USER, sam_entries,
+                          num_entries);
+}
+
+/* Return cached entries for a domain.  Return false if there are no cached
+   entries, or the cached information has expired for the domain. */
+
+BOOL winbindd_fetch_group_cache(char *domain_name,
+                                struct acct_info **sam_entries,
+                                int *num_entries)
+{
+       return fetch_cache(domain_name, CACHE_TYPE_GROUP, sam_entries,
+                          num_entries);
+}
+
+static BOOL fetch_cache_entry(char *domain, char *name, void *buf, int len)
+{
+       TDB_DATA data;
+       fstring keystr;
+    
+       /* Create key for lookup */
+       slprintf(keystr, sizeof(keystr), "%s/%s", domain, name);
+    
+       /* Look up cache entry */
+       data = tdb_fetch_by_string(cache_tdb, keystr);
+       if (!data.dptr) return False;
+        
+        DEBUG(4, ("returning cached entry for %s/%s\n", domain, name));
+
+        /* Copy found entry into buffer */        
+        memcpy((char *)buf, data.dptr, len < data.dsize ? len : data.dsize);
+       free(data.dptr);
+       return True;
+}
+
+/* Fetch an individual user cache entry */
+BOOL winbindd_fetch_user_cache_entry(char *domain_name, char *user, 
+                                     struct winbindd_pw *pw)
+{
+       uint32 seq_num;
+
+       if (lp_winbind_cache_time() == 0) return False;
+
+       seq_num = get_cache_sequence_number(domain_name, CACHE_TYPE_USER, user);
+       if (cache_domain_expired(domain_name, seq_num)) return False;
+
+       return fetch_cache_entry(domain_name, user, pw, sizeof(struct winbindd_pw));
+}
+
+/* Fetch an individual uid cache entry */
+BOOL winbindd_fetch_uid_cache_entry(char *domain_name, uid_t uid, 
+                                   struct winbindd_pw *pw)
+{
+       fstring uidstr;
+       uint32 seq_num;
+
+        if (lp_winbind_cache_time() == 0) return False;
+
+       slprintf(uidstr, sizeof(uidstr), "#%u", (unsigned)uid);
+       seq_num = get_cache_sequence_number(domain_name, CACHE_TYPE_USER, uidstr);
+       if (cache_domain_expired(domain_name, seq_num)) return False;
+
+       return fetch_cache_entry(domain_name, uidstr, pw, sizeof(struct winbindd_pw));
+}
+
+/* Fetch an individual group cache entry.  This function differs from the
+   user cache code as we need to store the group membership data. */
+
+BOOL winbindd_fetch_group_cache_entry(char *domain_name, char *group, 
+                                      struct winbindd_gr *gr,
+                                      void **extra_data, int *extra_data_len)
+{
+        TDB_DATA data;
+        fstring keystr;
+       uint32 seq_num;
+
+        if (lp_winbind_cache_time() == 0) return False;
+
+       seq_num = get_cache_sequence_number(domain_name, CACHE_TYPE_GROUP, group);
+       if (cache_domain_expired(domain_name, seq_num)) return False;
+
+        /* Fetch group data */
+        if (!fetch_cache_entry(domain_name, group, gr, sizeof(struct winbindd_gr))) return False;
+       
+        /* Fetch extra data */
+        slprintf(keystr, sizeof(keystr), "%s/%s DATA", domain_name, group);
+        data = tdb_fetch_by_string(cache_tdb, keystr);
+
+        if (!data.dptr) return False;
+
+       /* Extra data freed when data has been sent */
+       if (extra_data) *extra_data = data.dptr;
+       if (extra_data_len) *extra_data_len = data.dsize;
+       
+       return True;
+}
+
+
+/* Fetch an individual gid cache entry.  This function differs from the
+   user cache code as we need to store the group membership data. */
+
+BOOL winbindd_fetch_gid_cache_entry(char *domain_name, gid_t gid,
+                                   struct winbindd_gr *gr,
+                                   void **extra_data, int *extra_data_len)
+{
+        TDB_DATA data;
+        fstring keystr;
+       fstring gidstr;
+       uint32 seq_num;
+
+       slprintf(gidstr, sizeof(gidstr), "#%u", (unsigned)gid);
+       
+        if (lp_winbind_cache_time() == 0) return False;
+
+       seq_num = get_cache_sequence_number(domain_name, CACHE_TYPE_GROUP, gidstr);
+       if (cache_domain_expired(domain_name, seq_num)) return False;
+
+        /* Fetch group data */
+        if (!fetch_cache_entry(domain_name, gidstr, gr, sizeof(struct winbindd_gr))) return False;
+
+        /* Fetch extra data */
+        slprintf(keystr, sizeof(keystr), "%s/%s DATA", domain_name, gidstr);
+        data = tdb_fetch_by_string(cache_tdb, keystr);
+        if (!data.dptr) return False;
+
+       /* Extra data freed when data has been sent */
+       if (extra_data) *extra_data = data.dptr;
+       if (extra_data_len) *extra_data_len = data.dsize;
+
+       return True;
+}
+
+/* Flush cache data - easiest to just reopen the tdb */
+void winbindd_flush_cache(void)
+{
+       tdb_close(cache_tdb);
+       winbindd_cache_init();
+}
diff --git a/source/nsswitch/winbindd_group.c b/source/nsswitch/winbindd_group.c
new file mode 100644 (file)
index 0000000..db94bab
--- /dev/null
@@ -0,0 +1,763 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 2.0
+
+   Winbind daemon for ntdom nss module
+
+   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"
+
+/* Fill a grent structure from various other information */
+
+static void winbindd_fill_grent(struct winbindd_gr *gr, char *gr_name,
+                                gid_t unix_gid)
+{
+    /* Fill in uid/gid */
+
+    gr->gr_gid = unix_gid;
+
+    /* Group name and password */
+
+    safe_strcpy(gr->gr_name, gr_name, sizeof(gr->gr_name) - 1);
+    safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
+}
+
+/* Fill in group membership */
+
+struct grent_mem_group {
+    uint32 rid;
+    enum SID_NAME_USE name_type;
+    fstring domain_name;
+    struct winbindd_domain *domain;
+    struct grent_mem_group *prev, *next;
+};
+
+struct grent_mem_list {
+    fstring name;
+    struct grent_mem_list *prev, *next;
+};
+
+/* Name comparison function for qsort() */
+
+static int name_comp(struct grent_mem_list *n1, struct grent_mem_list *n2)
+{
+    /* Silly cases */
+
+    if (!n1 && !n2) return 0;
+    if (!n1) return -1;
+    if (!n2) return 1;
+
+    return strcmp(n1->name, n2->name);
+}
+
+static struct grent_mem_list *sort_groupmem_list(struct grent_mem_list *list,
+                                                 int num_gr_mem)
+{
+    struct grent_mem_list *groupmem_array, *temp;
+    int i;
+
+    /* Allocate and zero an array to hold sorted entries */
+
+    if ((groupmem_array = malloc(num_gr_mem * 
+                                 sizeof(struct grent_mem_list))) == NULL) {
+        return NULL;
+    }
+
+    memset((char *)groupmem_array, 0, num_gr_mem * 
+           sizeof(struct grent_mem_list));
+
+    /* Copy list to array */
+
+    for(temp = list, i = 0; temp && i < num_gr_mem; temp = temp->next, i++) {
+        fstrcpy(groupmem_array[i].name, temp->name);
+    }
+
+    /* Sort array */
+
+    qsort(groupmem_array, num_gr_mem, sizeof(struct grent_mem_list), 
+          name_comp);
+
+    /* Fix up resulting array to a linked list and return it */
+
+    for(i = 0; i < num_gr_mem; i++) {
+
+        /* Fix up previous link */
+
+        if (i != 0) {
+            groupmem_array[i].prev = &groupmem_array[i - 1];
+        }
+
+        /* Fix up next link */
+
+        if (i != (num_gr_mem - 1)) {
+            groupmem_array[i].next = &groupmem_array[i + 1];
+        }
+    }
+
+    return groupmem_array;
+}
+
+static BOOL winbindd_fill_grent_mem(struct winbindd_domain *domain,
+                                    uint32 group_rid, 
+                                    enum SID_NAME_USE group_name_type, 
+                                    struct winbindd_response *response)
+{
+    struct grent_mem_group *done_groups = NULL, *todo_groups = NULL;
+    struct grent_mem_group *temp_group;
+    struct grent_mem_list *groupmem_list = NULL;
+    struct winbindd_gr *gr;
+
+    if (response) {
+        gr = &response->data.gr;
+    } else {
+        return False;
+    }
+    
+    /* Initialise group membership information */
+
+    gr->num_gr_mem = 0;
+
+    /* Add first group to todo_groups list */
+
+    if ((temp_group = 
+         (struct grent_mem_group *)malloc(sizeof(*temp_group))) == NULL) {
+        return False;
+    }
+
+    ZERO_STRUCTP(temp_group);
+
+    temp_group->rid = group_rid;
+    temp_group->name_type = group_name_type;
+    temp_group->domain = domain;
+    fstrcpy(temp_group->domain_name, domain->name);
+
+    DLIST_ADD(todo_groups, temp_group);
+            
+    /* Iterate over all groups to find members of */
+
+    while(todo_groups != NULL) {
+        struct grent_mem_group *current_group = todo_groups;
+        uint32 num_names = 0, *rid_mem = NULL;
+        enum SID_NAME_USE *name_types = NULL;
+
+        DOM_SID **sids = NULL;
+        char **names = NULL;
+        BOOL done_group;
+        int i;
+
+        /* Check we haven't looked up this group before */
+
+        done_group = 0;
+
+        for (temp_group = done_groups; temp_group != NULL; 
+             temp_group = temp_group->next) {
+
+            if ((temp_group->rid == current_group->rid) &&
+                (strcmp(temp_group->domain_name, 
+                        current_group->domain_name) == 0)) {
+                
+                done_group = 1;
+            }
+        }
+
+        if (done_group) goto cleanup;
+
+        /* Lookup group membership for the current group */
+
+        if (current_group->name_type == SID_NAME_DOM_GRP) {
+
+            if (!winbindd_lookup_groupmem(current_group->domain, 
+                                          current_group->rid, &num_names, 
+                                          &rid_mem, &names, &name_types)) {
+
+                DEBUG(1, ("fill_grent_mem(): could not lookup membership "
+                          "for group rid %d in domain %s\n", 
+                          current_group->rid,
+                          current_group->domain->name));
+
+                /* Exit if we cannot lookup the membership for the group
+                   this function was called to look at */
+
+                if (current_group->rid == group_rid) {
+                    return False;
+                } else {
+                    goto cleanup;
+                }
+            }
+        }
+
+        if (current_group->name_type == SID_NAME_ALIAS) {
+
+            if (!winbindd_lookup_aliasmem(current_group->domain, 
+                                          current_group->rid, &num_names, 
+                                          &sids, &names, &name_types)) {
+
+                DEBUG(1, ("fill_grent_mem(): group rid %d not a local group\n",
+                          group_rid));
+
+                /* Exit if we cannot lookup the membership for the group
+                   this function was called to look at */
+
+                if (current_group->rid == group_rid) {
+                    return False;
+                } else {
+                    goto cleanup;
+                }
+            }
+        }
+
+        /* Now for each member of the group, add it to the group list if it
+           is a user, otherwise push it onto the todo_group list if it is a
+           group or an alias. */
+    
+        for (i = 0; i < num_names; i++) {
+            enum SID_NAME_USE name_type;
+            fstring name_part1, name_part2;
+            char *name_dom, *name_user, *the_name;
+            struct winbindd_domain *name_domain;
+        
+            /* Lookup name */
+
+            ZERO_STRUCT(name_part1);
+            ZERO_STRUCT(name_part2);
+
+            the_name = names[i];
+           parse_domain_user(the_name, name_part1, name_part2);
+
+            if (strcmp(name_part1, "") != 0) {
+                name_dom = name_part1;
+                name_user = name_part2;
+
+                if ((name_domain = find_domain_from_name(name_dom)) == NULL) {
+                    DEBUG(0, ("unable to look up domain record for domain "
+                              "%s\n", name_dom));
+                    continue;
+                }
+
+            } else {
+                name_dom = current_group->domain->name;
+                name_user = name_part2;
+                name_domain = current_group->domain;
+            }
+
+            if (winbindd_lookup_sid_by_name(name_domain, name_user, NULL, 
+                                            &name_type) == WINBINDD_OK) {
+
+                /* Check name type */
+
+                if (name_type == SID_NAME_USER) {
+                    struct grent_mem_list *entry;
+
+                    /* Add to group membership list */
+                
+                    if ((entry = (struct grent_mem_list *)
+                         malloc(sizeof(*entry))) != NULL) {
+
+                        /* Create name */
+                       slprintf(entry->name, sizeof(entry->name),
+                                "%s/%s", name_dom, name_user);
+                        
+                        /* Add to list */
+
+                        DLIST_ADD(groupmem_list, entry);
+                        gr->num_gr_mem++;
+                    }
+
+                } else {
+                    struct grent_mem_group *todo_group;
+                    DOM_SID todo_sid;
+                    uint32 todo_rid;
+
+                    /* Add group to todo list */
+
+                    if (winbindd_lookup_sid_by_name(name_domain, names[i], 
+                                                    &todo_sid, &name_type) 
+                        == WINBINDD_OK) {
+
+                        /* Fill in group entry */
+
+                        sid_split_rid(&todo_sid, &todo_rid);
+
+                        if ((todo_group = (struct grent_mem_group *)
+                             malloc(sizeof(*todo_group))) != NULL) {
+                            
+                            ZERO_STRUCTP(todo_group);
+
+                            todo_group->rid = todo_rid;
+                            todo_group->name_type = name_type;
+                            todo_group->domain = name_domain;
+
+                            fstrcpy(todo_group->domain_name, name_dom);
+
+                            DLIST_ADD(todo_groups, todo_group);
+                        }
+                    }
+                }
+            }
+        }
+
+    cleanup:
+        
+        /* Remove group from todo list and add to done_groups list */
+
+        DLIST_REMOVE(todo_groups, current_group);
+        DLIST_ADD(done_groups, current_group);
+
+        /* Free memory allocated in winbindd_lookup_{alias,group}mem() */
+
+        safe_free(name_types);
+        safe_free(rid_mem);
+
+        free_char_array(num_names, names);
+        free_sid_array(num_names, sids);
+    }
+    
+    /* Free done groups list */
+
+    temp_group = done_groups;
+
+    if (temp_group != NULL) {
+        while (temp_group != NULL) {
+            struct grent_mem_group *next;
+
+            DLIST_REMOVE(done_groups, temp_group);
+            next = temp_group->next;
+
+            free(temp_group);
+            temp_group = next;
+        }
+    }
+
+    /* Remove duplicates from group member list. */
+
+    if (gr->num_gr_mem > 0) {
+        struct grent_mem_list *sorted_groupmem_list, *temp;
+        int extra_data_len = 0;
+        fstring prev_name;
+        char *head;
+
+        /* Sort list */
+
+        sorted_groupmem_list = sort_groupmem_list(groupmem_list, 
+                                                  gr->num_gr_mem);
+        /* Remove duplicates by iteration */
+
+        fstrcpy(prev_name, "");
+
+        for(temp = sorted_groupmem_list; temp; temp = temp->next) {
+            if (strequal(temp->name, prev_name)) {
+
+                /* Got a duplicate name - delete it.  Don't panic as we're
+                   only adjusting the prev and next pointers so memory
+                   allocation is not messed up. */
+
+                DLIST_REMOVE(sorted_groupmem_list, temp);
+                gr->num_gr_mem--;
+
+            } else {
+
+                /* Got a unique name - count how long it is */
+
+                extra_data_len += strlen(temp->name) + 1;
+            }
+        }
+
+        extra_data_len++;       /* Don't forget null a terminator */
+
+        /* Convert sorted list into extra data field to send back to ntdom
+           client.  Add one to extra_data_len for null termination */
+
+        if ((response->extra_data = malloc(extra_data_len))) {
+
+            /* Initialise extra data */
+
+            memset(response->extra_data, 0, extra_data_len);
+
+            head = response->extra_data;
+
+            /* Fill in extra data */
+
+            for(temp = sorted_groupmem_list; temp; temp = temp->next) {
+                int len = strlen(temp->name) + 1;
+                
+                safe_strcpy(head, temp->name, len);
+                head[len - 1] = ',';
+                head += len;
+            }
+            
+            *head = '\0';
+
+            /* Update response length */
+
+            response->length = sizeof(struct winbindd_response) +
+                extra_data_len;
+        }
+
+        /* Free memory for sorted_groupmem_list.  It was allocated as an
+           array in sort_groupmem_list() so can be freed in one go. */
+
+        free(sorted_groupmem_list);
+
+        /* Free groupmem_list */
+
+        temp = groupmem_list;
+
+        while (temp != NULL) {
+            struct grent_mem_list *next;
+            
+            DLIST_REMOVE(groupmem_list, temp);
+            next = temp->next;
+            
+            free(temp);
+            temp = next;
+        }
+    }
+
+    return True;
+}
+
+/* Return a group structure from a group name */
+
+enum winbindd_result winbindd_getgrnam_from_group(struct winbindd_cli_state *state)
+{
+    DOM_SID group_sid;
+    struct winbindd_domain *domain;
+    enum SID_NAME_USE name_type;
+    uint32 group_rid;
+    fstring name_domain, name_group, name;
+    char *tmp;
+    gid_t gid;
+    int extra_data_len;
+
+    /* Parse domain and groupname */
+
+    memset(name_group, 0, sizeof(fstring));
+
+    tmp = state->request.data.groupname;
+    parse_domain_user(tmp, name_domain, name_group);
+
+    /* Reject names that don't have a domain - i.e name_domain contains the
+       entire name. */
+
+    if (strequal(name_group, "")) {
+        return WINBINDD_ERROR;
+    }    
+
+    /* Get info for the domain */
+
+    if ((domain = find_domain_from_name(name_domain)) == NULL) {
+        DEBUG(0, ("getgrname_from_group(): could not get domain sid for "
+                  "domain %s\n", name_domain));
+        return WINBINDD_ERROR;
+    }
+
+    /* Check for cached user entry */
+
+    if (winbindd_fetch_group_cache_entry(name_domain, name_group,
+                                        &state->response.data.gr,
+                                        &state->response.extra_data,
+                                        &extra_data_len)) {
+            state->response.length += extra_data_len;
+            return WINBINDD_OK;
+    }
+
+    slprintf(name, sizeof(name), "%s\\%s", name_domain, name_group);
+
+    /* Get rid and name type from name */
+        
+    if (!winbindd_lookup_sid_by_name(domain, name, &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, ("from_group: name '%s' is not a local or domain group: %d\n",
+                  name_group, name_type));
+        return WINBINDD_ERROR;
+    }
+
+    /* Fill in group structure */
+
+    sid_split_rid(&group_sid, &group_rid);
+
+    if (!winbindd_idmap_get_gid_from_rid(domain->name, group_rid, &gid)) {
+        DEBUG(1, ("error sursing unix gid for sid\n"));
+        return WINBINDD_ERROR;
+    }
+
+    winbindd_fill_grent(&state->response.data.gr, 
+                        state->request.data.groupname, gid);
+        
+    if (!winbindd_fill_grent_mem(domain, group_rid, name_type,
+                                 &state->response)) {
+        return WINBINDD_ERROR;
+    }
+
+    /* Update cached group info */
+
+    winbindd_fill_group_cache_entry(name_domain, name_group, 
+                                    &state->response.data.gr,
+                                    state->response.extra_data,
+                                    state->response.length - 
+                                    sizeof(struct winbindd_response));
+
+    return WINBINDD_OK;
+}
+
+/* Return a group structure from a gid number */
+
+enum winbindd_result winbindd_getgrnam_from_gid(struct winbindd_cli_state 
+                                                *state)
+{
+    struct winbindd_domain *domain;
+    DOM_SID group_sid;
+    enum SID_NAME_USE name_type;
+    fstring group_name;
+    uint32 group_rid;
+    int extra_data_len;
+
+    /* Get rid from gid */
+    if (!winbindd_idmap_get_rid_from_gid(state->request.data.gid, &group_rid,
+                                         &domain)) {
+        DEBUG(1, ("Could not convert gid %d to rid\n", 
+                  state->request.data.gid));
+        return WINBINDD_ERROR;
+    }
+
+    /* try a cached entry */
+    if (winbindd_fetch_gid_cache_entry(domain->name, state->request.data.gid,
+                                      &state->response.data.gr,
+                                      &state->response.extra_data,
+                                      &extra_data_len)) {
+            state->response.length += extra_data_len;
+            return WINBINDD_OK;
+    }
+
+    /* Get sid from gid */
+
+    sid_copy(&group_sid, &domain->sid);
+    sid_append_rid(&group_sid, group_rid);
+
+    if (!winbindd_lookup_name_by_sid(domain, &group_sid, group_name, 
+                                     &name_type)) {
+        DEBUG(1, ("Could not lookup sid\n"));
+        return WINBINDD_ERROR;
+    }
+
+    string_sub(group_name, "\\", "/", sizeof(fstring));
+
+    if (!((name_type == SID_NAME_ALIAS) || (name_type == SID_NAME_DOM_GRP))) {
+        DEBUG(1, ("from_gid: name '%s' is not a local or domain group: %d\n", 
+                  group_name, name_type));
+        return WINBINDD_ERROR;
+    }
+
+    /* Fill in group structure */
+
+    winbindd_fill_grent(&state->response.data.gr, group_name, 
+                        state->request.data.gid);
+
+    if (!winbindd_fill_grent_mem(domain, group_rid, name_type,
+                                 &state->response)) {
+        return WINBINDD_ERROR;
+    }
+
+    /* Update cached group info */
+    winbindd_fill_gid_cache_entry(domain->name, state->request.data.gid,
+                                 &state->response.data.gr,
+                                 state->response.extra_data,
+                                 state->response.length - 
+                                 sizeof(struct winbindd_response));
+
+    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 *tmp;
+
+    if (state == NULL) 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 (tmp = domain_list; tmp != NULL; tmp = tmp->next) {
+        struct getent_state *domain_state;
+
+        /* Skip domains other than WINBINDD_DOMAIN environment variable */
+
+        if ((strcmp(state->request.domain, "") != 0) &&
+            (strcmp(state->request.domain, tmp->name) != 0)) {
+                continue;
+        }
+
+        /* 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);
+
+        /* Add to list of open domains */
+
+        domain_state->domain = tmp;
+        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)
+{
+    if (state == NULL) return WINBINDD_ERROR;
+
+    free_getent_state(state->getgrent_state);
+    state->getgrent_state = NULL;
+
+    return WINBINDD_OK;
+}
+
+/* Fetch next group entry from netdom database */
+
+enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state)
+{
+    if (state == NULL) return WINBINDD_ERROR;
+
+    /* Process the current head of the getent_state list */
+
+    while(state->getgrent_state != NULL) {
+        struct getent_state *ent = state->getgrent_state;
+
+        /* Get list of entries if we haven't already got them */
+
+        if (!ent->got_sam_entries) {
+            uint32 status, start_ndx = 0, start_ndx2 = 0;
+        
+            if (!winbindd_fetch_group_cache(ent->domain->name, 
+                                            &ent->sam_entries,
+                                            &ent->num_sam_entries)) {
+
+                /* Fetch group entries */
+
+                if (!domain_handles_open(ent->domain)) goto cleanup;
+
+               /* Enumerate domain groups */
+                    
+               do {
+                        status =
+                               samr_enum_dom_groups(&ent->domain->sam_dom_handle,
+                                                    &start_ndx, 0x100000,
+                                                    &ent->sam_entries,
+                                                    &ent->num_sam_entries);
+               } while (status == STATUS_MORE_ENTRIES);
+                    
+               /* Enumerate domain aliases */
+                    
+               do {
+                        status = 
+                               samr_enum_dom_aliases(&ent->domain->sam_dom_handle,
+                                                     &start_ndx2, 0x100000,
+                                                     &ent->sam_entries,
+                                                     &ent->num_sam_entries);
+               } while (status == STATUS_MORE_ENTRIES);
+                
+                /* Fill cache with received entries */
+
+                winbindd_fill_group_cache(ent->domain->name, ent->sam_entries,
+                                          ent->num_sam_entries);
+            }
+
+            ent->got_sam_entries = True;
+        }
+
+        /* Send back a group */
+
+        while (ent->sam_entry_index < ent->num_sam_entries) {
+            enum winbindd_result result;
+            fstring domain_group_name;
+            char *group_name = (ent->sam_entries)
+                [ent->sam_entry_index].acct_name; 
+   
+            /* Prepend domain to name */
+
+           slprintf(domain_group_name, sizeof(domain_group_name),
+                    "%s/%s", ent->domain->name, group_name);
+   
+            /* Get group entry from group name */
+
+            fstrcpy(state->request.data.groupname, domain_group_name);
+            result = winbindd_getgrnam_from_group(state);
+
+            ent->sam_entry_index++;
+                                                      
+            if (result == WINBINDD_OK) {
+                return result;
+            }
+
+            /* Try next group */
+
+            DEBUG(1, ("could not getgrnam_from_group for group name %s\n",
+                      domain_group_name));
+        }
+
+        /* We've exhausted all users for this pipe - close it down and
+           start on the next one. */
+        
+    cleanup:
+
+        /* Free mallocated memory for sam entries.  The data stored here
+           may have been allocated from the cache. */
+
+        if (ent->sam_entries != NULL) free(ent->sam_entries);
+        ent->sam_entries = NULL;
+
+        /* Free state information for this domain */
+
+        {
+            struct getent_state *old_ent;
+
+            old_ent = state->getgrent_state;
+            DLIST_REMOVE(state->getgrent_state, state->getgrent_state);
+            free(old_ent);
+        }
+    }
+
+    /* Out of pipes so we're done */
+
+    return WINBINDD_ERROR;
+}
diff --git a/source/nsswitch/winbindd_idmap.c b/source/nsswitch/winbindd_idmap.c
new file mode 100644 (file)
index 0000000..cd3fd2c
--- /dev/null
@@ -0,0 +1,235 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 2.0
+
+   Winbind daemon - user related function
+
+   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"
+
+/* High water mark keys */
+
+#define HWM_GROUP  "GROUP HWM"
+#define HWM_USER   "USER HWM"
+
+/* Globals */
+
+static TDB_CONTEXT *idmap_tdb;
+
+/* Allocate either a user or group id from the pool */
+
+static BOOL allocate_id(int *id, BOOL isgroup)
+{
+    int hwm;
+
+    /* Get current high water mark */
+
+    if ((hwm = tdb_get_int(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_int(idmap_tdb, isgroup ? HWM_GROUP : HWM_USER, hwm);
+
+    return True;
+}
+
+/* Get an id from a rid */
+
+static BOOL get_id_from_rid(char *domain_name, uint32 rid, int *id,
+                            BOOL isgroup)
+{
+    TDB_DATA data, key;
+    fstring keystr;
+    BOOL result;
+
+    /* Check if rid is present in database */
+
+    slprintf(keystr, sizeof(keystr), "%s/%d", domain_name, rid);
+    
+    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;
+        }
+
+        free(data.dptr);
+
+    } else {
+
+        /* Allocate a new id for this rid */
+
+        if (id && 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;
+}
+
+/* Get a uid from a user rid */
+
+BOOL winbindd_idmap_get_uid_from_rid(char *domain_name, uint32 user_rid, 
+                                     uid_t *uid)
+{
+    return get_id_from_rid(domain_name, user_rid, uid, False);
+}
+
+/* Get a gid from a group rid */
+
+BOOL winbindd_idmap_get_gid_from_rid(char *domain_name, uint32 group_rid, 
+                                     gid_t *gid)
+{
+    return get_id_from_rid(domain_name, group_rid, gid, True);
+}
+
+BOOL get_rid_from_id(int id, uint32 *rid, struct winbindd_domain **domain,
+                     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) {
+        char *p = data.dptr;
+        fstring domain_name;
+        uint32 the_rid;
+
+        if (next_token(&p, domain_name, "/", sizeof(fstring))) {
+
+            the_rid = atoi(p);
+
+            if (rid) {
+                *rid = the_rid;
+            }
+
+            if (domain) {
+                *domain = find_domain_from_name(domain_name);
+            }
+
+            result = True;
+        }
+            
+        free(data.dptr);
+    }
+
+    return result;
+}
+
+/* Get a user rid from a uid */
+
+BOOL winbindd_idmap_get_rid_from_uid(uid_t uid, uint32 *user_rid,
+                                     struct winbindd_domain **domain)
+{
+    return get_rid_from_id((int)uid, user_rid, domain, False);
+}
+
+/* Get a group rid from a gid */
+
+BOOL winbindd_idmap_get_rid_from_gid(gid_t gid, uint32 *group_rid, 
+                                     struct winbindd_domain **domain)
+{
+    return get_rid_from_id((int)gid, group_rid, domain, True);
+}
+
+/* Initialise idmap database */
+
+BOOL winbindd_idmap_init(void)
+{
+    /* Open tdb cache */
+
+    if (!(idmap_tdb = tdb_open(lock_path("winbindd_idmap.tdb"), 0,
+                               TDB_NOLOCK | TDB_NOMMAP, 
+                               O_RDWR | O_CREAT, 0600))) {
+        DEBUG(0, ("Unable to open idmap database\n"));
+        return False;
+    }
+
+     /* Create high water marks for group and user id */
+
+    if (tdb_get_int(idmap_tdb, HWM_USER) == -1) {
+        if (tdb_store_int(idmap_tdb, HWM_USER, server_state.uid_low) == -1) {
+            DEBUG(0, ("Unable to initialise user hwm in idmap database\n"));
+            return False;
+        }
+    }
+
+    if (tdb_get_int(idmap_tdb, HWM_GROUP) == -1) {
+        if (tdb_store_int(idmap_tdb, HWM_GROUP, server_state.gid_low) == -1) {
+            DEBUG(0, ("Unable to initialise group hwm in idmap database\n"));
+            return False;
+        }
+    }
+
+    return True;   
+}
diff --git a/source/nsswitch/winbindd_nss.h b/source/nsswitch/winbindd_nss.h
new file mode 100644 (file)
index 0000000..0cf16e9
--- /dev/null
@@ -0,0 +1,118 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 2.0
+
+   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 _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 variable */
+
+/* Socket commands */
+
+enum winbindd_cmd {
+    WINBINDD_GETPWNAM_FROM_USER,     /* getpwnam stuff */
+    WINBINDD_GETPWNAM_FROM_UID,
+    WINBINDD_GETGRNAM_FROM_GROUP,    /* getgrnam stuff */
+    WINBINDD_GETGRNAM_FROM_GID,
+    WINBINDD_SETPWENT,               /* get/set/endpwent */
+    WINBINDD_ENDPWENT,
+    WINBINDD_GETPWENT,
+    WINBINDD_SETGRENT,               /* get/set/endgrent */
+    WINBINDD_ENDGRENT,
+    WINBINDD_GETGRENT,
+    WINBINDD_PAM_AUTH
+};
+
+/* Winbind request structure */
+
+struct winbindd_request {
+       enum winbindd_cmd cmd;   /* Winbindd command to execute */
+       pid_t pid;               /* pid of calling process */
+
+       union {
+               fstring username;    /* getpwnam() */
+               fstring groupname;   /* getgrnam() */
+               uid_t uid;           /* getpwuid() */
+               gid_t gid;           /* getgrgid() */
+               struct {
+                       /* the following is used by pam_winbind */
+                       fstring user;
+                       fstring pass;
+               } auth;
+       } data;
+        fstring domain;      /* {set,get,end}{pw,gr}ent() */
+};
+
+/* Response values */
+
+enum winbindd_result {
+    WINBINDD_ERROR,
+    WINBINDD_OK
+};
+
+/* Winbind response structure */
+
+struct winbindd_response {
+    
+    /* Header information */
+
+    int length;                           /* Length of response */
+    enum winbindd_result result;          /* Result code */
+
+    /* Fixed length return data */
+
+    union {
+        
+        /* getpwnam, getpwuid, getpwent */
+
+        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;
+            int pwent_ndx;
+        } pw;
+
+        /* getgrnam, getgrgid, getgrent */
+
+        struct winbindd_gr {
+            fstring gr_name;
+            fstring gr_passwd;
+            gid_t gr_gid;
+            int num_gr_mem;
+            int grent_ndx;
+        } gr;
+
+    } data;
+
+    /* Variable length return data */
+
+    void *extra_data;                    /* getgrnam, getgrgid, getgrent */
+};
+
+#endif
diff --git a/source/nsswitch/winbindd_pam.c b/source/nsswitch/winbindd_pam.c
new file mode 100644 (file)
index 0000000..011fbed
--- /dev/null
@@ -0,0 +1,97 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 3.0
+
+   Winbind daemon - pam auuth funcions
+
+   Copyright (C) Andrew Tridgell 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"
+/************************************************************************
+form a key for fetching a domain trust password
+************************************************************************/
+static char *trust_keystr(char *domain)
+{
+       static fstring keystr;
+       slprintf(keystr,sizeof(keystr),"%s/%s", SECRETS_MACHINE_ACCT_PASS, domain);
+       return keystr;
+}
+
+/************************************************************************
+ Routine to get the trust account password for a domain.
+ The user of this function must have locked the trust password file.
+************************************************************************/
+static BOOL _get_trust_account_password(char *domain, unsigned char *ret_pwd, time_t *pass_last_set_time)
+{
+       struct machine_acct_pass *pass;
+       size_t size;
+
+       if (!(pass = secrets_fetch(trust_keystr(domain), &size)) ||
+           size != sizeof(*pass)) return False;
+
+       if (pass_last_set_time) *pass_last_set_time = pass->mod_time;
+       memcpy(ret_pwd, pass->hash, 16);
+       free(pass);
+       return True;
+}
+
+
+/* Return a password structure from a username.  Specify whether cached data 
+   can be returned. */
+
+enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) 
+{
+       NET_USER_INFO_3 info3;
+       uchar ntpw[16];
+       uchar lmpw[16];
+       uchar trust_passwd[16];
+       uint32 status;
+       fstring server;
+       fstring name_domain, name_user;
+       extern pstring global_myname;
+
+       DEBUG(1,("winbindd_pam_auth user=%s\n", 
+                state->request.data.auth.user));
+
+       /* Parse domain and username */
+       parse_domain_user(state->request.data.auth.user, name_domain, name_user);
+
+       /* don't allow the null domain */
+       if (strcmp(name_domain,"") == 0) return WINBINDD_ERROR;
+
+       ZERO_STRUCT(info3);
+
+       if (!_get_trust_account_password(name_domain, trust_passwd, NULL)) return WINBINDD_ERROR;
+
+       nt_lm_owf_gen(state->request.data.auth.pass, ntpw, lmpw);
+
+       slprintf(server, sizeof(server), "\\\\%s", server_state.controller);
+
+       status = domain_client_validate_backend(server, 
+                                               name_user, name_domain,
+                                               global_myname, SEC_CHAN_WKSTA,
+                                               trust_passwd,
+                                               NULL,
+                                               lmpw, sizeof(lmpw),
+                                               ntpw, sizeof(ntpw), &info3);
+
+       if (status != NT_STATUS_NOPROBLEMO) return WINBINDD_ERROR;
+
+       return WINBINDD_OK;
+}
+
diff --git a/source/nsswitch/winbindd_proto.h b/source/nsswitch/winbindd_proto.h
new file mode 100644 (file)
index 0000000..774478f
--- /dev/null
@@ -0,0 +1,113 @@
+#ifndef _WINBINDD_PROTO_H_
+#define _WINBINDD_PROTO_H_
+/* This file is automatically generated with "make proto". DO NOT EDIT */
+
+
+/*The following definitions come from  nsswitch/winbindd.c  */
+
+int main(int argc, char **argv);
+
+/*The following definitions come from  nsswitch/winbindd_cache.c  */
+
+void winbindd_cache_init(void);
+void winbindd_fill_user_cache(char *domain_name, 
+                              struct acct_info *sam_entries,
+                              int num_sam_entries);
+void winbindd_fill_group_cache(char *domain_name,
+                               struct acct_info *sam_entries,
+                               int num_sam_entries);
+void winbindd_fill_user_cache_entry(char *domain, char *user_name, 
+                                    struct winbindd_pw *pw);
+void winbindd_fill_uid_cache_entry(char *domain, uid_t uid, 
+                                    struct winbindd_pw *pw);
+void winbindd_fill_group_cache_entry(char *domain, char *group_name, 
+                                     struct winbindd_gr *gr, void *extra_data,
+                                     int extra_data_len);
+void winbindd_fill_gid_cache_entry(char *domain, gid_t gid, 
+                                     struct winbindd_gr *gr, void *extra_data,
+                                     int extra_data_len);
+BOOL winbindd_fetch_user_cache(char *domain_name,
+                               struct acct_info **sam_entries,
+                               int *num_entries);
+BOOL winbindd_fetch_group_cache(char *domain_name,
+                                struct acct_info **sam_entries,
+                                int *num_entries);
+BOOL winbindd_fetch_user_cache_entry(char *domain_name, char *user, 
+                                     struct winbindd_pw *pw);
+BOOL winbindd_fetch_uid_cache_entry(char *domain_name, uid_t uid, 
+                                   struct winbindd_pw *pw);
+BOOL winbindd_fetch_group_cache_entry(char *domain_name, char *group, 
+                                      struct winbindd_gr *gr,
+                                      void **extra_data, int *extra_data_len);
+BOOL winbindd_fetch_gid_cache_entry(char *domain_name, gid_t gid,
+                                   struct winbindd_gr *gr,
+                                   void **extra_data, int *extra_data_len);
+void winbindd_flush_cache(void);
+
+/*The following definitions come from  nsswitch/winbindd_group.c  */
+
+enum winbindd_result winbindd_getgrnam_from_group(struct winbindd_cli_state *state);
+enum winbindd_result winbindd_getgrnam_from_gid(struct winbindd_cli_state 
+                                                *state);
+enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state);
+enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state);
+enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state);
+
+/*The following definitions come from  nsswitch/winbindd_idmap.c  */
+
+BOOL winbindd_idmap_get_uid_from_rid(char *domain_name, uint32 user_rid, 
+                                     uid_t *uid);
+BOOL winbindd_idmap_get_gid_from_rid(char *domain_name, uint32 group_rid, 
+                                     gid_t *gid);
+BOOL get_rid_from_id(int id, uint32 *rid, struct winbindd_domain **domain,
+                     BOOL isgroup);
+BOOL winbindd_idmap_get_rid_from_uid(uid_t uid, uint32 *user_rid,
+                                     struct winbindd_domain **domain);
+BOOL winbindd_idmap_get_rid_from_gid(gid_t gid, uint32 *group_rid, 
+                                     struct winbindd_domain **domain);
+BOOL winbindd_idmap_init(void);
+
+/*The following definitions come from  nsswitch/winbindd_pam.c  */
+
+enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) ;
+
+/*The following definitions come from  nsswitch/winbindd_user.c  */
+
+enum winbindd_result winbindd_getpwnam_from_user(struct winbindd_cli_state *state) ;
+enum winbindd_result winbindd_getpwnam_from_uid(struct winbindd_cli_state 
+                                                *state);
+enum winbindd_result winbindd_setpwent(struct winbindd_cli_state *state);
+enum winbindd_result winbindd_endpwent(struct winbindd_cli_state *state);
+enum winbindd_result winbindd_getpwent(struct winbindd_cli_state *state);
+
+/*The following definitions come from  nsswitch/winbindd_util.c  */
+
+BOOL domain_handles_open(struct winbindd_domain *domain);
+void establish_connections(void) ;
+BOOL lookup_domain_sid(char *domain_name, struct winbindd_domain *domain);
+BOOL get_domain_info(struct winbindd_domain *domain);
+BOOL winbindd_lookup_sid_by_name(struct winbindd_domain *domain,
+                                 char *name, DOM_SID *sid,
+                                 enum SID_NAME_USE *type);
+BOOL winbindd_lookup_name_by_sid(struct winbindd_domain *domain,
+                                 DOM_SID *sid, char *name,
+                                 enum SID_NAME_USE *type);
+BOOL winbindd_lookup_userinfo(struct winbindd_domain *domain,
+                              uint32 user_rid, SAM_USERINFO_CTR *user_info);
+BOOL winbindd_lookup_groupinfo(struct winbindd_domain *domain,
+                              uint32 group_rid, GROUP_INFO_CTR *info);
+BOOL winbindd_lookup_groupmem(struct winbindd_domain *domain,
+                              uint32 group_rid, uint32 *num_names, 
+                              uint32 **rid_mem, char ***names, 
+                              enum SID_NAME_USE **name_types);
+int winbindd_lookup_aliasmem(struct winbindd_domain *domain,
+                             uint32 alias_rid, uint32 *num_names, 
+                             DOM_SID ***sids, char ***names, 
+                             enum SID_NAME_USE **name_types);
+struct winbindd_domain *find_domain_from_name(char *domain_name);
+void free_getent_state(struct getent_state *state);
+BOOL winbindd_param_init(void);
+char *winbindd_cmd_to_string(enum winbindd_cmd cmd);
+void parse_domain_user(char *domuser, fstring domain, fstring user);
+uint32 domain_sequence_number(char *domain_name);
+#endif /* _WINBINDD_PROTO_H_ */
diff --git a/source/nsswitch/winbindd_user.c b/source/nsswitch/winbindd_user.c
new file mode 100644 (file)
index 0000000..3e7cba0
--- /dev/null
@@ -0,0 +1,412 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 2.0
+
+   Winbind daemon - user related function
+
+   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"
+
+/* Fill a pwent structure with information we have obtained */
+
+static void winbindd_fill_pwent(struct winbindd_pw *pw, char *name,
+                                uid_t unix_uid, gid_t unix_gid, 
+                                char *full_name)
+{
+    pstring homedir;
+    fstring name_domain, name_user;
+
+    if (!pw || !name) {
+        return;
+    }
+
+    /* Fill in uid/gid */
+
+    pw->pw_uid = unix_uid;
+    pw->pw_gid = unix_gid;
+
+    /* Username */
+
+    safe_strcpy(pw->pw_name, name, 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. */
+
+    parse_domain_user(name, name_domain, name_user);
+
+    pstrcpy(homedir, lp_template_homedir());
+
+    pstring_sub(homedir, "%U", name_user);
+    pstring_sub(homedir, "%D", name_domain);
+
+    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_ntdom module. */
+
+    safe_strcpy(pw->pw_passwd, "x", sizeof(pw->pw_passwd) - 1);
+}
+
+/* Return a password structure from a username.  Specify whether cached data 
+   can be returned. */
+
+enum winbindd_result winbindd_getpwnam_from_user(struct winbindd_cli_state *state) 
+{
+    uint32 name_type, user_rid, group_rid;
+    SAM_USERINFO_CTR user_info;
+    DOM_SID user_sid;
+    fstring name_domain, name_user, name, gecos_name;
+    struct winbindd_domain *domain;
+    uid_t uid;
+    gid_t gid;
+
+    /* Parse domain and username */
+    parse_domain_user(state->request.data.username, name_domain, name_user);
+
+    /* Reject names that don't have a domain - i.e name_domain contains the
+       entire name. */
+    if (strequal(name_domain, "")) {
+        return WINBINDD_ERROR;
+    }
+
+    /* 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));
+        return WINBINDD_ERROR;
+    }
+
+    /* Check for cached user entry */
+
+    if (winbindd_fetch_user_cache_entry(name_domain, name_user,
+                                       &state->response.data.pw)) {
+            return WINBINDD_OK;
+    }
+
+    slprintf(name,sizeof(name),"%s\\%s", name_domain, name_user);
+
+    /* Get rid and name type from name */
+    /* the following costs 1 packet */
+    if (!winbindd_lookup_sid_by_name(domain, name, &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() */
+    
+    sid_split_rid(&user_sid, &user_rid);
+
+    /* the following costs 3 packets */
+    if (!winbindd_lookup_userinfo(domain, user_rid, &user_info)) {
+        DEBUG(1, ("pwnam_from_user(): error getting user info for user '%s'\n",
+                  name_user));
+        return WINBINDD_ERROR;
+    }
+    
+    group_rid = user_info.info.id21->group_rid;
+    unistr2_to_ascii(gecos_name, &user_info.info.id21->uni_full_name,
+                     sizeof(gecos_name) - 1);
+
+    free_samr_userinfo_ctr(&user_info);
+
+    /* Resolve the uid number */
+
+    if (!winbindd_idmap_get_uid_from_rid(domain->name, user_rid, &uid)) {
+        DEBUG(1, ("error getting user id for user %s\n", name_user));
+        return WINBINDD_ERROR;
+    }
+
+    /* Resolve the gid number */   
+
+    if (!winbindd_idmap_get_gid_from_rid(domain->name, group_rid, &gid)) {
+        DEBUG(1, ("error getting group id for user %s\n", name_user));
+        return WINBINDD_ERROR;
+    }
+
+    /* Now take all this information and fill in a passwd structure */
+            
+    winbindd_fill_pwent(&state->response.data.pw, 
+                        state->request.data.username, uid, gid, 
+                        gecos_name);
+            
+    winbindd_fill_user_cache_entry(name_domain, name_user, 
+                                   &state->response.data.pw);
+
+    return WINBINDD_OK;
+}       
+
+/* Return a password structure given a uid number */
+
+enum winbindd_result winbindd_getpwnam_from_uid(struct winbindd_cli_state 
+                                                *state)
+{
+    DOM_SID user_sid;
+    struct winbindd_domain *domain;
+    uint32 user_rid, group_rid;
+    fstring user_name, gecos_name;
+    enum SID_NAME_USE name_type;
+    SAM_USERINFO_CTR user_info;
+    gid_t gid;
+
+    /* Get rid from uid */
+    if (!winbindd_idmap_get_rid_from_uid(state->request.data.uid, &user_rid,
+                                         &domain)) {
+        DEBUG(1, ("Could not convert uid %d to rid\n", 
+                  state->request.data.uid));
+        return WINBINDD_ERROR;
+    }
+    
+    /* Check for cached uid entry */
+    if (winbindd_fetch_uid_cache_entry(domain->name, state->request.data.uid,
+                                      &state->response.data.pw)) {
+            return WINBINDD_OK;
+    }
+
+
+    /* Get name and name type from rid */
+
+    sid_copy(&user_sid, &domain->sid);
+    sid_append_rid(&user_sid, user_rid);
+
+    if (!winbindd_lookup_name_by_sid(domain, &user_sid, user_name, 
+                                     &name_type)) {
+        fstring temp;
+
+        sid_to_string(temp, &user_sid);
+        DEBUG(1, ("Could not lookup sid %s\n", temp));
+        return WINBINDD_ERROR;
+    }
+
+    string_sub(user_name, "\\", "/", sizeof(fstring));
+
+    /* Get some user info */
+    
+    if (!winbindd_lookup_userinfo(domain, user_rid, &user_info)) {
+        DEBUG(1, ("pwnam_from_uid(): error getting user info for user '%s'\n",
+                  user_name));
+        return WINBINDD_ERROR;
+    }
+
+    group_rid = user_info.info.id21->group_rid;
+    unistr2_to_ascii(gecos_name, &user_info.info.id21->uni_full_name,
+                     sizeof(gecos_name) - 1);
+
+    free_samr_userinfo_ctr(&user_info);
+
+    /* Resolve gid number */
+
+    if (!winbindd_idmap_get_gid_from_rid(domain->name, group_rid, &gid)) {
+        DEBUG(1, ("error getting group id for user %s\n", user_name));
+        return WINBINDD_ERROR;
+    }
+
+    /* Fill in password structure */
+
+    winbindd_fill_pwent(&state->response.data.pw, user_name, 
+                        state->request.data.uid, gid, gecos_name);
+
+    winbindd_fill_uid_cache_entry(domain->name, state->request.data.uid,
+                                 &state->response.data.pw);
+
+    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 *tmp;
+
+    if (state == NULL) 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(tmp = domain_list; tmp != NULL; tmp = tmp->next) {
+        struct getent_state *domain_state;
+
+        /* Skip domains other than WINBINDD_DOMAIN environment variable */
+
+        if ((strcmp(state->request.domain, "") != 0) &&
+            (strcmp(state->request.domain, tmp->name) != 0)) {
+                continue;
+        }
+
+        /* 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);
+        domain_state->domain = tmp;
+
+        /* 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)
+{
+    if (state == NULL) return WINBINDD_ERROR;
+
+    free_getent_state(state->getpwent_state);    
+    state->getpwent_state = NULL;
+
+    return WINBINDD_OK;
+}
+
+/* Fetch next passwd entry from ntdom database */
+
+enum winbindd_result winbindd_getpwent(struct winbindd_cli_state *state)
+{
+    if (state == NULL) return WINBINDD_ERROR;
+
+    /* Process the current head of the getent_state list */
+
+    while(state->getpwent_state != NULL) {
+        struct getent_state *ent = state->getpwent_state;
+
+        /* Get list of user entries for this pipe */
+
+        if (!ent->got_sam_entries) {
+            uint32 status, start_ndx = 0;
+
+            /* Look in cache for entries, else get them direct */
+
+            if (!winbindd_fetch_user_cache(ent->domain->name, 
+                                           &ent->sam_entries,
+                                           &ent->num_sam_entries)) {
+
+                /* Fetch the user entries */
+
+                if (!domain_handles_open(ent->domain)) goto cleanup;
+
+                do {
+                    status =
+                        samr_enum_dom_users(
+                            &ent->domain->sam_dom_handle, &start_ndx, 0, 0, 
+                            0x10000, &ent->sam_entries, &ent->num_sam_entries);
+                } while (status == STATUS_MORE_ENTRIES);
+
+                /* Fill cache with received entries */
+            
+                winbindd_fill_user_cache(ent->domain->name, ent->sam_entries, 
+                                         ent->num_sam_entries);
+            }
+            
+            ent->got_sam_entries = True;
+        }
+        
+        /* Send back a user */
+
+        while (ent->sam_entry_index < ent->num_sam_entries) {
+            enum winbindd_result result;
+            fstring domain_user_name;
+            char *user_name = (ent->sam_entries)
+                [ent->sam_entry_index].acct_name; 
+                
+            /* Don't bother with machine accounts */
+
+            if (user_name[strlen(user_name) - 1] == '$') {
+                ent->sam_entry_index++;
+                continue;
+            }
+
+            /* Prepend domain to name */
+
+           slprintf(domain_user_name, sizeof(domain_user_name),
+                    "%s/%s", ent->domain->name, user_name);
+                
+            /* Get passwd entry from user name */
+                
+            fstrcpy(state->request.data.username, domain_user_name);
+            result = winbindd_getpwnam_from_user(state);
+
+            ent->sam_entry_index++;
+                
+            /* Return if user lookup worked */
+                
+            if (result == WINBINDD_OK) {
+                return result;
+            }
+                
+            /* Try next user */
+            
+            DEBUG(1, ("could not getpwnam_from_user for username %s\n",
+                      domain_user_name));
+        }
+
+        /* We've exhausted all users for this pipe - close it down and
+           start on the next one. */
+
+    cleanup:
+
+        /* Free mallocated memory for sam entries.  The data stored here
+           may have been allocated from the cache. */
+
+        if (ent->sam_entries != NULL) free(ent->sam_entries);
+        ent->sam_entries = NULL;
+
+        /* Free state information for this domain */
+
+        {
+            struct getent_state *old_ent;
+
+            old_ent = state->getpwent_state;
+            DLIST_REMOVE(state->getpwent_state, state->getpwent_state);
+            free(old_ent);
+        }
+    }
+
+    /* Out of pipes so we're done */
+
+    return WINBINDD_ERROR;
+}
diff --git a/source/nsswitch/winbindd_util.c b/source/nsswitch/winbindd_util.c
new file mode 100644 (file)
index 0000000..33150d1
--- /dev/null
@@ -0,0 +1,633 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 2.0
+
+   Winbind daemon for ntdom nss module
+
+   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"
+#include "sids.h"
+
+BOOL domain_handles_open(struct winbindd_domain *domain)
+{
+       return domain->sam_handle_open &&
+               domain->sam_dom_handle_open;
+}
+
+static BOOL resolve_dc_name(char *domain_name, fstring domain_controller)
+{
+       struct in_addr ip;
+       extern pstring global_myname;
+       
+       /* if its our primary domain and password server is not '*' then use the
+          password server parameter */
+       if (strcmp(domain_name,lp_workgroup()) == 0 && 
+           strcmp(lp_passwordserver(),"*") != 0) {
+               fstrcpy(domain_controller, lp_passwordserver());
+               return True;
+       }
+
+       if (!resolve_name(domain_name, &ip, 0x1B)) return False;
+
+       return lookup_pdc_name(global_myname, domain_name, &ip, domain_controller);
+}
+
+
+static struct winbindd_domain *add_trusted_domain(char *domain_name)
+{
+    struct winbindd_domain *domain;
+
+    DEBUG(1, ("adding trusted domain %s\n", domain_name));
+
+    /* Create new domain entry */
+
+    if ((domain = (struct winbindd_domain *)malloc(sizeof(*domain))) == NULL) {
+        return NULL;
+    }
+
+    /* Fill in fields */
+
+    ZERO_STRUCTP(domain);
+
+    if (domain_name) {
+        fstrcpy(domain->name, domain_name);
+    }
+
+    /* Link to domain list */
+
+    DLIST_ADD(domain_list, domain);
+
+    return domain;
+}
+
+/* Look up global info for the winbind daemon */
+static BOOL get_trusted_domains(void)
+{
+       uint32 enum_ctx = 0;
+       uint32 num_doms = 0;
+       char **domains = NULL;
+       DOM_SID **sids = NULL;
+       BOOL result;
+       int i;
+       
+       DEBUG(1, ("getting trusted domain list\n"));
+
+       /* Add our workgroup - keep handle to look up trusted domains */
+       if (!add_trusted_domain(lp_workgroup())) {
+               DEBUG(0, ("could not add record for domain %s\n", lp_workgroup()));
+               return False;
+       }
+       
+       /* Enumerate list of trusted domains */ 
+       result = lsa_enum_trust_dom(&server_state.lsa_handle, &enum_ctx,
+                                   &num_doms, &domains, &sids);
+       
+       if (!result || !domains) return False;
+       
+        /* Add each domain to the trusted domain list */
+       for(i = 0; i < num_doms; i++) {
+               if (!add_trusted_domain(domains[i])) {
+                       DEBUG(0, ("could not add record for domain %s\n", domains[i]));
+                       result = False;
+               }
+       }
+       
+       /* Free memory */       
+       free_char_array(num_doms, domains);
+       free_sid_array(num_doms, sids);
+       
+       return True;
+}
+
+
+/* Open sam and sam domain handles to a domain and cache the results */
+static BOOL open_sam_handles(struct winbindd_domain *domain)
+{
+       /* Get domain info */
+       if (!domain->got_domain_info) {
+               domain->got_domain_info = get_domain_info(domain);
+               if (!domain->got_domain_info) return False;
+       }
+
+       /* Open sam handle if it isn't already open */
+       if (!domain->sam_handle_open) {
+               domain->sam_handle_open = 
+                       samr_connect(domain->controller, SEC_RIGHTS_MAXIMUM_ALLOWED, 
+                                    &domain->sam_handle);
+               if (!domain->sam_handle_open) return False;
+       }
+
+       /* Open sam domain handle if it isn't already open */
+       if (!domain->sam_dom_handle_open) {
+               domain->sam_dom_handle_open =
+                       samr_open_domain(&domain->sam_handle, 
+                                        SEC_RIGHTS_MAXIMUM_ALLOWED, &domain->sid, 
+                                        &domain->sam_dom_handle);
+               if (!domain->sam_dom_handle_open) return False;
+       }
+       
+       return True;
+}
+
+static void winbindd_kill_connections(void)
+{
+       struct winbindd_domain *domain;
+
+       DEBUG(1,("killing winbindd connections\n"));
+
+       server_state.pwdb_initialised = False;
+       server_state.lsa_handle_open = False;
+       lsa_close(&server_state.lsa_handle);
+       
+       for (domain=domain_list; domain; domain=domain->next) {
+               if (domain->sam_dom_handle_open) {
+                       samr_close(&domain->sam_dom_handle);
+                       domain->sam_dom_handle_open = False;
+               }
+               if (domain->sam_handle_open) {
+                       samr_close(&domain->sam_handle);
+                       domain->sam_handle_open = False;
+               }
+               DLIST_REMOVE(domain_list, domain);
+               free(domain);
+       }
+}
+
+void establish_connections(void) 
+{
+       struct winbindd_domain *domain;
+       static time_t lastt;
+       time_t t;
+
+       t = time(NULL);
+       if (t - lastt < WINBINDD_ESTABLISH_LOOP) return;
+       lastt = t;
+
+       /* maybe the connection died - if so then close up and restart */
+       if (server_state.pwdb_initialised &&
+           server_state.lsa_handle_open &&
+           !rpc_hnd_ok(&server_state.lsa_handle)) {
+               winbindd_kill_connections();
+       }
+
+       if (!server_state.pwdb_initialised) {
+               fstrcpy(server_state.controller, lp_passwordserver());
+               if (strcmp(server_state.controller,"*") == 0) {
+                       if (!resolve_dc_name(lp_workgroup(), server_state.controller)) {
+                               return;
+                       }
+               }
+
+               server_state.pwdb_initialised = pwdb_initialise(False);
+               if (!server_state.pwdb_initialised) return;
+       }
+
+       /* Open lsa handle if it isn't already open */
+       if (!server_state.lsa_handle_open) {
+               server_state.lsa_handle_open =
+                       lsa_open_policy(server_state.controller, &server_state.lsa_handle, 
+                                       False, SEC_RIGHTS_MAXIMUM_ALLOWED);
+               if (!server_state.lsa_handle_open) return;
+
+               /* now we can talk to the server we can get some info */
+               get_trusted_domains();
+       }
+
+       for (domain=domain_list; domain; domain=domain->next) {
+               if (!domain_handles_open(domain)) {
+                       open_sam_handles(domain);
+               }
+       }
+}
+
+
+/* Connect to a domain controller using get_any_dc_name() to discover 
+   the domain name and sid */
+BOOL lookup_domain_sid(char *domain_name, struct winbindd_domain *domain)
+{
+    fstring level5_dom;
+    BOOL res;
+    uint32 enum_ctx = 0;
+    uint32 num_doms = 0;
+    char **domains = NULL;
+    DOM_SID **sids = NULL;
+
+    if (domain == NULL) {
+        return False;
+    }
+
+    DEBUG(1, ("looking up sid for domain %s\n", domain_name));
+
+    /* Get controller name for domain */
+    if (!resolve_dc_name(domain_name, domain->controller)) {
+           return False;
+    }
+
+    if (strequal(domain->controller, server_state.controller)) {
+           /* Do a level 5 query info policy */
+           return lsa_query_info_pol(&server_state.lsa_handle, 0x05, 
+                                     level5_dom, &domain->sid);
+    } 
+
+    /* Use lsaenumdomains to get sid for this domain */
+    
+    res = lsa_enum_trust_dom(&server_state.lsa_handle, &enum_ctx,
+                            &num_doms, &domains, &sids);
+    
+    /* Look for domain name */
+    
+    if (res && domains && sids) {
+            int found = False;
+            int i;
+           
+            for(i = 0; i < num_doms; i++) {
+                   if (strequal(domain_name, domains[i])) {
+                           sid_copy(&domain->sid, sids[i]);
+                           found = True;
+                           break;
+                   }
+            }
+           
+            res = found;
+    }
+    
+    /* Free memory */
+    
+    free_char_array(num_doms, domains);
+    free_sid_array(num_doms, sids);
+
+    return res;
+}
+
+
+/* Lookup domain controller and sid for a domain */
+
+BOOL get_domain_info(struct winbindd_domain *domain)
+{
+    fstring sid_str;
+
+    DEBUG(1, ("Getting domain info for domain %s\n", domain->name));
+
+    /* Lookup domain sid */        
+    if (!lookup_domain_sid(domain->name, domain)) {
+           DEBUG(0, ("could not find sid for domain %s\n", domain->name));
+           return False;
+    }
+    
+    /* Lookup OK */
+
+    domain->got_domain_info = 1;
+
+    sid_to_string(sid_str, &domain->sid);
+    DEBUG(1, ("found sid %s for domain %s\n", sid_str, domain->name));
+
+    return True;
+}        
+
+/* Lookup a sid in a domain from a name */
+
+BOOL winbindd_lookup_sid_by_name(struct winbindd_domain *domain,
+                                 char *name, DOM_SID *sid,
+                                 enum SID_NAME_USE *type)
+{
+    int num_sids = 0, num_names = 1;
+    DOM_SID *sids = NULL;
+    uint32 *types = NULL;
+    BOOL res;
+
+    /* Don't bother with machine accounts */
+
+    if (name[strlen(name) - 1] == '$') {
+        return False;
+    }
+
+    /* Lookup name */
+
+    res = lsa_lookup_names(&server_state.lsa_handle, num_names, (char **)&name,
+                          &sids, &types, &num_sids);
+
+    /* Return rid and type if lookup successful */
+
+    if (res) {
+
+        /* Return sid */
+
+        if ((sid != NULL) && (sids != NULL)) {
+            sid_copy(sid, &sids[0]);
+        }
+
+        /* Return name type */
+
+        if ((type != NULL) && (types != NULL)) {
+            *type = types[0];
+        }
+    }
+    
+    /* Free memory */
+
+    if (types != NULL) free(types);
+    if (sids != NULL) free(sids);
+
+    return res;
+}
+
+/* Lookup a name in a domain from a sid */
+
+BOOL winbindd_lookup_name_by_sid(struct winbindd_domain *domain,
+                                 DOM_SID *sid, char *name,
+                                 enum SID_NAME_USE *type)
+{
+    int num_sids = 1, num_names = 0;
+    uint32 *types = NULL;
+    char **names;
+    BOOL res;
+
+    /* Lookup name */
+    res = lsa_lookup_sids(&server_state.lsa_handle, num_sids, &sid, &names, 
+                         &types, &num_names);
+
+    /* Return name and type if successful */
+
+    if (res) {
+
+        /* Return name */
+
+        if ((names != NULL) && (name != NULL)) {
+            fstrcpy(name, names[0]);
+        }
+
+        /* Return name type */
+
+        if ((type != NULL) && (types != NULL)) {
+            *type = types[0];
+        }
+    }
+
+    /* Free memory */
+
+    safe_free(types);
+    free_char_array(num_names, names);
+
+    return res;
+}
+
+/* Lookup user information from a rid */
+
+BOOL winbindd_lookup_userinfo(struct winbindd_domain *domain,
+                              uint32 user_rid, SAM_USERINFO_CTR *user_info)
+{
+       if (!domain_handles_open(domain)) return False;
+
+       return get_samr_query_userinfo(&domain->sam_dom_handle, 0x15, user_rid, user_info);
+}                                   
+
+/* Lookup group information from a rid */
+
+BOOL winbindd_lookup_groupinfo(struct winbindd_domain *domain,
+                              uint32 group_rid, GROUP_INFO_CTR *info)
+{
+       if (!domain_handles_open(domain)) return False;
+
+       return get_samr_query_groupinfo(&domain->sam_dom_handle, 1, group_rid, info);
+}
+
+/* Lookup group membership given a rid */
+
+BOOL winbindd_lookup_groupmem(struct winbindd_domain *domain,
+                              uint32 group_rid, uint32 *num_names, 
+                              uint32 **rid_mem, char ***names, 
+                              enum SID_NAME_USE **name_types)
+{
+       if (!domain_handles_open(domain)) return False;
+    
+       return sam_query_groupmem(&domain->sam_dom_handle, group_rid, num_names, 
+                                 rid_mem, names, name_types);
+}
+
+/* Lookup alias membership given a rid */
+
+int winbindd_lookup_aliasmem(struct winbindd_domain *domain,
+                             uint32 alias_rid, uint32 *num_names, 
+                             DOM_SID ***sids, char ***names, 
+                             enum SID_NAME_USE **name_types)
+{
+    /* Open sam handles */
+    if (!domain_handles_open(domain)) return False;
+
+    return sam_query_aliasmem(domain->controller, 
+                             &domain->sam_dom_handle, alias_rid, num_names, 
+                             sids, names, name_types);
+}
+
+/* Globals for domain list stuff */
+
+struct winbindd_domain *domain_list = NULL;
+
+/* Given a domain name, return the struct winbindd domain info for it */
+
+struct winbindd_domain *find_domain_from_name(char *domain_name)
+{
+       struct winbindd_domain *tmp;
+
+       /* Search through list */
+       for (tmp = domain_list; tmp != NULL; tmp = tmp->next) {
+               if (strcmp(domain_name, tmp->name) == 0) {
+                       if (!tmp->got_domain_info) return NULL;
+                        return tmp;
+                }
+        }
+
+       /* Not found */
+       return NULL;
+}
+
+/* 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;
+
+        free(temp);
+        temp = next;
+    }
+}
+
+/* Parse list of arguments to winbind uid or winbind gid parameters */
+
+static BOOL parse_id_list(char *paramstr, BOOL is_user)
+{
+    uid_t id_low, id_high = 0;
+
+    /* Parse entry */
+
+    if (sscanf(paramstr, "%u-%u", &id_low, &id_high) != 2) {
+        DEBUG(0, ("winbid %s parameter invalid\n", 
+                  is_user ? "uid" : "gid"));
+        return False;
+    }
+    
+    /* Store id info */
+    
+    if (is_user) {
+        server_state.uid_low = id_low;
+        server_state.uid_high = id_high;
+    } else {
+        server_state.gid_low = id_low;
+        server_state.gid_high = id_high;
+    }
+
+    return True;
+}
+
+/* Initialise trusted domain info */
+
+BOOL winbindd_param_init(void)
+{
+    /* Parse winbind uid and winbind_gid parameters */
+
+    if (!(parse_id_list(lp_winbind_uid(), True) &&
+          parse_id_list(lp_winbind_gid(), False))) {
+        return False;
+    }
+
+    /* Check for reversed uid and gid ranges */
+        
+    if (server_state.uid_low > server_state.uid_high) {
+        DEBUG(0, ("uid range invalid\n"));
+        return False;
+    }
+    
+    if (server_state.gid_low > server_state.gid_high) {
+        DEBUG(0, ("gid range for invalid\n"));
+        return False;
+    }
+    
+    return True;
+}
+
+/* Convert a enum winbindd_cmd to a string */
+
+char *winbindd_cmd_to_string(enum winbindd_cmd cmd)
+{
+    char *result;
+
+    switch (cmd) {
+
+    case WINBINDD_GETPWNAM_FROM_USER:
+        result = "getpwnam from user";
+        break;
+            
+    case WINBINDD_GETPWNAM_FROM_UID:
+        result = "getpwnam from uid";
+        break;
+
+    case WINBINDD_GETGRNAM_FROM_GROUP:
+        result = "getgrnam from group";
+        break;
+
+    case WINBINDD_GETGRNAM_FROM_GID:
+        result = "getgrnam from gid";
+        break;
+
+    case WINBINDD_SETPWENT:
+        result = "setpwent";
+        break;
+
+    case WINBINDD_ENDPWENT:
+        result = "endpwent";
+        break;
+
+    case WINBINDD_GETPWENT:
+        result = "getpwent";
+        break;
+
+    case WINBINDD_SETGRENT:
+        result = "setgrent";
+        break;
+
+    case WINBINDD_ENDGRENT:
+        result = "endgrent"; 
+        break;
+
+    case WINBINDD_GETGRENT:
+        result = "getgrent";
+        break;
+
+    case WINBINDD_PAM_AUTH:
+        result = "pam_auth";
+        break;
+
+    default:
+        result = "invalid command";
+        break;
+    }
+
+    return result;
+};
+
+
+/* parse a string of the form DOMAIN/user into a domain and a user */
+void parse_domain_user(char *domuser, fstring domain, fstring user)
+{
+       char *p;
+       p = strchr(domuser,'/');
+       if (!p) p = strchr(domuser,'\\');
+       if (!p) {
+               fstrcpy(domain,"");
+               fstrcpy(user, domuser);
+               return;
+       }
+       
+       fstrcpy(user, p+1);
+       fstrcpy(domain, domuser);
+       domain[PTR_DIFF(p, domuser)] = 0;
+}
+
+/* find the sequence number for a domain */
+uint32 domain_sequence_number(char *domain_name)
+{
+       struct winbindd_domain *domain;
+       SAM_UNK_CTR ctr;
+
+       domain = find_domain_from_name(domain_name);
+       if (!domain) return DOM_SEQUENCE_NONE;
+
+       if (!samr_query_dom_info(&domain->sam_dom_handle, 2, &ctr)) {
+               DEBUG(2,("domain sequence query failed\n"));
+               return DOM_SEQUENCE_NONE;
+       }
+
+       DEBUG(4,("got domain sequence number for %s of %u\n", 
+                domain_name, (unsigned)ctr.info.inf2.seq_num));
+       
+       return ctr.info.inf2.seq_num;
+}