Preliminary merge of winbind into HEAD. Note that this compiles and links
authorTim Potter <tpot@samba.org>
Mon, 7 May 2001 04:32:40 +0000 (04:32 +0000)
committerTim Potter <tpot@samba.org>
Mon, 7 May 2001 04:32:40 +0000 (04:32 +0000)
but I haven't actually run it yet so it probably doesn't work.  (-:
(This used to be commit 59f95416b66db6df05289bde224de29c721978e5)

15 files changed:
source3/Makefile.in
source3/include/smb.h
source3/lib/util_str.c
source3/nsswitch/wb_client.c
source3/nsswitch/wb_common.c
source3/nsswitch/winbind_nss_config.h
source3/nsswitch/winbindd.c
source3/nsswitch/winbindd.h
source3/nsswitch/winbindd_cache.c
source3/nsswitch/winbindd_group.c
source3/nsswitch/winbindd_idmap.c
source3/nsswitch/winbindd_pam.c
source3/nsswitch/winbindd_proto.h
source3/nsswitch/winbindd_user.c
source3/nsswitch/winbindd_util.c

index 2b31ace4c374709f56f1afd8c501d0e9517243e5..e40a647fb7f7ad1751a7b497de95abac671a8536 100644 (file)
@@ -122,7 +122,7 @@ LIBSMB_OBJ = libsmb/clientgen.o libsmb/cliconnect.o libsmb/clifile.o \
             libsmb/passchange.o libsmb/unexpected.o $(RPC_PARSE_OBJ1)
 
 LIBMSRPC_OBJ = libsmb/cli_lsarpc.o libsmb/cli_samr.o libsmb/cli_spoolss.o \
-               rpc_client/cli_pipe.o
+               rpc_client/cli_pipe.o nsswitch/winbindd_glue.o
 
 RPC_SERVER_OBJ = rpc_server/srv_lsa.o rpc_server/srv_lsa_nt.o \
                  rpc_server/srv_lsa_hnd.o rpc_server/srv_netlog.o rpc_server/srv_netlog_nt.o \
@@ -145,11 +145,11 @@ RPC_PARSE_OBJ = rpc_parse/parse_lsa.o rpc_parse/parse_net.o \
                rpc_parse/parse_creds.o
 
 
-RPC_CLIENT_OBJ = rpc_client/cli_netlogon.o rpc_client/cli_pipe.o \
-               rpc_client/cli_lsarpc.o rpc_client/cli_connect.o \
-               rpc_client/cli_use.o rpc_client/cli_login.o \
-               rpc_client/cli_spoolss_notify.o rpc_client/ncacn_np_use.o \
-               lib/util_list.o
+#RPC_CLIENT_OBJ = rpc_client/cli_netlogon.o rpc_client/cli_pipe.o \
+#              rpc_client/cli_lsarpc.o rpc_client/cli_connect.o \
+#              rpc_client/cli_use.o rpc_client/cli_login.o \
+#              rpc_client/cli_spoolss_notify.o rpc_client/ncacn_np_use.o \
+#              lib/util_list.o
 
 LOCKING_OBJ = locking/locking.o locking/brlock.o locking/posix.o
 
@@ -351,6 +351,9 @@ PAM_SMBPASS_OBJ_0 = pam_smbpass/pam_smb_auth.o pam_smbpass/pam_smb_passwd.o \
 
 PAM_SMBPASS_PICOOBJ = $(PAM_SMBPASS_OBJ_0:.o=.po)
 
+NSS_OBJ_0 = nsswitch/wins.o $(PARAM_OBJ) $(UBIQX_OBJ) $(LIBSMB_OBJ) $(LIB_OBJ) $(NSSWINS_OBJ)
+NSS_OBJ = $(NSS_OBJ_0:.o=.po)
+
 WINBINDD_OBJ1 = \
                nsswitch/winbindd.o       \
                nsswitch/winbindd_user.o  \
@@ -358,13 +361,14 @@ WINBINDD_OBJ1 = \
                nsswitch/winbindd_idmap.o \
                nsswitch/winbindd_util.o  \
                nsswitch/winbindd_cache.o \
-               nsswitch/winbindd_pam.o
+               nsswitch/winbindd_pam.o   \
+               nsswitch/winbindd_sid.o   \
+               nsswitch/winbindd_misc.o
 
 WINBINDD_OBJ = \
-               $(WINBINDD_OBJ1) \
-               $(RPC_CLIENT_OBJ) $(RPC_PARSE_OBJ) $(STUB_UID_OBJ) \
+               $(WINBINDD_OBJ1) $(NOPROTO_OBJ) $(PASSDB_OBJ) \
                $(LIBNMB_OBJ) $(PARAM_OBJ) $(UBIQX_OBJ) $(LIB_OBJ) \
-               $(NSSWINS_OBJ) $(SIDDB_OBJ) $(LIBSMB_OBJ)
+               $(LIBSMB_OBJ) $(LIBMSRPC_OBJ) $(RPC_PARSE_OBJ) $(GROUPDB_OBJ)
 
 WBINFO_OBJ = nsswitch/wbinfo.o
 
@@ -372,10 +376,6 @@ WINBIND_NSS_OBJ = nsswitch/winbind_nss.o nsswitch/wb_common.o
 
 WINBIND_NSS_PICOBJS = $(WINBIND_NSS_OBJ:.o=.po)
 
-
-NSS_OBJ_0 = nsswitch/wins.o $(PARAM_OBJ) $(UBIQX_OBJ) $(LIBSMB_OBJ) $(LIB_OBJ) $(NSSWINS_OBJ)
-NSS_OBJ = $(NSS_OBJ_0:.o=.po)
-
 ######################################################################
 # now the rules...
 ######################################################################
@@ -623,8 +623,8 @@ bin/wbinfo: $(WBINFO_OBJ) $(PARAM_OBJ) $(LIB_OBJ) $(NOPROTO_OBJ) $(UBIQX_OBJ) bi
        @$(LINK) -o $@ $(WBINFO_OBJ) $(PARAM_OBJ) $(LIB_OBJ) $(NOPROTO_OBJ) \
                $(UBIQX_OBJ) $(LIBS)
 
-nsswitch: nsswitch/libnss_wins.so nsswitch/pam_winbind.so \
-       nsswitch/libnss_winbind.so bin/wbinfo
+nsswitch: nsswitch/pam_winbind.so nsswitch/libnss_winbind.so bin/winbindd \
+       bin/wbinfo
 
 bin/pam_smbpass.@SHLIBEXT@: $(PAM_SMBPASS_PICOOBJ)
        @echo "Linking shared library $@"
index 0993d349a28121e31152b2b89c6bf6dc66bcc1ae..8b18ee50104f154bf418fac03c4340f9deb16d30 100644 (file)
@@ -1649,4 +1649,12 @@ typedef struct user_struct
 #include "nsswitch/winbindd_nss.h"
 #include "smb_acls.h"
 
+/* Used by winbindd_glue functions */
+
+typedef struct {
+       struct cli_state *cli;
+       POLICY_HND handle;
+       TALLOC_CTX *mem_ctx;
+} CLI_POLICY_HND;
+
 #endif /* _SMB_H */
index d09bd6a2b42aa1b78be53636e2c45d859e037ee6..b517d93dd8d1576fc619d27bee88a708a27df3cd 100644 (file)
@@ -1282,24 +1282,3 @@ char *string_truncate(char *s, int length)
        }
        return s;
 }
-
-/* 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;
-       char *sep = lp_winbind_separator();
-       if (!sep) sep = "\\";
-       p = strchr(domuser,*sep);
-       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;
-       strupper(domain);
-}
index 746e5406bc80ee2ddb11d73e5302a434659497f7..366edbbe4150c0eacf5741edf8752a483abf4fd7 100644 (file)
 
 #include "includes.h"
 
+/* Copy of parse_domain_user from winbindd_util.c.  Parse a string of the
+   form DOMAIN/user into a domain and a user */
+
+static void parse_domain_user(char *domuser, fstring domain, fstring user)
+{
+        char *p;
+        char *sep = lp_winbind_separator();
+        if (!sep) sep = "\\";
+        p = strchr(domuser,*sep);
+        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;
+        strupper(domain);
+}
+
 /* Call winbindd to convert a name to a sid */
 
 BOOL winbind_lookup_name(const char *name, DOM_SID *sid, enum SID_NAME_USE *name_type)
index 4040e1cff2cc2aa597e6596f3f8b230d1cf7157e..837600742433418c35b32ee80afa7c1ad7371739 100644 (file)
@@ -169,6 +169,7 @@ int write_sock(void *buffer, int count)
        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(). */
@@ -177,8 +178,8 @@ int write_sock(void *buffer, int count)
                FD_SET(established_socket, &r_fds);
                ZERO_STRUCT(tv);
                
-               if (select(established_socket + 1, &r_fds, 
-                                    NULL, NULL, &tv) == -1) {
+               if ((selret = select(established_socket + 1, &r_fds, 
+                                    NULL, NULL, &tv)) == -1) {
                        close_sock();
                        return -1;                   /* Select error */
                }
index 5c09a5dd1bcf8f1adc515fb9ffc822d83f4e8c07..39fe006557f7fe20a888e4a8d95d93ac58a08674 100644 (file)
 
 #include <config.h>
 
-#ifdef RELIANTUNIX
-/*
- * <unistd.h> has to be included before any other to get
- * large file support on Reliant UNIX. Yes, it's broken :-).
- */
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#endif /* RELIANTUNIX */
-
 #include <stdio.h>
 
 #ifdef HAVE_STDLIB_H
@@ -173,13 +163,4 @@ typedef int BOOL;
 /* zero a structure given a pointer to the structure */
 #define ZERO_STRUCTP(x) { if ((x) != NULL) memset((char *)(x), 0, sizeof(*(x))); }
     
-/* Some systems (SCO) treat UNIX domain sockets as FIFOs */
-
-#ifndef S_IFSOCK
-#define S_IFSOCK S_IFIFO
-#endif
-#ifndef S_ISSOCK
-#define S_ISSOCK(mode)  ((mode & S_IFSOCK) == S_IFSOCK)
-#endif
-
 #endif
index b0e35f3cd6ef11a68b782f07fc5a04e03a1534a4..9d315a0811df9348051100cbd13bf0a8c7b42146 100644 (file)
 
 #include "winbindd.h"
 
+pstring servicesf = CONFIGFILE;
+
 /* List of all connected clients */
 
-static struct winbindd_cli_state *client_list;
+struct winbindd_cli_state *client_list;
+static int num_clients;
 
 /* Reload configuration */
 
-static BOOL reload_services_file(void)
+static BOOL reload_services_file(BOOL test)
 {
-        pstring servicesf = CONFIGFILE;
        BOOL ret;
 
+       if (lp_loaded()) {
+               pstring fname;
+
+               pstrcpy(fname,lp_configfile());
+               if (file_exist(fname,NULL) && !strcsequal(fname,servicesf)) {
+                       pstrcpy(servicesf,fname);
+                       test = False;
+               }
+       }
+
        reopen_logs();
        ret = lp_load(servicesf,False,False,True);
 
@@ -43,61 +55,75 @@ static BOOL reload_services_file(void)
        return(ret);
 }
 
-/* Print client information */
-
-static void do_print_client_info(void)
+void winbindd_dump_status(void)
 {
-    struct winbindd_cli_state *client;
-    int i;
-
-    if (client_list == NULL) {
-        DEBUG(0, ("no clients in list\n"));
-        return;
-    }
+       struct winbindd_cli_state *tmp;
+
+       DEBUG(0, ("Global status for winbindd:\n"));
+
+       /* Print client state information */
+       
+       DEBUG(0, ("\t%d clients currently active\n", num_clients));
+       
+       if (DEBUGLEVEL >= 2) {
+               DEBUG(2, ("\tclient list:\n"));
+               for(tmp = client_list; tmp; tmp = tmp->next) {
+                       DEBUG(2, ("\t\tpid %d, sock %d, rbl %d, wbl %d\n",
+                                 tmp->pid, tmp->sock, tmp->read_buf_len, 
+                                 tmp->write_buf_len));
+               }
+       }
+}
 
-    DEBUG(0, ("client list is:\n"));
+/* Print winbindd status to log file */
 
-    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++;
-    }
+static void do_print_winbindd_status(void)
+{
+       winbindd_dump_status();
+       winbindd_idmap_dump_status();
+       winbindd_cache_dump_status();
 }
 
 /* Flush client cache */
 
 static void do_flush_caches(void)
 {
-    /* Clear cached user and group enumation info */
-
-    winbindd_flush_cache();
+       /* 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;
+       pstring path;
+       
+       /* Remove socket file */
+       
+       snprintf(path, sizeof(path), "%s/%s", 
+                WINBINDD_SOCKET_DIR, WINBINDD_SOCKET_NAME);
+       unlink(path);
+       
+       exit(0);
+}
 
-    /* Remove socket file */
+static BOOL print_winbindd_status;
 
-    slprintf(path, sizeof(path)-1, "%s/%s", 
-            WINBINDD_SOCKET_DIR, WINBINDD_SOCKET_NAME);
-    unlink(path);
-
-    exit(0);
+static void sigusr1_handler(int signum)
+{
+       BlockSignals(True, SIGUSR1);
+       print_winbindd_status = True;
+       BlockSignals(False, SIGUSR1);
 }
 
-static BOOL print_client_info;
-
-static BOOL flush_cache;
+static BOOL do_sighup;
 
 static void sighup_handler(int signum)
 {
-    BlockSignals(True, SIGHUP);
-    flush_cache = True;
-    BlockSignals(False, SIGHUP);
+       BlockSignals(True, SIGHUP);
+       do_sighup = True;
+       BlockSignals(False, SIGHUP);
 }
 
 /* Create winbindd socket */
@@ -120,14 +146,14 @@ static int create_sock(void)
 
             if (mkdir(WINBINDD_SOCKET_DIR, 0755) == -1) {
                 DEBUG(0, ("error creating socket directory %s: %s\n",
-                          WINBINDD_SOCKET_DIR, sys_errlist[errno]));
+                          WINBINDD_SOCKET_DIR, strerror(errno)));
                 return -1;
             }
 
         } else {
 
             DEBUG(0, ("lstat failed on socket directory %s: %s\n",
-                      WINBINDD_SOCKET_DIR, sys_errlist[errno]));
+                      WINBINDD_SOCKET_DIR, strerror(errno)));
             return -1;
         }
 
@@ -159,7 +185,7 @@ static int create_sock(void)
         return -1;
     }
 
-    slprintf(path, sizeof(path)-1, "%s/%s", 
+    snprintf(path, sizeof(path), "%s/%s", 
             WINBINDD_SOCKET_DIR, WINBINDD_SOCKET_NAME);
 
     unlink(path);
@@ -170,7 +196,7 @@ static int create_sock(void)
     if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) == -1) {
         DEBUG(0, ("bind failed on winbind socket %s: %s\n",
                   path,
-                  sys_errlist[errno]));
+                  strerror(errno)));
         close(sock);
         return -1;
     }
@@ -178,7 +204,7 @@ static int create_sock(void)
     if (listen(sock, 5) == -1) {
         DEBUG(0, ("listen failed on winbind socket %s: %s\n",
                   path,
-                  sys_errlist[errno]));
+                  strerror(errno)));
         close(sock);
         return -1;
     }
@@ -190,377 +216,420 @@ static int create_sock(void)
     return sock;
 }
 
-static void process_request(struct winbindd_cli_state *state)
-{
-    /* Process command */
+struct dispatch_table {
+       enum winbindd_cmd cmd;
+       enum winbindd_result (*fn)(struct winbindd_cli_state *state);
+};
 
-    state->response.result = WINBINDD_ERROR;
-    state->response.length = sizeof(struct winbindd_response);
+static struct dispatch_table dispatch_table[] = {
+       
+       /* User functions */
 
-    DEBUG(3,("processing command %s from pid %d\n", 
-            winbindd_cmd_to_string(state->request.cmd), state->pid));
+       { WINBINDD_GETPWNAM_FROM_USER, winbindd_getpwnam_from_user },
+       { WINBINDD_GETPWNAM_FROM_UID, winbindd_getpwnam_from_uid },
+       { WINBINDD_SETPWENT, winbindd_setpwent },
+       { WINBINDD_ENDPWENT, winbindd_endpwent },
+       { WINBINDD_GETPWENT, winbindd_getpwent },
+       { WINBINDD_GETGROUPS, winbindd_getgroups },
 
-    if (!server_state.lsa_handle_open) return;
+       /* Group functions */
 
-    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;
+       { WINBINDD_GETGRNAM_FROM_GROUP, winbindd_getgrnam_from_group },
+       { WINBINDD_GETGRNAM_FROM_GID, winbindd_getgrnam_from_gid },
+       { WINBINDD_SETGRENT, winbindd_setgrent },
+       { WINBINDD_ENDGRENT, winbindd_endgrent },
+       { WINBINDD_GETGRENT, winbindd_getgrent },
 
-        /* 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 */
 
-       /* pam auth functions */
-    case WINBINDD_PAM_AUTH:
-        state->response.result = winbindd_pam_auth(state);
-        break;
+       { WINBINDD_PAM_AUTH, winbindd_pam_auth },
+       { WINBINDD_PAM_CHAUTHTOK, winbindd_pam_chauthtok },
 
-        /* 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 */
+       /* Enumeration functions */
 
-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;
-    }
+        { WINBINDD_LIST_USERS, winbindd_list_users },
+        { WINBINDD_LIST_GROUPS, winbindd_list_groups },
+       { WINBINDD_LIST_TRUSTDOM, winbindd_list_trusted_domains },
 
-    DEBUG(6,("accepted socket %d\n", sock));
+       /* SID related functions */
 
-    /* Create new connection structure */
+       { WINBINDD_LOOKUPSID, winbindd_lookupsid },
+       { WINBINDD_LOOKUPNAME, winbindd_lookupname },
 
-    if ((state = (struct winbindd_cli_state *)
-         malloc(sizeof(*state))) == NULL) {
+       /* S*RS related functions */
 
-        return;
-    }
+       { WINBINDD_SID_TO_UID, winbindd_sid_to_uid },
+       { WINBINDD_SID_TO_GID, winbindd_sid_to_gid },
+       { WINBINDD_GID_TO_SID, winbindd_gid_to_sid },
+       { WINBINDD_UID_TO_SID, winbindd_uid_to_sid },
 
-    ZERO_STRUCTP(state);
-    state->sock = sock;
+       /* Miscellaneous */
 
-    /* Add to connection list */
+       { WINBINDD_CHECK_MACHACC, winbindd_check_machine_acct },
 
-    DLIST_ADD(client_list, state);
-}
+       /* End of list */
 
