diff options
-rw-r--r-- | source3/Makefile.in | 32 | ||||
-rw-r--r-- | source3/include/smb.h | 8 | ||||
-rw-r--r-- | source3/lib/util_str.c | 21 | ||||
-rw-r--r-- | source3/nsswitch/wb_client.c | 22 | ||||
-rw-r--r-- | source3/nsswitch/wb_common.c | 5 | ||||
-rw-r--r-- | source3/nsswitch/winbind_nss_config.h | 19 | ||||
-rw-r--r-- | source3/nsswitch/winbindd.c | 849 | ||||
-rw-r--r-- | source3/nsswitch/winbindd.h | 64 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_cache.c | 249 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_group.c | 1371 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_idmap.c | 80 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_pam.c | 88 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_proto.h | 90 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_user.c | 845 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_util.c | 638 |
15 files changed, 2710 insertions, 1671 deletions
diff --git a/source3/Makefile.in b/source3/Makefile.in index 2b31ace4c3..e40a647fb7 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -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 $@" diff --git a/source3/include/smb.h b/source3/include/smb.h index 0993d349a2..8b18ee5010 100644 --- a/source3/include/smb.h +++ b/source3/include/smb.h @@ -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 */ diff --git a/source3/lib/util_str.c b/source3/lib/util_str.c index d09bd6a2b4..b517d93dd8 100644 --- a/source3/lib/util_str.c +++ b/source3/lib/util_str.c @@ -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); -} diff --git a/source3/nsswitch/wb_client.c b/source3/nsswitch/wb_client.c index 746e5406bc..366edbbe41 100644 --- a/source3/nsswitch/wb_client.c +++ b/source3/nsswitch/wb_client.c @@ -25,6 +25,28 @@ #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) diff --git a/source3/nsswitch/wb_common.c b/source3/nsswitch/wb_common.c index 4040e1cff2..8376007424 100644 --- a/source3/nsswitch/wb_common.c +++ b/source3/nsswitch/wb_common.c @@ -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 */ } diff --git a/source3/nsswitch/winbind_nss_config.h b/source3/nsswitch/winbind_nss_config.h index 5c09a5dd1b..39fe006557 100644 --- a/source3/nsswitch/winbind_nss_config.h +++ b/source3/nsswitch/winbind_nss_config.h @@ -29,16 +29,6 @@ #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 diff --git a/source3/nsswitch/winbindd.c b/source3/nsswitch/winbindd.c index b0e35f3cd6..9d315a0811 100644 --- a/source3/nsswitch/winbindd.c +++ b/source3/nsswitch/winbindd.c @@ -23,17 +23,29 @@ #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; } diff --git a/source3/nsswitch/winbindd.h b/source3/nsswitch/winbindd.h index d333b32bcf..c848047089 100644 --- a/source3/nsswitch/winbindd.h +++ b/source3/nsswitch/winbindd.h @@ -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 */ diff --git a/source3/nsswitch/winbindd_cache.c b/source3/nsswitch/winbindd_cache.c index 7b263dfe00..2e404a7eb0 100644 --- a/source3/nsswitch/winbindd_cache.c +++ b/source3/nsswitch/winbindd_cache.c @@ -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) +{ +} diff --git a/source3/nsswitch/winbindd_group.c b/source3/nsswitch/winbindd_group.c index 77825cd579..67cf10a1f5 100644 --- a/source3/nsswitch/winbindd_group.c +++ b/source3/nsswitch/winbindd_group.c @@ -25,741 +25,918 @@ /* 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; } diff --git a/source3/nsswitch/winbindd_idmap.c b/source3/nsswitch/winbindd_idmap.c index 11f7b8aae7..2bb942396b 100644 --- a/source3/nsswitch/winbindd_idmap.c +++ b/source3/nsswitch/winbindd_idmap.c @@ -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 */ +} diff --git a/source3/nsswitch/winbindd_pam.c b/source3/nsswitch/winbindd_pam.c index c74afd8e29..57b2394799 100644 --- a/source3/nsswitch/winbindd_pam.c +++ b/source3/nsswitch/winbindd_pam.c @@ -23,6 +23,28 @@ #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; +} diff --git a/source3/nsswitch/winbindd_proto.h b/source3/nsswitch/winbindd_proto.h index 774478fea5..6e03ec842f 100644 --- a/source3/nsswitch/winbindd_proto.h +++ b/source3/nsswitch/winbindd_proto.h @@ -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_ */ diff --git a/source3/nsswitch/winbindd_user.c b/source3/nsswitch/winbindd_user.c index f3e62d2f7f..66e220f927 100644 --- a/source3/nsswitch/winbindd_user.c +++ b/source3/nsswitch/winbindd_user.c @@ -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 @@ -25,144 +25,166 @@ /* 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; } diff --git a/source3/nsswitch/winbindd_util.c b/source3/nsswitch/winbindd_util.c index 80d6955e6c..0661586376 100644 --- a/source3/nsswitch/winbindd_util.c +++ b/source3/nsswitch/winbindd_util.c @@ -22,36 +22,40 @@ */ #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); +} |