-/* Remove a client connection from client connection list */
+       { WINBINDD_NUM_CMDS, NULL }
+};
 
-static void remove_client(struct winbindd_cli_state *state)
+static void process_request(struct winbindd_cli_state *state)
 {
-    /* It's a dead client - hold a funeral */
+       struct dispatch_table *table = dispatch_table;
 
-    if (state != NULL) {
+       /* Free response data - we may be interrupted and receive another
+          command before being able to send this data off. */
 
-        /* Close socket */
+       safe_free(state->response.extra_data);  
 
-        close(state->sock);
+       ZERO_STRUCT(state->response);
 
-        /* Free any getent state */
+       state->response.result = WINBINDD_ERROR;
+       state->response.length = sizeof(struct winbindd_response);
 
-        free_getent_state(state->getpwent_state);
-        free_getent_state(state->getgrent_state);
+       /* Process command */
 
-        /* Free any extra data */
+       if (!server_state.lsa_handle_open) return;
 
-        safe_free(state->response.extra_data);
+       for (table = dispatch_table; table->fn; table++) {
+               if (state->request.cmd == table->cmd) {
+                       state->response.result = table->fn(state);
+                       break;
+               }
+       }
 
-        /* Remove from list and free */
+       /* In case extra data pointer is NULL */
 
-        DLIST_REMOVE(client_list, state);
-        free(state);
-    }
+       if (!state->response.extra_data) {
+               state->response.length = sizeof(struct winbindd_response);
+       }
 }
 
-/* Process a complete received packet from a client */
+/* Process a new connection by adding it to the client connection list */
 
-static void process_packet(struct winbindd_cli_state *state)
+static void new_connection(int accept_sock)
 {
-    /* Process request */
+       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);
+       num_clients++;
+}
 
-    state->pid = state->request.pid;
+/* Remove a client connection from client connection list */
 
-    process_request(state);
+static void remove_client(struct winbindd_cli_state *state)
+{
+       /* It's a dead client - hold a funeral */
+       
+       if (state != NULL) {
+               
+               /* Close socket */
+               
+               close(state->sock);
+               
+               /* Free any getent state */
+               
+               free_getent_state(state->getpwent_state);
+               free_getent_state(state->getgrent_state);
+               
+               /* We may have some extra data that was not freed if the
+                  client was killed unexpectedly */
+
+               safe_free(state->response.extra_data);
+               
+               /* Remove from list and free */
+               
+               DLIST_REMOVE(client_list, state);
+               free(state);
+               num_clients--;
+       }
+}
 
-    /* Update client state */
+/* Process a complete received packet from a client */
 
-    state->read_buf_len = 0;
-    state->write_buf_len = sizeof(state->response);
+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(struct winbindd_response);
 }
 
 /* Read some data from a client connection */
 
 static void client_read(struct winbindd_cli_state *state)
 {
-    int n;
+       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;
+       /* 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) ? strerror(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? */
+       char *data;
+       int num_written;
+       
+       /* Write some data */
+       
+       if (!state->write_extra_data) {
+
+               /* Write response structure */
+               
+               data = (char *)&state->response + sizeof(state->response) - 
+                       state->write_buf_len;
+
+       } else {
+
+               /* Write extra data */
+               
+               data = (char *)state->response.extra_data + 
+                       state->response.length - 
+                       sizeof(struct winbindd_response) - 
+                       state->write_buf_len;
+       }
+       
+       num_written = write(state->sock, data, state->write_buf_len);
+       
+       /* Write failed, kill cilent */
+       
+       if (num_written == -1 || num_written == 0) {
+               
+               DEBUG(3,("write failed on sock %d, pid %d: %s\n",
+                        state->sock, state->pid, 
+                        (num_written == -1) ? strerror(errno) : "EOF"));
+               
+               state->finished = True;
+
+               safe_free(state->response.extra_data);
+               state->response.extra_data = NULL;
+
+               return;
+       }
+       
+       /* Update client state */
+       
+       state->write_buf_len -= num_written;
+       
+       /* Have we written all data? */
+       
+       if (state->write_buf_len == 0) {
+               
+               /* Take care of extra data */
+               
+               if (state->write_extra_data) {
+
+                       safe_free(state->response.extra_data);
+                       state->response.extra_data = NULL;
+
+                       state->write_extra_data = False;
+
+               } else if (state->response.length > 
+                          sizeof(struct winbindd_response)) {
+                       
+                       /* Start writing extra data */
+
+                       state->write_buf_len = 
+                               state->response.length -
+                               sizeof(struct winbindd_response);
+                       
+                       state->write_extra_data = True;
+               }
+       }
+}
 
-    if (state->write_buf_len == 0) {
+/* 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. */
 
-        /* Take care of extra data */
+static void process_loop(int accept_sock)
+{
+       /* We'll be doing this a lot */
 
-        if (state->response.length > sizeof(struct winbindd_response)) {
+       while (1) {
+               struct winbindd_cli_state *state;
+               fd_set r_fds, w_fds;
+               int maxfd = accept_sock, selret;
+               struct timeval timeout;
 
-            if (state->write_extra_data) {
+               /* Free up temporary memory */
 
-                /* Already written extra data - free it */
+               lp_talloc_free();
 
-                safe_free(state->response.extra_data);
-                state->response.extra_data = NULL;
-                state->write_extra_data = False;
+               /* Do any connection establishment that is needed */
 
-            } else {
+               establish_connections(False);       /* Honour timeout */
 
-                /* Start writing extra data */
+               /* Initialise fd lists for select() */
 
-                state->write_buf_len = state->response.length -
-                    sizeof(struct winbindd_response);
-                state->write_extra_data = True;
-            }
-        }
-    }
-}
+               FD_ZERO(&r_fds);
+               FD_ZERO(&w_fds);
+               FD_SET(accept_sock, &r_fds);
 
-/* 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. */
+               timeout.tv_sec = WINBINDD_ESTABLISH_LOOP;
+               timeout.tv_usec = 0;
 
-static void process_loop(int accept_sock)
-{
-    /* We'll be doing this a lot */
+               /* Set up client readers and writers */
 
-    while (1) {
-        struct winbindd_cli_state *state;
-        fd_set r_fds, w_fds;
-        int maxfd = accept_sock, selret;
-       struct timeval timeout;
+               state = client_list;
 
-       /* do any connection establishment that is needed */
-       establish_connections();            
+               while (state) {
 
-        /* Initialise fd lists for select() */
+                       /* Dispose of client connection if it is marked as 
+                          finished */ 
 
-        FD_ZERO(&r_fds);
-        FD_ZERO(&w_fds);
-        FD_SET(accept_sock, &r_fds);
+                       if (state->finished) {
+                               struct winbindd_cli_state *next = state->next;
 
-       timeout.tv_sec = WINBINDD_ESTABLISH_LOOP;
-       timeout.tv_usec = 0;
+                               remove_client(state);
+                               state = next;
+                               continue;
+                       }
 
-        /* Set up client readers and writers */
+                       /* Select requires we know the highest fd used */
 
-        state = client_list;
+                       if (state->sock > maxfd) maxfd = state->sock;
 
-        while (state) {
-            /* Dispose of client connection if it is marked as finished */ 
+                       /* Add fd for reading */
 
-            if (state->finished) {
-                struct winbindd_cli_state *next = state->next;
+                       if (state->read_buf_len != sizeof(state->request)) {
+                               FD_SET(state->sock, &r_fds);
+                       }
 
-                remove_client(state);
-                state = next;
-                continue;
-            }
+                       /* Add fd for writing */
 
-            /* Select requires we know the highest fd used */
+                       if (state->write_buf_len) {
+                               FD_SET(state->sock, &w_fds);
+                       }
 
-            if (state->sock > maxfd) maxfd = state->sock;
+                       state = state->next;
+               }
 
-            /* Add fd for reading */
+               /* Check signal handling things */
 
-            if (state->read_buf_len != sizeof(state->request)) {
-                FD_SET(state->sock, &r_fds);
-            }
+               if (do_sighup) {
 
-            /* Add fd for writing */
+                       /* Flush winbindd cache */
 
-            if (state->write_buf_len) {
-                FD_SET(state->sock, &w_fds);
-            }
+                       do_flush_caches();
+                       reload_services_file(True);
 
-            state = state->next;
-        }
+                       /* Close and re-open all connections.  This will also
+                          refresh the trusted domains list */
 
-        /* Check signal handling things */
+                       winbindd_kill_all_connections();
+                       establish_connections(True); /* Force re-establish */
 
-        if (flush_cache) {
-            do_flush_caches();
-            reload_services_file();
-            flush_cache = False;
-        }
+                       do_sighup = False;
+               }
 
-        if (print_client_info) {
-            do_print_client_info();
-            print_client_info = False;
-        }
+               if (print_winbindd_status) {
+                       do_print_winbindd_status();
+                       print_winbindd_status = False;
+               }
 
-        /* Call select */
+               /* Call select */
         
-        selret = select(maxfd + 1, &r_fds, &w_fds, NULL, &timeout);
+               selret = select(maxfd + 1, &r_fds, &w_fds, NULL, &timeout);
 
-       if (selret == 0) continue;
+               if (selret == 0) continue;
 
-        if ((selret == -1 && errno != EINTR) || selret == 0) {
+               if ((selret == -1 && errno != EINTR) || selret == 0) {
 
-            /* Select error, something is badly wrong */
+                       /* Select error, something is badly wrong */
 
-            perror("select");
-            exit(1);
-        }
+                       perror("select");
+                       exit(1);
+               }
 
-        /* Create a new connection if accept_sock readable */
+               /* Create a new connection if accept_sock readable */
 
-        if (selret > 0) {
+               if (selret > 0) {
 
-            if (FD_ISSET(accept_sock, &r_fds)) {
-                new_connection(accept_sock);
-            }
+                       if (FD_ISSET(accept_sock, &r_fds)) {
+                               new_connection(accept_sock);
+                       }
             
-            /* Process activity on client connections */
+                       /* Process activity on client connections */
             
-            for (state = client_list; state ; state = state->next) {
+                       for (state = client_list; state; state = state->next) {
                 
-                /* Data available for reading */
+                               /* Data available for reading */
                 
-                if (FD_ISSET(state->sock, &r_fds)) {
+                               if (FD_ISSET(state->sock, &r_fds)) {
                     
-                    /* Read data */
+                                       /* Read data */
                     
-                    client_read(state);
+                                       client_read(state);
                     
-                    /* A request packet might be complete */
+                                       /* A request packet might be 
+                                          complete */
                     
-                    if (state->read_buf_len == sizeof(state->request)) {
-                        process_packet(state);
-                    }
-                }
+                                       if (state->read_buf_len == 
+                                           sizeof(state->request)) {
+                                               process_packet(state);
+                                       }
+                               }
                 
-                /* Data available for writing */
+                               /* Data available for writing */
                 
-                if (FD_ISSET(state->sock, &w_fds)) {
-                       client_write(state);
-                }
-            }
-        }
-    }
+                               if (FD_ISSET(state->sock, &w_fds)) {
+                                       client_write(state);
+                               }
+                       }
+               }
+       }
 }
 
 /* Main function */
@@ -569,87 +638,125 @@ 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) {
+       extern pstring global_myname;
+       extern pstring debugf;
+       int accept_sock;
+       BOOL interactive = False;
+       int opt, new_debuglevel = -1;
+
+       /* Set environment variable so we don't recursively call ourselves.
+          This may also be useful interactively. */
+       SETENV(WINBINDD_DONT_ENV, "1", 1);
+
+       /* Initialise samba/rpc client stuff */
+
+       while ((opt = getopt(argc, argv, "id:s:")) != EOF) {
+               switch (opt) {
+
+                       /* Don't become a daemon */
+
                case 'i':
                        interactive = True;
                        break;
+
+                       /* Run with specified debug level */
+
+               case 'd':
+                       new_debuglevel = atoi(optarg);
+                       break;
+
+                       /* Load a different smb.conf file */
+
+               case 's':
+                       pstrcpy(servicesf,optarg);
+                       break;
+
                default:
-                       printf("Unknown option %c (%d)\n", (char)opt, opt);
+                       printf("Unknown option %c\n", (char)opt);
                        exit(1);
                }
-    }
+       }
 
-    /* Initialise samba/rpc client stuff */
-    slprintf(debugf, sizeof(debugf)-1, "%s/log.winbindd", LOGFILEBASE);
-    setup_logging("winbindd", interactive);
-    reopen_logs();
+       snprintf(debugf, sizeof(debugf), "%s/log.winbindd", LOGFILEBASE);
+       setup_logging("winbindd", interactive);
+       reopen_logs();
 
-    if (!*global_myname) {
-        char *p;
+       if (!*global_myname) {
+               char *p;
 
-        fstrcpy(global_myname, myhostname());
-        p = strchr(global_myname, '.');
-        if (p) {
-            *p = 0;
-        }
-    }
+               fstrcpy(global_myname, myhostname());
+               p = strchr(global_myname, '.');
+               if (p) {
+                       *p = 0;
+               }
+       }
 
-    TimeInit();
-    charset_initialise();
-    codepage_initialise(lp_client_code_page());
+       TimeInit();
+       charset_initialise();
 
-    if (!lp_load(CONFIGFILE, True, False, False)) {
-        DEBUG(0, ("error opening config file\n"));
-        exit(1);
-    }
+       if (!reload_services_file(False)) {
+               DEBUG(0, ("error opening config file\n"));
+               exit(1);
+       }
 
-    if (!interactive) {
-           become_daemon();
-    }
-    load_interfaces();
+       if (new_debuglevel != -1) {
+               DEBUGLEVEL = new_debuglevel;
+       }
 
-    secrets_init();
+       codepage_initialise(lp_client_code_page());
 
-    ZERO_STRUCT(server_state);
+       if (!interactive) {
+               become_daemon();
+       }
 
-    /* Winbind daemon initialisation */
-    if (!winbindd_param_init()) {
-           return 1;
-    }
+       load_interfaces();
 
-    if (!winbindd_idmap_init()) {
-        return 1;
-    }
+       secrets_init();
 
-    winbindd_cache_init();
+       ZERO_STRUCT(server_state);
 
-    /* Setup signal handlers */
+       /* Winbind daemon initialisation */
 
-    CatchSignal(SIGINT, termination_handler);         /* Exit on these sigs */
-    CatchSignal(SIGQUIT, termination_handler);
-    CatchSignal(SIGTERM, termination_handler);
+       if (!winbindd_param_init()) {
+               return 1;
+       }
 
-    CatchSignal(SIGPIPE, SIG_IGN);                    /* Ignore sigpipe */
+       if (!winbindd_idmap_init()) {
+               return 1;
+       }
 
-    CatchSignal(SIGHUP, sighup_handler);
+       winbindd_cache_init();
 
-    /* Create UNIX domain socket */
+       /* Unblock all signals we are interested in as they may have been
+          blocked by the parent process. */
 
-    if ((accept_sock = create_sock()) == -1) {
-        DEBUG(0, ("failed to create socket\n"));
-        return 1;
-    }
+       BlockSignals(False, SIGINT);
+       BlockSignals(False, SIGQUIT);
+       BlockSignals(False, SIGTERM);
+       BlockSignals(False, SIGUSR1);
+       BlockSignals(False, SIGHUP);
+
+       /* Setup signal handlers */
+       
+       CatchSignal(SIGINT, termination_handler);      /* Exit on these sigs */
+       CatchSignal(SIGQUIT, termination_handler);
+       CatchSignal(SIGTERM, termination_handler);
+
+       CatchSignal(SIGPIPE, SIG_IGN);                 /* Ignore sigpipe */
+
+       CatchSignal(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 */
+       /* Loop waiting for requests */
 
-    process_loop(accept_sock);
+       process_loop(accept_sock);
 
-    return 0;
+       return 0;
 }
index d333b32bcfc06b9613ba7ff891a39685a1560954..c848047089c0b22461ea426efd2d9890e51d1408 100644 (file)
@@ -49,12 +49,23 @@ struct winbindd_cli_state {
     struct getent_state *getgrent_state;      /* State for getgrent() */
 };
 
+/* State between get{pw,gr}ent() calls */
+
 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;
+       struct getent_state *prev, *next;
+       void *sam_entries;
+       uint32 sam_entry_index, num_sam_entries;
+       uint32 dispinfo_ndx;
+       BOOL got_all_sam_entries, got_sam_entries;
+       struct winbindd_domain *domain;
+};
+
+/* Storage for cached getpwent() user entries */
+
+struct getpwent_user {
+       fstring name;                        /* Account name */
+       fstring gecos;                       /* User information */
+       uint32 user_rid, group_rid;          /* NT user and group rids */
 };
 
 /* Server state structure */
@@ -68,7 +79,7 @@ struct winbindd_state {
        gid_t gid_low, gid_high;               /* Range of gids to allocate */
        
        /* Cached handle to lsa pipe */
-       POLICY_HND lsa_handle;
+       CLI_POLICY_HND lsa_handle;
        BOOL lsa_handle_open;
        BOOL pwdb_initialised;
 };
@@ -79,19 +90,21 @@ extern struct winbindd_state server_state;  /* Server 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 */
+       /* Domain information */
 
-    /* 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 */
+       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 */
+       
+       CLI_POLICY_HND sam_handle, sam_dom_handle;
+       BOOL sam_handle_open, sam_dom_handle_open;
+       time_t last_check;
+       
+       struct winbindd_domain *prev, *next;   /* Linked list info */
 };
 
 extern struct winbindd_domain *domain_list;  /* List of domains we know */
@@ -99,8 +112,23 @@ extern struct winbindd_domain *domain_list;  /* List of domains we know */
 #include "winbindd_proto.h"
 
 #include "rpc_parse.h"
+#include "rpc_client.h"
 
 #define WINBINDD_ESTABLISH_LOOP 30
 #define DOM_SEQUENCE_NONE ((uint32)-1)
 
+/* SETENV */
+#if HAVE_SETENV
+#define SETENV(name, value, overwrite) setenv(name,value,overwrite)
+#elif HAVE_PUTENV
+#define SETENV(name, value, overwrite)                                  \
+{                                                                       \
+       fstring envvar;                                                  \
+       slprintf(envvar, sizeof(fstring), "%s=%s", name, value);         \
+       putenv(envvar);                                                  \
+}
+#else
+#define SETENV(name, value, overwrite) ;
+#endif
+
 #endif /* _WINBINDD_H */
index 7b263dfe009d784ee5edb9ba42ebb11717a543cf..2e404a7eb0d16fa7845c702d7a62ed9718010e51 100644 (file)
@@ -38,10 +38,10 @@ struct cache_rec {
 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))) {
+                                  TDB_NOLOCK, O_RDWR | O_CREAT | O_TRUNC, 
+                                  0600))) {
                DEBUG(0, ("Unable to open tdb cache - user and group caching "
                          "disabled\n"));
        }
@@ -55,8 +55,7 @@ static uint32 cached_sequence_number(char *domain_name)
        struct cache_rec rec;
        time_t t = time(NULL);
 
-       slprintf(keystr, sizeof(keystr)-1, "CACHESEQ/%s", domain_name);
-       dos_to_unix(keystr, True);             /* Convert key to unix-codepage */
+       snprintf(keystr, sizeof(keystr), "CACHESEQ/%s", domain_name);
        dbuf = tdb_fetch_by_string(cache_tdb, keystr);
        if (!dbuf.dptr || dbuf.dsize != sizeof(rec)) {
                goto refetch;
@@ -65,7 +64,7 @@ static uint32 cached_sequence_number(char *domain_name)
        free(dbuf.dptr);
 
        if (t < (rec.mod_time + lp_winbind_cache_time())) {
-               DEBUG(4,("cached sequence number for %s is %u\n",
+               DEBUG(3,("cached sequence number for %s is %u\n",
                         domain_name, (unsigned)rec.seq_num));
                return rec.seq_num;
        }
@@ -82,54 +81,56 @@ static uint32 cached_sequence_number(char *domain_name)
 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));
+               DEBUG(3,("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)
+static void set_cache_sequence_number(char *domain_name, char *cache_type, 
+                                     char *subkey)
 {
        fstring keystr;
-       slprintf(keystr,sizeof(keystr)-1,"CACHESEQ %s/%s/%s",
+
+       snprintf(keystr, sizeof(keystr),"CACHESEQ %s/%s/%s",
                 domain_name, cache_type, subkey?subkey:"");
-       dos_to_unix(keystr, True);             /* Convert key to unix-codepage */
+
        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)
+static uint32 get_cache_sequence_number(char *domain_name, char *cache_type, 
+                                       char *subkey)
 {
        fstring keystr;
        uint32 seq_num;
-       slprintf(keystr,sizeof(keystr)-1,"CACHESEQ %s/%s/%s",
+
+       snprintf(keystr, sizeof(keystr), "CACHESEQ %s/%s/%s",
                 domain_name, cache_type, subkey?subkey:"");
-       dos_to_unix(keystr, True);             /* Convert key to unix-codepage */
        seq_num = (uint32)tdb_fetch_int(cache_tdb, keystr);
-       DEBUG(4,("%s is %u\n", keystr, (unsigned)seq_num));
+
+       DEBUG(3,("%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)
+
+static void store_cache(char *domain_name, char *cache_type,
+                       void *sam_entries, int buflen)
 {
        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));
+       if (!sam_entries || buflen == 0) return;
 
        /* Store data as a mega-huge chunk in the tdb */
-       slprintf(keystr, sizeof(keystr)-1, "%s CACHE DATA/%s", cache_type,
+       snprintf(keystr, sizeof(keystr), "%s CACHE DATA/%s", cache_type,
                 domain_name);
-       dos_to_unix(keystr, True);             /* Convert key to unix-codepage */
-       tdb_store_by_string(cache_tdb, keystr, 
-                           sam_entries, sizeof(struct acct_info) * num_sam_entries);
+
+       tdb_store_by_string(cache_tdb, keystr, sam_entries, buflen);
 
        /* Stamp cache with current seq number */
        set_cache_sequence_number(domain_name, cache_type, NULL);
@@ -137,97 +138,124 @@ static void fill_cache(char *domain_name, char *cache_type,
 
 /* Fill the user cache with supplied data */
 
-void winbindd_fill_user_cache(char *domain_name
-                              struct acct_info *sam_entries,
-                              int num_sam_entries)
+void winbindd_store_user_cache(char *domain
+                              struct getpwent_user *sam_entries,
+                              int num_sam_entries)
 {
-       fill_cache(domain_name, CACHE_TYPE_USER, sam_entries, num_sam_entries);
+       DEBUG(3, ("storing user cache %s/%d entries\n", domain,
+                 num_sam_entries));
+
+       store_cache(domain, CACHE_TYPE_USER, sam_entries,
+                   num_sam_entries * sizeof(struct getpwent_user));
 }
 
 /* Fill the group cache with supplied data */
 
-void winbindd_fill_group_cache(char *domain_name,
-                               struct acct_info *sam_entries,
-                               int num_sam_entries)
+void winbindd_store_group_cache(char *domain,
+                               struct acct_info *sam_entries,
+                               int num_sam_entries)
 {
-       fill_cache(domain_name, CACHE_TYPE_GROUP, sam_entries, num_sam_entries);
+       DEBUG(0, ("storing group cache %s/%d entries\n", domain,
+                 num_sam_entries));              
+
+       store_cache(domain, CACHE_TYPE_GROUP, sam_entries, 
+                   num_sam_entries * sizeof(struct acct_info));
 }
 
-static void fill_cache_entry(char *domain, char *cache_type, char *name, void *buf, int len)
+static void store_cache_entry(char *domain, char *cache_type, char *name, 
+                             void *buf, int len)
 {
        fstring keystr;
 
        /* Create key for store */
-       slprintf(keystr, sizeof(keystr)-1, "%s/%s/%s", cache_type, domain, name);
-       dos_to_unix(keystr, True);             /* Convert key to unix-codepage */
-
-       DEBUG(4, ("filling cache entry %s\n", keystr));
+       snprintf(keystr, sizeof(keystr), "%s/%s/%s", cache_type, domain, name);
 
        /* 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)
+
+void winbindd_store_user_cache_entry(char *domain, char *user_name, 
+                                    struct winbindd_pw *pw)
 {
         if (lp_winbind_cache_time() == 0) return;
 
-        fill_cache_entry(domain, CACHE_TYPE_USER, user_name, pw, sizeof(struct winbindd_pw));
+        store_cache_entry(domain, CACHE_TYPE_USER, 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, 
+
+void winbindd_store_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)-1, "#%u", (unsigned)uid);
-        fill_cache_entry(domain, CACHE_TYPE_USER, uidstr, pw, sizeof(struct winbindd_pw));
+        snprintf(uidstr, sizeof(uidstr), "#%u", (unsigned)uid);
+
+       DEBUG(3, ("storing uid cache entry %s/%s\n", domain, uidstr));
+
+        store_cache_entry(domain, CACHE_TYPE_USER, 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)
+void winbindd_store_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;
 
+       DEBUG(3, ("storing group cache entry %s/%s\n", domain, group_name));
+
         /* Fill group data */
-        fill_cache_entry(domain, CACHE_TYPE_GROUP, group_name, gr, sizeof(struct winbindd_gr));
+
+        store_cache_entry(domain, CACHE_TYPE_GROUP, group_name, gr, 
+                         sizeof(struct winbindd_gr));
 
         /* Fill extra data */
-        slprintf(keystr, sizeof(keystr)-1, "%s/%s/%s DATA", CACHE_TYPE_GROUP, domain, group_name);
-        dos_to_unix(keystr, True);         /* Convert key to unix-codepage */
+
+        snprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, 
+                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)
+
+void winbindd_store_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)-1, "#%u", (unsigned)gid);
+       snprintf(gidstr, sizeof(gidstr), "#%u", (unsigned)gid);
 
         if (lp_winbind_cache_time() == 0) return;
 
+       DEBUG(3, ("storing gid cache entry %s/%s\n", domain, gidstr));
+
         /* Fill group data */
-        fill_cache_entry(domain, CACHE_TYPE_GROUP, gidstr, gr, sizeof(struct winbindd_gr));
+
+        store_cache_entry(domain, CACHE_TYPE_GROUP, gidstr, gr, 
+                         sizeof(struct winbindd_gr));
 
         /* Fill extra data */
-        slprintf(keystr, sizeof(keystr)-1, "%s/%s/%s DATA", CACHE_TYPE_GROUP, domain, gidstr);
-        dos_to_unix(keystr, True);         /* Convert key to unix-codepage */
+
+        snprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, 
+                domain, gidstr);
+
         tdb_store_by_string(cache_tdb, keystr, extra_data, extra_data_len);
 
        set_cache_sequence_number(domain, CACHE_TYPE_GROUP, gidstr);
@@ -235,7 +263,7 @@ void winbindd_fill_gid_cache_entry(char *domain, gid_t gid,
 
 /* 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)
+                        void **sam_entries, int *buflen)
 {
         TDB_DATA data;
         fstring keystr;
@@ -243,20 +271,21 @@ static BOOL fetch_cache(char *domain_name, char *cache_type,
         if (lp_winbind_cache_time() == 0) return False;
 
        /* Parameter check */
-       if (!sam_entries || !num_sam_entries) {
+       if (!sam_entries || !buflen) {
                return False;
        }
 
        /* Check cache data is current */
        if (cache_domain_expired(domain_name, 
-                                get_cache_sequence_number(domain_name, cache_type, NULL))) {
+                                get_cache_sequence_number(domain_name, 
+                                                          cache_type, 
+                                                          NULL))) {
                return False;
        }
        
         /* Create key */        
-        slprintf(keystr, sizeof(keystr)-1, "%s CACHE DATA/%s", cache_type,
+        snprintf(keystr, sizeof(keystr), "%s CACHE DATA/%s", cache_type,
                  domain_name);
-        dos_to_unix(keystr, True);         /* Convert key to unix-codepage */
        
         /* Fetch cache information */
        data = tdb_fetch_by_string(cache_tdb, keystr);
@@ -268,10 +297,7 @@ static BOOL fetch_cache(char *domain_name, char *cache_type,
           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));
+       *buflen = data.dsize;
        
        return True;
 }
@@ -279,40 +305,58 @@ static BOOL fetch_cache(char *domain_name, char *cache_type,
 /* 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,
+BOOL winbindd_fetch_user_cache(char *domain_name, 
+                              struct getpwent_user **sam_entries,
                                int *num_entries)
 {
-       return fetch_cache(domain_name, CACHE_TYPE_USER, sam_entries,
-                          num_entries);
+       BOOL result;
+       int buflen;
+
+       result = fetch_cache(domain_name, CACHE_TYPE_USER, 
+                            (void **)sam_entries, &buflen);
+
+       *num_entries = buflen / sizeof(struct getpwent_user);
+
+       DEBUG(3, ("fetched %d cache entries for %s\n", *num_entries,
+                 domain_name));
+
+       return result;
 }
 
 /* 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,
+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);
+       BOOL result;
+       int buflen;
+
+       result = fetch_cache(domain_name, CACHE_TYPE_GROUP, 
+                            (void **)sam_entries, &buflen);
+
+       *num_entries = buflen / sizeof(struct acct_info);
+
+       DEBUG(3, ("fetched %d cache entries for %s\n", *num_entries,
+                 domain_name));
+
+       return result;
 }
 
-static BOOL fetch_cache_entry(char *domain, char *cache_type, char *name, void *buf, int len)
+static BOOL fetch_cache_entry(char *domain, char *cache_type, char *name, 
+                             void *buf, int len)
 {
        TDB_DATA data;
        fstring keystr;
     
        /* Create key for lookup */
-       slprintf(keystr, sizeof(keystr)-1, "%s/%s/%s", cache_type, domain, name);
-       dos_to_unix(keystr, True);             /* Convert key to unix-codepage */
+       snprintf(keystr, sizeof(keystr), "%s/%s/%s", cache_type, 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);
@@ -327,10 +371,12 @@ BOOL winbindd_fetch_user_cache_entry(char *domain_name, char *user,
 
        if (lp_winbind_cache_time() == 0) return False;
 
-       seq_num = get_cache_sequence_number(domain_name, CACHE_TYPE_USER, user);
+       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, CACHE_TYPE_USER, user, pw, sizeof(struct winbindd_pw));
+       return fetch_cache_entry(domain_name, CACHE_TYPE_USER, user, pw, 
+                                sizeof(struct winbindd_pw));
 }
 
 /* Fetch an individual uid cache entry */
@@ -342,11 +388,13 @@ BOOL winbindd_fetch_uid_cache_entry(char *domain_name, uid_t uid,
 
         if (lp_winbind_cache_time() == 0) return False;
 
-       slprintf(uidstr, sizeof(uidstr)-1, "#%u", (unsigned)uid);
-       seq_num = get_cache_sequence_number(domain_name, CACHE_TYPE_USER, uidstr);
+       snprintf(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, CACHE_TYPE_USER, uidstr, pw, sizeof(struct winbindd_pw));
+       return fetch_cache_entry(domain_name, CACHE_TYPE_USER, uidstr, pw, 
+                                sizeof(struct winbindd_pw));
 }
 
 /* Fetch an individual group cache entry.  This function differs from the
@@ -362,15 +410,21 @@ BOOL winbindd_fetch_group_cache_entry(char *domain_name, char *group,
 
         if (lp_winbind_cache_time() == 0) return False;
 
-       seq_num = get_cache_sequence_number(domain_name, CACHE_TYPE_GROUP, group);
+       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, CACHE_TYPE_GROUP, group, gr, sizeof(struct winbindd_gr))) return False;
+        if (!fetch_cache_entry(domain_name, CACHE_TYPE_GROUP, group, gr, 
+                              sizeof(struct winbindd_gr))) {
+               return False;
+       }
        
         /* Fetch extra data */
-        slprintf(keystr, sizeof(keystr)-1, "%s/%s/%s DATA", CACHE_TYPE_GROUP, domain_name, group);
-        dos_to_unix(keystr, True);         /* Convert key to unix-codepage */
+        snprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, 
+                domain_name, group);
+
         data = tdb_fetch_by_string(cache_tdb, keystr);
 
         if (!data.dptr) return False;
@@ -395,20 +449,24 @@ BOOL winbindd_fetch_gid_cache_entry(char *domain_name, gid_t gid,
        fstring gidstr;
        uint32 seq_num;
 
-       slprintf(gidstr, sizeof(gidstr)-1, "#%u", (unsigned)gid);
+       snprintf(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);
+       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, CACHE_TYPE_GROUP, 
-                              gidstr, gr, sizeof(struct winbindd_gr))) return False;
+                              gidstr, gr, sizeof(struct winbindd_gr))) {
+               return False;
+       }
 
         /* Fetch extra data */
-        slprintf(keystr, sizeof(keystr)-1, "%s/%s/%s DATA", CACHE_TYPE_GROUP, domain_name, gidstr);
-        dos_to_unix(keystr, True);         /* Convert key to unix-codepage */
+        snprintf(keystr, sizeof(keystr), "%s/%s/%s DATA", CACHE_TYPE_GROUP, 
+                domain_name, gidstr);
         data = tdb_fetch_by_string(cache_tdb, keystr);
         if (!data.dptr) return False;
 
@@ -425,3 +483,8 @@ void winbindd_flush_cache(void)
        tdb_close(cache_tdb);
        winbindd_cache_init();
 }
+
+/* Print cache status information */
+void winbindd_cache_dump_status(void)
+{
+}
index 77825cd579839d2337949443318659abfdd1e7cc..67cf10a1f5d81da9c5e597460717a5262c23bdd8 100644 (file)
 
 /* Fill a grent structure from various other information */
 
-static void winbindd_fill_grent(struct winbindd_gr *gr, char *gr_name,
-                                gid_t unix_gid)
+static BOOL fill_grent(struct winbindd_gr *gr, char *gr_name,
+                      gid_t unix_gid)
 {
-    /* Fill in uid/gid */
+       /* Fill in uid/gid */
 
-    gr->gr_gid = unix_gid;
-
-    /* Group name and password */
+       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);
 
-    safe_strcpy(gr->gr_name, gr_name, sizeof(gr->gr_name) - 1);
-    safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
+       return True;
 }
 
-/* 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;
-};
+/* Fill in the group membership field of a NT group given by group_rid */
 
-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)
+static BOOL fill_grent_mem(struct winbindd_domain *domain,
+                          uint32 group_rid, 
+                          enum SID_NAME_USE group_name_type, 
+                          int *num_gr_mem, char **gr_mem, int *gr_mem_len)
 {
-    /* Silly cases */
-
-    if (!n1 && !n2) return 0;
-    if (!n1) return -1;
-    if (!n2) return 1;
-
-    return strcmp(n1->name, n2->name);
+       uint32 *rid_mem = NULL, num_names = 0;
+       enum SID_NAME_USE *name_types = NULL;
+       int buf_len, buf_ndx, i;
+       char **names = NULL, *buf;
+       BOOL result = False;
+       
+       if (!num_gr_mem || !gr_mem || !gr_mem_len) return False;
+       
+       /* Initialise group membership information */
+       
+       DEBUG(10, ("fill_grent_mem(): group %s rid 0x%x\n",
+                  domain ? domain->name : "NULL", group_rid));
+
+       *num_gr_mem = 0;
+       
+       if (group_name_type != SID_NAME_DOM_GRP) {
+               DEBUG(1, ("fill_grent_mem(): rid %d in domain %s isn't a "
+                         "domain group\n", group_rid, domain->name));
+               return False;
+       }
+
+       /* Lookup group members */
+
+       if (!winbindd_lookup_groupmem(domain, 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", 
+                         group_rid, domain->name));
+
+               return False;
+       }
+
+       DEBUG(10, ("fill_grent_mem(): looked up %d names\n", num_names));
+
+       if (DEBUGLEVEL >= 10) {
+               for (i = 0; i < num_names; i++) {
+                       DEBUG(10, ("\t%20s %x %d\n", names[i], rid_mem[i],
+                                  name_types[i]));
+               }
+       }
+
+       /* Add members to list */
+
+       buf = NULL;
+       buf_len = buf_ndx = 0;
+
+ again:
+
+       for (i = 0; i < num_names; i++) {
+               char *the_name;
+               fstring name;
+               int len;
+                       
+               the_name = names[i];
+
+               DEBUG(10, ("fill_grent_mem(): processing name %s\n", the_name));
+
+               /* Only add domain users */
+
+               if (name_types[i] != SID_NAME_USER) {
+                       DEBUG(3, ("fill_grent_mem(): name %s isn't a domain "
+                                 "user\n", the_name));
+                       continue;
+               }
+
+               /* Don't bother with machine accounts */
+               
+               if (the_name[strlen(the_name) - 1] == '$') {
+                       DEBUG(10, ("fill_grent_mem(): %s is machine account\n",
+                                  the_name));
+                       continue;
+               }
+
+               /* Append domain name */
+
+               snprintf(name, sizeof(name), "%s%s%s", domain->name,
+                        lp_winbind_separator(), the_name);
+
+               len = strlen(name);
+               
+               /* Add to list or calculate buffer length */
+
+               if (!buf) {
+                       buf_len += len + 1; /* List is comma separated */
+                       (*num_gr_mem)++;
+                       DEBUG(10, ("fill_grent_mem(): buf_len + %d = %d\n", len + 1,
+                                  buf_len));
+               } else {
+                       DEBUG(10, ("fill_grent_mem(): appending %s at index %d\n",
+                                  name, len));
+                       safe_strcpy(&buf[buf_ndx], name, len);
+                       buf_ndx += len;
+                       buf[buf_ndx] = ',';
+                       buf_ndx++;
+               }
+       }
+
+       /* Allocate buffer */
+
+       if (!buf) {
+               if (!(buf = malloc(buf_len))) {
+                       DEBUG(1, ("fill_grent_mem(): out of memory\n"));
+                       result = False;
+                       goto cleanup;
+               }
+               memset(buf, 0, buf_len);
+               goto again;
+       }
+
+       if (buf && buf_ndx > 0) {
+               buf[buf_ndx - 1] = '\0';
+       }
+
+       *gr_mem = buf;
+       *gr_mem_len = buf_len;
+
+       DEBUG(10, ("fill_grent_mem(): num_mem = %d, len = %d, mem = %s\n", *num_gr_mem,
+                  buf_len, buf));
+
+       result = True;
+
+ cleanup:
+       
+       /* Free memory allocated in winbindd_lookup_groupmem() */
+       
+       safe_free(name_types);
+       safe_free(rid_mem);
+       
+       free_char_array(num_names, names);
+       
+       DEBUG(10, ("fill_grent_mem(): returning %d\n", result));
+
+       return result;
 }
 
-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;
-    }
+/* Return a group structure from a group name */
 
-    memset((char *)groupmem_array, 0, num_gr_mem * 
-           sizeof(struct grent_mem_list));
+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, *gr_mem;
+       gid_t gid;
+       int extra_data_len, gr_mem_len;
+       
+       DEBUG(3, ("[%5d]: getgrnam %s\n", state->pid,
+                 state->request.data.groupname));
+
+       /* Parse domain and groupname */
+       
+       memset(name_group, 0, sizeof(fstring));
+
+       tmp = state->request.data.groupname;
+       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;
+       }
+
+       if (!domain_handles_open(domain)) {
+               return WINBINDD_ERROR;
+       }
+
+       /* Check for cached group 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;
+       }
+
+       snprintf(name, sizeof(name), "%s\\%s", name_domain, name_group);
+
+       /* Get rid and name type from name */
+        
+       if (!winbindd_lookup_sid_by_name(name, &group_sid, &name_type)) {
+               DEBUG(1, ("group %s in domain %s does not exist\n", 
+                         name_group, name_domain));
+               return WINBINDD_ERROR;
+       }
 
-    /* Copy list to array */
+       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;
+       }
 
-    for(temp = list, i = 0; temp && i < num_gr_mem; temp = temp->next, i++) {
-        fstrcpy(groupmem_array[i].name, temp->name);
-    }
+       /* Fill in group structure */
 
-    /* Sort array */
+       sid_split_rid(&group_sid, &group_rid);
 
-    qsort(groupmem_array, num_gr_mem, sizeof(struct grent_mem_list), 
-          name_comp);
+       if (!winbindd_idmap_get_gid_from_rid(domain->name, group_rid, &gid)) {
+               DEBUG(1, ("error sursing unix gid for sid\n"));
+               return WINBINDD_ERROR;
+       }
 
-    /* Fix up resulting array to a linked list and return it */
+       if (!fill_grent(&state->response.data.gr, 
+                       state->request.data.groupname, gid) ||
+           !fill_grent_mem(domain, group_rid, name_type,
+                           &state->response.data.gr.num_gr_mem,
+                           &gr_mem, &gr_mem_len)) {
+               return WINBINDD_ERROR;
+       }
 
-    for(i = 0; i < num_gr_mem; i++) {
+       /* Group membership lives at start of extra data */
 
-        /* Fix up previous link */
+       state->response.data.gr.gr_mem_ofs = 0;
 
-        if (i != 0) {
-            groupmem_array[i].prev = &groupmem_array[i - 1];
-        }
+       state->response.length += gr_mem_len;
+       state->response.extra_data = gr_mem;
 
-        /* Fix up next link */
+       /* Update cached group info */
 
-        if (i != (num_gr_mem - 1)) {
-            groupmem_array[i].next = &groupmem_array[i + 1];
-        }
-    }
+       winbindd_store_group_cache_entry(name_domain, name_group, 
+                                        &state->response.data.gr,
+                                        state->response.extra_data,
+                                        gr_mem_len);
 
-    return groupmem_array;
+       return WINBINDD_OK;
 }
 
-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 */
+/* Return a group structure from a gid number */
 
-        done_group = 0;
+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, gr_mem_len;
+       char *gr_mem;
 
-        for (temp_group = done_groups; temp_group != NULL; 
-             temp_group = temp_group->next) {
+       /* Bug out if the gid isn't in the winbind range */
 
-            if ((temp_group->rid == current_group->rid) &&
-                (strcmp(temp_group->domain_name, 
-                        current_group->domain_name) == 0)) {
-                
-                done_group = 1;
-            }
-        }
+       if ((state->request.data.gid < server_state.gid_low) ||
+           (state->request.data.gid > server_state.gid_high)) {
+               return WINBINDD_ERROR;
+       }
 
-        if (done_group) goto cleanup;
+       DEBUG(3, ("[%5d]: getgrgid %d\n", state->pid, 
+                 state->request.data.gid));
 
-        /* Lookup group membership for the current group */
+       /* Get rid from gid */
 
-        if (current_group->name_type == SID_NAME_DOM_GRP) {
+       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;
+       }
 
-            if (!winbindd_lookup_groupmem(current_group->domain, 
-                                          current_group->rid, &num_names, 
-                                          &rid_mem, &names, &name_types)) {
+       if (!domain_handles_open(domain)) {
+               return WINBINDD_ERROR;
+       }
 
-                DEBUG(1, ("fill_grent_mem(): could not lookup membership "
-                          "for group rid %d in domain %s\n", 
-                          current_group->rid,
-                          current_group->domain->name));
+       /* Try a cached entry */
 
-                /* Exit if we cannot lookup the membership for the group
-                   this function was called to look at */
+       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;
+       }
 
-                if (current_group->rid == group_rid) {
-                    return False;
-                } else {
-                    goto cleanup;
-                }
-            }
-        }
+       /* Get sid from gid */
 
-        if (current_group->name_type == SID_NAME_ALIAS) {
+       sid_copy(&group_sid, &domain->sid);
+       sid_append_rid(&group_sid, group_rid);
 
-            if (!winbindd_lookup_aliasmem(current_group->domain, 
-                                          current_group->rid, &num_names, 
-                                          &sids, &names, &name_types)) {
+       if (!winbindd_lookup_name_by_sid(&group_sid, group_name, &name_type)) {
+               DEBUG(1, ("Could not lookup sid\n"));
+               return WINBINDD_ERROR;
+       }
 
-                DEBUG(1, ("fill_grent_mem(): group rid %d not a local group\n",
-                          group_rid));
+       if (strcmp(lp_winbind_separator(),"\\")) {
+               string_sub(group_name, "\\", lp_winbind_separator(), 
+                          sizeof(fstring));
+       }
 
-                /* Exit if we cannot lookup the membership for the group
-                   this function was called to look at */
+       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;
+       }
 
-                if (current_group->rid == group_rid) {
-                    return False;
-                } else {
-                    goto cleanup;
-                }
-            }
-        }
+       /* Fill in group structure */
 
-        /* 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 */
+       if (!fill_grent(&state->response.data.gr, group_name, 
+                       state->request.data.gid) ||
+           !fill_grent_mem(domain, group_rid, name_type,
+                           &state->response.data.gr.num_gr_mem,
+                           &gr_mem, &gr_mem_len)) {
+               return WINBINDD_ERROR;
+       }
 
-            ZERO_STRUCT(name_part1);
-            ZERO_STRUCT(name_part2);
 
-            the_name = names[i];
-           parse_domain_user(the_name, name_part1, name_part2);
+       /* Group membership lives at start of extra data */
 
-            if (strcmp(name_part1, "") != 0) {
-                name_dom = name_part1;
-                name_user = name_part2;
+       state->response.data.gr.gr_mem_ofs = 0;
 
-                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;
-                }
+       state->response.length += gr_mem_len;
+       state->response.extra_data = gr_mem;
 
-            } else {
-                name_dom = current_group->domain->name;
-                name_user = name_part2;
-                name_domain = current_group->domain;
-            }
+       /* Update cached group info */
 
-            if (winbindd_lookup_sid_by_name(name_domain, name_user, NULL, 
-                                            &name_type) == WINBINDD_OK) {
+       winbindd_store_gid_cache_entry(domain->name, state->request.data.gid,
+                                      &state->response.data.gr,
+                                      state->response.extra_data,
+                                      gr_mem_len);
 
-                /* Check name type */
+       return WINBINDD_OK;
+}
 
-                if (name_type == SID_NAME_USER) {
-                    struct grent_mem_list *entry;
+/*
+ * set/get/endgrent functions
+ */
 
-                    /* Add to group membership list */
-                
-                    if ((entry = (struct grent_mem_list *)
-                         malloc(sizeof(*entry))) != NULL) {
+/* "Rewind" file pointer for group database enumeration */
 
-                        /* Create name */
-                       slprintf(entry->name, sizeof(entry->name)-1,
-                                "%s%s%s", name_dom, lp_winbind_separator(), name_user);
-                        
-                        /* Add to list */
+enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state)
+{
+       struct winbindd_domain *tmp;
+
+       DEBUG(3, ("[%5d]: setgrent\n", state->pid));
+
+       if (state == NULL) return WINBINDD_ERROR;
+       
+       /* Check user has enabled this */
+
+       if (!lp_winbind_enum_groups()) {
+               return WINBINDD_ERROR;
+       }
+
+       /* Free old static data if it exists */
+       
+       if (state->getgrent_state != NULL) {
+               free_getent_state(state->getgrent_state);
+               state->getgrent_state = NULL;
+       }
+       
+       /* Create sam pipes for each domain we know about */
+       
+       for (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) &&
+                   !check_domain_env(state->request.domain, tmp->name)) {
+                       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;
+}
 
-                        DLIST_ADD(groupmem_list, entry);
-                        gr->num_gr_mem++;
-                    }
+/* Close file pointer to ntdom group database */
 
-                } else {
-                    struct grent_mem_group *todo_group;
-                    DOM_SID todo_sid;
-                    uint32 todo_rid;
+enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state)
+{
+       DEBUG(3, ("[%5d]: endgrent\n", state->pid));
 
-                    /* Add group to todo list */
+       if (state == NULL) return WINBINDD_ERROR;
 
-                    if (winbindd_lookup_sid_by_name(name_domain, names[i], 
-                                                    &todo_sid, &name_type) 
-                        == WINBINDD_OK) {
+       free_getent_state(state->getgrent_state);
+       state->getgrent_state = NULL;
+       
+       return WINBINDD_OK;
+}
 
-                        /* Fill in group entry */
+/* Get the list of domain groups and domain aliases for a domain.  We fill in
+   the sam_entries and num_sam_entries fields with domain group information.  
+   The dispinfo_ndx field is incremented to the index of the next group to 
+   fetch. Return True if some groups were returned, False otherwise. */
 
-                        sid_split_rid(&todo_sid, &todo_rid);
+#define MAX_FETCH_SAM_ENTRIES 100
 
-                        if ((todo_group = (struct grent_mem_group *)
-                             malloc(sizeof(*todo_group))) != NULL) {
-                            
-                            ZERO_STRUCTP(todo_group);
+static BOOL get_sam_group_entries(struct getent_state *ent)
+{
+       uint32 status, num_entries, start_ndx = 0;
+       struct acct_info *name_list = NULL;
+        
+       if (ent->got_all_sam_entries) {
+               return False;
+       }
+
+#if 0
+       if (winbindd_fetch_group_cache(ent->domain->name, 
+                                      &ent->sam_entries,
+                                      &ent->num_sam_entries)) {
+               return True;
+       }
+#endif
+               
+       /* Fetch group entries */
+               
+       if (!domain_handles_open(ent->domain)) {
+               return False;
+       }
+
+       /* Free any existing group info */
+
+       if (ent->sam_entries) {
+               free(ent->sam_entries);
+               ent->sam_entries = NULL;
+               ent->num_sam_entries = 0;
+       }
+               
+       /* Enumerate domain groups */
+               
+       do {
+               struct acct_info *sam_grp_entries = NULL;
+
+               num_entries = 0;
+
+               status =
+                       samr_enum_dom_groups(&ent->domain->
+                                            sam_dom_handle,
+                                            &start_ndx,
+                                            0x8000, /* buffer size? */
+                                            (struct acct_info **)
+                                            &sam_grp_entries,
+                                            &num_entries);
+
+               /* Copy entries into return buffer */
+
+               if (num_entries) {
+
+                       name_list = Realloc(name_list,
+                                           sizeof(struct acct_info) *
+                                           (ent->num_sam_entries +
+                                            num_entries));
+
+                       memcpy(&name_list[ent->num_sam_entries],
+                              sam_grp_entries, 
+                              num_entries * sizeof(struct acct_info));
+
+                       safe_free(sam_grp_entries);
+               }
+
+               ent->num_sam_entries += num_entries;
+
+               if (status != STATUS_MORE_ENTRIES) {
+                       break;
+               }
+
+       } while (ent->num_sam_entries < MAX_FETCH_SAM_ENTRIES);
+               
+#if 0
+       /* Fill cache with received entries */
+
+       winbindd_store_group_cache(ent->domain->name, ent->sam_entries,
+                                  ent->num_sam_entries);
+#endif
+
+       /* Fill in remaining fields */
+
+       ent->sam_entries = name_list;
+       ent->sam_entry_index = 0;
+       ent->got_all_sam_entries = (status != STATUS_MORE_ENTRIES);
+
+       return ent->num_sam_entries > 0;
+}
 
-                            todo_group->rid = todo_rid;
-                            todo_group->name_type = name_type;
-                            todo_group->domain = name_domain;
+/* Fetch next group entry from ntdom database */
 
-                            fstrcpy(todo_group->domain_name, name_dom);
+#define MAX_GETGRENT_GROUPS 500
 
-                            DLIST_ADD(todo_groups, todo_group);
-                        }
-                    }
-                }
-            }
-        }
+enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state)
+{
+       struct getent_state *ent;
+       struct winbindd_gr *group_list = NULL;
+       int num_groups, group_list_ndx = 0, i, gr_mem_list_len = 0;
+       char *sep, *new_extra_data, *gr_mem_list = NULL;
 
-    cleanup:
-        
-        /* Remove group from todo list and add to done_groups list */
+       DEBUG(3, ("[%5d]: getgrent\n", state->pid));
 
-        DLIST_REMOVE(todo_groups, current_group);
-        DLIST_ADD(done_groups, current_group);
+       if (state == NULL) return WINBINDD_ERROR;
 
-        /* Free memory allocated in winbindd_lookup_{alias,group}mem() */
+       /* Check user has enabled this */
 
-        safe_free(name_types);
-        safe_free(rid_mem);
+       if (!lp_winbind_enum_groups()) {
+               return WINBINDD_ERROR;
+       }
 
-        free_char_array(num_names, names);
-        free_sid_array(num_names, sids);
-    }
-    
-    /* Free done groups list */
+       num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
 
-    temp_group = done_groups;
+       if ((state->response.extra_data = 
+            malloc(num_groups * sizeof(struct winbindd_gr))) == NULL) {
+               return WINBINDD_ERROR;
+       }
 
-    if (temp_group != NULL) {
-        while (temp_group != NULL) {
-            struct grent_mem_group *next;
+       state->response.data.num_entries = 0;
 
-            DLIST_REMOVE(done_groups, temp_group);
-            next = temp_group->next;
+       group_list = (struct winbindd_gr *)state->response.extra_data;
+       sep = lp_winbind_separator();
 
-            free(temp_group);
-            temp_group = next;
-        }
-    }
+       if (!(ent = state->getgrent_state)) {
+               return WINBINDD_ERROR;
+       }
 
-    /* Remove duplicates from group member list. */
+       /* Start sending back groups */
 
-    if (gr->num_gr_mem > 0) {
-        struct grent_mem_list *sorted_groupmem_list, *temp;
-        int extra_data_len = 0;
-        fstring prev_name;
-        char *head;
+       for (i = 0; i < num_groups; i++) {
+               struct acct_info *name_list = NULL;
+               fstring domain_group_name;
+               uint32 result;
+               gid_t group_gid;
+               
+               /* Do we need to fetch another chunk of groups? */
 
-        /* Sort list */
+       tryagain:
 
-        sorted_groupmem_list = sort_groupmem_list(groupmem_list, 
-                                                  gr->num_gr_mem);
-        /* Remove duplicates by iteration */
+               DEBUG(10, ("getgrent(): entry_index = %d, num_entries = %d\n",
+                          ent->sam_entry_index, ent->num_sam_entries));
 
-        fstrcpy(prev_name, "");
+               if (ent->num_sam_entries == ent->sam_entry_index) {
 
-        for(temp = sorted_groupmem_list; temp; temp = temp->next) {
-            if (strequal(temp->name, prev_name)) {
+                       while(ent && !get_sam_group_entries(ent)) {
+                               struct getent_state *next_ent;
 
-                /* 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. */
+                               DEBUG(10, ("getgrent(): freeing state info for "
+                                          "domain %s\n", ent->domain->name));
 
-                DLIST_REMOVE(sorted_groupmem_list, temp);
-                gr->num_gr_mem--;
+                               /* Free state information for this domain */
 
-            } else {
+                               safe_free(ent->sam_entries);
+                               ent->sam_entries = NULL;
 
-                /* Got a unique name - count how long it is */
+                               next_ent = ent->next;
+                               DLIST_REMOVE(state->getgrent_state, ent);
+                               
+                               free(ent);
+                               ent = next_ent;
+                       }
 
-                extra_data_len += strlen(temp->name) + 1;
-            }
-        }
+                       /* No more domains */
 
-        extra_data_len++;       /* Don't forget null a terminator */
+                       if (!ent) break;
+               }
+               
+               name_list = ent->sam_entries;
+               
+               /* Lookup group info */
+               
+               if (!winbindd_idmap_get_gid_from_rid(
+                       ent->domain->name,
+                       name_list[ent->sam_entry_index].rid,
+                       &group_gid)) {
+                       
+                       DEBUG(1, ("getgrent(): could not look up gid for group %s\n",
+                                 name_list[ent->sam_entry_index].acct_name));
 
-        /* Convert sorted list into extra data field to send back to ntdom
-           client.  Add one to extra_data_len for null termination */
+                       ent->sam_entry_index++;
+                       goto tryagain;
+               }
 
-        if ((response->extra_data = malloc(extra_data_len))) {
+               DEBUG(10, ("getgrent(): got gid %d for group %x\n", group_gid,
+                          name_list[ent->sam_entry_index].rid));
+               
+               /* Fill in group entry */
 
-            /* Initialise extra data */
+               slprintf(domain_group_name, sizeof(domain_group_name) - 1,
+                        "%s%s%s", ent->domain->name, lp_winbind_separator(), 
+                        name_list[ent->sam_entry_index].acct_name);
+   
+               result = fill_grent(&group_list[group_list_ndx], 
+                                   domain_group_name, group_gid);
 
-            memset(response->extra_data, 0, extra_data_len);
+               /* Fill in group membership entry */
 
-            head = response->extra_data;
+               if (result) {
+                       int gr_mem_len;
+                       char *gr_mem, *new_gr_mem_list;
 
-            /* Fill in extra data */
+                       /* Get group membership */
 
-            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';
+                       result = fill_grent_mem(
+                               ent->domain,
+                               name_list[ent->sam_entry_index].rid,
+                               SID_NAME_DOM_GRP,
+                               &group_list[group_list_ndx].num_gr_mem, 
+                               &gr_mem, &gr_mem_len);
 
-            /* Update response length */
+                       /* Append to group membership list */
 
-            response->length = sizeof(struct winbindd_response) +
-                extra_data_len;
-        }
+                       new_gr_mem_list = Realloc(
+                               gr_mem_list,
+                               gr_mem_list_len + gr_mem_len);
 
-        /* Free memory for sorted_groupmem_list.  It was allocated as an
-           array in sort_groupmem_list() so can be freed in one go. */
+                       if (!new_gr_mem_list) {
+                               DEBUG(0, ("getgrent(): out of memory\n"));
+                               free(gr_mem_list);
+                               gr_mem_list_len = 0;
+                               break;
+                       }
 
-        free(sorted_groupmem_list);
+                       DEBUG(10, ("getgrent(): list_len = %d, mem_len = %d\n",
+                                  gr_mem_list_len, gr_mem_len));
 
-        /* Free groupmem_list */
+                       gr_mem_list = new_gr_mem_list;
 
-        temp = groupmem_list;
+                       memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
+                              gr_mem_len);
 
-        while (temp != NULL) {
-            struct grent_mem_list *next;
-            
-            DLIST_REMOVE(groupmem_list, temp);
-            next = temp->next;
-            
-            free(temp);
-            temp = next;
-        }
-    }
+                       safe_free(gr_mem);
 
-    return True;
-}
+                       group_list[group_list_ndx].gr_mem_ofs = 
+                               gr_mem_list_len;
 
-/* Return a group structure from a group name */
+                       gr_mem_list_len += gr_mem_len;
+               }
 
-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;
+               ent->sam_entry_index++;
+               
+               /* Add group to return list */
+               
+               if (result) {
 
-    /* Parse domain and groupname */
+                       DEBUG(10, ("getgrent(): adding group num_entries = %d\n",
+                                  state->response.data.num_entries));
 
-    memset(name_group, 0, sizeof(fstring));
+                       group_list_ndx++;
+                       state->response.data.num_entries++;
+                       
+                       state->response.length +=
+                               sizeof(struct winbindd_gr);
+                       
+               } else {
+                       DEBUG(0, ("could not lookup domain group %s\n", 
+                                 domain_group_name));
+               }
+       }
 
-    tmp = state->request.data.groupname;
-    parse_domain_user(tmp, name_domain, name_group);
+       /* Copy the list of group memberships to the end of the extra data */
 
-    /* Reject names that don't have a domain - i.e name_domain contains the
-       entire name. */
+       if (group_list_ndx == 0) {
+               goto done;
+       }
 
-    if (strequal(name_group, "")) {
-        return WINBINDD_ERROR;
-    }    
+       new_extra_data = Realloc(
+               state->response.extra_data,
+               group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
 
-    /* Get info for the domain */
+       if (!new_extra_data) {
+               DEBUG(0, ("out of memory\n"));
+               group_list_ndx = 0;
+               safe_free(state->response.extra_data);
+               state->response.extra_data = NULL;
+               safe_free(gr_mem_list);
 
-    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;
-    }
+               return WINBINDD_ERROR;
+       }
 
-    /* Check for cached user entry */
+       state->response.extra_data = new_extra_data;
 
-    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;
-    }
+       memcpy(&((char *)state->response.extra_data)
+              [group_list_ndx * sizeof(struct winbindd_gr)], 
+              gr_mem_list, gr_mem_list_len);
 
-    slprintf(name, sizeof(name)-1, "%s\\%s", name_domain, name_group);
+               safe_free(gr_mem_list);
 
-    /* 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;
-    }
+       state->response.length += gr_mem_list_len;
 
-    /* Update cached group info */
+       DEBUG(10, ("getgrent(): returning %d groups, length = %d\n",
+                  group_list_ndx, gr_mem_list_len));
 
-    winbindd_fill_group_cache_entry(name_domain, name_group, 
-                                    &state->response.data.gr,
-                                    state->response.extra_data,
-                                    state->response.length - 
-                                    sizeof(struct winbindd_response));
+       /* Out of domains */
 
-    return WINBINDD_OK;
+ done:
+       return (group_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
 }
 
-/* Return a group structure from a gid number */
+/* List domain groups without mapping to unix ids */
 
-enum winbindd_result winbindd_getgrnam_from_gid(struct winbindd_cli_state 
-                                                *state)
+enum winbindd_result winbindd_list_groups(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;
-    }
-
-    if (strcmp(lp_winbind_separator(),"\\")) {
-           string_sub(group_name, "\\", lp_winbind_separator(), 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;
-}
+        uint32 total_entries = 0;
+        struct winbindd_domain *domain;
+       struct getent_state groups;
+       char *extra_data = NULL;
+       int extra_data_len = 0, i;
 
-/*
- * set/get/endgrent functions
- */
+       DEBUG(3, ("[%5d]: list groups\n", state->pid));
 
-/* "Rewind" file pointer for group database enumeration */
+        /* Enumerate over trusted domains */
 
-enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state)
-{
-    struct winbindd_domain *tmp;
+        for (domain = domain_list; domain; domain = domain->next) {
 
-    if (state == NULL) return WINBINDD_ERROR;
+               /* Skip domains other than WINBINDD_DOMAIN environment
+                  variable */ 
 
-    /* Free old static data if it exists */
+               if ((strcmp(state->request.domain, "") != 0) &&
+                   !check_domain_env(state->request.domain, domain->name)) {
+                       continue;
+               }
 
-    if (state->getgrent_state != NULL) {
-        free_getent_state(state->getgrent_state);
-        state->getgrent_state = NULL;
-    }
+               /* Get list of sam groups */
 
-    /* Create sam pipes for each domain we know about */
+               ZERO_STRUCT(groups);
+               groups.domain = domain;
 
-    for (tmp = domain_list; tmp != NULL; tmp = tmp->next) {
-        struct getent_state *domain_state;
+               get_sam_group_entries(&groups);
 
-        /* Skip domains other than WINBINDD_DOMAIN environment variable */
+               if (groups.num_sam_entries == 0) continue;
 
-        if ((strcmp(state->request.domain, "") != 0) &&
-            (strcmp(state->request.domain, tmp->name) != 0)) {
-                continue;
-        }
+               /* Allocate some memory for extra data.  Note that we limit
+                  account names to sizeof(fstring) = 128 characters.  */
 
-        /* Create a state record for this domain */
+               total_entries += groups.num_sam_entries;
+               extra_data = Realloc(extra_data, 
+                                    sizeof(fstring) * total_entries);
 
-        if ((domain_state = (struct getent_state *)
-             malloc(sizeof(struct getent_state))) == NULL) {
+               if (!extra_data) {
+                       return WINBINDD_ERROR;
+               }
 
-            return WINBINDD_ERROR;
-        }
+               /* Pack group list into extra data fields */
 
-        ZERO_STRUCTP(domain_state);
+               for (i = 0; i < groups.num_sam_entries; i++) {
+                       char *group_name = ((struct acct_info *)
+                                           groups.sam_entries)[i].acct_name; 
+                       fstring name;
 
-        /* Add to list of open domains */
+                       /* Convert unistring to ascii */
 
-        domain_state->domain = tmp;
-        DLIST_ADD(state->getgrent_state, domain_state);
-    }
+                       snprintf(name, sizeof(name), "%s%s%s",
+                                domain->name, lp_winbind_separator(),
+                                group_name);
 
-    return WINBINDD_OK;
-}
+                       /* Append to extra data */
+                       
+                       memcpy(&extra_data[extra_data_len], name, 
+                              strlen(name));
+                       extra_data_len += strlen(name);
 
-/* Close file pointer to ntdom group database */
+                       extra_data[extra_data_len++] = ',';
+               }
+       }
 
-enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state)
-{
-    if (state == NULL) return WINBINDD_ERROR;
+       /* Assign extra_data fields in response structure */
+
+       if (extra_data) {
+               extra_data[extra_data_len - 1] = '\0';
+               state->response.extra_data = extra_data;
+               state->response.length += extra_data_len;
+       }
 
-    free_getent_state(state->getgrent_state);
-    state->getgrent_state = NULL;
+       /* No domains may have responded but that's still OK so don't
+          return an error. */
 
-    return WINBINDD_OK;
+        return WINBINDD_OK;
 }
 
-/* Fetch next group entry from netdom database */
+/* Get user supplementary groups.  This is much quicker than trying to
+   invert the groups database. */
 
-enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state)
+enum winbindd_result winbindd_getgroups(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)-1,
-                    "%s%s%s", ent->domain->name, lp_winbind_separator(), 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;
+       fstring name_domain, name_user, name;
+       DOM_SID user_sid;
+       enum SID_NAME_USE name_type;
+       uint32 user_rid, num_groups, num_gids;
+       DOM_GID *user_groups = NULL;
+       struct winbindd_domain *domain;
+       enum winbindd_result result;
+       gid_t *gid_list;
+       int i;
+       
+       DEBUG(3, ("[%5d]: getgroups %s\n", state->pid,
+                 state->request.data.username));
+
+       if (state == NULL) return WINBINDD_ERROR;
+
+       /* 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;
+       }
+
+       if (!domain_handles_open(domain)) {
+               return WINBINDD_ERROR;
+       }
+
+       slprintf(name, sizeof(name) - 1, "%s\\%s", name_domain, name_user);
+       
+       /* Get rid and name type from name.  The following costs 1 packet */
+
+       if (!winbindd_lookup_sid_by_name(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;
+       }
+
+       sid_split_rid(&user_sid, &user_rid);
+
+       if (!winbindd_lookup_usergroups(domain, user_rid, &num_groups,
+                                       &user_groups)) {
+               return WINBINDD_ERROR;
+       }
+
+       /* Copy data back to client */
+
+       num_gids = 0;
+       gid_list = malloc(sizeof(gid_t) * num_groups);
+
+       if (state->response.extra_data) {
+               result = WINBINDD_ERROR;
+               goto done;
+       }
+
+       for (i = 0; i < num_groups; i++) {
+               if (!winbindd_idmap_get_gid_from_rid(
+                       domain->name, user_groups[i].g_rid, 
+                       &gid_list[num_gids])) {
+
+                       DEBUG(1, ("unable to convert group rid %d to gid\n", 
+                                 user_groups[i].g_rid));
+                       continue;
+               }
+                       
+               num_gids++;
+       }
+
+       state->response.data.num_entries = num_gids;
+       state->response.extra_data = gid_list;
+       state->response.length += num_gids * sizeof(gid_t);
+
+       result = WINBINDD_OK;
+
+ done:
+       safe_free(user_groups);
+
+       return result;
 }
index 11f7b8aae7a76e3f9431433bcb54258e77d00be6..2bb942396b45db18108e45d00a7b2a983e083b6f 100644 (file)
@@ -73,12 +73,11 @@ static BOOL get_id_from_rid(char *domain_name, uint32 rid, int *id,
 {
     TDB_DATA data, key;
     fstring keystr;
-    BOOL result;
+    BOOL result = False;
 
     /* Check if rid is present in database */
 
-    slprintf(keystr, sizeof(keystr)-1, "%s/%d", domain_name, rid);
-       dos_to_unix(keystr, True);             /* Convert key to unix-codepage */
+    slprintf(keystr, sizeof(keystr), "%s/%d", domain_name, rid);
     
     key.dptr = keystr;
     key.dsize = strlen(keystr) + 1;
@@ -116,7 +115,7 @@ static BOOL get_id_from_rid(char *domain_name, uint32 rid, int *id,
 
             /* Store new id */
             
-            slprintf(keystr2, sizeof(keystr2)-1, "%s %d", isgroup ? "GID" :
+            slprintf(keystr2, sizeof(keystr2), "%s %d", isgroup ? "GID" :
                      "UID", *id);
 
             data.dptr = keystr2;
@@ -155,7 +154,7 @@ BOOL get_rid_from_id(int id, uint32 *rid, struct winbindd_domain **domain,
     fstring keystr;
     BOOL result = False;
 
-    slprintf(keystr, sizeof(keystr)-1, "%s %d", isgroup ? "GID" : "UID", id);
+    slprintf(keystr, sizeof(keystr), "%s %d", isgroup ? "GID" : "UID", id);
 
     key.dptr = keystr;
     key.dsize = strlen(keystr) + 1;
@@ -177,11 +176,17 @@ BOOL get_rid_from_id(int id, uint32 *rid, struct winbindd_domain **domain,
 
             if (domain) {
                 *domain = find_domain_from_name(domain_name);
+               if (*domain == NULL) {
+                       DEBUG(1, ("unknown domain %s for rid %d\n",
+                                 domain_name, the_rid));
+                       result = False;
+                       goto done;
+               }
             }
 
             result = True;
         }
-            
+    done:            
         free(data.dptr);
     }
 
@@ -211,8 +216,7 @@ 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))) {
+                               TDB_NOLOCK, O_RDWR | O_CREAT, 0600))) {
         DEBUG(0, ("Unable to open idmap database\n"));
         return False;
     }
@@ -235,3 +239,63 @@ BOOL winbindd_idmap_init(void)
 
     return True;   
 }
+
+/* Dump status information to log file.  Display different stuff based on
+   the debug level:
+
+   Debug Level        Information Displayed
+   =================================================================
+   0                  Percentage of [ug]id range allocated
+   0                  High water marks (next allocated ids)
+*/
+
+#define DUMP_INFO 0
+
+void winbindd_idmap_dump_status(void)
+{
+    int user_hwm, group_hwm;
+
+    DEBUG(0, ("Status for winbindd idmap:\n"));
+
+    /* Get current high water marks */
+
+    if ((user_hwm = tdb_fetch_int(idmap_tdb, HWM_USER)) == -1) {
+        DEBUG(DUMP_INFO, ("\tCould not get userid high water mark!\n"));
+    }
+
+    if ((group_hwm = tdb_fetch_int(idmap_tdb, HWM_GROUP)) == -1) {
+        DEBUG(DUMP_INFO, ("\tCould not get groupid high water mark!\n"));
+    }
+
+    /* Display next ids to allocate */
+
+    if (user_hwm != -1) {
+        DEBUG(DUMP_INFO, ("\tNext userid to allocate is %d\n", user_hwm));
+    }
+
+    if (group_hwm != -1) {
+        DEBUG(DUMP_INFO, ("\tNext groupid to allocate is %d\n", group_hwm));
+    }
+
+    /* Display percentage of id range already allocated. */
+
+    if (user_hwm != -1) {
+        int num_users = user_hwm - server_state.uid_low;
+        int total_users = server_state.uid_high - server_state.uid_low;
+
+        DEBUG(DUMP_INFO, ("\tUser id range is %d%% full (%d of %d)\n", 
+                          num_users * 100 / total_users, num_users,
+                          total_users));
+    }
+
+    if (group_hwm != -1) {
+        int num_groups = group_hwm - server_state.gid_low;
+        int total_groups = server_state.gid_high - server_state.gid_low;
+
+        DEBUG(DUMP_INFO, ("\tGroup id range is %d%% full (%d of %d)\n",
+                          num_groups * 100 / total_groups, num_groups,
+                          total_groups));
+    }
+
+    /* Display complete mapping of users and groups to rids */
+}
index c74afd8e291c319e595e70103c25216eecdf82fa..57b23947995e884dad016109c071cf799011702d 100644 (file)
 
 #include "winbindd.h"
 
+/* Copy of parse_domain_user from winbindd_util.c.  Parse a string of the
+   form DOMAIN/user into a domain and a user */
+
+static void parse_domain_user(char *domuser, fstring domain, fstring user)
+{
+        char *p;
+        char *sep = lp_winbind_separator();
+        if (!sep) sep = "\\";
+        p = strchr(domuser,*sep);
+        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;
+        strupper(domain);
+}
+
 /* Return a password structure from a username.  Specify whether cached data 
    can be returned. */
 
@@ -31,31 +53,37 @@ enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state)
        NET_USER_INFO_3 info3;
        uchar ntpw[16];
        uchar lmpw[16];
-       uint8 trust_passwd[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));
+       DEBUG(3, ("[%5d]: pam auth %s\n", state->pid,
+                 state->request.data.auth.user));
 
        /* Parse domain and username */
-       parse_domain_user(state->request.data.auth.user, name_domain, name_user);
+       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 (!secrets_fetch_trust_account_password(lp_workgroup(), 
-                                                 trust_passwd, NULL)) {
-               return WINBINDD_ERROR;
-       }
+       if (!_get_trust_account_password(lp_workgroup(), trust_passwd, NULL)) {
+            DEBUG(1, ("could not get trust password for domain %s\n",
+                      name_domain));
+            return WINBINDD_ERROR;
+        }
 
        nt_lm_owf_gen(state->request.data.auth.pass, ntpw, lmpw);
 
-       slprintf(server, sizeof(server)-1, "\\\\%s", server_state.controller);
+       slprintf(server, sizeof(server), "\\\\%s", server_state.controller);
+
+#if 0
+
+       /* XXX */
 
        status = domain_client_validate_backend(server, 
                                                name_user, name_domain,
@@ -64,9 +92,51 @@ enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state)
                                                NULL,
                                                lmpw, sizeof(lmpw),
                                                ntpw, sizeof(ntpw), &info3);
+#else
+       status = NT_STATUS_UNSUCCESSFUL;
+#endif
+       
 
        if (status != NT_STATUS_NOPROBLEMO) return WINBINDD_ERROR;
 
        return WINBINDD_OK;
 }
 
+/* Change a user password */
+
+enum winbindd_result winbindd_pam_chauthtok(struct winbindd_cli_state *state)
+{
+    char *oldpass, *newpass;
+    fstring domain, user;
+    uchar nt_oldhash[16];
+    uchar lm_oldhash[16];
+
+    DEBUG(3, ("[%5d]: pam chauthtok %s\n", state->pid,
+             state->request.data.chauthtok.user));
+
+    /* Setup crap */
+
+    if (state == NULL) return WINBINDD_ERROR;
+
+    parse_domain_user(state->request.data.chauthtok.user, domain, user);
+
+    oldpass = state->request.data.chauthtok.oldpass;
+    newpass = state->request.data.chauthtok.newpass;
+
+    nt_lm_owf_gen(oldpass, nt_oldhash, lm_oldhash);
+
+    /* Change password */
+
+#if 0
+
+    /* XXX */
+
+    if (!msrpc_sam_ntchange_pwd(server_state.controller, domain, user,
+                               lm_oldhash, nt_oldhash, newpass)) {
+        DEBUG(0, ("password change failed for user %s/%s\n", domain, user));
+        return WINBINDD_ERROR;
+    }
+#endif
+    
+    return WINBINDD_OK;
+}
index 774478fea5dee3ee33bd68d715b5149475347c85..6e03ec842f3dd5ba6db634395405ad9f2dbcad5d 100644 (file)
@@ -5,32 +5,33 @@
 
 /*The following definitions come from  nsswitch/winbindd.c  */
 
+void winbindd_dump_status(void);
 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, 
+void winbindd_store_user_cache(char *domain, 
+                              struct getpwent_user *sam_entries,
+                              int num_sam_entries);
+void winbindd_store_group_cache(char *domain,
+                               struct acct_info *sam_entries,
+                               int num_sam_entries);
+void winbindd_store_user_cache_entry(char *domain, char *user_name, 
+                                    struct winbindd_pw *pw);
+void winbindd_store_uid_cache_entry(char *domain, uid_t uid, 
                                     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,
+void winbindd_store_group_cache_entry(char *domain, char *group_name, 
+                                     struct winbindd_gr *gr, void *extra_data,
+                                     int extra_data_len);
+void winbindd_store_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 getpwent_user **sam_entries,
                                int *num_entries);
-BOOL winbindd_fetch_group_cache(char *domain_name,
-                                struct acct_info **sam_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);
@@ -43,15 +44,19 @@ 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);
+void winbindd_cache_dump_status(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_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);
+enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state);
+enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state);
 
 /*The following definitions come from  nsswitch/winbindd_idmap.c  */
 
@@ -66,34 +71,60 @@ BOOL winbindd_idmap_get_rid_from_uid(uid_t uid, uint32 *user_rid,
 BOOL winbindd_idmap_get_rid_from_gid(gid_t gid, uint32 *group_rid, 
                                      struct winbindd_domain **domain);
 BOOL winbindd_idmap_init(void);
+void winbindd_idmap_dump_status(void);
+
+/*The following definitions come from  nsswitch/winbindd_misc.c  */
+
+BOOL _get_trust_account_password(char *domain, unsigned char *ret_pwd, 
+                                       time_t *pass_last_set_time);
+enum winbindd_result winbindd_check_machine_acct(
+       struct winbindd_cli_state *state);
+enum winbindd_result winbindd_list_trusted_domains(struct winbindd_cli_state
+                                                  *state);
 
 /*The following definitions come from  nsswitch/winbindd_pam.c  */
 
 enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) ;
+enum winbindd_result winbindd_pam_chauthtok(struct winbindd_cli_state *state);
+
+/*The following definitions come from  nsswitch/winbindd_sid.c  */
+
+enum winbindd_result winbindd_lookupsid(struct winbindd_cli_state *state);
+enum winbindd_result winbindd_lookupname(struct winbindd_cli_state *state);
+enum winbindd_result winbindd_sid_to_uid(struct winbindd_cli_state *state);
+enum winbindd_result winbindd_sid_to_gid(struct winbindd_cli_state *state);
+enum winbindd_result winbindd_uid_to_sid(struct winbindd_cli_state *state);
+enum winbindd_result winbindd_gid_to_sid(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_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);
+enum winbindd_result winbindd_list_users(struct winbindd_cli_state *state);
 
 /*The following definitions come from  nsswitch/winbindd_util.c  */
 
+void debug_conn_state(void);
 BOOL domain_handles_open(struct winbindd_domain *domain);
-void establish_connections(void) ;
+void winbindd_kill_connections(struct winbindd_domain *domain);
+void winbindd_kill_all_connections(void);
+void establish_connections(BOOL force_reestablish) ;
 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,
+BOOL winbindd_lookup_sid_by_name(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,
+BOOL winbindd_lookup_name_by_sid(DOM_SID *sid, fstring 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_usergroups(struct winbindd_domain *domain,
+                               uint32 user_rid, uint32 *num_groups,
+                               DOM_GID **user_groups);
 BOOL winbindd_lookup_groupinfo(struct winbindd_domain *domain,
                               uint32 group_rid, GROUP_INFO_CTR *info);
 BOOL winbindd_lookup_groupmem(struct winbindd_domain *domain,
@@ -105,9 +136,14 @@ int winbindd_lookup_aliasmem(struct winbindd_domain *domain,
                              DOM_SID ***sids, char ***names, 
                              enum SID_NAME_USE **name_types);
 struct winbindd_domain *find_domain_from_name(char *domain_name);
+struct winbindd_domain *find_domain_from_sid(DOM_SID *sid);
 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);
+uint32 winbindd_query_dispinfo(struct winbindd_domain *domain,
+                            uint32 *start_ndx, uint16 info_level, 
+                            uint32 *num_entries, SAM_DISPINFO_CTR *ctr);
+BOOL check_domain_env(char *domain_env, char *domain);
+void parse_domain_user(char *domuser, fstring domain, fstring user);
 #endif /* _WINBINDD_PROTO_H_ */
index f3e62d2f7f101298162edc397e4749e2d466c242..66e220f927ca110b471cc63a183a67bb4c09092a 100644 (file)
@@ -2,7 +2,7 @@
    Unix SMB/Netbios implementation.
    Version 2.0
 
-   Winbind daemon - user related function
+   Winbind daemon - user related functions
 
    Copyright (C) Tim Potter 2000
    
 
 /* 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)
+static BOOL winbindd_fill_pwent(char *domain_name, char *name, 
+                               uint32 user_rid, uint32 group_rid, 
+                                char *full_name, struct winbindd_pw *pw)
 {
-    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);
+       fstring name_domain, name_user;
+       pstring homedir;
+       
+       if (!pw || !name) {
+               return False;
+       }
+       
+       /* Resolve the uid number */
+       
+       if (!winbindd_idmap_get_uid_from_rid(domain_name, user_rid, 
+                                            &pw->pw_uid)) {
+               DEBUG(1, ("error getting user id for rid %d\n", user_rid));
+               return False;
+       }
+       
+       /* Resolve the gid number */   
+       
+       if (!winbindd_idmap_get_gid_from_rid(domain_name, group_rid, 
+                                            &pw->pw_gid)) {
+               DEBUG(1, ("error getting group id for rid %d\n", group_rid));
+               return False;
+       }
+
+       /* 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());
+       
+       /*
+        * Insist name_user is lowercase, name_domain is uppercase.
+        * This is a hack. JRA.
+        */
+
+       strlower(name_user);
+       strupper(name_domain);
+
+       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 True;
 }
 
 /* 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) 
+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. */
+       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;
+       
+       DEBUG(3, ("[%5d]: getpwnam %s\n", state->pid,
+                 state->request.data.username));
+       
+       /* 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)-1,"%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() */
+       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;
+       }
+
+       if (!domain_handles_open(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) - 1, "%s\\%s", name_domain, name_user);
+       
+       /* Get rid and name type from name.  The following costs 1 packet */
+
+       if (!winbindd_lookup_sid_by_name(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;
-    }
+       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;
+       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);
+       
+       /* Now take all this information and fill in a passwd structure */
+       
+       if (!winbindd_fill_pwent(domain->name, state->request.data.username, 
+                                user_rid, group_rid, gecos_name,
+                                &state->response.data.pw)) {
+               return WINBINDD_ERROR;
+       }
+       
+       winbindd_store_user_cache_entry(name_domain, name_user, 
+                                       &state->response.data.pw);
+       
+       return WINBINDD_OK;
 }       
 
 /* Return a password structure given a uid number */
@@ -170,77 +192,95 @@ enum winbindd_result winbindd_getpwnam_from_user(struct winbindd_cli_state *stat
 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;
-    }
-
-    if (strcmp("\\", lp_winbind_separator())) {
-           string_sub(user_name, "\\", lp_winbind_separator(), 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;
+       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;
+       
+       /* Bug out if the uid isn't in the winbind range */
+
+       if ((state->request.data.uid < server_state.uid_low ) ||
+           (state->request.data.uid > server_state.uid_high)) {
+               return WINBINDD_ERROR;
+       }
+
+       DEBUG(3, ("[%5d]: getpwuid %d\n", state->pid, 
+                 state->request.data.uid));
+       
+       /* Get rid from uid */
+
+       if (!winbindd_idmap_get_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;
+       }
+       
+       if (!domain_handles_open(domain)) {
+               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(&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;
+       }
+       
+       if (strcmp("\\", lp_winbind_separator())) {
+               string_sub(user_name, "\\", lp_winbind_separator(), 
+                          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 */
+
+       if (!winbindd_fill_pwent(domain->name, user_name, user_rid, group_rid,
+                                gecos_name, &state->response.data.pw)) {
+               return WINBINDD_ERROR;
+       }
+       
+       winbindd_store_uid_cache_entry(domain->name, state->request.data.uid,
+                                      &state->response.data.pw);
+       
+       return WINBINDD_OK;
 }
 
 /*
@@ -253,8 +293,16 @@ enum winbindd_result winbindd_setpwent(struct winbindd_cli_state *state)
 {
     struct winbindd_domain *tmp;
 
+    DEBUG(3, ("[%5d]: setpwent\n", state->pid));
+
     if (state == NULL) return WINBINDD_ERROR;
     
+    /* Check user has enabled this */
+
+    if (!lp_winbind_enum_users()) {
+           return WINBINDD_ERROR;
+    }
+
     /* Free old static data if it exists */
 
     if (state->getpwent_state != NULL) {
@@ -270,7 +318,7 @@ enum winbindd_result winbindd_setpwent(struct winbindd_cli_state *state)
         /* Skip domains other than WINBINDD_DOMAIN environment variable */
 
         if ((strcmp(state->request.domain, "") != 0) &&
-            (strcmp(state->request.domain, tmp->name) != 0)) {
+           !check_domain_env(state->request.domain, tmp->name)) {
                 continue;
         }
 
@@ -297,6 +345,8 @@ enum winbindd_result winbindd_setpwent(struct winbindd_cli_state *state)
 
 enum winbindd_result winbindd_endpwent(struct winbindd_cli_state *state)
 {
+    DEBUG(3, ("[%5d]: endpwent\n", state->pid));
+
     if (state == NULL) return WINBINDD_ERROR;
 
     free_getent_state(state->getpwent_state);    
@@ -305,110 +355,339 @@ enum winbindd_result winbindd_endpwent(struct winbindd_cli_state *state)
     return WINBINDD_OK;
 }
 
+/* Get partial list of domain users for a domain.  We fill in the sam_entries,
+   and num_sam_entries fields with domain user information.  The dispinfo_ndx
+   field is incremented to the index of the next user to fetch.  Return True if
+   some users were returned, False otherwise. */
+
+#define MAX_FETCH_SAM_ENTRIES 100
+
+static BOOL get_sam_user_entries(struct getent_state *ent)
+{
+       uint32 status, num_entries;
+       SAM_DISPINFO_1 info1;
+       SAM_DISPINFO_CTR ctr;
+       struct getpwent_user *name_list = NULL;
+       uint32 group_rid;
+
+       if (ent->got_all_sam_entries) {
+               return False;
+       }
+
+       ZERO_STRUCT(info1);
+       ZERO_STRUCT(ctr);
+
+       ctr.sam.info1 = &info1;
+
+#if 0
+       /* Look in cache for entries, else get them direct */
+                   
+       if (winbindd_fetch_user_cache(ent->domain->name,
+                                     (struct getpwent_user **)
+                                     &ent->sam_entries, 
+                                     &ent->num_sam_entries)) {
+               return True;
+       }
+#endif
+
+       /* For the moment we set the primary group for every user to be the
+          Domain Users group.  There are serious problems with determining
+          the actual primary group for large domains.  This should really
+          be made into a 'winbind force group' smb.conf parameter or
+          something like that. */ 
+
+       group_rid = DOMAIN_GROUP_RID_USERS;
+
+       if (!domain_handles_open(ent->domain)) {
+               return WINBINDD_ERROR;
+       }
+
+       /* Free any existing user info */
+
+       if (ent->sam_entries) {
+               free(ent->sam_entries);
+               ent->sam_entries = NULL;
+               ent->num_sam_entries = 0;
+       }
+
+       /* Call query_dispinfo to get a list of usernames and user rids */
+
+       do {
+               int i;
+                                       
+               num_entries = 0;
+
+               status = winbindd_query_dispinfo(ent->domain, 
+                                                &ent->dispinfo_ndx, 1,
+                                                &num_entries, &ctr);
+               
+               if (num_entries) {
+                       name_list = Realloc(name_list, 
+                                           sizeof(struct getpwent_user) *
+                                           (ent->num_sam_entries + 
+                                            num_entries));
+               }
+
+               for (i = 0; i < num_entries; i++) {
+
+                       /* Store account name and gecos */
+
+                       unistr2_to_ascii(
+                               name_list[ent->num_sam_entries + i].name, 
+                               &info1.str[i].uni_acct_name, 
+                               sizeof(fstring));
+
+                       unistr2_to_ascii(
+                               name_list[ent->num_sam_entries + i].gecos, 
+                               &info1.str[i].uni_full_name, 
+                               sizeof(fstring));
+
+                       /* User and group ids */
+
+                       name_list[ent->num_sam_entries + i].user_rid =
+                               info1.sam[i].rid_user;
+
+                       name_list[ent->num_sam_entries + i].
+                               group_rid = group_rid;
+               }
+               
+               ent->num_sam_entries += num_entries;
+
+               if (status != STATUS_MORE_ENTRIES) {
+                       break;
+               }
+
+       } while (ent->num_sam_entries < MAX_FETCH_SAM_ENTRIES);
+       
+#if 0
+       /* Fill cache with received entries */
+       
+       winbindd_store_user_cache(ent->domain->name, ent->sam_entries, 
+                                 ent->num_sam_entries);
+#endif
+
+       /* Fill in remaining fields */
+       
+       ent->sam_entries = name_list;
+       ent->sam_entry_index = 0;
+       ent->got_all_sam_entries = (status != STATUS_MORE_ENTRIES);
+
+       return ent->num_sam_entries > 0;
+}
+
 /* Fetch next passwd entry from ntdom database */
 
+#define MAX_GETPWENT_USERS 500
+
 enum winbindd_result winbindd_getpwent(struct winbindd_cli_state *state)
 {
-    if (state == NULL) return WINBINDD_ERROR;
+       struct getent_state *ent;
+       struct winbindd_pw *user_list;
+       int num_users, user_list_ndx = 0, i;
+       char *sep;
 
-    /* Process the current head of the getent_state list */
+       DEBUG(3, ("[%5d]: getpwent\n", state->pid));
 
-    while(state->getpwent_state != NULL) {
-        struct getent_state *ent = state->getpwent_state;
+       if (state == NULL) return WINBINDD_ERROR;
 
-        /* Get list of user entries for this pipe */
+       /* Check user has enabled this */
 
-        if (!ent->got_sam_entries) {
-            uint32 status, start_ndx = 0;
+       if (!lp_winbind_enum_users()) {
+               return WINBINDD_ERROR;
+       }
 
-            /* Look in cache for entries, else get them direct */
+       /* Allocate space for returning a chunk of users */
 
-            if (!winbindd_fetch_user_cache(ent->domain->name, 
-                                           &ent->sam_entries,
-                                           &ent->num_sam_entries)) {
+       num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries);
+       
+       if ((state->response.extra_data = 
+            malloc(num_users * sizeof(struct winbindd_pw))) == NULL) {
+               return WINBINDD_ERROR;
+       }
 
-                /* Fetch the user entries */
+       memset(state->response.extra_data, 0, num_users * 
+              sizeof(struct winbindd_pw));
 
-                if (!domain_handles_open(ent->domain)) goto cleanup;
+       user_list = (struct winbindd_pw *)state->response.extra_data;
+       sep = lp_winbind_separator();
+       
+       if (!(ent = state->getpwent_state)) {
+               return WINBINDD_ERROR;
+       }
 
-                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);
+       /* Start sending back users */
 
-                /* 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)-1,
-                    "%s%s%s", ent->domain->name, lp_winbind_separator(), 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));
-        }
+       for (i = 0; i < num_users; i++) {
+               struct getpwent_user *name_list = NULL;
+               fstring domain_user_name;
+               uint32 result;
 
-        /* We've exhausted all users for this pipe - close it down and
-           start on the next one. */
+               /* Do we need to fetch another chunk of users? */
 
-    cleanup:
+               if (ent->num_sam_entries == ent->sam_entry_index) {
 
-        /* Free mallocated memory for sam entries.  The data stored here
-           may have been allocated from the cache. */
+                       while(ent && !get_sam_user_entries(ent)) {
+                               struct getent_state *next_ent;
 
-        if (ent->sam_entries != NULL) free(ent->sam_entries);
-        ent->sam_entries = NULL;
+                               /* Free state information for this domain */
 
-        /* Free state information for this domain */
+                               safe_free(ent->sam_entries);
+                               ent->sam_entries = NULL;
 
-        {
-            struct getent_state *old_ent;
+                               next_ent = ent->next;
+                               DLIST_REMOVE(state->getpwent_state, ent);
+
+                               free(ent);
+                               ent = next_ent;
+                       }
+                       /* No more domains */
+
+                       if (!ent) break;
+               }
+
+               name_list = ent->sam_entries;
+
+               /* Skip machine accounts */
+
+               if (name_list[ent->sam_entry_index].
+                   name[strlen(name_list[ent->sam_entry_index].name) - 1] 
+                   == '$') {
+                       ent->sam_entry_index++;
+                       continue;
+               }
+
+               /* Lookup user info */
+               
+               slprintf(domain_user_name, sizeof(domain_user_name) - 1,
+                        "%s%s%s", ent->domain->name, sep,
+                        name_list[ent->sam_entry_index].name);
+               
+               result = winbindd_fill_pwent(
+                       ent->domain->name, 
+                       domain_user_name,
+                       name_list[ent->sam_entry_index].user_rid,
+                       name_list[ent->sam_entry_index].group_rid,
+                       name_list[ent->sam_entry_index].gecos,
+                       &user_list[user_list_ndx]);
+               
+               ent->sam_entry_index++;
+               
+               /* Add user to return list */
+               
+               if (result) {
+                               
+                       user_list_ndx++;
+                       state->response.data.num_entries++;
+                       state->response.length += 
+                               sizeof(struct winbindd_pw);
+
+               } else {
+                       DEBUG(1, ("could not lookup domain user %s\n",
+                                 domain_user_name));
+               }
+               
+       }
+
+       /* Out of domains */
+
+       return (user_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
+}
 
-            old_ent = state->getpwent_state;
-            DLIST_REMOVE(state->getpwent_state, state->getpwent_state);
-            free(old_ent);
+/* List domain users without mapping to unix ids */
+
+enum winbindd_result winbindd_list_users(struct winbindd_cli_state *state)
+{
+        struct winbindd_domain *domain;
+        SAM_DISPINFO_CTR ctr;
+       SAM_DISPINFO_1 info1;
+        uint32 num_entries = 0, total_entries = 0;
+       char *extra_data = NULL;
+       int extra_data_len = 0;
+
+       DEBUG(3, ("[%5d]: list users\n", state->pid));
+
+        /* Enumerate over trusted domains */
+
+       ctr.sam.info1 = &info1;
+
+        for (domain = domain_list; domain; domain = domain->next) {
+               uint32 status, start_ndx = 0;
+
+               /* Skip domains other than WINBINDD_DOMAIN environment
+                  variable */ 
+
+               if ((strcmp(state->request.domain, "") != 0) &&
+                   !check_domain_env(state->request.domain, domain->name)) {
+                       continue;
+               }
+
+               if (!domain_handles_open(domain)) {
+                       continue;
+               }
+
+                /* Query display info */
+
+               do {
+                       int i;
+
+                       status = winbindd_query_dispinfo(domain, &start_ndx, 
+                                                        1, &num_entries, 
+                                                        &ctr);
+
+                       if (num_entries == 0) {
+                               continue;
+                       }
+
+                       /* Allocate some memory for extra data */
+
+                       total_entries += num_entries;
+                       
+                       extra_data = Realloc(extra_data, sizeof(fstring) * 
+                                            total_entries);
+                       
+                       if (!extra_data) {
+                               return WINBINDD_ERROR;
+                       }
+                       
+                       /* Pack user list into extra data fields */
+                       
+                       for (i = 0; i < num_entries; i++) {
+                               UNISTR2 *uni_acct_name;
+                               fstring acct_name, name;
+
+                               /* Convert unistring to ascii */
+                               
+                               uni_acct_name = &ctr.sam.info1->str[i]. 
+                                       uni_acct_name;
+                               unistr2_to_ascii(acct_name, uni_acct_name,
+                                                sizeof(acct_name) - 1);
+                                                 
+                               slprintf(name, sizeof(name) - 1, "%s%s%s",
+                                        domain->name, lp_winbind_separator(),
+                                        acct_name);
+
+                               /* Append to extra data */
+                       
+                               memcpy(&extra_data[extra_data_len], name, 
+                                      strlen(name));
+                               extra_data_len += strlen(name);
+                               
+                               extra_data[extra_data_len++] = ',';
+                       }   
+               } while (status == STATUS_MORE_ENTRIES);
         }
-    }
 
-    /* Out of pipes so we're done */
+       /* Assign extra_data fields in response structure */
+
+       if (extra_data) {
+               extra_data[extra_data_len - 1] = '\0';
+               state->response.extra_data = extra_data;
+               state->response.length += extra_data_len;
+       }
+
+       /* No domains responded but that's still OK so don't return an
+          error. */
 
-    return WINBINDD_ERROR;
+       return WINBINDD_OK;
 }
index 80d6955e6c0866c961d853dd84b34eaa1ff1f940..066158637665b03f45e3fa3ee8c8e65f5cb6dc40 100644 (file)
 */
 
 #include "winbindd.h"
+#include "sids.h"
 
-BOOL domain_handles_open(struct winbindd_domain *domain)
-{
-       return domain->sam_handle_open &&
-               domain->sam_dom_handle_open &&
-               rpc_hnd_ok(&domain->sam_handle) &&
-               rpc_hnd_ok(&domain->sam_dom_handle);
-}
+/* Debug connection state */
 
-static BOOL resolve_dc_name(char *domain_name, fstring domain_controller)
+void debug_conn_state(void)
 {
-       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 && !lp_wildcard_dc()) {
-               fstrcpy(domain_controller, lp_passwordserver());
-               return True;
-       }
+       struct winbindd_domain *domain;
 
-       if (!resolve_name(domain_name, &ip, 0x1B)) return False;
+       DEBUG(3, ("server: dc=%s, pwdb_init=%d, lsa_hnd=%d\n", 
+                 server_state.controller,
+                 server_state.pwdb_initialised,
+                 server_state.lsa_handle_open));
 
-       return lookup_pdc_name(global_myname, domain_name, &ip, 
-                              domain_controller);
+       for (domain = domain_list; domain; domain = domain->next) {
+               DEBUG(3, ("%s: dc=%s, got_sid=%d, sam_hnd=%d sam_dom_hnd=%d\n",
+                         domain->name, domain->controller,
+                         domain->got_domain_info, domain->sam_handle_open,
+                         domain->sam_dom_handle_open));
+       }
 }
 
+/* Add a trusted domain to our list of domains */
+
 static struct winbindd_domain *add_trusted_domain(char *domain_name)
 {
-    struct winbindd_domain *domain;
+    struct winbindd_domain *domain, *tmp;
+
+    for (tmp = domain_list; tmp != NULL; tmp = tmp->next) {
+           if (strcmp(domain_name, tmp->name) == 0) {
+                   DEBUG(3, ("domain %s already in trusted list\n",
+                             domain_name));
+                   return tmp;
+           }
+    }
 
     DEBUG(1, ("adding trusted domain %s\n", domain_name));
 
@@ -77,12 +81,13 @@ static struct winbindd_domain *add_trusted_domain(char *domain_name)
 }
 
 /* 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;
+       DOM_SID *sids = NULL;
        BOOL result;
        int i;
        
@@ -110,100 +115,184 @@ static BOOL get_trusted_domains(void)
                }
        }
        
-       /* Free memory */       
-       free_char_array(num_doms, domains);
-       free_sid_array(num_doms, sids);
-       
        return True;
 }
 
+/* Open sam and sam domain handles */
 
-/* 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 */
+       /* Get domain info (sid and controller name) */
+
        if (!domain->got_domain_info) {
                domain->got_domain_info = get_domain_info(domain);
                if (!domain->got_domain_info) return False;
        }
 
-       if ((domain->sam_handle_open && !rpc_hnd_ok(&domain->sam_handle)) ||
-           (domain->sam_dom_handle_open && 
-            !rpc_hnd_ok(&domain->sam_dom_handle))) {
+       /* Shut down existing sam handles */
 
-               domain->got_domain_info = get_domain_info(domain);
-               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;
-               }
+       if (domain->sam_dom_handle_open) {
+               samr_close(&domain->sam_dom_handle);
+               domain->sam_dom_handle_open = False;
        }
 
-       /* Open sam handle if it isn't already open */
+       if (domain->sam_handle_open) {
+               samr_close(&domain->sam_handle);
+               domain->sam_handle_open = False;
+       }
 
-       if (!domain->sam_handle_open) {
+       /* Open sam handle */
 
-               domain->sam_handle_open = 
-                       samr_connect(domain->controller, 
-                                    SEC_RIGHTS_MAXIMUM_ALLOWED, 
-                                    &domain->sam_handle);
+       domain->sam_handle_open = 
+               samr_connect(domain->controller, 
+                            SEC_RIGHTS_MAXIMUM_ALLOWED, 
+                            &domain->sam_handle);
 
-               if (!domain->sam_handle_open) return False;
-       }
+       if (!domain->sam_handle_open) return False;
+
+       /* Open sam domain handle */
+
+       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 BOOL rpc_hnd_ok(CLI_POLICY_HND *hnd)
+{
+       return hnd->cli->fd != -1;
+}
 
-       /* Open sam domain handle if it isn't already open */
+/* Return true if the SAM domain handles are open and responding.  */
 
-       if (!domain->sam_dom_handle_open) {
+BOOL domain_handles_open(struct winbindd_domain *domain)
+{
+       time_t t;
+       BOOL result;
 
-               domain->sam_dom_handle_open =
-                       samr_open_domain(&domain->sam_handle, 
-                                        SEC_RIGHTS_MAXIMUM_ALLOWED, 
-                                        &domain->sid, &domain->sam_dom_handle);
+       /* Check we haven't checked too recently */
 
-               if (!domain->sam_dom_handle_open) return False;
+       t = time(NULL);
+
+       if ((t - domain->last_check) < WINBINDD_ESTABLISH_LOOP) {
+               return domain->sam_handle_open &&
+                       domain->sam_dom_handle_open;
        }
        
-       return True;
+       DEBUG(3, ("checking domain handles for domain %s\n", domain->name));
+       debug_conn_state();
+
+       domain->last_check = t;
+
+       /* Open sam handles if they are marked as closed */
+
+       if (!domain->sam_handle_open || !domain->sam_dom_handle_open) {
+       reopen:
+               DEBUG(3, ("opening sam handles\n"));
+               return open_sam_handles(domain);
+       }
+
+       /* Check sam handles are ok - the domain controller may have failed
+          and we need to move to a BDC. */
+
+       if (!rpc_hnd_ok(&domain->sam_handle) || 
+           !rpc_hnd_ok(&domain->sam_dom_handle)) {
+
+               /* We want to close the current connection but attempt
+                  to open a new set, possibly to a new dc.  If this
+                  doesn't work then return False as we have no dc
+                  to talk to. */
+
+               DEBUG(3, ("sam handles not responding\n"));
+
+               winbindd_kill_connections(domain);
+               goto reopen;
+       }
+
+       result = domain->sam_handle_open && domain->sam_dom_handle_open;
+
+       return result;
 }
 
-/* Close all LSA and SAM connections */
+/* Shut down connections to all domain controllers */
 
-static void winbindd_kill_connections(void)
+void winbindd_kill_connections(struct winbindd_domain *domain)
 {
-       struct winbindd_cli_state *cli;
-       struct winbindd_domain *domain;
+       BOOL is_server = False;
+       struct winbindd_domain *server_domain;
+
+       /* Find pointer to domain of pdc */
+
+       server_domain = find_domain_from_name(lp_workgroup());
+       if (!server_domain) return;
+
+       /* If NULL passed, use pdc */
+
+       if (!domain) {
+               domain = server_domain;
+       }
+
+       if (domain == server_domain || 
+           strequal(domain->name, lp_workgroup())) {
+               is_server = True;
+       }
 
-       DEBUG(1,("killing winbindd connections\n"));
+       /* Log a level 0 message - this is probably a domain controller
+          failure */
 
-       /* Close LSA connection */
+       DEBUG(0, ("killing connections to domain %s with controller %s\n", 
+                 domain->name, domain->controller));
 
-       server_state.pwdb_initialised = False;
-       server_state.lsa_handle_open = False;
-       lsa_close(&server_state.lsa_handle);
+       debug_conn_state();
+
+       if (is_server) {
+               server_state.pwdb_initialised = False;
+               server_state.lsa_handle_open = False;
+               lsa_close(&server_state.lsa_handle);
+       }
+       
+       /* Close domain sam handles but don't free them as this
+          severely traumatises the getent state.  The connections
+          will be reopened later. */
+
+       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;
+       }
+
+       /* Re-lookup domain info which includes domain controller name */
        
-       /* Close SAM connections */
+       domain->got_domain_info = False;
+}
+
+/* Kill connections to all servers */
+
+void winbindd_kill_all_connections(void)
+{
+       struct winbindd_domain *domain;
+
+       /* Iterate over domain list */
 
        domain = domain_list;
 
-       while(domain) {
+       while (domain) {
                struct winbindd_domain *next;
 
-               /* Close SAM handles */
+               /* Kill conections */
 
-               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;
-               }
+               winbindd_kill_connections(domain);
 
-               /* Remove from list */
+               /* Remove domain from list */
 
                next = domain->next;
                DLIST_REMOVE(domain_list, domain);
@@ -211,68 +300,106 @@ static void winbindd_kill_connections(void)
 
                domain = next;
        }
+}
 
-       /* We also need to go through and trash any pointers to domains in
-          get{pw,gr}ent state records */
+static BOOL get_any_dc_name(char *domain, fstring srv_name)
+{
+       struct in_addr *ip_list, dc_ip;
+       extern pstring global_myname;
+       int count, i;
 
-       for (cli = client_list; cli; cli = cli->next) {
-               free_getent_state(cli->getpwent_state);
-               free_getent_state(cli->getgrent_state);
+       /* Lookup domain controller name */
+               
+       if (!get_dc_list(False, lp_workgroup(), &ip_list, &count))
+               return False;
+               
+       /* Firstly choose a PDC/BDC who has the same network address as any
+          of our interfaces. */
+       
+       for (i = 0; i < count; i++) {
+               if(!is_local_net(ip_list[i]))
+                       goto got_ip;
        }
+       
+       i = (sys_random() % count);
+       
+ got_ip:
+       dc_ip = ip_list[i];
+       free(ip_list);
+               
+       if (!lookup_pdc_name(global_myname, lp_workgroup(),
+                            &dc_ip, server_state.controller))
+               return False;
+
+       return True;
 }
 
-/* Try to establish connections to NT servers */
+/* Attempt to connect to all domain controllers we know about */
 
-void establish_connections(void
+void establish_connections(BOOL force_reestablish
 {
-       struct winbindd_domain *domain;
        static time_t lastt;
        time_t t;
 
+       /* Check we haven't checked too recently */
+
        t = time(NULL);
-       if (t - lastt < WINBINDD_ESTABLISH_LOOP) return;
+       if ((t - lastt < WINBINDD_ESTABLISH_LOOP) && !force_reestablish) {
+               return;
+       }
        lastt = t;
 
-       /* maybe the connection died - if so then close up and restart */
+       DEBUG(3, ("establishing connections\n"));
+       debug_conn_state();
+
+       /* 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();
+               winbindd_kill_connections(NULL);
        }
 
        if (!server_state.pwdb_initialised) {
-               fstrcpy(server_state.controller, lp_passwordserver());
-               if (lp_wildcard_dc()) {
-                       if (!resolve_dc_name(lp_workgroup(), server_state.controller)) {
-                               return;
-                       }
+
+               /* Lookup domain controller name */
+
+               if (!get_any_dc_name(lp_workgroup(), 
+                                    server_state.controller)) {
+                       return;
                }
 
-               server_state.pwdb_initialised = pwdb_initialise(False);
-               if (!server_state.pwdb_initialised) return;
+               /* Initialise password database and sids */
+               
+//             server_state.pwdb_initialised = pwdb_initialise(False);
+               server_state.pwdb_initialised = True;
+
+               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);
+                       lsa_open_policy(server_state.controller, 
+                                       False, SEC_RIGHTS_MAXIMUM_ALLOWED,
+                                       &server_state.lsa_handle);
+
                if (!server_state.lsa_handle_open) return;
 
-               /* now we can talk to the server we can get some info */
+               /* 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);
-               }
-       }
+       debug_conn_state();
 }
 
-
 /* 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;
@@ -280,7 +407,7 @@ BOOL lookup_domain_sid(char *domain_name, struct winbindd_domain *domain)
     uint32 enum_ctx = 0;
     uint32 num_doms = 0;
     char **domains = NULL;
-    DOM_SID **sids = NULL;
+    DOM_SID *sids = NULL;
 
     if (domain == NULL) {
         return False;
@@ -289,7 +416,10 @@ BOOL lookup_domain_sid(char *domain_name, struct winbindd_domain *domain)
     DEBUG(1, ("looking up sid for domain %s\n", domain_name));
 
     /* Get controller name for domain */
-    if (!resolve_dc_name(domain_name, domain->controller)) {
+
+    if (!get_any_dc_name(domain_name, domain->controller)) {
+           DEBUG(0, ("Could not resolve domain controller for domain %s\n",
+                     domain_name));
            return False;
     }
 
@@ -312,7 +442,7 @@ BOOL lookup_domain_sid(char *domain_name, struct winbindd_domain *domain)
            
             for(i = 0; i < num_doms; i++) {
                    if (strequal(domain_name, domains[i])) {
-                           sid_copy(&domain->sid, sids[i]);
+                           sid_copy(&domain->sid, &sids[i]);
                            found = True;
                            break;
                    }
@@ -321,15 +451,9 @@ BOOL lookup_domain_sid(char *domain_name, struct winbindd_domain *domain)
             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)
@@ -339,8 +463,14 @@ BOOL get_domain_info(struct winbindd_domain *domain)
     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));
+
+           /* Could be a DC failure - shut down connections to this domain */
+
+           winbindd_kill_connections(domain);
+
            return False;
     }
     
@@ -356,8 +486,7 @@ BOOL get_domain_info(struct winbindd_domain *domain)
 
 /* Lookup a sid in a domain from a name */
 
-BOOL winbindd_lookup_sid_by_name(struct winbindd_domain *domain,
-                                 char *name, DOM_SID *sid,
+BOOL winbindd_lookup_sid_by_name(char *name, DOM_SID *sid,
                                  enum SID_NAME_USE *type)
 {
     int num_sids = 0, num_names = 1;
@@ -403,8 +532,7 @@ BOOL winbindd_lookup_sid_by_name(struct winbindd_domain *domain,
 
 /* Lookup a name in a domain from a sid */
 
-BOOL winbindd_lookup_name_by_sid(struct winbindd_domain *domain,
-                                 DOM_SID *sid, char *name,
+BOOL winbindd_lookup_name_by_sid(DOM_SID *sid, fstring name,
                                  enum SID_NAME_USE *type)
 {
     int num_sids = 1, num_names = 0;
@@ -413,8 +541,9 @@ BOOL winbindd_lookup_name_by_sid(struct winbindd_domain *domain,
     BOOL res;
 
     /* Lookup name */
-    res = lsa_lookup_sids(&server_state.lsa_handle, num_sids, &sid, &names, 
-                         &types, &num_names);
+
+    res = lsa_lookup_sids(&server_state.lsa_handle, num_sids, sid, 
+                         &names, &types, &num_names);
 
     /* Return name and type if successful */
 
@@ -446,19 +575,49 @@ BOOL winbindd_lookup_name_by_sid(struct winbindd_domain *domain,
 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);
+       return get_samr_query_userinfo(&domain->sam_dom_handle, 0x15, 
+                                      user_rid, user_info);
 }                                   
 
+/* Lookup groups a user is a member of.  I wish Unix had a call like this! */
+
+BOOL winbindd_lookup_usergroups(struct winbindd_domain *domain,
+                               uint32 user_rid, uint32 *num_groups,
+                               DOM_GID **user_groups)
+{
+       POLICY_HND user_pol;
+       BOOL result;
+
+        if (!samr_open_user(&domain->sam_dom_handle, 
+                           SEC_RIGHTS_MAXIMUM_ALLOWED,
+                           user_rid, &user_pol)) {
+               return False;
+       }
+
+       if (cli_samr_query_usergroups(domain->sam_dom_handle.cli,
+                                     domain->sam_dom_handle.mem_ctx,
+                                     &user_pol, num_groups, user_groups)
+           != NT_STATUS_NOPROBLEMO) {
+               result = False;
+               goto done;
+       }
+
+       result = True;
+
+done:
+       cli_samr_close(domain->sam_dom_handle.cli,
+                      domain->sam_dom_handle.mem_ctx, &user_pol);
+
+       return True;
+}
+
 /* 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);
+       return get_samr_query_groupinfo(&domain->sam_dom_handle, 1, 
+                                       group_rid, info);
 }
 
 /* Lookup group membership given a rid */
@@ -468,40 +627,48 @@ BOOL winbindd_lookup_groupmem(struct winbindd_domain *domain,
                               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);
+       return sam_query_groupmem(&domain->sam_dom_handle, group_rid, 
+                                 num_names, rid_mem, names, name_types);
 }
 
-/* Lookup alias membership given a rid */
+/* Globals for domain list stuff */
+
+struct winbindd_domain *domain_list = NULL;
 
-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)
+/* Given a domain name, return the struct winbindd domain info for it 
+   if it is actually working. */
+
+struct winbindd_domain *find_domain_from_name(char *domain_name)
 {
-    /* Open sam handles */
-    if (!domain_handles_open(domain)) return False;
+       struct winbindd_domain *tmp;
 
-    return sam_query_aliasmem(domain->controller, 
-                             &domain->sam_dom_handle, alias_rid, num_names, 
-                             sids, names, name_types);
-}
+       /* Search through list */
 
-/* Globals for domain list stuff */
+       for (tmp = domain_list; tmp != NULL; tmp = tmp->next) {
+               if (strcmp(domain_name, tmp->name) == 0) {
 
-struct winbindd_domain *domain_list = NULL;
+                       if (!tmp->got_domain_info) {
+                               get_domain_info(tmp);
+                       }
+
+                        return tmp->got_domain_info ? tmp : NULL;
+                }
+        }
+
+       /* Not found */
+
+       return 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 *find_domain_from_sid(DOM_SID *sid)
 {
        struct winbindd_domain *tmp;
 
        /* Search through list */
        for (tmp = domain_list; tmp != NULL; tmp = tmp->next) {
-               if (strcmp(domain_name, tmp->name) == 0) {
+               if (sid_equal(sid, &tmp->sid)) {
                        if (!tmp->got_domain_info) return NULL;
                         return tmp;
                 }
@@ -544,14 +711,14 @@ static BOOL parse_id_list(char *paramstr, BOOL is_user)
     /* Give a nicer error message if no parameters specified */
 
     if (strequal(paramstr, "")) {
-        DEBUG(0, ("winbid %s parameter missing\n", is_user ? "uid" : "gid"));
+        DEBUG(0, ("winbind %s parameter missing\n", is_user ? "uid" : "gid"));
         return False;
     }
     
     /* Parse entry */
 
     if (sscanf(paramstr, "%u-%u", &id_low, &id_high) != 2) {
-        DEBUG(0, ("winbid %s parameter invalid\n", 
+        DEBUG(0, ("winbind %s parameter invalid\n", 
                   is_user ? "uid" : "gid"));
         return False;
     }
@@ -588,7 +755,7 @@ BOOL winbindd_param_init(void)
     }
     
     if (server_state.gid_low > server_state.gid_high) {
-        DEBUG(0, ("gid range for invalid\n"));
+        DEBUG(0, ("gid range invalid\n"));
         return False;
     }
     
@@ -597,85 +764,83 @@ BOOL winbindd_param_init(void)
 
 /* Convert a enum winbindd_cmd to a string */
 
-char *winbindd_cmd_to_string(enum winbindd_cmd cmd)
-{
-    char *result;
+struct cmdstr_table {
+       enum winbindd_cmd cmd;
+       char *desc;
+};
 
-    switch (cmd) {
+static struct cmdstr_table cmdstr_table[] = {
+       
+       /* User functions */
 
-    case WINBINDD_GETPWNAM_FROM_USER:
-        result = "getpwnam from user";
-        break;
-            
-    case WINBINDD_GETPWNAM_FROM_UID:
-        result = "getpwnam from uid";
-        break;
+       { WINBINDD_GETPWNAM_FROM_USER, "getpwnam from user" },
+       { WINBINDD_GETPWNAM_FROM_UID, "getpwnam from uid" },
+       { WINBINDD_SETPWENT, "setpwent" },
+       { WINBINDD_ENDPWENT, "endpwent" },
+       { WINBINDD_GETPWENT, "getpwent" },
+       { WINBINDD_GETGROUPS, "getgroups" },
 
-    case WINBINDD_GETGRNAM_FROM_GROUP:
-        result = "getgrnam from group";
-        break;
+       /* Group functions */
 
-    case WINBINDD_GETGRNAM_FROM_GID:
-        result = "getgrnam from gid";
-        break;
+       { WINBINDD_GETGRNAM_FROM_GROUP, "getgrnam from group" },
+       { WINBINDD_GETGRNAM_FROM_GID, "getgrnam from gid" },
+       { WINBINDD_SETGRENT, "setgrent" },
+       { WINBINDD_ENDGRENT, "endgrent" },
+       { WINBINDD_GETGRENT, "getgrent" },
 
-    case WINBINDD_SETPWENT:
-        result = "setpwent";
-        break;
+       /* PAM auth functions */
 
-    case WINBINDD_ENDPWENT:
-        result = "endpwent";
-        break;
+       { WINBINDD_PAM_AUTH, "pam auth" },
+       { WINBINDD_PAM_CHAUTHTOK, "pam chauthtok" },
 
-    case WINBINDD_GETPWENT:
-        result = "getpwent";
-        break;
+       /* List things */
 
-    case WINBINDD_SETGRENT:
-        result = "setgrent";
-        break;
+        { WINBINDD_LIST_USERS, "list users" },
+        { WINBINDD_LIST_GROUPS, "list groups" },
+       { WINBINDD_LIST_TRUSTDOM, "list trusted domains" },
 
-    case WINBINDD_ENDGRENT:
-        result = "endgrent"; 
-        break;
+       /* SID related functions */
 
-    case WINBINDD_GETGRENT:
-        result = "getgrent";
-        break;
+       { WINBINDD_LOOKUPSID, "lookup sid" },
+       { WINBINDD_LOOKUPNAME, "lookup name" },
 
-    case WINBINDD_PAM_AUTH:
-        result = "pam_auth";
-        break;
+       /* S*RS related functions */
 
-    default:
-        result = "invalid command";
-        break;
-    }
+       { WINBINDD_SID_TO_UID, "sid to uid" },
+       { WINBINDD_SID_TO_GID, "sid to gid " },
+       { WINBINDD_GID_TO_SID, "gid to sid" },
+       { WINBINDD_UID_TO_SID, "uid to sid" },
 
-    return result;
-};
+       /* Miscellaneous other stuff */
 
+       { WINBINDD_CHECK_MACHACC, "check machine acct pw" },
 
-/* parse a string of the form DOMAIN/user into a domain and a user */
-void parse_domain_user(char *domuser, fstring domain, fstring user)
+       /* End of list */
+
+       { WINBINDD_NUM_CMDS, NULL }
+};
+
+char *winbindd_cmd_to_string(enum winbindd_cmd cmd)
 {
-       char *p;
-       char *sep = lp_winbind_separator();
-       if (!sep) sep = "\\";
-       p = strchr(domuser,*sep);
-       if (!p) p = strchr(domuser,'\\');
-       if (!p) {
-               fstrcpy(domain,"");
-               fstrcpy(user, domuser);
-               return;
+       struct cmdstr_table *table = cmdstr_table;
+       char *result = NULL;
+
+       for(table = cmdstr_table; table->desc; table++) {
+               if (cmd == table->cmd) {
+                       result = table->desc;
+                       break;
+               }
        }
        
-       fstrcpy(user, p+1);
-       fstrcpy(domain, domuser);
-       domain[PTR_DIFF(p, domuser)] = 0;
-}
+       if (result == NULL) {
+               result = "invalid command";
+       }
+
+       return result;
+};
 
 /* find the sequence number for a domain */
+
 uint32 domain_sequence_number(char *domain_name)
 {
        struct winbindd_domain *domain;
@@ -685,6 +850,11 @@ uint32 domain_sequence_number(char *domain_name)
        if (!domain) return DOM_SEQUENCE_NONE;
 
        if (!samr_query_dom_info(&domain->sam_dom_handle, 2, &ctr)) {
+
+               /* If this fails, something bad has gone wrong */
+
+               winbindd_kill_connections(domain);
+
                DEBUG(2,("domain sequence query failed\n"));
                return DOM_SEQUENCE_NONE;
        }
@@ -694,3 +864,57 @@ uint32 domain_sequence_number(char *domain_name)
        
        return ctr.info.inf2.seq_num;
 }
+
+/* Query display info for a domain.  This returns enough information plus a
+   bit extra to give an overview of domain users for the User Manager
+   application. */
+
+uint32 winbindd_query_dispinfo(struct winbindd_domain *domain,
+                            uint32 *start_ndx, uint16 info_level, 
+                            uint32 *num_entries, SAM_DISPINFO_CTR *ctr)
+{
+       uint32 status;
+
+       status = samr_query_dispinfo(&domain->sam_dom_handle, start_ndx,
+                                    info_level, num_entries, ctr);
+
+       return status;
+}
+
+/* Check if a domain is present in a comma-separated list of domains */
+
+BOOL check_domain_env(char *domain_env, char *domain)
+{
+       fstring name;
+       char *tmp = domain_env;
+
+       while(next_token(&tmp, name, ",", sizeof(fstring))) {
+               if (strequal(name, domain)) {
+                       return True;
+               }
+       }
+
+       return False;
+}
+
+
+/* Parse a string of the form DOMAIN/user into a domain and a user */
+
+void parse_domain_user(char *domuser, fstring domain, fstring user)
+{
+       char *p;
+       char *sep = lp_winbind_separator();
+       if (!sep) sep = "\\";
+       p = strchr(domuser,*sep);
+       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;
+       strupper(domain);
+}