diff options
author | Gerald Carter <jerry@samba.org> | 2005-06-08 22:10:34 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 10:57:08 -0500 |
commit | fed660877c16562265327c6093ea645cf4176b5c (patch) | |
tree | e92ae1356542ba095d806bbe1093fa56fbc8ddcc /source3/nsswitch | |
parent | 66bb4f03c3466205488f72e4878e8801c5bbb295 (diff) | |
download | samba-fed660877c16562265327c6093ea645cf4176b5c.tar.gz samba-fed660877c16562265327c6093ea645cf4176b5c.tar.bz2 samba-fed660877c16562265327c6093ea645cf4176b5c.zip |
r7415: * big change -- volker's new async winbindd from trunk
(This used to be commit a0ac9a8ffd4af31a0ebc423b4acbb2f043d865b8)
Diffstat (limited to 'source3/nsswitch')
-rw-r--r-- | source3/nsswitch/wb_client.c | 26 | ||||
-rw-r--r-- | source3/nsswitch/wbinfo.c | 83 | ||||
-rw-r--r-- | source3/nsswitch/winbindd.c | 733 | ||||
-rw-r--r-- | source3/nsswitch/winbindd.h | 83 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_ads.c | 166 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_async.c | 1403 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_cache.c | 260 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_cm.c | 970 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_dual.c | 445 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_group.c | 591 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_ldap.c | 646 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_misc.c | 400 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_nss.h | 61 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_pam.c | 418 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_passdb.c | 19 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_reconnect.c | 273 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_rpc.c | 669 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_sid.c | 697 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_user.c | 384 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_util.c | 600 |
20 files changed, 6290 insertions, 2637 deletions
diff --git a/source3/nsswitch/wb_client.c b/source3/nsswitch/wb_client.c index 5005f72457..32ac4319a0 100644 --- a/source3/nsswitch/wb_client.c +++ b/source3/nsswitch/wb_client.c @@ -257,6 +257,32 @@ BOOL winbind_allocate_rid(uint32 *rid) return True; } +BOOL winbind_allocate_rid_and_gid(uint32 *rid, gid_t *gid) +{ + struct winbindd_request request; + struct winbindd_response response; + int result; + + /* Initialise request */ + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + /* Make request */ + + result = winbindd_request(WINBINDD_ALLOCATE_RID_AND_GID, &request, + &response); + + if (result != NSS_STATUS_SUCCESS) + return False; + + /* Copy out result */ + *rid = response.data.rid_and_gid.rid; + *gid = response.data.rid_and_gid.gid; + + return True; +} + /* Fetch the list of groups a user is a member of from winbindd. This is used by winbind_getgroups. */ diff --git a/source3/nsswitch/wbinfo.c b/source3/nsswitch/wbinfo.c index baa35d7cfb..8407bb1e3a 100644 --- a/source3/nsswitch/wbinfo.c +++ b/source3/nsswitch/wbinfo.c @@ -167,6 +167,32 @@ static BOOL wbinfo_get_usersids(char *user_sid) return True; } +static BOOL wbinfo_get_userdomgroups(const char *user_sid) +{ + struct winbindd_request request; + struct winbindd_response response; + NSS_STATUS result; + + ZERO_STRUCT(response); + + /* Send request */ + fstrcpy(request.data.sid, user_sid); + + result = winbindd_request(WINBINDD_GETUSERDOMGROUPS, &request, + &response); + + if (result != NSS_STATUS_SUCCESS) + return False; + + if (response.data.num_entries != 0) + printf("%s", (char *)response.extra_data); + + SAFE_FREE(response.extra_data); + + return True; +} + + /* Convert NetBIOS name to IP */ static BOOL wbinfo_wins_byname(char *name) @@ -224,7 +250,6 @@ static BOOL wbinfo_wins_byip(char *ip) static BOOL wbinfo_list_domains(void) { struct winbindd_response response; - fstring name; ZERO_STRUCT(response); @@ -238,9 +263,19 @@ static BOOL wbinfo_list_domains(void) if (response.extra_data) { const char *extra_data = (char *)response.extra_data; - - while(next_token(&extra_data, name, ",", sizeof(fstring))) + fstring name; + char *p; + + while(next_token(&extra_data, name, "\n", sizeof(fstring))) { + p = strchr(name, '\\'); + if (p == 0) { + d_printf("Got invalid response: %s\n", + extra_data); + return False; + } + *p = 0; d_printf("%s\n", name); + } SAFE_FREE(response.extra_data); } @@ -316,6 +351,32 @@ static BOOL wbinfo_domain_info(const char *domain_name) return True; } +/* Get a foreign DC's name */ +static BOOL wbinfo_getdcname(const char *domain_name) +{ + struct winbindd_request request; + struct winbindd_response response; + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + fstrcpy(request.domain_name, domain_name); + + /* Send request */ + + if (winbindd_request(WINBINDD_GETDCNAME, &request, &response) != + NSS_STATUS_SUCCESS) { + d_printf("Could not get dc name for %s\n", domain_name); + return False; + } + + /* Display response */ + + d_printf("%s\n", response.data.dc_name); + + return True; +} + /* Check trust account password */ static BOOL wbinfo_check_secret(void) @@ -889,6 +950,8 @@ enum { OPT_GET_AUTH_USER, OPT_DOMAIN_NAME, OPT_SEQUENCE, + OPT_GETDCNAME, + OPT_USERDOMGROUPS, OPT_USERSIDS }; @@ -924,9 +987,13 @@ int main(int argc, char **argv) { "sequence", 0, POPT_ARG_NONE, 0, OPT_SEQUENCE, "Show sequence numbers of all domains" }, { "domain-info", 'D', POPT_ARG_STRING, &string_arg, 'D', "Show most of the info we have about the domain" }, { "user-groups", 'r', POPT_ARG_STRING, &string_arg, 'r', "Get user groups", "USER" }, + { "user-domgroups", 0, POPT_ARG_STRING, &string_arg, + OPT_USERDOMGROUPS, "Get user domain groups", "SID" }, { "user-sids", 0, POPT_ARG_STRING, &string_arg, OPT_USERSIDS, "Get user group sids for user SID", "SID" }, { "authenticate", 'a', POPT_ARG_STRING, &string_arg, 'a', "authenticate user", "user%password" }, { "set-auth-user", 0, POPT_ARG_STRING, &string_arg, OPT_SET_AUTH_USER, "Store user and password used by winbindd (root only)", "user%password" }, + { "getdcname", 0, POPT_ARG_STRING, &string_arg, OPT_GETDCNAME, + "Get a DC name for a foreign domain", "domainname" }, { "get-auth-user", 0, POPT_ARG_NONE, NULL, OPT_GET_AUTH_USER, "Retrieve user and password used by winbindd (root only)", NULL }, { "ping", 'p', POPT_ARG_NONE, 0, 'p', "Ping winbindd to see if it is alive" }, { "domain", 0, POPT_ARG_STRING, &opt_domain_name, OPT_DOMAIN_NAME, "Define to the domain to restrict operation", "domain" }, @@ -1079,6 +1146,13 @@ int main(int argc, char **argv) goto done; } break; + case OPT_USERDOMGROUPS: + if (!wbinfo_get_userdomgroups(string_arg)) { + d_printf("Could not get user's domain groups " + "for user SID %s\n", string_arg); + goto done; + } + break; case 'a': { BOOL got_error = False; @@ -1116,6 +1190,9 @@ int main(int argc, char **argv) case OPT_GET_AUTH_USER: wbinfo_get_auth_user(); break; + case OPT_GETDCNAME: + wbinfo_getdcname(string_arg); + break; /* generic configuration options */ case OPT_DOMAIN_NAME: break; diff --git a/source3/nsswitch/winbindd.c b/source3/nsswitch/winbindd.c index 81dbf327fc..f083dfe44a 100644 --- a/source3/nsswitch/winbindd.c +++ b/source3/nsswitch/winbindd.c @@ -6,6 +6,7 @@ Copyright (C) by Tim Potter 2000-2002 Copyright (C) Andrew Tridgell 2002 Copyright (C) Jelmer Vernooij 2003 + Copyright (C) Volker Lendecke 2004 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -27,6 +28,7 @@ BOOL opt_nocache = False; BOOL opt_dual_daemon = True; +static BOOL interactive = False; extern BOOL override_logfile; @@ -131,7 +133,6 @@ static void winbindd_status(void) static void print_winbindd_status(void) { winbindd_status(); - winbindd_cm_status(); } /* Flush client cache */ @@ -163,6 +164,17 @@ static void terminate(void) pstr_sprintf(path, "%s/%s", WINBINDD_SOCKET_DIR, WINBINDD_SOCKET_NAME); unlink(path); + +#if 0 + if (interactive) { + TALLOC_CTX *mem_ctx = talloc_init("end_description"); + char *description = talloc_describe_all(mem_ctx); + + DEBUG(3, ("tallocs left:\n%s\n", description)); + talloc_destroy(mem_ctx); + } +#endif + exit(0); } @@ -190,14 +202,11 @@ static void sighup_handler(int signum) sys_select_signal(); } +static BOOL do_sigchld; + static void sigchld_handler(int signum) { - pid_t pid; - int status; - - while ((pid = wait(&status)) != -1 || errno == EINTR) { - continue; /* Reap children */ - } + do_sigchld = True; sys_select_signal(); } @@ -215,13 +224,7 @@ static void msg_shutdown(int msg_type, pid_t src, void *buf, size_t len) terminate(); } -struct dispatch_table { - enum winbindd_cmd cmd; - enum winbindd_result (*fn)(struct winbindd_cli_state *state); - const char *winbindd_cmd_name; -}; - -static struct dispatch_table dispatch_table[] = { +static struct winbindd_dispatch_table dispatch_table[] = { /* User functions */ @@ -234,6 +237,8 @@ static struct dispatch_table dispatch_table[] = { { WINBINDD_GETGROUPS, winbindd_getgroups, "GETGROUPS" }, { WINBINDD_GETUSERSIDS, winbindd_getusersids, "GETUSERSIDS" }, + { WINBINDD_GETUSERDOMGROUPS, winbindd_getuserdomgroups, + "GETUSERDOMGROUPS" }, /* Group functions */ @@ -247,14 +252,15 @@ static struct dispatch_table dispatch_table[] = { /* PAM auth functions */ { WINBINDD_PAM_AUTH, winbindd_pam_auth, "PAM_AUTH" }, - { WINBINDD_PAM_AUTH_CRAP, winbindd_pam_auth_crap, "AUTH_CRAP" }, + { WINBINDD_PAM_AUTH_CRAP, winbindd_crap_auth, "AUTH_CRAP" }, { WINBINDD_PAM_CHAUTHTOK, winbindd_pam_chauthtok, "CHAUTHTOK" }, /* Enumeration functions */ { WINBINDD_LIST_USERS, winbindd_list_users, "LIST_USERS" }, { WINBINDD_LIST_GROUPS, winbindd_list_groups, "LIST_GROUPS" }, - { WINBINDD_LIST_TRUSTDOM, winbindd_list_trusted_domains, "LIST_TRUSTDOM" }, + { WINBINDD_LIST_TRUSTDOM, winbindd_list_trusted_domains, + "LIST_TRUSTDOM" }, { WINBINDD_SHOW_SEQUENCE, winbindd_show_sequence, "SHOW_SEQUENCE" }, /* SID related functions */ @@ -266,20 +272,25 @@ static struct dispatch_table dispatch_table[] = { { WINBINDD_SID_TO_UID, winbindd_sid_to_uid, "SID_TO_UID" }, { WINBINDD_SID_TO_GID, winbindd_sid_to_gid, "SID_TO_GID" }, - { WINBINDD_GID_TO_SID, winbindd_gid_to_sid, "GID_TO_SID" }, { WINBINDD_UID_TO_SID, winbindd_uid_to_sid, "UID_TO_SID" }, + { WINBINDD_GID_TO_SID, winbindd_gid_to_sid, "GID_TO_SID" }, { WINBINDD_ALLOCATE_RID, winbindd_allocate_rid, "ALLOCATE_RID" }, + { WINBINDD_ALLOCATE_RID_AND_GID, winbindd_allocate_rid_and_gid, + "ALLOCATE_RID_AND_GID" }, /* Miscellaneous */ { WINBINDD_CHECK_MACHACC, winbindd_check_machine_acct, "CHECK_MACHACC" }, { WINBINDD_PING, winbindd_ping, "PING" }, { WINBINDD_INFO, winbindd_info, "INFO" }, - { WINBINDD_INTERFACE_VERSION, winbindd_interface_version, "INTERFACE_VERSION" }, + { WINBINDD_INTERFACE_VERSION, winbindd_interface_version, + "INTERFACE_VERSION" }, { WINBINDD_DOMAIN_NAME, winbindd_domain_name, "DOMAIN_NAME" }, { WINBINDD_DOMAIN_INFO, winbindd_domain_info, "DOMAIN_INFO" }, { WINBINDD_NETBIOS_NAME, winbindd_netbios_name, "NETBIOS_NAME" }, - { WINBINDD_PRIV_PIPE_DIR, winbindd_priv_pipe_dir, "WINBINDD_PRIV_PIPE_DIR" }, + { WINBINDD_PRIV_PIPE_DIR, winbindd_priv_pipe_dir, + "WINBINDD_PRIV_PIPE_DIR" }, + { WINBINDD_GETDCNAME, winbindd_getdcname, "GETDCNAME" }, /* WINS functions */ @@ -293,7 +304,7 @@ static struct dispatch_table dispatch_table[] = { static void process_request(struct winbindd_cli_state *state) { - struct dispatch_table *table = dispatch_table; + struct winbindd_dispatch_table *table = dispatch_table; /* Free response data - we may be interrupted and receive another command before being able to send this data off. */ @@ -302,26 +313,255 @@ static void process_request(struct winbindd_cli_state *state) ZERO_STRUCT(state->response); - state->response.result = WINBINDD_ERROR; + state->response.result = WINBINDD_PENDING; state->response.length = sizeof(struct winbindd_response); + state->mem_ctx = talloc_init("winbind request"); + if (state->mem_ctx == NULL) + return; + /* Process command */ for (table = dispatch_table; table->fn; table++) { if (state->request.cmd == table->cmd) { - DEBUG(10,("process_request: request fn %s\n", table->winbindd_cmd_name )); + DEBUG(10,("process_request: request fn %s\n", + table->winbindd_cmd_name )); state->response.result = table->fn(state); break; } } - if (!table->fn) - DEBUG(10,("process_request: unknown request fn number %d\n", (int)state->request.cmd )); + if (!table->fn) { + DEBUG(10,("process_request: unknown request fn number %d\n", + (int)state->request.cmd )); + state->response.result = WINBINDD_ERROR; + } +} + +/* + * A list of file descriptors being monitored by select in the main processing + * loop. fd_event->handler is called whenever the socket is readable/writable. + */ + +static struct fd_event *fd_events = NULL; + +void add_fd_event(struct fd_event *ev) +{ + struct fd_event *match; + + /* only add unique fd_event structs */ + + for (match=fd_events; match; match=match->next ) { +#ifdef DEVELOPER + SMB_ASSERT( match != ev ); +#else + if ( match == ev ) + return; +#endif + } + + DLIST_ADD(fd_events, ev); +} + +void remove_fd_event(struct fd_event *ev) +{ + DLIST_REMOVE(fd_events, ev); +} + +/* + * Handler for fd_events to complete a read/write request, set up by + * setup_async_read/setup_async_write. + */ + +static void rw_callback(struct fd_event *event, int flags) +{ + size_t todo; + ssize_t done = 0; + + todo = event->length - event->done; + + if (event->flags & EVENT_FD_WRITE) { + SMB_ASSERT(flags == EVENT_FD_WRITE); + done = sys_write(event->fd, + &((char *)event->data)[event->done], + todo); + + if (done <= 0) { + event->flags = 0; + event->finished(event->private, False); + return; + } + } + + if (event->flags & EVENT_FD_READ) { + SMB_ASSERT(flags == EVENT_FD_READ); + done = sys_read(event->fd, &((char *)event->data)[event->done], + todo); - /* In case extra data pointer is NULL */ + if (done <= 0) { + event->flags = 0; + event->finished(event->private, False); + return; + } + } + + event->done += done; + + if (event->done == event->length) { + event->flags = 0; + event->finished(event->private, True); + } +} + +/* + * Request an async read/write on a fd_event structure. (*finished) is called + * when the request is completed or an error had occurred. + */ + +void setup_async_read(struct fd_event *event, void *data, size_t length, + void (*finished)(void *private, BOOL success), + void *private) +{ + SMB_ASSERT(event->flags == 0); + event->data = data; + event->length = length; + event->done = 0; + event->handler = rw_callback; + event->finished = finished; + event->private = private; + event->flags = EVENT_FD_READ; +} + +void setup_async_write(struct fd_event *event, void *data, size_t length, + void (*finished)(void *private, BOOL success), + void *private) +{ + SMB_ASSERT(event->flags == 0); + event->data = data; + event->length = length; + event->done = 0; + event->handler = rw_callback; + event->finished = finished; + event->private = private; + event->flags = EVENT_FD_WRITE; +} + +/* + * This is the main event loop of winbind requests. It goes through a + * state-machine of 3 read/write requests, 4 if you have extra data to send. + * + * An idle winbind client has a read request of 4 bytes outstanding, + * finalizing function is request_len_recv, checking the length. request_recv + * then processes the packet. The processing function then at some point has + * to call request_finished which schedules sending the response. + */ + +static void request_len_recv(void *private, BOOL success); +static void request_recv(void *private, BOOL success); +void request_finished(struct winbindd_cli_state *state); +void request_finished_cont(void *private, BOOL success); +static void response_main_sent(void *private, BOOL success); +static void response_extra_sent(void *private, BOOL success); + +static void response_extra_sent(void *private, BOOL success) +{ + struct winbindd_cli_state *state = + talloc_get_type_abort(private, struct winbindd_cli_state); + + if (state->mem_ctx != NULL) { + talloc_destroy(state->mem_ctx); + state->mem_ctx = NULL; + } + + if (!success) { + state->finished = True; + return; + } + + SAFE_FREE(state->response.extra_data); + + setup_async_read(&state->fd_event, &state->request, sizeof(uint32), + request_len_recv, state); +} + +static void response_main_sent(void *private, BOOL success) +{ + struct winbindd_cli_state *state = + talloc_get_type_abort(private, struct winbindd_cli_state); + + if (!success) { + state->finished = True; + return; + } + + if (state->response.length == sizeof(state->response)) { + if (state->mem_ctx != NULL) { + talloc_destroy(state->mem_ctx); + state->mem_ctx = NULL; + } + + setup_async_read(&state->fd_event, &state->request, + sizeof(uint32), request_len_recv, state); + return; + } + + setup_async_write(&state->fd_event, state->response.extra_data, + state->response.length - sizeof(state->response), + response_extra_sent, state); +} + +void request_finished(struct winbindd_cli_state *state) +{ + setup_async_write(&state->fd_event, &state->response, + sizeof(state->response), response_main_sent, state); +} + +void request_finished_cont(void *private, BOOL success) +{ + struct winbindd_cli_state *state = + talloc_get_type_abort(private, struct winbindd_cli_state); + + if (!success) + state->response.result = WINBINDD_ERROR; + request_finished(state); +} + +static void request_recv(void *private, BOOL success) +{ + struct winbindd_cli_state *state = + talloc_get_type_abort(private, struct winbindd_cli_state); + + if (!success) { + state->finished = True; + return; + } + + process_request(state); + + if (state->response.result != WINBINDD_PENDING) + request_finished(state); +} + +static void request_len_recv(void *private, BOOL success) +{ + struct winbindd_cli_state *state = + talloc_get_type_abort(private, struct winbindd_cli_state); + + if (!success) { + state->finished = True; + return; + } + + if (*(uint32 *)(&state->request) != sizeof(state->request)) { + DEBUG(0,("process_loop: Invalid request size received: %d\n", + *(uint32 *)(&state->request))); + state->finished = True; + return; + } - if (!state->response.extra_data) - state->response.length = sizeof(struct winbindd_response); + setup_async_read(&state->fd_event, (uint32 *)(&state->request)+1, + sizeof(state->request) - sizeof(uint32), + request_recv, state); } /* Process a new connection by adding it to the client connection list */ @@ -348,16 +588,22 @@ static void new_connection(int listen_sock, BOOL privileged) /* Create new connection structure */ - if ((state = SMB_MALLOC_P(struct winbindd_cli_state)) == NULL) + if ((state = TALLOC_ZERO_P(NULL, struct winbindd_cli_state)) == NULL) return; - ZERO_STRUCTP(state); state->sock = sock; state->last_access = time(NULL); state->privileged = privileged; + state->fd_event.fd = state->sock; + state->fd_event.flags = 0; + add_fd_event(&state->fd_event); + + setup_async_read(&state->fd_event, &state->request, sizeof(uint32), + request_len_recv, state); + /* Add to connection list */ winbindd_add_client(state); @@ -384,11 +630,18 @@ static void remove_client(struct winbindd_cli_state *state) client was killed unexpectedly */ SAFE_FREE(state->response.extra_data); + + if (state->mem_ctx != NULL) { + talloc_destroy(state->mem_ctx); + state->mem_ctx = NULL; + } + + remove_fd_event(&state->fd_event); /* Remove from list and free */ winbindd_remove_client(state); - SAFE_FREE(state); + talloc_free(state); } } @@ -403,7 +656,8 @@ static BOOL remove_idle_client(void) for (state = winbindd_client_list(); state; state = state->next) { if (state->read_buf_len == 0 && state->write_buf_len == 0 && - !state->getpwent_state && !state->getgrent_state) { + state->response.result != WINBINDD_PENDING && + !state->getpwent_state && !state->getgrent_state) { nidle++; if (!last_access || state->last_access < last_access) { last_access = state->last_access; @@ -432,7 +686,7 @@ void winbind_process_packet(struct winbindd_cli_state *state) state->request.null_term = '\0'; state->pid = state->request.pid; - + process_request(state); /* Update client state */ @@ -446,124 +700,6 @@ void winbind_process_packet(struct winbindd_cli_state *state) } } -/* Read some data from a client connection */ - -void winbind_client_read(struct winbindd_cli_state *state) -{ - int n; - - /* Read data */ - - n = sys_read(state->sock, state->read_buf_len + - (char *)&state->request, - sizeof(state->request) - state->read_buf_len); - - DEBUG(10,("client_read: read %d bytes. Need %ld more for a full request.\n", n, (unsigned long)(sizeof(state->request) - n - state->read_buf_len) )); - - /* Read failed, kill client */ - - if (n == -1 || n == 0) { - DEBUG(5,("read failed on sock %d, pid %lu: %s\n", - state->sock, (unsigned long)state->pid, - (n == -1) ? strerror(errno) : "EOF")); - - state->finished = True; - return; - } - - /* Update client state */ - - state->read_buf_len += n; - state->last_access = time(NULL); -} - -/* Write some data to a client connection */ - -static void client_write(struct winbindd_cli_state *state) -{ - char *data; - int num_written; - - /* Write some data */ - /* - * The fancy calculation of data below allows us to handle the - * case where write (sys_write) does not write all the data we - * gave it. In that case, we will come back through here again - * because of the loop above us, and we want to pick up where - * we left off. - */ - - if (!state->write_extra_data) { - - /* Write response structure */ - - data = (char *)&state->response + sizeof(state->response) - - state->write_buf_len; - - } else { - - /* Write extra data */ - - data = (char *)state->response.extra_data + - state->response.length - - sizeof(struct winbindd_response) - - state->write_buf_len; - } - - num_written = sys_write(state->sock, data, state->write_buf_len); - - DEBUG(10,("client_write: wrote %d bytes.\n", num_written )); - - /* Write failed, kill cilent */ - - if (num_written == -1 || num_written == 0) { - - DEBUG(3,("write failed on sock %d, pid %lu: %s\n", - state->sock, (unsigned long)state->pid, - (num_written == -1) ? strerror(errno) : "EOF")); - - state->finished = True; - - SAFE_FREE(state->response.extra_data); - - return; - } - - /* Update client state */ - - state->write_buf_len -= num_written; - state->last_access = time(NULL); - - /* Have we written all data? */ - - if (state->write_buf_len == 0) { - - /* Take care of extra data */ - - if (state->write_extra_data) { - - SAFE_FREE(state->response.extra_data); - - state->write_extra_data = False; - - DEBUG(10,("client_write: client_write: complete response written.\n")); - - } else if (state->response.length > - sizeof(struct winbindd_response)) { - - /* Start writing extra data */ - - state->write_buf_len = - state->response.length - - sizeof(struct winbindd_response); - - DEBUG(10,("client_write: need to write %d extra data bytes.\n", (int)state->write_buf_len)); - - state->write_extra_data = True; - } - } -} - /* Process incoming clients on listen_sock. We use a tricky non-blocking, non-forking, non-threaded model which allows us to handle many simultaneous connections while remaining impervious to many denial of @@ -571,211 +707,182 @@ static void client_write(struct winbindd_cli_state *state) static void process_loop(void) { - /* We'll be doing this a lot */ + struct winbindd_cli_state *state; + struct fd_event *ev; + fd_set r_fds, w_fds; + int maxfd, listen_sock, listen_priv_sock, selret; + struct timeval timeout; - while (1) { - struct winbindd_cli_state *state; - fd_set r_fds, w_fds; - int maxfd, listen_sock, listen_priv_sock, selret; - struct timeval timeout; + /* We'll be doing this a lot */ - again: - /* Handle messages */ + /* Handle messages */ - message_dispatch(); + message_dispatch(); - /* refresh the trusted domain cache */ + /* refresh the trusted domain cache */ - rescan_trusted_domains(); - - /* Free up temporary memory */ + rescan_trusted_domains(); - lp_talloc_free(); - main_loop_talloc_free(); + /* Free up temporary memory */ - /* Initialise fd lists for select() */ + lp_talloc_free(); + main_loop_talloc_free(); - listen_sock = open_winbindd_socket(); - listen_priv_sock = open_winbindd_priv_socket(); - - if (listen_sock == -1 || listen_priv_sock == -1) { - perror("open_winbind_socket"); - exit(1); - } + /* Initialise fd lists for select() */ - maxfd = MAX(listen_sock, listen_priv_sock); + listen_sock = open_winbindd_socket(); + listen_priv_sock = open_winbindd_priv_socket(); - FD_ZERO(&r_fds); - FD_ZERO(&w_fds); - FD_SET(listen_sock, &r_fds); - FD_SET(listen_priv_sock, &r_fds); - - timeout.tv_sec = WINBINDD_ESTABLISH_LOOP; - timeout.tv_usec = 0; - - if (opt_dual_daemon) { - maxfd = dual_select_setup(&w_fds, maxfd); - } - - /* Set up client readers and writers */ + if (listen_sock == -1 || listen_priv_sock == -1) { + perror("open_winbind_socket"); + exit(1); + } - state = winbindd_client_list(); + maxfd = MAX(listen_sock, listen_priv_sock); - while (state) { + FD_ZERO(&r_fds); + FD_ZERO(&w_fds); + FD_SET(listen_sock, &r_fds); + FD_SET(listen_priv_sock, &r_fds); - /* Dispose of client connection if it is marked as - finished */ + timeout.tv_sec = WINBINDD_ESTABLISH_LOOP; + timeout.tv_usec = 0; - if (state->finished) { - struct winbindd_cli_state *next = state->next; + if (opt_dual_daemon) { + maxfd = dual_select_setup(&w_fds, maxfd); + } - remove_client(state); - state = next; - continue; - } + /* Set up client readers and writers */ - /* Select requires we know the highest fd used */ + state = winbindd_client_list(); - if (state->sock > maxfd) - maxfd = state->sock; + while (state) { - /* Add fd for reading */ + struct winbindd_cli_state *next = state->next; - if (state->read_buf_len != sizeof(state->request)) - FD_SET(state->sock, &r_fds); + /* Dispose of client connection if it is marked as + finished */ - /* Add fd for writing */ + if (state->finished) + remove_client(state); - if (state->write_buf_len) - FD_SET(state->sock, &w_fds); + state = next; + } - state = state->next; + for (ev = fd_events; ev; ev = ev->next) { + if (ev->flags & EVENT_FD_READ) { + FD_SET(ev->fd, &r_fds); + maxfd = MAX(ev->fd, maxfd); + } + if (ev->flags & EVENT_FD_WRITE) { + FD_SET(ev->fd, &w_fds); + maxfd = MAX(ev->fd, maxfd); } + } - /* Call select */ + /* Call select */ - selret = sys_select(maxfd + 1, &r_fds, &w_fds, NULL, &timeout); + selret = sys_select(maxfd + 1, &r_fds, &w_fds, NULL, &timeout); - if (selret == 0) - continue; + if (selret == 0) + goto no_fds_ready; - 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 listen_sock readable */ + /* Create a new connection if listen_sock readable */ - if (selret > 0) { + if (opt_dual_daemon) { + dual_select(&w_fds); + } - if (opt_dual_daemon) { - dual_select(&w_fds); - } + ev = fd_events; + while (ev != NULL) { + struct fd_event *next = ev->next; + int flags = 0; + if (FD_ISSET(ev->fd, &r_fds)) + flags |= EVENT_FD_READ; + if (FD_ISSET(ev->fd, &w_fds)) + flags |= EVENT_FD_WRITE; + if (flags) + ev->handler(ev, flags); + ev = next; + } - if (FD_ISSET(listen_sock, &r_fds)) { - while (winbindd_num_clients() > WINBINDD_MAX_SIMULTANEOUS_CLIENTS - 1) { - DEBUG(5,("winbindd: Exceeding %d client connections, removing idle connection.\n", - WINBINDD_MAX_SIMULTANEOUS_CLIENTS)); - if (!remove_idle_client()) { - DEBUG(0,("winbindd: Exceeding %d client connections, no idle connection found\n", - WINBINDD_MAX_SIMULTANEOUS_CLIENTS)); - break; - } - } - /* new, non-privileged connection */ - new_connection(listen_sock, False); + if (FD_ISSET(listen_sock, &r_fds)) { + while (winbindd_num_clients() > + WINBINDD_MAX_SIMULTANEOUS_CLIENTS - 1) { + DEBUG(5,("winbindd: Exceeding %d client " + "connections, removing idle " + "connection.\n", + WINBINDD_MAX_SIMULTANEOUS_CLIENTS)); + if (!remove_idle_client()) { + DEBUG(0,("winbindd: Exceeding %d " + "client connections, no idle " + "connection found\n", + WINBINDD_MAX_SIMULTANEOUS_CLIENTS)); + break; } + } + /* new, non-privileged connection */ + new_connection(listen_sock, False); + } - if (FD_ISSET(listen_priv_sock, &r_fds)) { - while (winbindd_num_clients() > WINBINDD_MAX_SIMULTANEOUS_CLIENTS - 1) { - DEBUG(5,("winbindd: Exceeding %d client connections, removing idle connection.\n", - WINBINDD_MAX_SIMULTANEOUS_CLIENTS)); - if (!remove_idle_client()) { - DEBUG(0,("winbindd: Exceeding %d client connections, no idle connection found\n", - WINBINDD_MAX_SIMULTANEOUS_CLIENTS)); - break; - } - } - /* new, privileged connection */ - new_connection(listen_priv_sock, True); - } - - /* Process activity on client connections */ - - for (state = winbindd_client_list(); state; - state = state->next) { - - /* Data available for writing */ - - if (FD_ISSET(state->sock, &w_fds)) - client_write(state); - } - - for (state = winbindd_client_list(); state; - state = state->next) { - - /* Data available for reading */ - - if (FD_ISSET(state->sock, &r_fds)) { - - /* Read data */ - - winbind_client_read(state); - - /* - * If we have the start of a - * packet, then check the - * length field to make sure - * the client's not talking - * Mock Swedish. - */ - - if (state->read_buf_len >= sizeof(uint32) - && *(uint32 *) &state->request != sizeof(state->request)) { - DEBUG(0,("process_loop: Invalid request size from pid %lu: %d bytes sent, should be %ld\n", - (unsigned long)state->request.pid, *(uint32 *) &state->request, (unsigned long)sizeof(state->request))); - DEBUGADD(0, ("This usually means that you are running old wbinfo, pam_winbind or libnss_winbind clients\n")); - - remove_client(state); - break; - } - - /* A request packet might be - complete */ - - if (state->read_buf_len == - sizeof(state->request)) { - winbind_process_packet(state); - winbindd_demote_client(state); - goto again; - } - } + if (FD_ISSET(listen_priv_sock, &r_fds)) { + while (winbindd_num_clients() > + WINBINDD_MAX_SIMULTANEOUS_CLIENTS - 1) { + DEBUG(5,("winbindd: Exceeding %d client " + "connections, removing idle " + "connection.\n", + WINBINDD_MAX_SIMULTANEOUS_CLIENTS)); + if (!remove_idle_client()) { + DEBUG(0,("winbindd: Exceeding %d " + "client connections, no idle " + "connection found\n", + WINBINDD_MAX_SIMULTANEOUS_CLIENTS)); + break; } } + /* new, privileged connection */ + new_connection(listen_priv_sock, True); + } + + no_fds_ready: #if 0 - winbindd_check_cache_size(time(NULL)); + winbindd_check_cache_size(time(NULL)); #endif - /* Check signal handling things */ + /* Check signal handling things */ - if (do_sigterm) - terminate(); + if (do_sigterm) + terminate(); - if (do_sighup) { + if (do_sighup) { - DEBUG(3, ("got SIGHUP\n")); + DEBUG(3, ("got SIGHUP\n")); - msg_reload_services(MSG_SMB_CONF_UPDATED, (pid_t) 0, NULL, 0); - do_sighup = False; - } + msg_reload_services(MSG_SMB_CONF_UPDATED, (pid_t) 0, NULL, 0); + do_sighup = False; + } + + if (do_sigusr2) { + print_winbindd_status(); + do_sigusr2 = False; + } - if (do_sigusr2) { - print_winbindd_status(); - do_sigusr2 = False; + if (do_sigchld) { + pid_t pid; + + do_sigchld = False; + + while ((pid = sys_waitpid(-1, NULL, WNOHANG)) > 0) { + winbind_child_died(pid); } } } @@ -787,7 +894,6 @@ struct winbindd_state server_state; /* Server state information */ int main(int argc, char **argv) { pstring logfile; - static BOOL interactive = False; static BOOL Fork = True; static BOOL log_stdout = False; struct poptOption long_options[] = { @@ -886,7 +992,7 @@ int main(int argc, char **argv) if ( (!winbindd_param_init()) || (!winbindd_upgrade_idmap()) || (!idmap_init(lp_idmap_backend())) ) { DEBUG(1, ("Could not init idmap -- netlogon proxy only\n")); - idmap_proxyonly(); + idmap_set_proxyonly(); } /* Unblock all signals we are interested in as they may have been @@ -948,9 +1054,12 @@ int main(int argc, char **argv) init_domain_list(); + init_idmap_child(); + /* Loop waiting for requests */ - process_loop(); + while (1) + process_loop(); trustdom_cache_shutdown(); diff --git a/source3/nsswitch/winbindd.h b/source3/nsswitch/winbindd.h index aed0c00c9d..c8d2ebf686 100644 --- a/source3/nsswitch/winbindd.h +++ b/source3/nsswitch/winbindd.h @@ -32,11 +32,25 @@ #undef DBGC_CLASS #define DBGC_CLASS DBGC_WINBIND -/* Client state structure */ +/* bits for fd_event.flags */ +#define EVENT_FD_READ 1 +#define EVENT_FD_WRITE 2 + +struct fd_event { + struct fd_event *next, *prev; + int fd; + int flags; /* see EVENT_FD_* flags */ + void (*handler)(struct fd_event *fde, int flags); + void *data; + size_t length, done; + void (*finished)(void *private, BOOL success); + void *private; +}; struct winbindd_cli_state { struct winbindd_cli_state *prev, *next; /* Linked list pointers */ int sock; /* Open socket from client */ + struct fd_event fd_event; pid_t pid; /* pid of client */ int read_buf_len, write_buf_len; /* Indexes in request/response */ BOOL finished; /* Can delete from list */ @@ -44,10 +58,13 @@ struct winbindd_cli_state { time_t last_access; /* Time of last access (read or write) */ BOOL privileged; /* Is the client 'privileged' */ + TALLOC_CTX *mem_ctx; /* memory per request */ struct winbindd_request request; /* Request from client */ struct winbindd_response response; /* Respose to client */ - BOOL getpwent_initialized; /* Has getpwent_state been initialized? */ - BOOL getgrent_initialized; /* Has getgrent_state been initialized? */ + BOOL getpwent_initialized; /* Has getpwent_state been + * initialized? */ + BOOL getgrent_initialized; /* Has getgrent_state been + * initialized? */ struct getent_state *getpwent_state; /* State for getpwent() */ struct getent_state *getgrent_state; /* State for getgrent() */ }; @@ -81,15 +98,56 @@ struct winbindd_state { gid_t gid_low, gid_high; /* Range of gids to allocate */ }; +struct winbindd_dispatch_table { + enum winbindd_cmd cmd; + enum winbindd_result (*fn)(struct winbindd_cli_state *state); + const char *winbindd_cmd_name; +}; + extern struct winbindd_state server_state; /* Server information */ typedef struct { char *acct_name; char *full_name; - DOM_SID *user_sid; /* NT user and primary group SIDs */ - DOM_SID *group_sid; + DOM_SID user_sid; /* NT user and primary group SIDs */ + DOM_SID group_sid; } WINBIND_USERINFO; +/* Our connection to the DC */ + +struct winbindd_cm_conn { + struct cli_state *cli; + + struct rpc_pipe_client *samr_pipe; + POLICY_HND sam_connect_handle, sam_domain_handle; + + struct rpc_pipe_client *lsa_pipe; + POLICY_HND lsa_policy; + + /* Auth2 pipe is the pipe used to setup the netlogon schannel key + * using rpccli_net_auth2. It needs to be kept open. */ + + struct rpc_pipe_client *netlogon_auth2_pipe; + unsigned char sess_key[16]; /* Current session key. */ + DOM_CRED clnt_cred; /* Client NETLOGON credential. */ + struct rpc_pipe_client *netlogon_pipe; +}; + +struct winbindd_async_request; + +/* Async child */ + +struct winbindd_child { + struct winbindd_child *next, *prev; + + pid_t pid; + struct winbindd_domain *domain; + pstring logfilename; + + struct fd_event event; + struct winbindd_async_request *requests; +}; + /* Structures to hold per domain information */ struct winbindd_domain { @@ -123,6 +181,14 @@ struct winbindd_domain { uint32 sequence_number; NTSTATUS last_status; + /* The smb connection */ + + struct winbindd_cm_conn conn; + + /* The child pid we're talking to */ + + struct winbindd_child child; + /* Linked list info */ struct winbindd_domain *prev, *next; @@ -181,13 +247,14 @@ struct winbindd_methods { NTSTATUS (*lookup_usergroups)(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, const DOM_SID *user_sid, - uint32 *num_groups, DOM_SID ***user_gids); + uint32 *num_groups, DOM_SID **user_gids); /* Lookup all aliases that the sids delivered are member of. This is * to implement 'domain local groups' correctly */ NTSTATUS (*lookup_useraliases)(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, - uint32 num_sids, DOM_SID **sids, + uint32 num_sids, + const DOM_SID *sids, uint32 *num_aliases, uint32 **alias_rids); @@ -196,7 +263,7 @@ struct winbindd_methods { TALLOC_CTX *mem_ctx, const DOM_SID *group_sid, uint32 *num_names, - DOM_SID ***sid_mem, char ***names, + DOM_SID **sid_mem, char ***names, uint32 **name_types); /* return the current global sequence number */ diff --git a/source3/nsswitch/winbindd_ads.c b/source3/nsswitch/winbindd_ads.c index 5f23e755d4..0f4dee4f4a 100644 --- a/source3/nsswitch/winbindd_ads.c +++ b/source3/nsswitch/winbindd_ads.c @@ -27,8 +27,6 @@ #ifdef HAVE_ADS -extern struct winbindd_methods msrpc_methods, cache_methods; - #undef DBGC_CLASS #define DBGC_CLASS DBGC_WINBIND @@ -78,6 +76,7 @@ static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain) status = ads_connect(ads); if (!ADS_ERR_OK(status) || !ads->config.realm) { + extern struct winbindd_methods msrpc_methods, cache_methods; DEBUG(1,("ads_connect for domain %s failed: %s\n", domain->name, ads_errstr(status))); ads_destroy(&ads); @@ -157,9 +156,6 @@ static NTSTATUS query_user_list(struct winbindd_domain *domain, for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) { char *name, *gecos; - DOM_SID sid; - DOM_SID *sid2; - DOM_SID *group_sid; uint32 group; uint32 atype; @@ -171,7 +167,8 @@ static NTSTATUS query_user_list(struct winbindd_domain *domain, name = ads_pull_username(ads, mem_ctx, msg); gecos = ads_pull_string(ads, mem_ctx, msg, "name"); - if (!ads_pull_sid(ads, msg, "objectSid", &sid)) { + if (!ads_pull_sid(ads, msg, "objectSid", + &(*info)[i].user_sid)) { DEBUG(1,("No sid for %s !?\n", name)); continue; } @@ -180,20 +177,9 @@ static NTSTATUS query_user_list(struct winbindd_domain *domain, continue; } - sid2 = TALLOC_P(mem_ctx, DOM_SID); - if (!sid2) { - status = NT_STATUS_NO_MEMORY; - goto done; - } - - sid_copy(sid2, &sid); - - group_sid = rid_to_talloced_sid(domain, mem_ctx, group); - (*info)[i].acct_name = name; (*info)[i].full_name = gecos; - (*info)[i].user_sid = sid2; - (*info)[i].group_sid = group_sid; + sid_compose(&(*info)[i].group_sid, &domain->sid, group); i++; } @@ -386,8 +372,6 @@ static NTSTATUS query_user(struct winbindd_domain *domain, char *sidstr; uint32 group_rid; NTSTATUS status = NT_STATUS_UNSUCCESSFUL; - DOM_SID *sid2; - fstring sid_string; DEBUG(3,("ads: query_user\n")); @@ -404,13 +388,15 @@ static NTSTATUS query_user(struct winbindd_domain *domain, free(ldap_exp); free(sidstr); if (!ADS_ERR_OK(rc) || !msg) { - DEBUG(1,("query_user(sid=%s) ads_search: %s\n", sid_to_string(sid_string, sid), ads_errstr(rc))); + DEBUG(1,("query_user(sid=%s) ads_search: %s\n", + sid_string_static(sid), ads_errstr(rc))); goto done; } count = ads_count_replies(ads, msg); if (count != 1) { - DEBUG(1,("query_user(sid=%s): Not found\n", sid_to_string(sid_string, sid))); + DEBUG(1,("query_user(sid=%s): Not found\n", + sid_string_static(sid))); goto done; } @@ -418,20 +404,13 @@ static NTSTATUS query_user(struct winbindd_domain *domain, info->full_name = ads_pull_string(ads, mem_ctx, msg, "name"); if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group_rid)) { - DEBUG(1,("No primary group for %s !?\n", sid_to_string(sid_string, sid))); - goto done; - } - - sid2 = TALLOC_P(mem_ctx, DOM_SID); - if (!sid2) { - status = NT_STATUS_NO_MEMORY; + DEBUG(1,("No primary group for %s !?\n", + sid_string_static(sid))); goto done; } - sid_copy(sid2, sid); - - info->user_sid = sid2; - info->group_sid = rid_to_talloced_sid(domain, mem_ctx, group_rid); + sid_copy(&info->user_sid, sid); + sid_compose(&info->group_sid, &domain->sid, group_rid); status = NT_STATUS_OK; @@ -449,7 +428,7 @@ static NTSTATUS lookup_usergroups_alt(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, const char *user_dn, DOM_SID *primary_group, - uint32 *num_groups, DOM_SID ***user_gids) + uint32 *num_groups, DOM_SID **user_sids) { ADS_STATUS rc; NTSTATUS status = NT_STATUS_UNSUCCESSFUL; @@ -502,34 +481,24 @@ static NTSTATUS lookup_usergroups_alt(struct winbindd_domain *domain, goto done; } - (*user_gids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID *, count + 1); - (*user_gids)[0] = primary_group; - - *num_groups = 1; - - for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) { + *user_sids = NULL; + *num_groups = 0; + + add_sid_to_array(mem_ctx, primary_group, user_sids, num_groups); + + for (msg = ads_first_entry(ads, res); msg; + msg = ads_next_entry(ads, msg)) { DOM_SID group_sid; if (!ads_pull_sid(ads, msg, "objectSid", &group_sid)) { DEBUG(1,("No sid for this group ?!?\n")); continue; } - - if (sid_equal(&group_sid, primary_group)) continue; - - (*user_gids)[*num_groups] = TALLOC_P(mem_ctx, DOM_SID); - if (!(*user_gids)[*num_groups]) { - status = NT_STATUS_NO_MEMORY; - goto done; - } - sid_copy((*user_gids)[*num_groups], &group_sid); - - (*num_groups)++; - + add_sid_to_array(mem_ctx, &group_sid, user_sids, num_groups); } - status = NT_STATUS_OK; + status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY; DEBUG(3,("ads lookup_usergroups (alt) for dn=%s\n", user_dn)); done: @@ -543,7 +512,7 @@ done: static NTSTATUS lookup_usergroups(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, const DOM_SID *sid, - uint32 *num_groups, DOM_SID ***user_gids) + uint32 *num_groups, DOM_SID **user_sids) { ADS_STRUCT *ads = NULL; const char *attrs[] = {"tokenGroups", "primaryGroupID", NULL}; @@ -553,7 +522,7 @@ static NTSTATUS lookup_usergroups(struct winbindd_domain *domain, char *user_dn; DOM_SID *sids; int i; - DOM_SID *primary_group; + DOM_SID primary_group; uint32 primary_group_rid; fstring sid_string; NTSTATUS status = NT_STATUS_UNSUCCESSFUL; @@ -596,7 +565,8 @@ static NTSTATUS lookup_usergroups(struct winbindd_domain *domain, goto done; } - primary_group = rid_to_talloced_sid(domain, mem_ctx, primary_group_rid); + sid_copy(&primary_group, &domain->sid); + sid_append_rid(&primary_group, primary_group_rid); count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids); @@ -607,30 +577,23 @@ static NTSTATUS lookup_usergroups(struct winbindd_domain *domain, unless we are talking to a buggy Win2k server */ if (count == 0) { return lookup_usergroups_alt(domain, mem_ctx, user_dn, - primary_group, - num_groups, user_gids); + &primary_group, + num_groups, user_sids); } - (*user_gids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID *, count + 1); - (*user_gids)[0] = primary_group; - - *num_groups = 1; + *user_sids = NULL; + *num_groups = 0; + + add_sid_to_array(mem_ctx, &primary_group, user_sids, num_groups); - for (i=0;i<count;i++) { - if (sid_equal(&sids[i], primary_group)) continue; - - (*user_gids)[*num_groups] = TALLOC_P(mem_ctx, DOM_SID); - if (!(*user_gids)[*num_groups]) { - status = NT_STATUS_NO_MEMORY; - goto done; - } + for (i=0;i<count;i++) + add_sid_to_array_unique(mem_ctx, &sids[i], + user_sids, num_groups); - sid_copy((*user_gids)[*num_groups], &sids[i]); - (*num_groups)++; - } + status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY; - status = NT_STATUS_OK; - DEBUG(3,("ads lookup_usergroups for sid=%s\n", sid_to_string(sid_string, sid))); + DEBUG(3,("ads lookup_usergroups for sid=%s\n", + sid_to_string(sid_string, sid))); done: return status; } @@ -641,7 +604,7 @@ done: static NTSTATUS lookup_groupmem(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, const DOM_SID *group_sid, uint32 *num_names, - DOM_SID ***sid_mem, char ***names, + DOM_SID **sid_mem, char ***names, uint32 **name_types) { ADS_STATUS rc; @@ -652,8 +615,7 @@ static NTSTATUS lookup_groupmem(struct winbindd_domain *domain, NTSTATUS status = NT_STATUS_UNSUCCESSFUL; char *sidstr; char **members; - int i; - size_t num_members; + int i, num_members; fstring sid_string; BOOL more_values; const char **attrs; @@ -753,7 +715,7 @@ static NTSTATUS lookup_groupmem(struct winbindd_domain *domain, the problem is that the members are in the form of distinguised names */ - (*sid_mem) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID *, num_members); + (*sid_mem) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_members); (*name_types) = TALLOC_ZERO_ARRAY(mem_ctx, uint32, num_members); (*names) = TALLOC_ZERO_ARRAY(mem_ctx, char *, num_members); @@ -765,12 +727,7 @@ static NTSTATUS lookup_groupmem(struct winbindd_domain *domain, if (dn_lookup(ads, mem_ctx, members[i], &name, &name_type, &sid)) { (*names)[*num_names] = name; (*name_types)[*num_names] = name_type; - (*sid_mem)[*num_names] = TALLOC_P(mem_ctx, DOM_SID); - if (!(*sid_mem)[*num_names]) { - status = NT_STATUS_NO_MEMORY; - goto done; - } - sid_copy((*sid_mem)[*num_names], &sid); + sid_copy(&(*sid_mem)[*num_names], &sid); (*num_names)++; } } @@ -827,9 +784,9 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain, struct ds_domain_trust *domains = NULL; int count = 0; int i; - struct cli_state *cli = NULL; /* i think we only need our forest and downlevel trusted domains */ uint32 flags = DS_DOMAIN_IN_FOREST | DS_DOMAIN_DIRECT_OUTBOUND; + struct rpc_pipe_client *cli; DEBUG(3,("ads: trusted_domains\n")); @@ -837,16 +794,27 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain, *alt_names = NULL; *names = NULL; *dom_sids = NULL; - - if ( !NT_STATUS_IS_OK(result = cm_fresh_connection(domain, PI_NETLOGON, &cli)) ) { - DEBUG(5, ("trusted_domains: Could not open a connection to %s for PIPE_NETLOGON (%s)\n", + + { + unsigned char *session_key; + DOM_CRED *creds; + + result = cm_connect_netlogon(domain, mem_ctx, &cli, + &session_key, &creds); + } + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(5, ("trusted_domains: Could not open a connection to %s " + "for PIPE_NETLOGON (%s)\n", domain->name, nt_errstr(result))); return NT_STATUS_UNSUCCESSFUL; } if ( NT_STATUS_IS_OK(result) ) - result = cli_ds_enum_domain_trusts( cli, mem_ctx, cli->desthost, - flags, &domains, (unsigned int *)&count ); + result = rpccli_ds_enum_domain_trusts(cli, mem_ctx, + cli->cli->desthost, + flags, &domains, + (unsigned int *)&count); if ( NT_STATUS_IS_OK(result) && count) { @@ -854,20 +822,17 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain, if ( !(*names = TALLOC_ARRAY(mem_ctx, char *, count)) ) { DEBUG(0, ("trusted_domains: out of memory\n")); - result = NT_STATUS_NO_MEMORY; - goto done; + return NT_STATUS_NO_MEMORY; } if ( !(*alt_names = TALLOC_ARRAY(mem_ctx, char *, count)) ) { DEBUG(0, ("trusted_domains: out of memory\n")); - result = NT_STATUS_NO_MEMORY; - goto done; + return NT_STATUS_NO_MEMORY; } if ( !(*dom_sids = TALLOC_ARRAY(mem_ctx, DOM_SID, count)) ) { DEBUG(0, ("trusted_domains: out of memory\n")); - result = NT_STATUS_NO_MEMORY; - goto done; + return NT_STATUS_NO_MEMORY; } /* Copy across names and sids */ @@ -882,13 +847,6 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain, *num_domains = count; } -done: - - /* remove connection; This is a special case to the \NETLOGON pipe */ - - if ( cli ) - cli_shutdown( cli ); - return result; } diff --git a/source3/nsswitch/winbindd_async.c b/source3/nsswitch/winbindd_async.c new file mode 100644 index 0000000000..9ac2acafd0 --- /dev/null +++ b/source3/nsswitch/winbindd_async.c @@ -0,0 +1,1403 @@ +/* + Unix SMB/CIFS implementation. + + Async helpers for blocking functions + + Copyright (C) Volker Lendecke 2005 + + The helpers always consist of three functions: + + * A request setup function that takes the necessary parameters together + with a continuation function that is to be called upon completion + + * A private continuation function that is internal only. This is to be + called by the lower-level functions in do_async(). Its only task is to + properly call the continuation function named above. + + * A worker function that is called inside the appropriate child process. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "winbindd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +struct do_async_state { + TALLOC_CTX *mem_ctx; + struct winbindd_request request; + struct winbindd_response response; + void (*cont)(TALLOC_CTX *mem_ctx, + BOOL success, + struct winbindd_response *response, + void *c, void *private); + void *c, *private; +}; + +static void do_async_recv(void *private, BOOL success) +{ + struct do_async_state *state = + talloc_get_type_abort(private, struct do_async_state); + + state->cont(state->mem_ctx, success, &state->response, + state->c, state->private); +} + +static void do_async(TALLOC_CTX *mem_ctx, struct winbindd_child *child, + const struct winbindd_request *request, + void (*cont)(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private), + void *c, void *private) +{ + struct do_async_state *state; + + state = TALLOC_P(mem_ctx, struct do_async_state); + if (state == NULL) { + DEBUG(0, ("talloc failed\n")); + cont(mem_ctx, False, NULL, c, private); + return; + } + + state->mem_ctx = mem_ctx; + state->request = *request; + state->request.length = sizeof(state->request); + state->cont = cont; + state->c = c; + state->private = private; + + async_request(mem_ctx, child, &state->request, + &state->response, do_async_recv, state); +} + +static void do_async_domain(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain, + const struct winbindd_request *request, + void (*cont)(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private), + void *c, void *private) +{ + struct do_async_state *state; + + state = TALLOC_P(mem_ctx, struct do_async_state); + if (state == NULL) { + DEBUG(0, ("talloc failed\n")); + cont(mem_ctx, False, NULL, c, private); + return; + } + + state->mem_ctx = mem_ctx; + state->request = *request; + state->request.length = sizeof(state->request); + state->cont = cont; + state->c = c; + state->private = private; + + async_domain_request(mem_ctx, domain, &state->request, + &state->response, do_async_recv, state); +} + +static void idmap_set_mapping_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private) +{ + void (*cont)(void *priv, BOOL succ) = c; + + if (!success) { + DEBUG(5, ("Could not trigger idmap_set_mapping\n")); + cont(private, False); + return; + } + + if (response->result != WINBINDD_OK) { + DEBUG(5, ("idmap_set_mapping returned an error\n")); + cont(private, False); + return; + } + + cont(private, True); +} + +void idmap_set_mapping_async(TALLOC_CTX *mem_ctx, const DOM_SID *sid, + unid_t id, int id_type, + void (*cont)(void *private, BOOL success), + void *private) +{ + struct winbindd_request request; + ZERO_STRUCT(request); + request.cmd = WINBINDD_DUAL_IDMAPSET; + if (id_type == ID_USERID) + request.data.dual_idmapset.uid = id.uid; + else + request.data.dual_idmapset.gid = id.gid; + request.data.dual_idmapset.type = id_type; + sid_to_string(request.data.dual_idmapset.sid, sid); + + do_async(mem_ctx, idmap_child(), &request, idmap_set_mapping_recv, + cont, private); +} + +enum winbindd_result winbindd_dual_idmapset(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + DOM_SID sid; + unid_t id; + NTSTATUS result; + + DEBUG(3, ("[%5lu]: dual_idmapset\n", (unsigned long)state->pid)); + + if (!string_to_sid(&sid, state->request.data.dual_idmapset.sid)) + return WINBINDD_ERROR; + + if (state->request.data.dual_idmapset.type == ID_USERID) + id.uid = state->request.data.dual_idmapset.uid; + else + id.gid = state->request.data.dual_idmapset.gid; + + result = idmap_set_mapping(&sid, id, + state->request.data.dual_idmapset.type); + return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; +} + +static void idmap_sid2uid_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private); + +void idmap_sid2uid_async(TALLOC_CTX *mem_ctx, const DOM_SID *sid, BOOL alloc, + void (*cont)(void *private, BOOL success, uid_t uid), + void *private) +{ + struct winbindd_request request; + ZERO_STRUCT(request); + request.cmd = WINBINDD_DUAL_SID2UID; + sid_to_string(request.data.dual_sid2id.sid, sid); + request.data.dual_sid2id.alloc = alloc; + do_async(mem_ctx, idmap_child(), &request, idmap_sid2uid_recv, + cont, private); +} + +enum winbindd_result winbindd_dual_sid2uid(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + DOM_SID sid; + NTSTATUS result; + + DEBUG(3, ("[%5lu]: sid to uid %s\n", (unsigned long)state->pid, + state->request.data.dual_sid2id.sid)); + + if (!string_to_sid(&sid, state->request.data.dual_sid2id.sid)) { + DEBUG(1, ("Could not get convert sid %s from string\n", + state->request.data.dual_sid2id.sid)); + return WINBINDD_ERROR; + } + + /* Find uid for this sid and return it, possibly ask the slow remote + * idmap */ + + result = idmap_sid_to_uid(&sid, &(state->response.data.uid), + state->request.data.dual_sid2id.alloc ? + 0 : ID_QUERY_ONLY); + + return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; +} + +static void idmap_sid2uid_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private) +{ + void (*cont)(void *priv, BOOL succ, uid_t uid) = c; + + if (!success) { + DEBUG(5, ("Could not trigger sid2uid\n")); + cont(private, False, 0); + return; + } + + if (response->result != WINBINDD_OK) { + DEBUG(5, ("sid2uid returned an error\n")); + cont(private, False, 0); + return; + } + + cont(private, True, response->data.uid); +} + +static void uid2name_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private); + +void winbindd_uid2name_async(TALLOC_CTX *mem_ctx, uid_t uid, + void (*cont)(void *private, BOOL success, + const char *name), + void *private) +{ + struct winbindd_request request; + ZERO_STRUCT(request); + request.cmd = WINBINDD_DUAL_UID2NAME; + request.data.uid = uid; + do_async(mem_ctx, idmap_child(), &request, uid2name_recv, + cont, private); +} + +enum winbindd_result winbindd_dual_uid2name(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + struct passwd *pw; + + DEBUG(3, ("[%5lu]: uid2name %lu\n", (unsigned long)state->pid, + (unsigned long)state->request.data.uid)); + + pw = getpwuid(state->request.data.uid); + if (pw == NULL) { + DEBUG(5, ("User %lu not found\n", + (unsigned long)state->request.data.uid)); + return WINBINDD_ERROR; + } + + fstrcpy(state->response.data.name.name, pw->pw_name); + return WINBINDD_OK; +} + +static void uid2name_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private) +{ + void (*cont)(void *priv, BOOL succ, const char *name) = c; + + if (!success) { + DEBUG(5, ("Could not trigger uid2name\n")); + cont(private, False, NULL); + return; + } + + if (response->result != WINBINDD_OK) { + DEBUG(5, ("uid2name returned an error\n")); + cont(private, False, NULL); + return; + } + + cont(private, True, response->data.name.name); +} + +static void name2uid_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private); + +static void winbindd_name2uid_async(TALLOC_CTX *mem_ctx, const char *name, + void (*cont)(void *private, BOOL success, + uid_t uid), + void *private) +{ + struct winbindd_request request; + ZERO_STRUCT(request); + request.cmd = WINBINDD_DUAL_NAME2UID; + fstrcpy(request.data.username, name); + do_async(mem_ctx, idmap_child(), &request, name2uid_recv, + cont, private); +} + +enum winbindd_result winbindd_dual_name2uid(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + struct passwd *pw; + + /* Ensure null termination */ + state->request.data.username + [sizeof(state->request.data.username)-1] = '\0'; + + DEBUG(3, ("[%5lu]: name2uid %s\n", (unsigned long)state->pid, + state->request.data.username)); + + pw = getpwnam(state->request.data.username); + if (pw == NULL) { + return WINBINDD_ERROR; + } + + state->response.data.uid = pw->pw_uid; + return WINBINDD_OK; +} + +static void name2uid_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private) +{ + void (*cont)(void *priv, BOOL succ, uid_t uid) = c; + + if (!success) { + DEBUG(5, ("Could not trigger name2uid\n")); + cont(private, False, 0); + return; + } + + if (response->result != WINBINDD_OK) { + DEBUG(5, ("name2uid returned an error\n")); + cont(private, False, 0); + return; + } + + cont(private, True, response->data.uid); +} + +static void idmap_sid2gid_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private); + +void idmap_sid2gid_async(TALLOC_CTX *mem_ctx, const DOM_SID *sid, BOOL alloc, + void (*cont)(void *private, BOOL success, gid_t gid), + void *private) +{ + struct winbindd_request request; + ZERO_STRUCT(request); + request.cmd = WINBINDD_DUAL_SID2GID; + sid_to_string(request.data.dual_sid2id.sid, sid); + request.data.dual_sid2id.alloc = alloc; + do_async(mem_ctx, idmap_child(), &request, idmap_sid2gid_recv, + cont, private); +} + +enum winbindd_result winbindd_dual_sid2gid(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + DOM_SID sid; + NTSTATUS result; + + DEBUG(3, ("[%5lu]: sid to gid %s\n", (unsigned long)state->pid, + state->request.data.dual_sid2id.sid)); + + if (!string_to_sid(&sid, state->request.data.dual_sid2id.sid)) { + DEBUG(1, ("Could not get convert sid %s from string\n", + state->request.data.dual_sid2id.sid)); + return WINBINDD_ERROR; + } + + /* Find gid for this sid and return it, possibly ask the slow remote + * idmap */ + + result = idmap_sid_to_gid(&sid, &(state->response.data.gid), + state->request.data.dual_sid2id.alloc ? + 0 : ID_QUERY_ONLY); + + return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; +} + +static void idmap_sid2gid_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private) +{ + void (*cont)(void *priv, BOOL succ, gid_t gid) = c; + + if (!success) { + DEBUG(5, ("Could not trigger sid2gid\n")); + cont(private, False, 0); + return; + } + + if (response->result != WINBINDD_OK) { + DEBUG(5, ("sid2gid returned an error\n")); + cont(private, False, 0); + return; + } + + cont(private, True, response->data.gid); +} + +static void gid2name_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private) +{ + void (*cont)(void *priv, BOOL succ, const char *name) = c; + + if (!success) { + DEBUG(5, ("Could not trigger gid2name\n")); + cont(private, False, NULL); + return; + } + + if (response->result != WINBINDD_OK) { + DEBUG(5, ("gid2name returned an error\n")); + cont(private, False, NULL); + return; + } + + cont(private, True, response->data.name.name); +} + +void winbindd_gid2name_async(TALLOC_CTX *mem_ctx, gid_t gid, + void (*cont)(void *private, BOOL success, + const char *name), + void *private) +{ + struct winbindd_request request; + ZERO_STRUCT(request); + request.cmd = WINBINDD_DUAL_GID2NAME; + request.data.gid = gid; + do_async(mem_ctx, idmap_child(), &request, gid2name_recv, + cont, private); +} + +enum winbindd_result winbindd_dual_gid2name(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + struct group *gr; + + DEBUG(3, ("[%5lu]: gid2name %lu\n", (unsigned long)state->pid, + (unsigned long)state->request.data.gid)); + + gr = getgrgid(state->request.data.gid); + if (gr == NULL) + return WINBINDD_ERROR; + + fstrcpy(state->response.data.name.name, gr->gr_name); + return WINBINDD_OK; +} + +static void name2gid_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private); + +static void winbindd_name2gid_async(TALLOC_CTX *mem_ctx, const char *name, + void (*cont)(void *private, BOOL success, + gid_t gid), + void *private) +{ + struct winbindd_request request; + ZERO_STRUCT(request); + request.cmd = WINBINDD_DUAL_NAME2GID; + fstrcpy(request.data.groupname, name); + do_async(mem_ctx, idmap_child(), &request, name2gid_recv, + cont, private); +} + +enum winbindd_result winbindd_dual_name2gid(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + struct group *gr; + + /* Ensure null termination */ + state->request.data.groupname + [sizeof(state->request.data.groupname)-1] = '\0'; + + DEBUG(3, ("[%5lu]: name2gid %s\n", (unsigned long)state->pid, + state->request.data.groupname)); + + gr = getgrnam(state->request.data.groupname); + if (gr == NULL) { + return WINBINDD_ERROR; + } + + state->response.data.gid = gr->gr_gid; + return WINBINDD_OK; +} + +static void name2gid_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private) +{ + void (*cont)(void *priv, BOOL succ, gid_t gid) = c; + + if (!success) { + DEBUG(5, ("Could not trigger name2gid\n")); + cont(private, False, 0); + return; + } + + if (response->result != WINBINDD_OK) { + DEBUG(5, ("name2gid returned an error\n")); + cont(private, False, 0); + return; + } + + cont(private, True, response->data.gid); +} + + +static void lookupsid_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private) +{ + void (*cont)(void *priv, BOOL succ, const char *dom_name, + const char *name, enum SID_NAME_USE type) = c; + + if (!success) { + DEBUG(5, ("Could not trigger lookupsid\n")); + cont(private, False, NULL, NULL, SID_NAME_UNKNOWN); + return; + } + + if (response->result != WINBINDD_OK) { + DEBUG(5, ("lookupsid returned an error\n")); + cont(private, False, NULL, NULL, SID_NAME_UNKNOWN); + return; + } + + cont(private, True, response->data.name.dom_name, + response->data.name.name, response->data.name.type); +} + +void winbindd_lookupsid_async(TALLOC_CTX *mem_ctx, const DOM_SID *sid, + void (*cont)(void *private, BOOL success, + const char *dom_name, + const char *name, + enum SID_NAME_USE type), + void *private) +{ + struct winbindd_domain *domain; + struct winbindd_request request; + + domain = find_lookup_domain_from_sid(sid); + if (domain == NULL) { + DEBUG(5, ("Could not find domain for sid %s\n", + sid_string_static(sid))); + cont(private, False, NULL, NULL, SID_NAME_UNKNOWN); + return; + } + + ZERO_STRUCT(request); + request.cmd = WINBINDD_LOOKUPSID; + fstrcpy(request.data.sid, sid_string_static(sid)); + + do_async_domain(mem_ctx, domain, &request, lookupsid_recv, + cont, private); +} + +enum winbindd_result winbindd_dual_lookupsid(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + enum SID_NAME_USE type; + DOM_SID sid; + fstring name; + fstring dom_name; + + /* Ensure null termination */ + state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; + + DEBUG(3, ("[%5lu]: lookupsid %s\n", (unsigned long)state->pid, + state->request.data.sid)); + + /* Lookup sid from PDC using lsa_lookup_sids() */ + + if (!string_to_sid(&sid, state->request.data.sid)) { + DEBUG(5, ("%s not a SID\n", state->request.data.sid)); + return WINBINDD_ERROR; + } + + /* Lookup the sid */ + + if (!winbindd_lookup_name_by_sid(state->mem_ctx, &sid, dom_name, name, + &type)) { + return WINBINDD_ERROR; + } + + fstrcpy(state->response.data.name.dom_name, dom_name); + fstrcpy(state->response.data.name.name, name); + state->response.data.name.type = type; + + return WINBINDD_OK; +} + +static void lookupname_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private) +{ + void (*cont)(void *priv, BOOL succ, const DOM_SID *sid, + enum SID_NAME_USE type) = c; + DOM_SID sid; + + if (!success) { + DEBUG(5, ("Could not trigger lookup_name\n")); + cont(private, False, NULL, SID_NAME_UNKNOWN); + return; + } + + if (response->result != WINBINDD_OK) { + DEBUG(5, ("lookup_name returned an error\n")); + cont(private, False, NULL, SID_NAME_UNKNOWN); + return; + } + + if (!string_to_sid(&sid, response->data.sid.sid)) { + DEBUG(0, ("Could not convert string %s to sid\n", + response->data.sid.sid)); + cont(private, False, NULL, SID_NAME_UNKNOWN); + return; + } + + cont(private, True, &sid, response->data.sid.type); +} + +void winbindd_lookupname_async(TALLOC_CTX *mem_ctx, const char *dom_name, + const char *name, + void (*cont)(void *private, BOOL success, + const DOM_SID *sid, + enum SID_NAME_USE type), + void *private) +{ + struct winbindd_request request; + struct winbindd_domain *domain; + + domain = find_lookup_domain_from_name(dom_name); + + if (domain == NULL) { + DEBUG(5, ("Could not find domain for name %s\n", dom_name)); + cont(private, False, NULL, SID_NAME_UNKNOWN); + return; + } + + ZERO_STRUCT(request); + request.cmd = WINBINDD_LOOKUPNAME; + fstrcpy(request.data.name.dom_name, dom_name); + fstrcpy(request.data.name.name, name); + + do_async_domain(mem_ctx, domain, &request, lookupname_recv, + cont, private); +} + +enum winbindd_result winbindd_dual_lookupname(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + enum SID_NAME_USE type; + char *name_domain, *name_user; + DOM_SID sid; + char *p; + + /* Ensure null termination */ + state->request.data.sid[sizeof(state->request.data.name.dom_name)-1]='\0'; + + /* Ensure null termination */ + state->request.data.sid[sizeof(state->request.data.name.name)-1]='\0'; + + /* cope with the name being a fully qualified name */ + p = strstr(state->request.data.name.name, lp_winbind_separator()); + if (p) { + *p = 0; + name_domain = state->request.data.name.name; + name_user = p+1; + } else { + name_domain = state->request.data.name.dom_name; + name_user = state->request.data.name.name; + } + + DEBUG(3, ("[%5lu]: lookupname %s%s%s\n", (unsigned long)state->pid, + name_domain, lp_winbind_separator(), name_user)); + + /* Lookup name from PDC using lsa_lookup_names() */ + if (!winbindd_lookup_sid_by_name(state->mem_ctx, domain, name_domain, + name_user, &sid, &type)) { + return WINBINDD_ERROR; + } + + sid_to_string(state->response.data.sid.sid, &sid); + state->response.data.sid.type = type; + + return WINBINDD_OK; +} + +static BOOL print_sidlist(TALLOC_CTX *mem_ctx, const DOM_SID *sids, + int num_sids, char **result) +{ + int i; + size_t buflen = 0; + ssize_t len = 0; + + *result = NULL; + for (i=0; i<num_sids; i++) { + sprintf_append(mem_ctx, result, &len, &buflen, + "%s\n", sid_string_static(&sids[i])); + } + + if ((num_sids != 0) && (*result == NULL)) { + return False; + } + + return True; +} + +static BOOL parse_sidlist(TALLOC_CTX *mem_ctx, char *sidstr, + DOM_SID **sids, int *num_sids) +{ + char *p, *q; + + p = sidstr; + if (p == NULL) + return True; + + while (p[0] != '\0') { + DOM_SID sid; + q = strchr(p, '\n'); + if (q == NULL) { + DEBUG(0, ("Got invalid sidstr: %s\n", p)); + return False; + } + *q = '\0'; + q += 1; + if (!string_to_sid(&sid, p)) { + DEBUG(0, ("Could not parse sid %s\n", p)); + return False; + } + add_sid_to_array(mem_ctx, &sid, sids, num_sids); + p = q; + } + return True; +} + +static void getsidaliases_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private) +{ + void (*cont)(void *priv, BOOL succ, + DOM_SID *aliases, int num_aliases) = c; + char *aliases_str; + DOM_SID *sids = NULL; + int num_sids = 0; + + if (!success) { + DEBUG(5, ("Could not trigger getsidaliases\n")); + cont(private, success, NULL, 0); + return; + } + + if (response->result != WINBINDD_OK) { + DEBUG(5, ("getsidaliases returned an error\n")); + cont(private, False, NULL, 0); + return; + } + + aliases_str = response->extra_data; + + if (aliases_str == NULL) { + DEBUG(10, ("getsidaliases return 0 SIDs\n")); + cont(private, True, NULL, 0); + return; + } + + if (!parse_sidlist(mem_ctx, aliases_str, &sids, &num_sids)) { + DEBUG(0, ("Could not parse sids\n")); + cont(private, False, NULL, 0); + return; + } + + SAFE_FREE(response->extra_data); + + cont(private, True, sids, num_sids); +} + +void winbindd_getsidaliases_async(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *sids, int num_sids, + void (*cont)(void *private, + BOOL success, + const DOM_SID *aliases, + int num_aliases), + void *private) +{ + struct winbindd_request request; + char *sidstr = NULL; + char *keystr; + + if (num_sids == 0) { + cont(private, True, NULL, 0); + return; + } + + if (!print_sidlist(mem_ctx, sids, num_sids, &sidstr)) { + cont(private, False, NULL, 0); + return; + } + + keystr = cache_store_request_data(mem_ctx, sidstr); + if (keystr == NULL) { + cont(private, False, NULL, 0); + return; + } + + ZERO_STRUCT(request); + request.cmd = WINBINDD_DUAL_GETSIDALIASES; + fstrcpy(request.domain_name, domain->name); + fstrcpy(request.data.dual_sidaliases.cache_key, keystr); + + do_async_domain(mem_ctx, domain, &request, getsidaliases_recv, + cont, private); +} + +enum winbindd_result winbindd_dual_getsidaliases(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + DOM_SID *sids = NULL; + int num_sids = 0; + char *key = state->request.data.dual_sidaliases.cache_key; + char *sidstr; + int i, num_aliases; + uint32 *alias_rids; + NTSTATUS result; + + DEBUG(3, ("[%5lu]: getsidaliases\n", (unsigned long)state->pid)); + + /* Ensure null termination */ + state->request.domain_name[sizeof(state->request.domain_name)-1]='\0'; + state->request.data.dual_sidaliases.cache_key + [sizeof(state->request.data.dual_sidaliases.cache_key)-1]='\0'; + + sidstr = cache_retrieve_request_data(state->mem_ctx, key); + if (sidstr == NULL) + sidstr = talloc_strdup(state->mem_ctx, "\n"); /* No SID */ + + DEBUG(10, ("Sidlist: %s\n", sidstr)); + + if (!parse_sidlist(state->mem_ctx, sidstr, &sids, &num_sids)) { + DEBUG(0, ("Could not parse SID list: %s\n", sidstr)); + return WINBINDD_ERROR; + } + + num_aliases = 0; + alias_rids = NULL; + + result = domain->methods->lookup_useraliases(domain, + state->mem_ctx, + num_sids, sids, + &num_aliases, + &alias_rids); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(3, ("Could not lookup_useraliases: %s\n", + nt_errstr(result))); + return WINBINDD_ERROR; + } + + num_sids = 0; + sids = NULL; + + DEBUG(10, ("Got %d aliases\n", num_aliases)); + + for (i=0; i<num_aliases; i++) { + DOM_SID sid; + DEBUGADD(10, (" rid %d\n", alias_rids[i])); + sid_copy(&sid, &domain->sid); + sid_append_rid(&sid, alias_rids[i]); + add_sid_to_array(state->mem_ctx, &sid, &sids, &num_sids); + } + + if (!print_sidlist(NULL, sids, num_sids, + (char **)&state->response.extra_data)) { + DEBUG(0, ("Could not print_sidlist\n")); + return WINBINDD_ERROR; + } + + if (state->response.extra_data != NULL) { + DEBUG(10, ("aliases_list: %s\n", + (char *)state->response.extra_data)); + state->response.length += strlen(state->response.extra_data)+1; + } + + return WINBINDD_OK; +} + +struct gettoken_state { + TALLOC_CTX *mem_ctx; + DOM_SID user_sid; + struct winbindd_domain *alias_domain; + struct winbindd_domain *builtin_domain; + DOM_SID *sids; + int num_sids; + void (*cont)(void *private, BOOL success, DOM_SID *sids, int num_sids); + void *private; +}; + +static void gettoken_recvdomgroups(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private); +static void gettoken_recvaliases(void *private, BOOL success, + const DOM_SID *aliases, + int num_aliases); + + +void winbindd_gettoken_async(TALLOC_CTX *mem_ctx, const DOM_SID *user_sid, + void (*cont)(void *private, BOOL success, + DOM_SID *sids, int num_sids), + void *private) +{ + struct winbindd_domain *domain; + struct winbindd_request request; + struct gettoken_state *state; + + state = TALLOC_P(mem_ctx, struct gettoken_state); + if (state == NULL) { + DEBUG(0, ("talloc failed\n")); + cont(private, False, NULL, 0); + return; + } + + state->mem_ctx = mem_ctx; + sid_copy(&state->user_sid, user_sid); + state->alias_domain = find_our_domain(); + state->builtin_domain = find_builtin_domain(); + state->cont = cont; + state->private = private; + + domain = find_domain_from_sid_noinit(user_sid); + if (domain == NULL) { + DEBUG(5, ("Could not find domain from SID %s\n", + sid_string_static(user_sid))); + cont(private, False, NULL, 0); + return; + } + + ZERO_STRUCT(request); + request.cmd = WINBINDD_GETUSERDOMGROUPS; + fstrcpy(request.data.sid, sid_string_static(user_sid)); + + do_async_domain(mem_ctx, domain, &request, gettoken_recvdomgroups, + NULL, state); +} + +static void gettoken_recvdomgroups(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private) +{ + struct gettoken_state *state = + talloc_get_type_abort(private, struct gettoken_state); + char *sids_str; + + if (!success) { + DEBUG(10, ("Could not get domain groups\n")); + state->cont(state->private, False, NULL, 0); + return; + } + + sids_str = response->extra_data; + + if (sids_str == NULL) { + DEBUG(10, ("Received no domain groups\n")); + state->cont(state->private, True, NULL, 0); + return; + } + + state->sids = NULL; + state->num_sids = 0; + + add_sid_to_array(mem_ctx, &state->user_sid, &state->sids, + &state->num_sids); + + if (!parse_sidlist(mem_ctx, sids_str, &state->sids, + &state->num_sids)) { + DEBUG(0, ("Could not parse sids\n")); + state->cont(state->private, False, NULL, 0); + return; + } + + SAFE_FREE(response->extra_data); + + if (state->alias_domain == NULL) { + DEBUG(10, ("Don't expand domain local groups\n")); + state->cont(state->private, True, state->sids, + state->num_sids); + return; + } + + winbindd_getsidaliases_async(state->alias_domain, mem_ctx, + state->sids, state->num_sids, + gettoken_recvaliases, state); +} + +static void gettoken_recvaliases(void *private, BOOL success, + const DOM_SID *aliases, + int num_aliases) +{ + struct gettoken_state *state = private; + int i; + + if (!success) { + DEBUG(10, ("Could not receive domain local groups\n")); + state->cont(state->private, False, NULL, 0); + return; + } + + for (i=0; i<num_aliases; i++) + add_sid_to_array(state->mem_ctx, &aliases[i], + &state->sids, &state->num_sids); + + if (state->builtin_domain != NULL) { + struct winbindd_domain *builtin_domain = state->builtin_domain; + DEBUG(10, ("Expanding our own local groups\n")); + state->builtin_domain = NULL; + winbindd_getsidaliases_async(builtin_domain, state->mem_ctx, + state->sids, state->num_sids, + gettoken_recvaliases, state); + return; + } + + state->cont(state->private, True, state->sids, state->num_sids); +} + +struct sid2uid_state { + TALLOC_CTX *mem_ctx; + DOM_SID sid; + char *username; + uid_t uid; + void (*cont)(void *private, BOOL success, uid_t uid); + void *private; +}; + +static void sid2uid_lookup_sid_recv(void *private, BOOL success, + const char *dom_name, const char *name, + enum SID_NAME_USE type); +static void sid2uid_noalloc_recv(void *private, BOOL success, uid_t uid); +static void sid2uid_alloc_recv(void *private, BOOL success, uid_t uid); +static void sid2uid_name2uid_recv(void *private, BOOL success, uid_t uid); +static void sid2uid_set_mapping_recv(void *private, BOOL success); + +void winbindd_sid2uid_async(TALLOC_CTX *mem_ctx, const DOM_SID *sid, + void (*cont)(void *private, BOOL success, + uid_t uid), + void *private) +{ + struct sid2uid_state *state; + NTSTATUS result; + uid_t uid; + + if (idmap_proxyonly()) { + DEBUG(10, ("idmap proxy only\n")); + cont(private, False, 0); + return; + } + + /* Query only the local tdb, everything else might possibly block */ + + result = idmap_sid_to_uid(sid, &uid, ID_QUERY_ONLY|ID_CACHE_ONLY); + + if (NT_STATUS_IS_OK(result)) { + cont(private, True, uid); + return; + } + + state = TALLOC_P(mem_ctx, struct sid2uid_state); + if (state == NULL) { + DEBUG(0, ("talloc failed\n")); + cont(private, False, 0); + return; + } + + state->mem_ctx = mem_ctx; + state->sid = *sid; + state->cont = cont; + state->private = private; + + /* Let's see if it's really a user before allocating a uid */ + + winbindd_lookupsid_async(mem_ctx, sid, sid2uid_lookup_sid_recv, state); +} + +static void sid2uid_lookup_sid_recv(void *private, BOOL success, + const char *dom_name, const char *name, + enum SID_NAME_USE type) +{ + struct sid2uid_state *state = + talloc_get_type_abort(private, struct sid2uid_state); + + if (!success) { + DEBUG(5, ("Could not trigger lookup_sid\n")); + state->cont(state->private, False, 0); + return; + } + + if ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER)) { + DEBUG(5, ("SID is not a user\n")); + state->cont(state->private, False, 0); + return; + } + + state->username = talloc_strdup(state->mem_ctx, name); + + /* Ask the possibly blocking remote IDMAP */ + + idmap_sid2uid_async(state->mem_ctx, &state->sid, False, + sid2uid_noalloc_recv, state); +} + +static void sid2uid_noalloc_recv(void *private, BOOL success, uid_t uid) +{ + struct sid2uid_state *state = + talloc_get_type_abort(private, struct sid2uid_state); + + if (success) { + DEBUG(10, ("found uid for sid %s in remote backend\n", + sid_string_static(&state->sid))); + state->cont(state->private, True, uid); + return; + } + + if (lp_winbind_trusted_domains_only() && + (sid_compare_domain(&state->sid, &find_our_domain()->sid) == 0)) { + DEBUG(10, ("Trying to go via nss\n")); + winbindd_name2uid_async(state->mem_ctx, state->username, + sid2uid_name2uid_recv, state); + return; + } + + /* To be done: Here we're going to try the unixinfo pipe */ + + /* Now allocate a uid */ + + idmap_sid2uid_async(state->mem_ctx, &state->sid, True, + sid2uid_alloc_recv, state); +} + +static void sid2uid_alloc_recv(void *private, BOOL success, uid_t uid) +{ + struct sid2uid_state *state = + talloc_get_type_abort(private, struct sid2uid_state); + + if (!success) { + DEBUG(5, ("Could not allocate uid\n")); + state->cont(state->private, False, 0); + return; + } + + state->cont(state->private, True, uid); +} + +static void sid2uid_name2uid_recv(void *private, BOOL success, uid_t uid) +{ + struct sid2uid_state *state = + talloc_get_type_abort(private, struct sid2uid_state); + unid_t id; + + if (!success) { + DEBUG(5, ("Could not find uid for name %s\n", + state->username)); + state->cont(state->private, False, 0); + return; + } + + state->uid = uid; + + id.uid = uid; + idmap_set_mapping_async(state->mem_ctx, &state->sid, id, ID_USERID, + sid2uid_set_mapping_recv, state); +} + +static void sid2uid_set_mapping_recv(void *private, BOOL success) +{ + struct sid2uid_state *state = + talloc_get_type_abort(private, struct sid2uid_state); + + if (!success) { + DEBUG(5, ("Could not set ID mapping for sid %s\n", + sid_string_static(&state->sid))); + state->cont(state->private, False, 0); + return; + } + + state->cont(state->private, True, state->uid); +} + +struct sid2gid_state { + TALLOC_CTX *mem_ctx; + DOM_SID sid; + char *groupname; + gid_t gid; + void (*cont)(void *private, BOOL success, gid_t gid); + void *private; +}; + +static void sid2gid_lookup_sid_recv(void *private, BOOL success, + const char *dom_name, const char *name, + enum SID_NAME_USE type); +static void sid2gid_noalloc_recv(void *private, BOOL success, gid_t gid); +static void sid2gid_alloc_recv(void *private, BOOL success, gid_t gid); +static void sid2gid_name2gid_recv(void *private, BOOL success, gid_t gid); +static void sid2gid_set_mapping_recv(void *private, BOOL success); + +void winbindd_sid2gid_async(TALLOC_CTX *mem_ctx, const DOM_SID *sid, + void (*cont)(void *private, BOOL success, + gid_t gid), + void *private) +{ + struct sid2gid_state *state; + NTSTATUS result; + gid_t gid; + + if (idmap_proxyonly()) { + DEBUG(10, ("idmap proxy only\n")); + cont(private, False, 0); + return; + } + + /* Query only the local tdb, everything else might possibly block */ + + result = idmap_sid_to_gid(sid, &gid, ID_QUERY_ONLY|ID_CACHE_ONLY); + + if (NT_STATUS_IS_OK(result)) { + cont(private, True, gid); + return; + } + + state = TALLOC_P(mem_ctx, struct sid2gid_state); + if (state == NULL) { + DEBUG(0, ("talloc failed\n")); + cont(private, False, 0); + return; + } + + state->mem_ctx = mem_ctx; + state->sid = *sid; + state->cont = cont; + state->private = private; + + /* Let's see if it's really a user before allocating a gid */ + + winbindd_lookupsid_async(mem_ctx, sid, sid2gid_lookup_sid_recv, state); +} + +static void sid2gid_lookup_sid_recv(void *private, BOOL success, + const char *dom_name, const char *name, + enum SID_NAME_USE type) +{ + struct sid2gid_state *state = + talloc_get_type_abort(private, struct sid2gid_state); + + if (!success) { + DEBUG(5, ("Could not trigger lookup_sid\n")); + state->cont(state->private, False, 0); + return; + } + + if (((type != SID_NAME_DOM_GRP) && (type != SID_NAME_ALIAS) && + (type != SID_NAME_WKN_GRP))) { + DEBUG(5, ("SID is not a group\n")); + state->cont(state->private, False, 0); + return; + } + + state->groupname = talloc_strdup(state->mem_ctx, name); + + /* Ask the possibly blocking remote IDMAP and allocate */ + + idmap_sid2gid_async(state->mem_ctx, &state->sid, False, + sid2gid_noalloc_recv, state); +} + +static void sid2gid_noalloc_recv(void *private, BOOL success, gid_t gid) +{ + struct sid2gid_state *state = + talloc_get_type_abort(private, struct sid2gid_state); + + if (success) { + DEBUG(10, ("found gid for sid %s in remote backend\n", + sid_string_static(&state->sid))); + state->cont(state->private, True, gid); + return; + } + + if (lp_winbind_trusted_domains_only() && + (sid_compare_domain(&state->sid, &find_our_domain()->sid) == 0)) { + DEBUG(10, ("Trying to go via nss\n")); + winbindd_name2gid_async(state->mem_ctx, state->groupname, + sid2gid_name2gid_recv, state); + return; + } + + /* To be done: Here we're going to try the unixinfo pipe */ + + /* Now allocate a gid */ + + idmap_sid2gid_async(state->mem_ctx, &state->sid, True, + sid2gid_alloc_recv, state); +} + +static void sid2gid_alloc_recv(void *private, BOOL success, gid_t gid) +{ + struct sid2gid_state *state = + talloc_get_type_abort(private, struct sid2gid_state); + + if (!success) { + DEBUG(5, ("Could not allocate gid\n")); + state->cont(state->private, False, 0); + return; + } + + state->cont(state->private, True, gid); +} + +static void sid2gid_name2gid_recv(void *private, BOOL success, gid_t gid) +{ + struct sid2gid_state *state = + talloc_get_type_abort(private, struct sid2gid_state); + unid_t id; + + if (!success) { + DEBUG(5, ("Could not find gid for name %s\n", + state->groupname)); + state->cont(state->private, False, 0); + return; + } + + state->gid = gid; + + id.gid = gid; + idmap_set_mapping_async(state->mem_ctx, &state->sid, id, ID_GROUPID, + sid2gid_set_mapping_recv, state); +} + +static void sid2gid_set_mapping_recv(void *private, BOOL success) +{ + struct sid2gid_state *state = + talloc_get_type_abort(private, struct sid2gid_state); + + if (!success) { + DEBUG(5, ("Could not set ID mapping for sid %s\n", + sid_string_static(&state->sid))); + state->cont(state->private, False, 0); + return; + } + + state->cont(state->private, True, state->gid); +} + +static void query_user_recv(TALLOC_CTX *mem_ctx, BOOL success, + struct winbindd_response *response, + void *c, void *private) +{ + void (*cont)(void *priv, BOOL succ, const char *acct_name, + const char *full_name, uint32 group_rid) = c; + + if (!success) { + DEBUG(5, ("Could not trigger query_user\n")); + cont(private, False, NULL, NULL, -1); + return; + } + + cont(private, True, response->data.user_info.acct_name, + response->data.user_info.full_name, + response->data.user_info.group_rid); +} + +void query_user_async(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain, + const DOM_SID *sid, + void (*cont)(void *private, BOOL success, + const char *acct_name, + const char *full_name, + uint32 group_rid), + void *private) +{ + struct winbindd_request request; + ZERO_STRUCT(request); + request.cmd = WINBINDD_DUAL_USERINFO; + sid_to_string(request.data.sid, sid); + do_async_domain(mem_ctx, domain, &request, query_user_recv, + cont, private); +} + diff --git a/source3/nsswitch/winbindd_cache.c b/source3/nsswitch/winbindd_cache.c index e036de72a7..90ccb43a6e 100644 --- a/source3/nsswitch/winbindd_cache.c +++ b/source3/nsswitch/winbindd_cache.c @@ -26,12 +26,6 @@ #include "includes.h" #include "winbindd.h" -extern BOOL opt_nocache; -extern struct winbindd_methods msrpc_methods; -extern struct winbindd_methods ads_methods; -extern BOOL opt_dual_daemon; -extern BOOL background_process; - #undef DBGC_CLASS #define DBGC_CLASS DBGC_WINBIND @@ -53,6 +47,8 @@ static struct winbind_cache *wcache; /* flush the cache */ void wcache_flush_cache(void) { + extern BOOL opt_nocache; + if (!wcache) return; if (wcache->tdb) { @@ -106,9 +102,11 @@ static struct winbind_cache *get_cache(struct winbindd_domain *domain) struct winbind_cache *ret = wcache; if (!domain->backend) { + extern struct winbindd_methods reconnect_methods; switch (lp_security()) { #ifdef HAVE_ADS case SEC_ADS: { + extern struct winbindd_methods ads_methods; /* always obey the lp_security parameter for our domain */ if (domain->primary) { domain->backend = &ads_methods; @@ -132,7 +130,7 @@ static struct winbind_cache *get_cache(struct winbindd_domain *domain) default: DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name)); - domain->backend = &msrpc_methods; + domain->backend = &reconnect_methods; } } @@ -212,7 +210,10 @@ static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx) smb_panic("centry_string"); } - ret = TALLOC(mem_ctx, len+1); + if (mem_ctx != NULL) + ret = TALLOC(mem_ctx, len+1); + else + ret = SMB_MALLOC(len+1); if (!ret) { smb_panic("centry_string out of memory\n"); } @@ -225,20 +226,15 @@ static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx) /* pull a string from a cache entry, using the supplied talloc context */ -static DOM_SID *centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx) +static BOOL centry_sid(struct cache_entry *centry, DOM_SID *sid) { - DOM_SID *sid; char *sid_string; - - sid = TALLOC_P(mem_ctx, DOM_SID); - if (!sid) - return NULL; - - sid_string = centry_string(centry, mem_ctx); + sid_string = centry_string(centry, NULL); if (!string_to_sid(sid, sid_string)) { - return NULL; + return False; } - return sid; + SAFE_FREE(sid_string); + return True; } /* the server is considered down if it can't give us a sequence number */ @@ -471,11 +467,13 @@ static struct cache_entry *wcache_fetch(struct winbind_cache *cache, centry->sequence_number = centry_uint32(centry); if (centry_expired(domain, kstr, centry)) { + extern BOOL opt_dual_daemon; DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n", kstr, domain->name )); if (opt_dual_daemon) { + extern BOOL background_process; background_process = True; DEBUG(10,("wcache_fetch: background processing expired entry %s for domain %s\n", kstr, domain->name )); @@ -654,9 +652,9 @@ static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WI return; centry_put_string(centry, info->acct_name); centry_put_string(centry, info->full_name); - centry_put_sid(centry, info->user_sid); - centry_put_sid(centry, info->group_sid); - centry_end(centry, "U/%s", sid_to_string(sid_string, info->user_sid)); + centry_put_sid(centry, &info->user_sid); + centry_put_sid(centry, &info->group_sid); + centry_end(centry, "U/%s", sid_to_string(sid_string, &info->user_sid)); DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name)); centry_free(centry); } @@ -691,8 +689,8 @@ static NTSTATUS query_user_list(struct winbindd_domain *domain, for (i=0; i<(*num_entries); i++) { (*info)[i].acct_name = centry_string(centry, mem_ctx); (*info)[i].full_name = centry_string(centry, mem_ctx); - (*info)[i].user_sid = centry_sid(centry, mem_ctx); - (*info)[i].group_sid = centry_sid(centry, mem_ctx); + centry_sid(centry, &(*info)[i].user_sid); + centry_sid(centry, &(*info)[i].group_sid); } do_cached: @@ -729,10 +727,12 @@ do_query: status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info); if (!NT_STATUS_IS_OK(status)) - DEBUG(3, ("query_user_list: returned 0x%08x, retrying\n", NT_STATUS_V(status))); - if (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL)) { - DEBUG(3, ("query_user_list: flushing connection cache\n")); - winbindd_cm_flush(); + DEBUG(3, ("query_user_list: returned 0x%08x, " + "retrying\n", NT_STATUS_V(status))); + if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) { + DEBUG(3, ("query_user_list: flushing " + "connection cache\n")); + invalidate_cm_connection(&domain->conn); } } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && @@ -747,17 +747,17 @@ do_query: for (i=0; i<(*num_entries); i++) { centry_put_string(centry, (*info)[i].acct_name); centry_put_string(centry, (*info)[i].full_name); - centry_put_sid(centry, (*info)[i].user_sid); - centry_put_sid(centry, (*info)[i].group_sid); + centry_put_sid(centry, &(*info)[i].user_sid); + centry_put_sid(centry, &(*info)[i].group_sid); if (domain->backend->consistent) { /* when the backend is consistent we can pre-prime some mappings */ wcache_save_name_to_sid(domain, NT_STATUS_OK, domain->name, (*info)[i].acct_name, - (*info)[i].user_sid, + &(*info)[i].user_sid, SID_NAME_USER); wcache_save_sid_to_name(domain, NT_STATUS_OK, - (*info)[i].user_sid, + &(*info)[i].user_sid, domain->name, (*info)[i].acct_name, SID_NAME_USER); @@ -939,7 +939,6 @@ static NTSTATUS name_to_sid(struct winbindd_domain *domain, struct cache_entry *centry = NULL; NTSTATUS status; fstring uname; - DOM_SID *sid2; if (!cache->tdb) goto do_query; @@ -950,13 +949,7 @@ static NTSTATUS name_to_sid(struct winbindd_domain *domain, if (!centry) goto do_query; *type = (enum SID_NAME_USE)centry_uint32(centry); - sid2 = centry_sid(centry, mem_ctx); - if (!sid2) { - ZERO_STRUCTP(sid); - } else { - sid_copy(sid, sid2); - } - + centry_sid(centry, sid); status = centry->status; DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status %s\n", @@ -1089,8 +1082,8 @@ static NTSTATUS query_user(struct winbindd_domain *domain, info->acct_name = centry_string(centry, mem_ctx); info->full_name = centry_string(centry, mem_ctx); - info->user_sid = centry_sid(centry, mem_ctx); - info->group_sid = centry_sid(centry, mem_ctx); + centry_sid(centry, &info->user_sid); + centry_sid(centry, &info->group_sid); status = centry->status; DEBUG(10,("query_user: [Cached] - cached info for domain %s status %s\n", @@ -1124,7 +1117,7 @@ do_query: static NTSTATUS lookup_usergroups(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, const DOM_SID *user_sid, - uint32 *num_groups, DOM_SID ***user_gids) + uint32 *num_groups, DOM_SID **user_gids) { struct winbind_cache *cache = get_cache(domain); struct cache_entry *centry = NULL; @@ -1157,11 +1150,11 @@ static NTSTATUS lookup_usergroups(struct winbindd_domain *domain, if (*num_groups == 0) goto do_cached; - (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID *, *num_groups); + (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups); if (! (*user_gids)) smb_panic("lookup_usergroups out of memory"); for (i=0; i<(*num_groups); i++) { - (*user_gids)[i] = centry_sid(centry, mem_ctx); + centry_sid(centry, &(*user_gids)[i]); } do_cached: @@ -1194,7 +1187,7 @@ do_query: goto skip_save; centry_put_uint32(centry, *num_groups); for (i=0; i<(*num_groups); i++) { - centry_put_sid(centry, (*user_gids)[i]); + centry_put_sid(centry, &(*user_gids)[i]); } centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid)); centry_free(centry); @@ -1205,7 +1198,7 @@ skip_save: static NTSTATUS lookup_useraliases(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, - uint32 num_sids, DOM_SID **sids, + uint32 num_sids, const DOM_SID *sids, uint32 *num_aliases, uint32 **alias_rids) { struct winbind_cache *cache = get_cache(domain); @@ -1228,7 +1221,7 @@ static NTSTATUS lookup_useraliases(struct winbindd_domain *domain, for (i=0; i<num_sids; i++) { sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist, - sid_string_static(sids[i])); + sid_string_static(&sids[i])); if (sidlist == NULL) return NT_STATUS_NO_MEMORY; } @@ -1265,7 +1258,7 @@ static NTSTATUS lookup_useraliases(struct winbindd_domain *domain, if (!NT_STATUS_IS_OK(domain->last_status)) return domain->last_status; - DEBUG(10,("lookup_useraliases: [Cached] - doing backend query for info " + DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info " "for domain %s\n", domain->name )); status = domain->backend->lookup_useraliases(domain, mem_ctx, @@ -1291,7 +1284,7 @@ static NTSTATUS lookup_useraliases(struct winbindd_domain *domain, static NTSTATUS lookup_groupmem(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, const DOM_SID *group_sid, uint32 *num_names, - DOM_SID ***sid_mem, char ***names, + DOM_SID **sid_mem, char ***names, uint32 **name_types) { struct winbind_cache *cache = get_cache(domain); @@ -1312,7 +1305,7 @@ static NTSTATUS lookup_groupmem(struct winbindd_domain *domain, if (*num_names == 0) goto do_cached; - (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID *, *num_names); + (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names); (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names); (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names); @@ -1321,7 +1314,7 @@ static NTSTATUS lookup_groupmem(struct winbindd_domain *domain, } for (i=0; i<(*num_names); i++) { - (*sid_mem)[i] = centry_sid(centry, mem_ctx); + centry_sid(centry, &(*sid_mem)[i]); (*names)[i] = centry_string(centry, mem_ctx); (*name_types)[i] = centry_uint32(centry); } @@ -1359,7 +1352,7 @@ do_query: goto skip_save; centry_put_uint32(centry, *num_names); for (i=0; i<(*num_names); i++) { - centry_put_sid(centry, (*sid_mem)[i]); + centry_put_sid(centry, &(*sid_mem)[i]); centry_put_string(centry, (*names)[i]); centry_put_uint32(centry, (*name_types)[i]); } @@ -1466,3 +1459,166 @@ struct winbindd_methods cache_methods = { trusted_domains, alternate_name }; + +static BOOL init_wcache(void) +{ + if (wcache == NULL) { + wcache = SMB_XMALLOC_P(struct winbind_cache); + ZERO_STRUCTP(wcache); + } + + if (wcache->tdb != NULL) + return True; + + wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 5000, + TDB_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 0600); + + if (wcache->tdb == NULL) { + DEBUG(0,("Failed to open winbindd_cache.tdb!\n")); + return False; + } + + return True; +} + +void cache_store_response(pid_t pid, struct winbindd_response *response) +{ + fstring key_str; + + if (!init_wcache()) + return; + + DEBUG(10, ("Storing response for pid %d, len %d\n", + pid, response->length)); + + fstr_sprintf(key_str, "DR/%d", pid); + if (tdb_store(wcache->tdb, string_tdb_data(key_str), + make_tdb_data((void *)response, sizeof(*response)), + TDB_REPLACE) == -1) + return; + + if (response->length == sizeof(*response)) + return; + + /* There's extra data */ + + DEBUG(10, ("Storing extra data: len=%d\n", + response->length - sizeof(*response))); + + fstr_sprintf(key_str, "DE/%d", pid); + if (tdb_store(wcache->tdb, string_tdb_data(key_str), + make_tdb_data(response->extra_data, + response->length - sizeof(*response)), + TDB_REPLACE) == 0) + return; + + /* We could not store the extra data, make sure the tdb does not + * contain a main record with wrong dangling extra data */ + + fstr_sprintf(key_str, "DR/%d", pid); + tdb_delete(wcache->tdb, string_tdb_data(key_str)); + + return; +} + +BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response) +{ + TDB_DATA data; + fstring key_str; + + if (!init_wcache()) + return False; + + DEBUG(10, ("Retrieving response for pid %d\n", pid)); + + fstr_sprintf(key_str, "DR/%d", pid); + data = tdb_fetch(wcache->tdb, string_tdb_data(key_str)); + + if (data.dptr == NULL) + return False; + + if (data.dsize != sizeof(*response)) + return False; + + memcpy(response, data.dptr, data.dsize); + SAFE_FREE(data.dptr); + + if (response->length == sizeof(*response)) { + response->extra_data = NULL; + return True; + } + + /* There's extra data */ + + DEBUG(10, ("Retrieving extra data length=%d\n", + response->length - sizeof(*response))); + + fstr_sprintf(key_str, "DE/%d", pid); + data = tdb_fetch(wcache->tdb, string_tdb_data(key_str)); + + if (data.dptr == NULL) { + DEBUG(0, ("Did not find extra data\n")); + return False; + } + + if (data.dsize != (response->length - sizeof(*response))) { + DEBUG(0, ("Invalid extra data length: %d\n", data.dsize)); + SAFE_FREE(data.dptr); + return False; + } + + response->extra_data = data.dptr; + return True; +} + +char *cache_store_request_data(TALLOC_CTX *mem_ctx, char *request_string) +{ + int i; + + if (!init_wcache()) + return NULL; + + for (i=0; i<2; i++) { + char *key = talloc_strdup(mem_ctx, generate_random_str(16)); + if (key == NULL) + return NULL; + DEBUG(10, ("Storing request key %s\n", key)); + if (tdb_store_bystring(wcache->tdb, key, + string_tdb_data(request_string), + TDB_INSERT) == 0) + return key; + } + return NULL; +} + +char *cache_retrieve_request_data(TALLOC_CTX *mem_ctx, char *key) +{ + TDB_DATA data; + char *result = NULL; + + if (!init_wcache()) + return NULL; + + DEBUG(10, ("Retrieving key %s\n", key)); + + data = tdb_fetch_bystring(wcache->tdb, key); + if (data.dptr == NULL) + return NULL; + + if (strnlen(data.dptr, data.dsize) != (data.dsize)) { + DEBUG(0, ("Received invalid request string\n")); + goto done; + } + result = TALLOC_ARRAY(mem_ctx, char, data.dsize+1); + if (result != NULL) { + memcpy(result, data.dptr, data.dsize); + result[data.dsize] = '\0'; + } + if (tdb_delete_bystring(wcache->tdb, key) != 0) { + DEBUG(0, ("Could not delete key %s\n", key)); + result = NULL; + } + done: + SAFE_FREE(data.dptr); + return result; +} diff --git a/source3/nsswitch/winbindd_cm.c b/source3/nsswitch/winbindd_cm.c index c5cf1d5f46..a6f09f4bf2 100644 --- a/source3/nsswitch/winbindd_cm.c +++ b/source3/nsswitch/winbindd_cm.c @@ -3,8 +3,10 @@ Winbind daemon connection manager - Copyright (C) Tim Potter 2001 - Copyright (C) Andrew Bartlett 2002 + Copyright (C) Tim Potter 2001 + Copyright (C) Andrew Bartlett 2002 + Copyright (C) Gerald (Jerry) Carter 2003-2005. + Copyright (C) Volker Lendecke 2004-2005 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -65,21 +67,6 @@ /* Global list of connections. Initially a DLIST but can become a hash table or whatever later. */ -struct winbindd_cm_conn { - struct winbindd_cm_conn *prev, *next; - fstring domain; - fstring controller; - fstring pipe_name; - struct cli_state *cli; - POLICY_HND pol; -}; - -static struct winbindd_cm_conn *cm_conns = NULL; - -static NTSTATUS get_connection_from_cache(struct winbindd_domain *domain, - const char *pipe_name, - struct winbindd_cm_conn **conn_out); - /* Choose between anonymous or authenticated connections. We need to use an authenticated connection if DCs have the RestrictAnonymous registry entry set > 0, or the "Additional restrictions for anonymous @@ -113,75 +100,45 @@ static void cm_get_ipc_userpass(char **username, char **domain, char **password) } } -/* - setup for schannel on any pipes opened on this connection -*/ -static NTSTATUS setup_schannel( struct cli_state *cli, const char *domain ) -{ - NTSTATUS ret; - uchar trust_password[16]; - uint32 sec_channel_type; - DOM_SID sid; - time_t lct; - - /* use the domain trust password if we're on a DC - and this is not our domain */ - - if ( IS_DC && !strequal(domain, lp_workgroup()) ) { - char *pass = NULL; - - if ( !secrets_fetch_trusted_domain_password( domain, - &pass, &sid, &lct) ) - { - return NT_STATUS_UNSUCCESSFUL; - } - - sec_channel_type = SEC_CHAN_DOMAIN; - E_md4hash(pass, trust_password); - SAFE_FREE( pass ); - - } else { - if (!secrets_fetch_trust_account_password(lp_workgroup(), - trust_password, NULL, &sec_channel_type)) - { - return NT_STATUS_UNSUCCESSFUL; - } - } - - ret = cli_nt_setup_netsec(cli, sec_channel_type, - AUTH_PIPE_NETSEC | AUTH_PIPE_SIGN, trust_password); - - return ret; -} - static BOOL get_dc_name_via_netlogon(const struct winbindd_domain *domain, fstring dcname, struct in_addr *dc_ip) { struct winbindd_domain *our_domain; NTSTATUS result; - struct winbindd_cm_conn *conn; + struct rpc_pipe_client *cli; TALLOC_CTX *mem_ctx; fstring tmp; char *p; + /* Hmmmm. We can only open one connection to the NETLOGON pipe at the + * moment.... */ + if (IS_DC) return False; if (domain->primary) return False; - if ((our_domain = find_our_domain()) == NULL) - return False; + our_domain = find_our_domain(); - result = get_connection_from_cache(our_domain, PIPE_NETLOGON, &conn); - if (!NT_STATUS_IS_OK(result)) + if ((mem_ctx = talloc_init("get_dc_name_via_netlogon")) == NULL) return False; - if ((mem_ctx = talloc_init("get_dc_name_via_netlogon")) == NULL) + { + /* These var's can be ignored -- we're not requesting + anything in the credential chain here */ + unsigned char *session_key; + DOM_CRED *creds; + result = cm_connect_netlogon(our_domain, mem_ctx, &cli, + &session_key, &creds); + } + + if (!NT_STATUS_IS_OK(result)) return False; - result = cli_netlogon_getdcname(conn->cli, mem_ctx, domain->name, tmp); + result = rpccli_netlogon_getdcname(cli, mem_ctx, domain->dcname, + domain->name, tmp); talloc_destroy(mem_ctx); @@ -208,7 +165,6 @@ static BOOL get_dc_name_via_netlogon(const struct winbindd_domain *domain, static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain, const int sockfd, - const int pipe_index, const char *controller, struct cli_state **cli, BOOL *retry) @@ -376,33 +332,11 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain, got_mutex = False; *retry = False; - /* Windows 2003 SP1 does not lie LsaOpenPolicy() over schannel. - Returns RPC_NT_CANNOT_SUPPPORT (0xc0020041) for that call. - So just drop it on the lsarpc pipe */ - - if ( (domain->primary || IS_DC) && (pipe_index!=PI_LSARPC) ) { - NTSTATUS status = setup_schannel( *cli, domain->name ); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(3,("schannel refused - continuing without " - "schannel (%s)\n", nt_errstr(status))); - } - } - /* set the domain if empty; needed for schannel connections */ if ( !*(*cli)->domain ) fstrcpy( (*cli)->domain, domain->name ); - if ( !cli_nt_session_open (*cli, pipe_index) ) { - - result = NT_STATUS_PIPE_NOT_AVAILABLE; - - /* This might be a NT4 DC */ - if ( is_win2k_pipe(pipe_index) ) - add_failed_connection = False; - - cli_shutdown(*cli); - goto done; - } + (*cli)->pipe_auth_flags = 0; result = NT_STATUS_OK; add_failed_connection = False; @@ -463,18 +397,158 @@ static BOOL add_sockaddr_to_array(TALLOC_CTX *mem_ctx, return True; } +static void mailslot_name(struct in_addr dc_ip, fstring name) +{ + fstr_sprintf(name, "\\MAILSLOT\\NET\\GETDC%X", dc_ip.s_addr); +} + +static BOOL send_getdc_request(struct in_addr dc_ip, + const char *domain_name, + const DOM_SID *sid) +{ + pstring outbuf; + char *p; + fstring my_acct_name; + fstring my_mailslot; + + mailslot_name(dc_ip, my_mailslot); + + memset(outbuf, '\0', sizeof(outbuf)); + + p = outbuf; + + SCVAL(p, 0, SAMLOGON); + p++; + + SCVAL(p, 0, 0); /* Count pointer ... */ + p++; + + SIVAL(p, 0, 0); /* The sender's token ... */ + p += 2; + + p += dos_PutUniCode(p, global_myname(), sizeof(pstring), True); + fstr_sprintf(my_acct_name, "%s$", global_myname()); + p += dos_PutUniCode(p, my_acct_name, sizeof(pstring), True); + + memcpy(p, my_mailslot, strlen(my_mailslot)+1); + p += strlen(my_mailslot)+1; + + SIVAL(p, 0, 0x80); + p+=4; + + SIVAL(p, 0, sid_size(sid)); + p+=4; + + p = ALIGN4(p, outbuf); + + sid_linearize(p, sid_size(sid), sid); + p += sid_size(sid); + + SIVAL(p, 0, 1); + SSVAL(p, 4, 0xffff); + SSVAL(p, 6, 0xffff); + p+=8; + + return cli_send_mailslot(False, "\\MAILSLOT\\NET\\NTLOGON", 0, + outbuf, PTR_DIFF(p, outbuf), + global_myname(), 0, domain_name, 0x1c, + dc_ip); +} + +static BOOL receive_getdc_response(struct in_addr dc_ip, + const char *domain_name, + fstring dc_name) +{ + struct packet_struct *packet; + fstring my_mailslot; + char *buf, *p; + fstring dcname, user, domain; + int len; + + mailslot_name(dc_ip, my_mailslot); + + packet = receive_unexpected(DGRAM_PACKET, 0, my_mailslot); + + if (packet == NULL) { + DEBUG(5, ("Did not receive packet for %s\n", my_mailslot)); + return False; + } + + DEBUG(5, ("Received packet for %s\n", my_mailslot)); + + buf = packet->packet.dgram.data; + len = packet->packet.dgram.datasize; + + if (len < 70) { + /* 70 is a completely arbitrary value to make sure + the SVAL below does not read uninitialized memory */ + DEBUG(3, ("GetDC got short response\n")); + return False; + } + + /* This should be (buf-4)+SVAL(buf-4, smb_vwv12)... */ + p = buf+SVAL(buf, smb_vwv10); + + if (CVAL(p,0) != SAMLOGON_R) { + DEBUG(8, ("GetDC got invalid response type %d\n", CVAL(p, 0))); + return False; + } + + p+=2; + pull_ucs2(buf, dcname, p, sizeof(dcname), PTR_DIFF(buf+len, p), + STR_TERMINATE|STR_NOALIGN); + p = skip_unibuf(p, PTR_DIFF(buf+len, p)); + pull_ucs2(buf, user, p, sizeof(dcname), PTR_DIFF(buf+len, p), + STR_TERMINATE|STR_NOALIGN); + p = skip_unibuf(p, PTR_DIFF(buf+len, p)); + pull_ucs2(buf, domain, p, sizeof(dcname), PTR_DIFF(buf+len, p), + STR_TERMINATE|STR_NOALIGN); + p = skip_unibuf(p, PTR_DIFF(buf+len, p)); + + if (!strequal(domain, domain_name)) { + DEBUG(3, ("GetDC: Expected domain %s, got %s\n", + domain_name, domain)); + return False; + } + + p = dcname; + if (*p == '\\') p += 1; + if (*p == '\\') p += 1; + + fstrcpy(dc_name, p); + + DEBUG(10, ("GetDC gave name %s for domain %s\n", + dc_name, domain)); + + return True; +} + /******************************************************************* convert an ip to a name *******************************************************************/ -static void dcip_to_name( const char *domainname, const char *realm, struct in_addr ip, fstring name ) +static void dcip_to_name( const char *domainname, const char *realm, + const DOM_SID *sid, struct in_addr ip, fstring name ) { - /* try node status request first */ + int i; + + /* try GETDC requests first */ + + send_getdc_request(ip, domainname, sid); + smb_msleep(100); + + for (i=0; i<5; i++) { + if (receive_getdc_response(ip, domainname, name)) + return; + smb_msleep(500); + } + + /* try node status request */ if ( name_status_find(domainname, 0x1c, 0x20, ip, name) ) return; - /* backup in case the ads stuff fails */ + /* backup in case the netbios stuff fails */ fstrcpy( name, inet_ntoa(ip) ); @@ -510,7 +584,6 @@ static void dcip_to_name( const char *domainname, const char *realm, struct in_a return; } - /******************************************************************* Retreive a list of IP address for domain controllers. Fill in the dcs[] with results. @@ -553,6 +626,8 @@ static BOOL get_dcs(TALLOC_CTX *mem_ctx, const struct winbindd_domain *domain, if ( iplist_size==0 && lp_security() == SEC_ADS ) get_sorted_dc_list(domain->alt_name, &ip_list, &iplist_size, True); + /* FIXME!! this is where we should re-insert the GETDC requests --jerry */ + /* now add to the dc array. We'll wait until the last minute to look up the name of the DC. But we fill in the char* for the ip now in to make the failed connection cache work */ @@ -616,7 +691,7 @@ static BOOL find_new_dc(TALLOC_CTX *mem_ctx, the name, now try to get the name */ if ( is_ipaddress(dcnames[fd_index]) || *dcnames[fd_index] == '\0' ) - dcip_to_name( domain->name, domain->alt_name, addr->sin_addr, dcname ); + dcip_to_name( domain->name, domain->alt_name, &domain->sid, addr->sin_addr, dcname ); else fstrcpy(dcname, dcnames[fd_index]); @@ -624,7 +699,6 @@ static BOOL find_new_dc(TALLOC_CTX *mem_ctx, } static NTSTATUS cm_open_connection(struct winbindd_domain *domain, - const int pipe_index, struct winbindd_cm_conn *new_conn) { TALLOC_CTX *mem_ctx; @@ -659,17 +733,8 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain, new_conn->cli = NULL; - result = cm_prepare_connection(domain, fd, pipe_index, - domain->dcname, - &new_conn->cli, &retry); - - if (NT_STATUS_IS_OK(result)) { - fstrcpy(new_conn->domain, domain->name); - /* Initialise SMB connection */ - fstrcpy(new_conn->pipe_name, - get_pipe_name_from_index(pipe_index)); - break; - } + result = cm_prepare_connection(domain, fd, domain->dcname, + &new_conn->cli, &retry); if (!retry) break; @@ -679,121 +744,86 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain, return result; } -/************************************************************************ - Wrapper around statuc cm_open_connection to retreive a freshly - setup cli_state struct -************************************************************************/ - -NTSTATUS cm_fresh_connection(struct winbindd_domain *domain, const int pipe_index, - struct cli_state **cli) -{ - NTSTATUS result; - struct winbindd_cm_conn conn; - - result = cm_open_connection( domain, pipe_index, &conn ); - - if ( NT_STATUS_IS_OK(result) ) - *cli = conn.cli; - - return result; -} - /* Return true if a connection is still alive */ -static BOOL connection_ok(struct winbindd_cm_conn *conn) +void invalidate_cm_connection(struct winbindd_cm_conn *conn) { - if (!conn) { - smb_panic("Invalid parameter passed to connection_ok(): conn was NULL!\n"); - return False; + if (conn->samr_pipe != NULL) { + cli_rpc_close(conn->samr_pipe); + conn->samr_pipe = NULL; } - if (!conn->cli) { - DEBUG(3, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n", - conn->controller, conn->domain, conn->pipe_name)); - return False; + if (conn->lsa_pipe != NULL) { + cli_rpc_close(conn->lsa_pipe); + conn->lsa_pipe = NULL; } - if (!conn->cli->initialised) { - DEBUG(3, ("Connection to %s for domain %s (pipe %s) was never initialised!\n", - conn->controller, conn->domain, conn->pipe_name)); - return False; + if (conn->netlogon_auth2_pipe != NULL) { + cli_rpc_close(conn->netlogon_auth2_pipe); + conn->netlogon_auth2_pipe = NULL; } - if (conn->cli->fd == -1) { - DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n", - conn->controller, conn->domain, conn->pipe_name)); - return False; + if (conn->netlogon_pipe != NULL) { + cli_rpc_close(conn->netlogon_pipe); + conn->netlogon_pipe = NULL; } - - return True; -} -/* Search the cache for a connection. If there is a broken one, - shut it down properly and return NULL. */ + if (conn->cli) + cli_shutdown(conn->cli); + + conn->cli = NULL; +} -static void find_cm_connection(struct winbindd_domain *domain, const char *pipe_name, - struct winbindd_cm_conn **conn_out) +void close_conns_after_fork(void) { - struct winbindd_cm_conn *conn; + struct winbindd_domain *domain; - for (conn = cm_conns; conn; ) { - if (strequal(conn->domain, domain->name) && - strequal(conn->pipe_name, pipe_name)) { - if (!connection_ok(conn)) { - /* Dead connection - remove it. */ - struct winbindd_cm_conn *conn_temp = conn->next; - if (conn->cli) - cli_shutdown(conn->cli); - DLIST_REMOVE(cm_conns, conn); - SAFE_FREE(conn); - conn = conn_temp; /* Keep the loop moving */ - continue; - } else { - break; - } - } - conn = conn->next; - } + for (domain = domain_list(); domain; domain = domain->next) { + if (domain->conn.cli == NULL) + continue; - *conn_out = conn; -} + if (domain->conn.cli->fd == -1) + continue; -/* Initialize a new connection up to the RPC BIND. */ + close(domain->conn.cli->fd); + domain->conn.cli->fd = -1; + } +} -static NTSTATUS new_cm_connection(struct winbindd_domain *domain, const char *pipe_name, - struct winbindd_cm_conn **conn_out) +static BOOL connection_ok(struct winbindd_domain *domain) { - struct winbindd_cm_conn *conn; - NTSTATUS result; + if (domain->conn.cli == NULL) { + DEBUG(8, ("Connection to %s for domain %s has NULL " + "cli!\n", domain->dcname, domain->name)); + return False; + } - if (!(conn = SMB_MALLOC_P(struct winbindd_cm_conn))) - return NT_STATUS_NO_MEMORY; - - ZERO_STRUCTP(conn); - - if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, get_pipe_index(pipe_name), conn))) { - DEBUG(3, ("Could not open a connection to %s for %s (%s)\n", - domain->name, pipe_name, nt_errstr(result))); - SAFE_FREE(conn); - return result; + if (!domain->conn.cli->initialised) { + DEBUG(3, ("Connection to %s for domain %s was never " + "initialised!\n", domain->dcname, domain->name)); + return False; } - DLIST_ADD(cm_conns, conn); - *conn_out = conn; - return NT_STATUS_OK; -} + if (domain->conn.cli->fd == -1) { + DEBUG(3, ("Connection to %s for domain %s has died or was " + "never started (fd == -1)\n", + domain->dcname, domain->name)); + return False; + } -/* Get a connection to the remote DC and open the pipe. If there is already a connection, use that */ + return True; +} + +/* Initialize a new connection up to the RPC BIND. */ -static NTSTATUS get_connection_from_cache(struct winbindd_domain *domain, const char *pipe_name, - struct winbindd_cm_conn **conn_out) +static NTSTATUS init_dc_connection(struct winbindd_domain *domain) { - find_cm_connection(domain, pipe_name, conn_out); - - if (*conn_out != NULL) + if (connection_ok(domain)) return NT_STATUS_OK; - return new_cm_connection(domain, pipe_name, conn_out); + invalidate_cm_connection(&domain->conn); + + return cm_open_connection(domain, &domain->conn); } /********************************************************************************** @@ -806,11 +836,15 @@ static NTSTATUS get_connection_from_cache(struct winbindd_domain *domain, const void set_dc_type_and_flags( struct winbindd_domain *domain ) { NTSTATUS result; - struct winbindd_cm_conn conn; DS_DOMINFO_CTR ctr; TALLOC_CTX *mem_ctx = NULL; + struct rpc_pipe_client *cli; + POLICY_HND pol; - ZERO_STRUCT( conn ); + char *domain_name = NULL; + char *dns_name = NULL; + DOM_SID *dom_sid = NULL; + ZERO_STRUCT( ctr ); domain->native_mode = False; @@ -820,100 +854,100 @@ void set_dc_type_and_flags( struct winbindd_domain *domain ) domain->initialized = True; return; } - - if ( !NT_STATUS_IS_OK(result = cm_open_connection(domain, PI_LSARPC_DS, &conn)) ) { - DEBUG(5, ("set_dc_type_and_flags: Could not open a connection to %s for PIPE_LSARPC (%s)\n", + + result = init_dc_connection(domain); + if (!NT_STATUS_IS_OK(result)) { + DEBUG(5, ("set_dc_type_and_flags: Could not open a connection " + "to %s: (%s)\n", domain->name, nt_errstr(result))); + domain->initialized = True; + return; + } + + cli = cli_rpc_open_noauth(domain->conn.cli, PI_LSARPC_DS); + + if (cli == NULL) { + DEBUG(5, ("set_dc_type_and_flags: Could not bind to " + "PI_LSARPC_DS on domain %s: (%s)\n", domain->name, nt_errstr(result))); domain->initialized = True; return; } - - if ( conn.cli ) { - if ( !NT_STATUS_IS_OK(cli_ds_getprimarydominfo( conn.cli, - conn.cli->mem_ctx, DsRolePrimaryDomainInfoBasic, &ctr)) ) { - goto done; - } + + result = rpccli_ds_getprimarydominfo(cli, cli->cli->mem_ctx, + DsRolePrimaryDomainInfoBasic, + &ctr); + cli_rpc_close(cli); + + if (!NT_STATUS_IS_OK(result)) { + domain->initialized = True; + return; } - - if ( (ctr.basic->flags & DSROLE_PRIMARY_DS_RUNNING) - && !(ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE) ) + + if ((ctr.basic->flags & DSROLE_PRIMARY_DS_RUNNING) && + !(ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE) ) domain->native_mode = True; - /* Cheat - shut down the DS pipe, and open LSA */ + cli = cli_rpc_open_noauth(domain->conn.cli, PI_LSARPC); - cli_nt_session_close(conn.cli); - - if ( cli_nt_session_open (conn.cli, PI_LSARPC) ) { - char *domain_name = NULL; - char *dns_name = NULL; - DOM_SID *dom_sid = NULL; + if (cli == NULL) { + domain->initialized = True; + return; + } - mem_ctx = talloc_init("set_dc_type_and_flags on domain %s\n", domain->name); - if (!mem_ctx) { - DEBUG(1, ("set_dc_type_and_flags: talloc_init() failed\n")); - return; - } + mem_ctx = talloc_init("set_dc_type_and_flags on domain %s\n", + domain->name); + if (!mem_ctx) { + DEBUG(1, ("set_dc_type_and_flags: talloc_init() failed\n")); + return; + } - result = cli_lsa_open_policy2(conn.cli, mem_ctx, True, - SEC_RIGHTS_MAXIMUM_ALLOWED, - &conn.pol); + result = rpccli_lsa_open_policy2(cli, mem_ctx, True, + SEC_RIGHTS_MAXIMUM_ALLOWED, &pol); - if (NT_STATUS_IS_OK(result)) { - /* This particular query is exactly what Win2k clients use - to determine that the DC is active directory */ - result = cli_lsa_query_info_policy2(conn.cli, mem_ctx, - &conn.pol, - 12, &domain_name, - &dns_name, NULL, - NULL, &dom_sid); - } + if (NT_STATUS_IS_OK(result)) { + /* This particular query is exactly what Win2k clients use + to determine that the DC is active directory */ + result = rpccli_lsa_query_info_policy2(cli, mem_ctx, &pol, + 12, &domain_name, + &dns_name, NULL, + NULL, &dom_sid); + } + + if (NT_STATUS_IS_OK(result)) { + if (domain_name) + fstrcpy(domain->name, domain_name); + + if (dns_name) + fstrcpy(domain->alt_name, dns_name); + + if (dom_sid) + sid_copy(&domain->sid, dom_sid); + domain->active_directory = True; + } else { + + result = rpccli_lsa_open_policy(cli, mem_ctx, True, + SEC_RIGHTS_MAXIMUM_ALLOWED, + &pol); + + if (!NT_STATUS_IS_OK(result)) + goto done; + + result = rpccli_lsa_query_info_policy(cli, mem_ctx, + &pol, 5, &domain_name, + &dom_sid); + if (NT_STATUS_IS_OK(result)) { if (domain_name) fstrcpy(domain->name, domain_name); - - if (dns_name) - fstrcpy(domain->alt_name, dns_name); if (dom_sid) sid_copy(&domain->sid, dom_sid); - - domain->active_directory = True; - } else { - - result = cli_lsa_open_policy(conn.cli, mem_ctx, True, - SEC_RIGHTS_MAXIMUM_ALLOWED, - &conn.pol); - - if (!NT_STATUS_IS_OK(result)) - goto done; - - result = cli_lsa_query_info_policy(conn.cli, mem_ctx, - &conn.pol, 5, &domain_name, - &dom_sid); - - if (NT_STATUS_IS_OK(result)) { - if (domain_name) - fstrcpy(domain->name, domain_name); - - if (dom_sid) - sid_copy(&domain->sid, dom_sid); - } } } - done: - - DEBUG(3,("add_trusted_domain: %s is an %s %s domain\n", domain->name, - domain->active_directory ? "ADS" : "NT4", - domain->native_mode ? "native mode" : - ((domain->active_directory && !domain->native_mode) ? "mixed mode" : ""))); - /* close the connection; no other calls use this pipe and it is called only - on reestablishing the domain list --jerry */ - - if ( conn.cli ) - cli_shutdown( conn.cli ); + cli_rpc_close(cli); talloc_destroy(mem_ctx); @@ -922,264 +956,278 @@ done: return; } +static BOOL cm_get_schannel_key(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + unsigned char **session_key) +{ + struct rpc_pipe_client *cli; + DOM_CRED *credentials; + if (lp_client_schannel() == False) + return False; -/* Return a LSA policy handle on a domain */ + return NT_STATUS_IS_OK(cm_connect_netlogon(domain, mem_ctx, + &cli, session_key, + &credentials)); +} -NTSTATUS cm_get_lsa_handle(struct winbindd_domain *domain, CLI_POLICY_HND **return_hnd) +NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, + struct rpc_pipe_client **cli, POLICY_HND *sam_handle) { struct winbindd_cm_conn *conn; - uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED; NTSTATUS result; - static CLI_POLICY_HND hnd; - /* Look for existing connections */ - - if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn))) + result = init_dc_connection(domain); + if (!NT_STATUS_IS_OK(result)) return result; - /* This *shitty* code needs scrapping ! JRA */ - - if (policy_handle_is_valid(&conn->pol)) { - hnd.pol = conn->pol; - hnd.cli = conn->cli; - *return_hnd = &hnd; + conn = &domain->conn; - return NT_STATUS_OK; - } - - result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False, - des_access, &conn->pol); + if (conn->samr_pipe == NULL) { + unsigned char *session_key; - if (!NT_STATUS_IS_OK(result)) { - /* Hit the cache code again. This cleans out the old connection and gets a new one */ - if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */ - if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn))) - return result; + if (cm_get_schannel_key(domain, mem_ctx, &session_key)) + conn->samr_pipe = cli_rpc_open_schannel(conn->cli, + PI_SAMR, + session_key, + domain->name); + else + conn->samr_pipe = cli_rpc_open_noauth(conn->cli, + PI_SAMR); - result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False, - des_access, &conn->pol); + if (conn->samr_pipe == NULL) { + result = NT_STATUS_PIPE_NOT_AVAILABLE; + goto done; } - if (!NT_STATUS_IS_OK(result)) { - cli_shutdown(conn->cli); - DLIST_REMOVE(cm_conns, conn); - SAFE_FREE(conn); - return result; - } - } + result = rpccli_samr_connect(conn->samr_pipe, mem_ctx, + SEC_RIGHTS_MAXIMUM_ALLOWED, + &conn->sam_connect_handle); + if (!NT_STATUS_IS_OK(result)) + goto done; - hnd.pol = conn->pol; - hnd.cli = conn->cli; + result = rpccli_samr_open_domain(conn->samr_pipe, + mem_ctx, + &conn->sam_connect_handle, + SEC_RIGHTS_MAXIMUM_ALLOWED, + &domain->sid, + &conn->sam_domain_handle); + } - *return_hnd = &hnd; + done: + if (!NT_STATUS_IS_OK(result)) { + invalidate_cm_connection(conn); + return NT_STATUS_UNSUCCESSFUL; + } - return NT_STATUS_OK; + *cli = conn->samr_pipe; + *sam_handle = conn->sam_domain_handle; + return result; } -/* Return a SAM policy handle on a domain */ - -NTSTATUS cm_get_sam_handle(struct winbindd_domain *domain, CLI_POLICY_HND **return_hnd) -{ +NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, + struct rpc_pipe_client **cli, POLICY_HND *lsa_policy) +{ struct winbindd_cm_conn *conn; - uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED; NTSTATUS result; - static CLI_POLICY_HND hnd; - /* Look for existing connections */ - - if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn))) + result = init_dc_connection(domain); + if (!NT_STATUS_IS_OK(result)) return result; - - /* This *shitty* code needs scrapping ! JRA */ - - if (policy_handle_is_valid(&conn->pol)) { - hnd.pol = conn->pol; - hnd.cli = conn->cli; - - *return_hnd = &hnd; - return NT_STATUS_OK; - } - - result = cli_samr_connect(conn->cli, conn->cli->mem_ctx, - des_access, &conn->pol); + conn = &domain->conn; - if (!NT_STATUS_IS_OK(result)) { - /* Hit the cache code again. This cleans out the old connection and gets a new one */ - if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */ - - if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn))) - return result; + if (conn->lsa_pipe == NULL) { + unsigned char *session_key; - result = cli_samr_connect(conn->cli, conn->cli->mem_ctx, - des_access, &conn->pol); - } + if (cm_get_schannel_key(domain, mem_ctx, &session_key)) + conn->lsa_pipe = cli_rpc_open_schannel(conn->cli, + PI_LSARPC, + session_key, + domain->name); + else + conn->lsa_pipe = cli_rpc_open_noauth(conn->cli, + PI_LSARPC); - if (!NT_STATUS_IS_OK(result)) { - - cli_shutdown(conn->cli); - DLIST_REMOVE(cm_conns, conn); - SAFE_FREE(conn); - - /* log a message for possible Windows 2003 SP1 DC's */ - if ( NT_STATUS_EQUAL(result,NT_STATUS_ACCESS_DENIED) && (lp_security() == SEC_DOMAIN) ) { - DEBUG(0,("samr_connect() received NT_STATUS_ACCESS_DENIED. If you are connecting \n")); - DEBUGADD(0,("to a Windows 2003 SP1 DC, this is a known issue. There are two current \n")); - DEBUGADD(0,("workarounds:\n")); - DEBUGADD(0,("(a) Move your configuration to security = ads, or\n")); - DEBUGADD(0,("(b) set 'client schannel = no' in smb.conf and use 'wbinfo --set-auth-user'\n")); - DEBUGADD(0,(" to define the credentials when connecting to the DC\n")); - } - - return result; + if (conn->lsa_pipe == NULL) { + result = NT_STATUS_PIPE_NOT_AVAILABLE; + goto done; } - } - hnd.pol = conn->pol; - hnd.cli = conn->cli; + result = rpccli_lsa_open_policy(conn->lsa_pipe, mem_ctx, True, + SEC_RIGHTS_MAXIMUM_ALLOWED, + &conn->lsa_policy); + } - *return_hnd = &hnd; + done: + if (!NT_STATUS_IS_OK(result)) { + invalidate_cm_connection(conn); + return NT_STATUS_UNSUCCESSFUL; + } - return NT_STATUS_OK; + *cli = conn->lsa_pipe; + *lsa_policy = conn->lsa_policy; + return result; } -/* Get a handle on a netlogon pipe. This is a bit of a hack to re-use the - netlogon pipe as no handle is returned. */ +/******************************************************************* + wrapper around retrieving the trust account password +*******************************************************************/ -NTSTATUS cm_get_netlogon_cli(struct winbindd_domain *domain, - const unsigned char *trust_passwd, - uint32 sec_channel_type, - BOOL fresh, - struct cli_state **cli) +static BOOL get_trust_pw(const char *domain, uint8 ret_pwd[16], + uint32 *channel) { - NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; - struct winbindd_cm_conn *conn; - fstring lock_name; - BOOL got_mutex; - - if (!cli) - return NT_STATUS_INVALID_PARAMETER; + DOM_SID sid; + char *pwd; + time_t last_set_time; - /* Open an initial conection - keep the mutex. */ + /* if we are a DC and this is not our domain, then lookup an account + for the domain trust */ - find_cm_connection(domain, PIPE_NETLOGON, &conn); + if ( IS_DC && !strequal(domain, lp_workgroup()) && + lp_allow_trusted_domains() ) { - if ( fresh && (conn != NULL) ) { - cli_shutdown(conn->cli); - conn->cli = NULL; + if (!secrets_fetch_trusted_domain_password(domain, &pwd, &sid, + &last_set_time)) { + DEBUG(0, ("get_trust_pw: could not fetch trust " + "account password for trusted domain %s\n", + domain)); + return False; + } - conn = NULL; + *channel = SEC_CHAN_DOMAIN; + E_md4hash(pwd, ret_pwd); + SAFE_FREE(pwd); - /* purge connection from cache */ - find_cm_connection(domain, PIPE_NETLOGON, &conn); - if (conn != NULL) { - DEBUG(0,("Could not purge connection\n")); - return NT_STATUS_UNSUCCESSFUL; - } + return True; } - if (conn != NULL) { - *cli = conn->cli; - return NT_STATUS_OK; - } + /* Just get the account for the requested domain. In the future this + * might also cover to be member of more than one domain. */ - result = new_cm_connection(domain, PIPE_NETLOGON, &conn); + if (secrets_fetch_trust_account_password(domain, ret_pwd, + &last_set_time, channel)) + return True; - if (!NT_STATUS_IS_OK(result)) - return result; - - fstr_sprintf(lock_name, "NETLOGON\\%s", conn->controller); + DEBUG(5, ("get_trust_pw: could not fetch trust account " + "password for domain %s\n", domain)); + return False; +} - if (!(got_mutex = secrets_named_mutex(lock_name, WINBIND_SERVER_MUTEX_WAIT_TIME))) { - DEBUG(0,("cm_get_netlogon_cli: mutex grab failed for %s\n", conn->controller)); - } - - if ( sec_channel_type == SEC_CHAN_DOMAIN ) - fstr_sprintf(conn->cli->mach_acct, "%s$", lp_workgroup()); - - /* This must be the remote domain (not ours) for schannel */ +NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + struct rpc_pipe_client **cli, + unsigned char **session_key, + DOM_CRED **credentials) +{ + struct winbindd_cm_conn *conn; + NTSTATUS result; - fstrcpy( conn->cli->domain, domain->name); - - result = cli_nt_establish_netlogon(conn->cli, sec_channel_type, trust_passwd); - - if (got_mutex) - secrets_named_mutex_release(lock_name); - - if (!NT_STATUS_IS_OK(result)) { - cli_shutdown(conn->cli); - DLIST_REMOVE(cm_conns, conn); - SAFE_FREE(conn); + uint32 neg_flags = NETLOGON_NEG_AUTH2_FLAGS; + uint8 mach_pwd[16]; + uint32 sec_chan_type; + DOM_CHAL clnt_chal, srv_chal, rcv_chal; + const char *server_name; + const char *account_name; + UTIME zerotime; + + result = init_dc_connection(domain); + if (!NT_STATUS_IS_OK(result)) return result; - } - *cli = conn->cli; + conn = &domain->conn; - return result; -} + if (conn->netlogon_pipe != NULL) { + *cli = conn->netlogon_pipe; + *session_key = (unsigned char *)&conn->sess_key; + *credentials = &conn->clnt_cred; + return NT_STATUS_OK; + } -/* Dump the current connection status */ + if (!get_trust_pw(domain->name, mach_pwd, &sec_chan_type)) + return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; -static void dump_conn_list(void) -{ - struct winbindd_cm_conn *con; + conn->netlogon_auth2_pipe = cli_rpc_open_noauth(conn->cli, + PI_NETLOGON); + if (conn->netlogon_auth2_pipe == NULL) + return NT_STATUS_UNSUCCESSFUL; - DEBUG(0, ("\tDomain Controller Pipe\n")); + if (lp_client_schannel() != False) + neg_flags |= NETLOGON_NEG_SCHANNEL; - for(con = cm_conns; con; con = con->next) { - char *msg; + generate_random_buffer(clnt_chal.data, 8); - /* Display pipe info */ - - if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) { - DEBUG(0, ("Error: not enough memory!\n")); - } else { - DEBUG(0, ("%s\n", msg)); - SAFE_FREE(msg); - } - } -} + server_name = talloc_asprintf(mem_ctx, "\\\\%s", domain->dcname); + account_name = talloc_asprintf(mem_ctx, "%s$", + domain->primary ? + global_myname() : domain->name); -void winbindd_cm_status(void) -{ - /* List open connections */ + if ((server_name == NULL) || (account_name == NULL)) + return NT_STATUS_NO_MEMORY; - DEBUG(0, ("winbindd connection manager status:\n")); + result = rpccli_net_req_chal(conn->netlogon_auth2_pipe, server_name, + global_myname(), &clnt_chal, &srv_chal); + if (!NT_STATUS_IS_OK(result)) + return result; - if (cm_conns) - dump_conn_list(); - else - DEBUG(0, ("\tNo active connections\n")); -} + /**************** Long-term Session key **************/ -/* Close all cached connections */ + /* calculate the session key */ + cred_session_key(&clnt_chal, &srv_chal, mach_pwd, conn->sess_key); + memset((char *)conn->sess_key+8, '\0', 8); -void winbindd_cm_flush(void) -{ - struct winbindd_cm_conn *conn, tmp; + /* calculate auth2 credentials */ + zerotime.time = 0; + cred_create(conn->sess_key, &clnt_chal, zerotime, + &conn->clnt_cred.challenge); - /* Flush connection cache */ + result = rpccli_net_auth2(conn->netlogon_auth2_pipe, server_name, + account_name, sec_chan_type, global_myname(), + &conn->clnt_cred.challenge, &neg_flags, + &rcv_chal); - for (conn = cm_conns; conn; conn = conn->next) { + if (!NT_STATUS_IS_OK(result)) + return result; - if (!connection_ok(conn)) - continue; + zerotime.time = 0; + if (!cred_assert(&rcv_chal, conn->sess_key, &srv_chal, zerotime)) { + DEBUG(0, ("Server replied with bad credential\n")); + return NT_STATUS_ACCESS_DENIED; + } - DEBUG(10, ("Closing connection to %s on %s\n", - conn->pipe_name, conn->controller)); + if ((lp_client_schannel() == True) && + ((neg_flags & NETLOGON_NEG_SCHANNEL) == 0)) { + DEBUG(3, ("Server did not offer schannel\n")); + cli_rpc_close(conn->netlogon_auth2_pipe); + conn->netlogon_auth2_pipe = NULL; + return NT_STATUS_ACCESS_DENIED; + } - if (conn->cli) - cli_shutdown(conn->cli); + if ((lp_client_schannel() == False) || + ((neg_flags & NETLOGON_NEG_SCHANNEL) == 0)) { + /* keep the existing connection to NETLOGON open */ + conn->netlogon_pipe = conn->netlogon_auth2_pipe; + conn->netlogon_auth2_pipe = NULL; + *cli = conn->netlogon_pipe; + *session_key = (unsigned char *)&conn->sess_key; + *credentials = &conn->clnt_cred; + return NT_STATUS_OK; + } - tmp.next = conn->next; + conn->netlogon_pipe = cli_rpc_open_schannel(conn->cli, PI_NETLOGON, + conn->sess_key, + domain->name); - DLIST_REMOVE(cm_conns, conn); - SAFE_FREE(conn); - conn = &tmp; + if (conn->netlogon_pipe == NULL) { + DEBUG(3, ("Could not open schannel'ed NETLOGON pipe\n")); + cli_rpc_close(conn->netlogon_auth2_pipe); + conn->netlogon_auth2_pipe = NULL; + return NT_STATUS_ACCESS_DENIED; } - /* Flush failed connection cache */ - - flush_negative_conn_cache(); + *cli = conn->netlogon_pipe; + *session_key = (unsigned char *)&conn->sess_key; + *credentials = &conn->clnt_cred; + + return NT_STATUS_OK; } diff --git a/source3/nsswitch/winbindd_dual.c b/source3/nsswitch/winbindd_dual.c index 587507ee29..f8b802dafa 100644 --- a/source3/nsswitch/winbindd_dual.c +++ b/source3/nsswitch/winbindd_dual.c @@ -4,6 +4,7 @@ Winbind background daemon Copyright (C) Andrew Tridgell 2002 + Copyright (C) Volker Lendecke 2004,2005 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -51,6 +52,39 @@ struct dual_list { static struct dual_list *dual_list; static struct dual_list *dual_list_end; +/* Read some data from a client connection */ + +static void dual_client_read(struct winbindd_cli_state *state) +{ + int n; + + /* Read data */ + + n = sys_read(state->sock, state->read_buf_len + + (char *)&state->request, + sizeof(state->request) - state->read_buf_len); + + DEBUG(10,("client_read: read %d bytes. Need %ld more for a full " + "request.\n", n, (unsigned long)(sizeof(state->request) - n - + state->read_buf_len) )); + + /* Read failed, kill client */ + + if (n == -1 || n == 0) { + DEBUG(5,("read failed on sock %d, pid %lu: %s\n", + state->sock, (unsigned long)state->pid, + (n == -1) ? strerror(errno) : "EOF")); + + state->finished = True; + return; + } + + /* Update client state */ + + state->read_buf_len += n; + state->last_access = time(NULL); +} + /* setup a select() including the dual daemon pipe */ @@ -176,7 +210,7 @@ void do_dual_daemon(void) main_loop_talloc_free(); /* fetch a request from the main daemon */ - winbind_client_read(&state); + dual_client_read(&state); if (state.finished) { /* we lost contact with our parent */ @@ -212,3 +246,412 @@ void do_dual_daemon(void) } } +/* + * Machinery for async requests sent to children. You set up a + * winbindd_request, select a child to query, and issue a async_request + * call. When the request is completed, the callback function you specified is + * called back with the private pointer you gave to async_request. + */ + +struct winbindd_async_request { + struct winbindd_async_request *next, *prev; + TALLOC_CTX *mem_ctx; + struct winbindd_child *child; + struct winbindd_request *request; + struct winbindd_response *response; + void (*continuation)(void *private, BOOL success); + void *private; +}; + +static void async_request_sent(void *private, BOOL success); +static void async_reply_recv(void *private, BOOL success); +static void schedule_async_request(struct winbindd_child *child); + +void async_request(TALLOC_CTX *mem_ctx, struct winbindd_child *child, + struct winbindd_request *request, + struct winbindd_response *response, + void (*continuation)(void *private, BOOL success), + void *private) +{ + struct winbindd_async_request *state, *tmp; + + SMB_ASSERT(continuation != NULL); + + state = TALLOC_P(mem_ctx, struct winbindd_async_request); + + if (state == NULL) { + DEBUG(0, ("talloc failed\n")); + continuation(private, False); + return; + } + + state->mem_ctx = mem_ctx; + state->child = child; + state->request = request; + state->response = response; + state->continuation = continuation; + state->private = private; + + DLIST_ADD_END(child->requests, state, tmp); + + schedule_async_request(child); + + return; +} + +static void async_request_sent(void *private, BOOL success) +{ + struct winbindd_async_request *state = + talloc_get_type_abort(private, struct winbindd_async_request); + + if (!success) { + DEBUG(5, ("Could not send async request\n")); + + state->response->length = sizeof(struct winbindd_response); + state->response->result = WINBINDD_ERROR; + state->continuation(state->private, False); + return; + } + + /* Request successfully sent to the child, setup the wait for reply */ + + setup_async_read(&state->child->event, + &state->response->result, + sizeof(state->response->result), + async_reply_recv, state); +} + +static void async_reply_recv(void *private, BOOL success) +{ + struct winbindd_async_request *state = + talloc_get_type_abort(private, struct winbindd_async_request); + struct winbindd_child *child = state->child; + + state->response->length = sizeof(struct winbindd_response); + + if (!success) { + DEBUG(5, ("Could not receive async reply\n")); + state->response->result = WINBINDD_ERROR; + } + + if (state->response->result == WINBINDD_OK) + SMB_ASSERT(cache_retrieve_response(child->pid, + state->response)); + + DLIST_REMOVE(child->requests, state); + + schedule_async_request(child); + + state->continuation(state->private, True); +} + +static BOOL fork_domain_child(struct winbindd_child *child); + +static void schedule_async_request(struct winbindd_child *child) +{ + struct winbindd_async_request *request = child->requests; + + if (request == NULL) { + return; + } + + if (child->event.flags != 0) { + return; /* Busy */ + } + + if ((child->pid == 0) && (!fork_domain_child(child))) { + /* Cancel all outstanding requests */ + + while (request != NULL) { + /* request might be free'd in the continuation */ + struct winbindd_async_request *next = request->next; + request->continuation(request->private, False); + request = next; + } + return; + } + + setup_async_write(&child->event, request->request, + sizeof(*request->request), + async_request_sent, request); + return; +} + +struct domain_request_state { + TALLOC_CTX *mem_ctx; + struct winbindd_domain *domain; + struct winbindd_request *request; + struct winbindd_response *response; + void (*continuation)(void *private, BOOL success); + void *private; +}; + +static void domain_init_recv(void *private, BOOL success); + +void async_domain_request(TALLOC_CTX *mem_ctx, + struct winbindd_domain *domain, + struct winbindd_request *request, + struct winbindd_response *response, + void (*continuation)(void *private, BOOL success), + void *private) +{ + struct domain_request_state *state; + + if (domain->initialized) { + async_request(mem_ctx, &domain->child, request, response, + continuation, private); + return; + } + + state = TALLOC_P(mem_ctx, struct domain_request_state); + if (state == NULL) { + DEBUG(0, ("talloc failed\n")); + continuation(private, False); + return; + } + + state->mem_ctx = mem_ctx; + state->domain = domain; + state->request = request; + state->response = response; + state->continuation = continuation; + state->private = private; + + init_child_connection(domain, domain_init_recv, state); +} + +static void domain_init_recv(void *private, BOOL success) +{ + struct domain_request_state *state = + talloc_get_type_abort(private, struct domain_request_state); + + if (!success) { + DEBUG(5, ("Domain init returned an error\n")); + state->continuation(state->private, False); + return; + } + + async_request(state->mem_ctx, &state->domain->child, + state->request, state->response, + state->continuation, state->private); +} + +struct winbindd_child_dispatch_table { + enum winbindd_cmd cmd; + enum winbindd_result (*fn)(struct winbindd_domain *domain, + struct winbindd_cli_state *state); + const char *winbindd_cmd_name; +}; + +static struct winbindd_child_dispatch_table child_dispatch_table[] = { + + { WINBINDD_LOOKUPSID, winbindd_dual_lookupsid, "LOOKUPSID" }, + { WINBINDD_LOOKUPNAME, winbindd_dual_lookupname, "LOOKUPNAME" }, + { WINBINDD_LIST_TRUSTDOM, winbindd_dual_list_trusted_domains, + "LIST_TRUSTDOM" }, + { WINBINDD_INIT_CONNECTION, winbindd_dual_init_connection, + "INIT_CONNECTION" }, + { WINBINDD_GETDCNAME, winbindd_dual_getdcname, "GETDCNAME" }, + { WINBINDD_SHOW_SEQUENCE, winbindd_dual_show_sequence, + "SHOW_SEQUENCE" }, + { WINBINDD_PAM_AUTH, winbindd_dual_pam_auth, "PAM_AUTH" }, + { WINBINDD_PAM_AUTH_CRAP, winbindd_dual_pam_auth_crap, "AUTH_CRAP" }, + { WINBINDD_CHECK_MACHACC, winbindd_dual_check_machine_acct, + "CHECK_MACHACC" }, + { WINBINDD_DUAL_SID2UID, winbindd_dual_sid2uid, "DUAL_SID2UID" }, + { WINBINDD_DUAL_SID2GID, winbindd_dual_sid2gid, "DUAL_SID2GID" }, + { WINBINDD_DUAL_UID2NAME, winbindd_dual_uid2name, "DUAL_UID2NAME" }, + { WINBINDD_DUAL_NAME2UID, winbindd_dual_name2uid, "DUAL_NAME2UID" }, + { WINBINDD_DUAL_GID2NAME, winbindd_dual_gid2name, "DUAL_GID2NAME" }, + { WINBINDD_DUAL_NAME2GID, winbindd_dual_name2gid, "DUAL_NAME2GID" }, + { WINBINDD_DUAL_IDMAPSET, winbindd_dual_idmapset, "DUAL_IDMAPSET" }, + { WINBINDD_DUAL_USERINFO, winbindd_dual_userinfo, "DUAL_USERINFO" }, + { WINBINDD_ALLOCATE_RID, winbindd_dual_allocate_rid, "ALLOCATE_RID" }, + { WINBINDD_ALLOCATE_RID_AND_GID, winbindd_dual_allocate_rid_and_gid, + "ALLOCATE_RID_AND_GID" }, + { WINBINDD_GETUSERDOMGROUPS, winbindd_dual_getuserdomgroups, + "GETUSERDOMGROUPS" }, + { WINBINDD_DUAL_GETSIDALIASES, winbindd_dual_getsidaliases, + "GETSIDALIASES" }, + /* End of list */ + + { WINBINDD_NUM_CMDS, NULL, "NONE" } +}; + +static void child_process_request(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + struct winbindd_child_dispatch_table *table; + + /* Free response data - we may be interrupted and receive another + command before being able to send this data off. */ + + state->response.result = WINBINDD_ERROR; + state->response.length = sizeof(struct winbindd_response); + + state->mem_ctx = talloc_init("winbind request"); + if (state->mem_ctx == NULL) + return; + + /* Process command */ + + for (table = child_dispatch_table; table->fn; table++) { + if (state->request.cmd == table->cmd) { + DEBUG(10,("process_request: request fn %s\n", + table->winbindd_cmd_name )); + state->response.result = table->fn(domain, state); + break; + } + } + + if (!table->fn) { + DEBUG(10,("process_request: unknown request fn number %d\n", + (int)state->request.cmd )); + state->response.result = WINBINDD_ERROR; + } + + talloc_destroy(state->mem_ctx); +} + +void setup_domain_child(struct winbindd_domain *domain, + struct winbindd_child *child, + const char *explicit_logfile) +{ + if (explicit_logfile != NULL) { + pstr_sprintf(child->logfilename, "%s/log.winbindd-%s", + dyn_LOGFILEBASE, explicit_logfile); + } else if (domain != NULL) { + pstr_sprintf(child->logfilename, "%s/log.wb-%s", + dyn_LOGFILEBASE, domain->name); + } else { + smb_panic("Internal error: domain == NULL && " + "explicit_logfile == NULL"); + } + + child->domain = domain; +} + +struct winbindd_child *children = NULL; + +void winbind_child_died(pid_t pid) +{ + struct winbindd_child *child; + + for (child = children; child != NULL; child = child->next) { + if (child->pid == pid) { + break; + } + } + + if (child == NULL) { + DEBUG(0, ("Unknown child %d died!\n", pid)); + return; + } + + remove_fd_event(&child->event); + close(child->event.fd); + child->event.fd = 0; + child->event.flags = 0; + child->pid = 0; + + schedule_async_request(child); +} + +static BOOL fork_domain_child(struct winbindd_child *child) +{ + int fdpair[2]; + struct winbindd_cli_state state; + extern BOOL override_logfile; + + if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fdpair) != 0) { + DEBUG(0, ("Could not open child pipe: %s\n", + strerror(errno))); + return False; + } + + ZERO_STRUCT(state); + state.pid = getpid(); + + child->pid = sys_fork(); + + if (child->pid == -1) { + DEBUG(0, ("Could not fork: %s\n", strerror(errno))); + return False; + } + + if (child->pid != 0) { + /* Parent */ + close(fdpair[0]); + child->next = child->prev = NULL; + DLIST_ADD(children, child); + child->event.fd = fdpair[1]; + child->event.flags = 0; + child->requests = NULL; + add_fd_event(&child->event); + return True; + } + + /* Child */ + + state.sock = fdpair[0]; + close(fdpair[1]); + + /* tdb needs special fork handling */ + if (tdb_reopen_all() == -1) { + DEBUG(0,("tdb_reopen_all failed.\n")); + _exit(0); + } + + close_conns_after_fork(); + + if (!override_logfile) { + lp_set_logfile(child->logfilename); + reopen_logs(); + } + + dual_daemon_pipe = -1; + opt_dual_daemon = False; + + while (1) { + /* free up any talloc memory */ + lp_talloc_free(); + main_loop_talloc_free(); + + /* fetch a request from the main daemon */ + dual_client_read(&state); + + if (state.finished) { + /* we lost contact with our parent */ + exit(0); + } + + /* process full rquests */ + if (state.read_buf_len == sizeof(state.request)) { + DEBUG(4,("child daemon request %d\n", + (int)state.request.cmd)); + + state.request.null_term = '\0'; + child_process_request(child->domain, &state); + + if (state.response.result == WINBINDD_OK) + cache_store_response(sys_getpid(), + &state.response); + + SAFE_FREE(state.response.extra_data); + + /* We just send the result code back, the result + * structure needs to be fetched via the + * winbindd_cache. Hmm. That needs fixing... */ + + if (write_data(state.sock, + (void *)&state.response.result, + sizeof(state.response.result)) != + sizeof(state.response.result)) { + DEBUG(0, ("Could not write result\n")); + exit(1); + } + + state.read_buf_len = 0; + } + } +} diff --git a/source3/nsswitch/winbindd_group.c b/source3/nsswitch/winbindd_group.c index c431e32cc1..1138762cc4 100644 --- a/source3/nsswitch/winbindd_group.c +++ b/source3/nsswitch/winbindd_group.c @@ -61,7 +61,7 @@ static BOOL fill_grent_mem(struct winbindd_domain *domain, enum SID_NAME_USE group_name_type, int *num_gr_mem, char **gr_mem, int *gr_mem_len) { - DOM_SID **sid_mem = NULL; + DOM_SID *sid_mem = NULL; uint32 num_names = 0; uint32 *name_types = NULL; unsigned int buf_len, buf_ndx, i; @@ -112,7 +112,8 @@ static BOOL fill_grent_mem(struct winbindd_domain *domain, if (DEBUGLEVEL >= 10) { for (i = 0; i < num_names; i++) - DEBUG(10, ("\t%20s %s %d\n", names[i], sid_to_string(sid_string, sid_mem[i]), + DEBUG(10, ("\t%20s %s %d\n", names[i], + sid_string_static(&sid_mem[i]), name_types[i])); } @@ -222,7 +223,8 @@ enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state) parse_domain_user(tmp, name_domain, name_group); - /* if no domain or our local domain, default to our local domain for aliases */ + /* if no domain or our local domain and no local tdb group, default to + * our local domain for aliases */ if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) { fstrcpy(name_domain, get_global_sam_name()); @@ -245,8 +247,8 @@ enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state) /* Get rid and name type from name */ - if (!winbindd_lookup_sid_by_name(domain, domain->name, name_group, &group_sid, - &name_type)) { + if (!winbindd_lookup_sid_by_name(state->mem_ctx, domain, domain->name, + name_group, &group_sid, &name_type)) { DEBUG(1, ("group %s in domain %s does not exist\n", name_group, name_domain)); return WINBINDD_ERROR; @@ -306,7 +308,7 @@ enum winbindd_result winbindd_getgrgid(struct winbindd_cli_state *state) return WINBINDD_ERROR; /* Get rid from gid */ - if (!NT_STATUS_IS_OK(idmap_gid_to_sid(&group_sid, state->request.data.gid))) { + if (!NT_STATUS_IS_OK(idmap_gid_to_sid(&group_sid, state->request.data.gid, 0))) { DEBUG(1, ("could not convert gid %lu to rid\n", (unsigned long)state->request.data.gid)); return WINBINDD_ERROR; @@ -314,14 +316,15 @@ enum winbindd_result winbindd_getgrgid(struct winbindd_cli_state *state) /* Get name from sid */ - if (!winbindd_lookup_name_by_sid(&group_sid, dom_name, group_name, &name_type)) { + if (!winbindd_lookup_name_by_sid(state->mem_ctx, &group_sid, dom_name, + group_name, &name_type)) { DEBUG(1, ("could not lookup sid\n")); return WINBINDD_ERROR; } /* Fill in group structure */ - domain = find_domain_from_sid(&group_sid); + domain = find_domain_from_sid_noinit(&group_sid); if (!domain) { DEBUG(1,("Can't find domain from sid\n")); @@ -793,10 +796,6 @@ enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state) if ( *which_domain && !strequal(which_domain, domain->name) ) continue; - - if ( !domain->initialized ) - set_dc_type_and_flags( domain ); - ZERO_STRUCT(groups); @@ -856,323 +855,155 @@ enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state) return WINBINDD_OK; } -static BOOL enum_alias_memberships(const DOM_SID *member_sid, - DOM_SID **aliases, int *num_aliases) -{ - TALLOC_CTX *mem_ctx = talloc_init("enum_alias_memberships"); - - uint32 *rids = NULL; - int i, num_rids = 0; - - BOOL result = False; - - if (mem_ctx == NULL) - return False; - - *aliases = NULL; - *num_aliases = 0; - - if (!pdb_enum_alias_memberships(mem_ctx, get_global_sam_sid(), - member_sid, 1, &rids, &num_rids)) - goto done; - - for (i=0; i<num_rids; i++) { - DOM_SID alias_sid; - sid_copy(&alias_sid, get_global_sam_sid()); - sid_append_rid(&alias_sid, rids[i]); - add_sid_to_array(NULL, &alias_sid, aliases, num_aliases); - } - - if (!pdb_enum_alias_memberships(mem_ctx, &global_sid_Builtin, - member_sid, 1, &rids, &num_rids)) - goto done; - - for (i=0; i<num_rids; i++) { - DOM_SID alias_sid; - sid_copy(&alias_sid, &global_sid_Builtin); - sid_append_rid(&alias_sid, rids[i]); - add_sid_to_array(NULL, &alias_sid, aliases, num_aliases); - } - - result = True; - done: - if (mem_ctx != NULL) - talloc_destroy(mem_ctx); - - return result; -} - -static void add_local_gids_from_sid(DOM_SID *sid, gid_t **gids, int *num) -{ - gid_t gid; - DOM_SID *aliases; - int j, num_aliases; - - DEBUG(10, ("Adding local gids from SID: %s\n", - sid_string_static(sid))); - - /* Don't expand aliases if not explicitly activated -- for now - -- jerry */ - - if (!lp_winbind_nested_groups()) - return; - - /* Add nested group memberships */ - - if (!enum_alias_memberships(sid, &aliases, &num_aliases)) - return; - - for (j=0; j<num_aliases; j++) { - enum SID_NAME_USE type; - - if (!local_sid_to_gid(&gid, &aliases[j], &type)) { - DEBUG(1, ("Got an alias membership with no alias\n")); - continue; - } - - if ((type != SID_NAME_ALIAS) && (type != SID_NAME_WKN_GRP)) { - DEBUG(1, ("Got an alias membership in a non-alias\n")); - continue; - } - - add_gid_to_array_unique(NULL, gid, gids, num); - } - SAFE_FREE(aliases); -} - -static void add_gids_from_user_sid(DOM_SID *sid, gid_t **gids, int *num) -{ - DEBUG(10, ("Adding gids from user SID: %s\n", - sid_string_static(sid))); - - add_local_gids_from_sid(sid, gids, num); -} - -static void add_gids_from_group_sid(DOM_SID *sid, gid_t **gids, int *num) -{ - gid_t gid; - - DEBUG(10, ("Adding gids from group SID: %s\n", - sid_string_static(sid))); - - if (NT_STATUS_IS_OK(idmap_sid_to_gid(sid, &gid, 0))) - add_gid_to_array_unique(NULL, gid, gids, num); - - add_local_gids_from_sid(sid, gids, num); -} - /* Get user supplementary groups. This is much quicker than trying to invert the groups database. We merge the groups from the gids and other_sids info3 fields as trusted domain, universal group memberships, and nested groups (win2k native mode only) are not returned by the getgroups RPC call but are present in the info3. */ +struct getgroups_state { + struct winbindd_cli_state *state; + struct winbindd_domain *domain; + char *domname; + char *username; + DOM_SID user_sid; + + const DOM_SID *token_sids; + int i, num_token_sids; + + gid_t *token_gids; + int num_token_gids; +}; + +static void getgroups_usersid_recv(void *private, BOOL success, + const DOM_SID *sid, enum SID_NAME_USE type); +static void getgroups_tokensids_recv(void *private, BOOL success, + DOM_SID *token_sids, int num_token_sids); +static void getgroups_sid2gid_recv(void *private, BOOL success, gid_t gid); + enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state) { - fstring name_domain, name_user; - DOM_SID user_sid, group_sid; - enum SID_NAME_USE name_type; - uint32 num_groups = 0; - uint32 num_gids = 0; - NTSTATUS status; - DOM_SID **user_grpsids; - struct winbindd_domain *domain; - enum winbindd_result result = WINBINDD_ERROR; - gid_t *gid_list = NULL; - unsigned int i; - TALLOC_CTX *mem_ctx; - NET_USER_INFO_3 *info3 = NULL; - + struct getgroups_state *s; + /* Ensure null termination */ - state->request.data.username[sizeof(state->request.data.username)-1]='\0'; + state->request.data.username + [sizeof(state->request.data.username)-1]='\0'; DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid, state->request.data.username)); - if (!(mem_ctx = talloc_init("winbindd_getgroups(%s)", - state->request.data.username))) - return WINBINDD_ERROR; - /* Parse domain and username */ - parse_domain_user(state->request.data.username, - name_domain, name_user); - - /* Get info for the domain */ - - if ((domain = find_domain_from_name(name_domain)) == NULL) { - DEBUG(7, ("could not find domain entry for domain %s\n", - name_domain)); - goto done; + s = TALLOC_P(state->mem_ctx, struct getgroups_state); + if (s == NULL) { + DEBUG(0, ("talloc failed\n")); + return WINBINDD_ERROR; } - if ( domain->primary && lp_winbind_trusted_domains_only()) { - DEBUG(7,("winbindd_getgroups: My domain -- rejecting " - "getgroups() for %s\\%s.\n", name_domain, name_user)); + s->state = state; + + if (!parse_domain_user_talloc(state->mem_ctx, + state->request.data.username, + &s->domname, &s->username)) { + DEBUG(0, ("Could not parse domain user: %s\n", + state->request.data.username)); return WINBINDD_ERROR; - } + } - /* Get rid and name type from name. The following costs 1 packet */ + /* Get info for the domain */ - if (!winbindd_lookup_sid_by_name(domain, domain->name, name_user, &user_sid, - &name_type)) { - DEBUG(4, ("user '%s' does not exist\n", name_user)); - goto done; - } + s->domain = find_domain_from_name_noinit(s->domname); - if (name_type != SID_NAME_USER && name_type != SID_NAME_COMPUTER) { - DEBUG(1, ("name '%s' is not a user name: %d\n", - name_user, name_type)); - goto done; + if (s->domain == NULL) { + DEBUG(7, ("could not find domain entry for domain %s\n", + s->domname)); + return WINBINDD_ERROR; } - add_gids_from_user_sid(&user_sid, &gid_list, &num_gids); - - /* Treat the info3 cache as authoritative as the - lookup_usergroups() function may return cached data. */ - - if ( !opt_nocache && (info3 = netsamlogon_cache_get(mem_ctx, &user_sid))) { - - struct winbindd_domain *our_domain = find_our_domain(); - - if (our_domain == NULL) { - DEBUG(0, ("Could not find our domain\n")); - goto done; - } - - DEBUG(10, ("winbindd_getgroups: info3 has %d groups, %d other sids\n", - info3->num_groups2, info3->num_other_sids)); - - num_groups = info3->num_other_sids + info3->num_groups2; - - /* Go through each other sid and convert it to a gid */ - - for (i = 0; i < info3->num_other_sids; i++) { - DOM_SID *sid = &info3->other_sids[i].sid; - fstring name; - fstring dom_name; - enum SID_NAME_USE sid_type; - - /* Is this sid known to us? It can either be - a trusted domain sid or a foreign sid. */ - - if (!winbindd_lookup_name_by_sid( sid, dom_name, - name, &sid_type)) { - DEBUG(10, ("winbindd_getgroups: could not " - "lookup name for %s\n", - sid_string_static(sid))); - continue; - } - - /* Check it is a domain group or an alias (domain - local group) in a win2k native mode domain. */ - - if (!((sid_type==SID_NAME_DOM_GRP) || - ((sid_type==SID_NAME_ALIAS) && - (our_domain->active_directory) && - (our_domain->native_mode) && - (sid_compare_domain(sid, &our_domain->sid) - == 0)))) { - DEBUG(10, ("winbindd_getgroups: sid type %d " - "for %s is not a domain group\n", - sid_type, sid_string_static(sid))); - continue; - } - - add_gids_from_group_sid(sid, &gid_list, &num_gids); - } - - for (i = 0; i < info3->num_groups2; i++) { - - /* create the group SID */ - - sid_copy( &group_sid, &domain->sid ); - sid_append_rid( &group_sid, info3->gids[i].g_rid ); - - add_gids_from_group_sid(&group_sid, &gid_list, - &num_gids); - } + if ( s->domain->primary && lp_winbind_trusted_domains_only()) { + DEBUG(7,("winbindd_getpwnam: My domain -- rejecting " + "getgroups() for %s\\%s.\n", s->domname, + s->username)); + return WINBINDD_ERROR; + } - SAFE_FREE(info3); + /* Get rid and name type from name. The following costs 1 packet */ - } else { - status = domain->methods->lookup_usergroups(domain, mem_ctx, - &user_sid, &num_groups, - &user_grpsids); - if (!NT_STATUS_IS_OK(status)) - goto done; + winbindd_lookupname_async(state->mem_ctx, s->domname, s->username, + getgroups_usersid_recv, s); + return WINBINDD_PENDING; +} - if (state->response.extra_data) - goto done; +static void getgroups_usersid_recv(void *private, BOOL success, + const DOM_SID *sid, enum SID_NAME_USE type) +{ + struct getgroups_state *s = private; - for (i = 0; i < num_groups; i++) { - add_gids_from_group_sid(user_grpsids[i], - &gid_list, &num_gids); - } + if ((!success) || + ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER))) { + s->state->response.result = WINBINDD_ERROR; + request_finished(s->state); + return; } - /* We want at least one group... */ - if (gid_list == NULL) - goto done; - - remove_duplicate_gids( &num_gids, gid_list ); - - /* Send data back to client */ + sid_copy(&s->user_sid, sid); - state->response.data.num_entries = num_gids; - state->response.extra_data = gid_list; - state->response.length += num_gids * sizeof(gid_t); - - result = WINBINDD_OK; - - done: - - talloc_destroy(mem_ctx); - - return result; + winbindd_gettoken_async(s->state->mem_ctx, &s->user_sid, + getgroups_tokensids_recv, s); } -static void add_sid_to_parray_unique(TALLOC_CTX *mem_ctx, const DOM_SID *sid, - DOM_SID ***sids, int *num_sids) +static void getgroups_tokensids_recv(void *private, BOOL success, + DOM_SID *token_sids, int num_token_sids) { - int i; + struct getgroups_state *s = private; - for (i=0; i<(*num_sids); i++) { - if (sid_compare(sid, (*sids)[i]) == 0) - return; + /* We need at least the user sid and the primary group in the token, + * otherwise it's an error */ + + if ((!success) || (num_token_sids < 2)) { + s->state->response.result = WINBINDD_ERROR; + request_finished(s->state); + return; } - *sids = TALLOC_REALLOC_ARRAY(mem_ctx, *sids, DOM_SID *, *num_sids+1); + s->token_sids = token_sids; + s->num_token_sids = num_token_sids; + s->i = 0; - if (*sids == NULL) - return; + s->token_gids = NULL; + s->num_token_gids = 0; - (*sids)[*num_sids] = TALLOC_P(mem_ctx, DOM_SID); - sid_copy((*sids)[*num_sids], sid); - *num_sids += 1; - return; + getgroups_sid2gid_recv(s, False, 0); } -static void add_local_sids_from_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid, - DOM_SID ***user_grpsids, - int *num_groups) +static void getgroups_sid2gid_recv(void *private, BOOL success, gid_t gid) { - DOM_SID *aliases = NULL; - int i, num_aliases = 0; + struct getgroups_state *s = private; - if (!enum_alias_memberships(sid, &aliases, &num_aliases)) - return; + if (success) + add_gid_to_array_unique(NULL, gid, + &s->token_gids, + &s->num_token_gids); - if (num_aliases == 0) - return; + if (s->i < s->num_token_sids) { + const DOM_SID *sid = &s->token_sids[s->i]; + s->i += 1; - for (i=0; i<num_aliases; i++) - add_sid_to_parray_unique(mem_ctx, &aliases[i], user_grpsids, - num_groups); + if (sid_equal(sid, &s->user_sid)) { + getgroups_sid2gid_recv(s, False, 0); + return; + } - SAFE_FREE(aliases); + winbindd_sid2gid_async(s->state->mem_ctx, sid, + getgroups_sid2gid_recv, s); + return; + } - return; + s->state->response.data.num_entries = s->num_token_gids; + s->state->response.extra_data = s->token_gids; + s->state->response.length += s->num_token_gids * sizeof(gid_t); + s->state->response.result = WINBINDD_OK; + request_finished(s->state); } /* Get user supplementary sids. This is equivalent to the @@ -1186,106 +1017,50 @@ static void add_local_sids_from_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid, you pass in another type of SID then you may get unpredictable results. */ + +static void getusersids_recv(void *private, BOOL success, DOM_SID *sids, + int num_sids); + enum winbindd_result winbindd_getusersids(struct winbindd_cli_state *state) { - DOM_SID user_sid; - NTSTATUS status; - DOM_SID **user_grpsids; - struct winbindd_domain *domain; - enum winbindd_result result = WINBINDD_ERROR; - unsigned int i; - TALLOC_CTX *mem_ctx; - char *ret = NULL; - uint32 num_groups; - unsigned ofs, ret_size = 0; + DOM_SID *user_sid; /* Ensure null termination */ state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; - if (!string_to_sid(&user_sid, state->request.data.sid)) { - DEBUG(1, ("Could not get convert sid %s from string\n", state->request.data.sid)); + user_sid = TALLOC_P(state->mem_ctx, DOM_SID); + if (user_sid == NULL) { + DEBUG(1, ("talloc failed\n")); return WINBINDD_ERROR; } - if (!(mem_ctx = talloc_init("winbindd_getusersids(%s)", - state->request.data.username))) { + if (!string_to_sid(user_sid, state->request.data.sid)) { + DEBUG(1, ("Could not get convert sid %s from string\n", + state->request.data.sid)); return WINBINDD_ERROR; } - /* Get info for the domain */ - if ((domain = find_domain_from_sid(&user_sid)) == NULL) { - DEBUG(0,("could not find domain entry for sid %s\n", - sid_string_static(&user_sid))); - goto done; - } - - status = domain->methods->lookup_usergroups(domain, mem_ctx, - &user_sid, &num_groups, - &user_grpsids); - if (!NT_STATUS_IS_OK(status)) - goto done; + winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv, + state); + return WINBINDD_PENDING; +} - if (num_groups == 0) { - goto no_groups; - } +static void getusersids_recv(void *private, BOOL success, DOM_SID *sids, + int num_sids) +{ + struct winbindd_cli_state *state = private; + char *ret = NULL; + unsigned ofs, ret_size = 0; + int i; - domain = find_our_domain(); + state->response.result = WINBINDD_ERROR; - if (domain == NULL) { - DEBUG(0, ("Could not find our domain\n")); + if (!success) goto done; - } - - /* Note that I do not check for AD or its mode. XP in a real NT4 - * domain also asks for this info. -- vl */ - - if (!IS_DC) { - uint32 *alias_rids = NULL; - int num_aliases; - - /* We need to include the user SID to expand */ - user_grpsids = TALLOC_REALLOC_ARRAY(mem_ctx, user_grpsids, - DOM_SID *, num_groups+1); - user_grpsids[num_groups] = &user_sid; - - status = domain->methods->lookup_useraliases(domain, mem_ctx, - num_groups+1, - user_grpsids, - &num_aliases, - &alias_rids); - - if (!NT_STATUS_IS_OK(status)) { - DEBUG(3, ("Could not expand alias sids: %s\n", - nt_errstr(status))); - goto done; - } - - for (i=0; i<num_aliases; i++) { - DOM_SID sid; - sid_copy(&sid, &domain->sid); - sid_append_rid(&sid, alias_rids[i]); - add_sid_to_parray_unique(mem_ctx, &sid, &user_grpsids, - &num_groups); - } - } - - if (lp_winbind_nested_groups()) { - int k; - /* num_groups is changed during the loop, that's why we have - to count down here.*/ - - for (k=num_groups-1; k>=0; k--) { - add_local_sids_from_sid(mem_ctx, user_grpsids[k], - &user_grpsids, &num_groups); - } - - add_local_sids_from_sid(mem_ctx, &user_sid, &user_grpsids, - &num_groups); - } /* work out the response size */ - for (i = 0; i < num_groups; i++) { - const char *s = sid_string_static(user_grpsids[i]); + for (i = 0; i < num_sids; i++) { + const char *s = sid_string_static(&sids[i]); ret_size += strlen(s) + 1; } @@ -1293,22 +1068,94 @@ enum winbindd_result winbindd_getusersids(struct winbindd_cli_state *state) ret = SMB_MALLOC(ret_size); if (!ret) goto done; ofs = 0; - for (i = 0; i < num_groups; i++) { - const char *s = sid_string_static(user_grpsids[i]); + for (i = 0; i < num_sids; i++) { + const char *s = sid_string_static(&sids[i]); safe_strcpy(ret + ofs, s, ret_size - ofs - 1); ofs += strlen(ret+ofs) + 1; } -no_groups: /* Send data back to client */ - state->response.data.num_entries = num_groups; + state->response.data.num_entries = num_sids; state->response.extra_data = ret; state->response.length += ret_size; - result = WINBINDD_OK; + state->response.result = WINBINDD_OK; done: - talloc_destroy(mem_ctx); + request_finished(state); +} - return result; +enum winbindd_result winbindd_getuserdomgroups(struct winbindd_cli_state *state) +{ + DOM_SID user_sid; + struct winbindd_domain *domain; + + /* Ensure null termination */ + state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; + + if (!string_to_sid(&user_sid, state->request.data.sid)) { + DEBUG(1, ("Could not get convert sid %s from string\n", + state->request.data.sid)); + return WINBINDD_ERROR; + } + + /* Get info for the domain */ + if ((domain = find_domain_from_sid_noinit(&user_sid)) == NULL) { + DEBUG(0,("could not find domain entry for sid %s\n", + sid_string_static(&user_sid))); + return WINBINDD_ERROR; + } + + async_domain_request(state->mem_ctx, domain, &state->request, + &state->response, request_finished_cont, state); + return WINBINDD_PENDING; } +enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + DOM_SID user_sid; + NTSTATUS status; + + int i, num_groups, len, bufsize; + DOM_SID *groups; + + /* Ensure null termination */ + state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; + + if (!string_to_sid(&user_sid, state->request.data.sid)) { + DEBUG(1, ("Could not get convert sid %s from string\n", + state->request.data.sid)); + return WINBINDD_ERROR; + } + + status = domain->methods->lookup_usergroups(domain, state->mem_ctx, + &user_sid, &num_groups, + &groups); + if (!NT_STATUS_IS_OK(status)) + return WINBINDD_ERROR; + + if (num_groups == 0) { + state->response.data.num_entries = 0; + state->response.extra_data = NULL; + return WINBINDD_OK; + } + + len=bufsize=0; + state->response.extra_data = NULL; + + for (i=0; i<num_groups; i++) { + sprintf_append(NULL, (char **)&state->response.extra_data, + &len, &bufsize, + "%s\n", sid_string_static(&groups[i])); + } + + if (state->response.extra_data == NULL) { + /* Hmmm. Allocation failed somewhere */ + return WINBINDD_ERROR; + } + + state->response.data.num_entries = num_groups; + state->response.length += len+1; + + return WINBINDD_OK; +} diff --git a/source3/nsswitch/winbindd_ldap.c b/source3/nsswitch/winbindd_ldap.c new file mode 100644 index 0000000000..4eedf0ce9f --- /dev/null +++ b/source3/nsswitch/winbindd_ldap.c @@ -0,0 +1,646 @@ +/* + Unix SMB/CIFS implementation. + + winbind ldap proxy code + + Copyright (C) Volker Lendecke + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "winbindd.h" + +/* This rw-buf api is made to avoid memcpy. For now do that like mad... The + idea is to write into a circular list of buffers where the ideal case is + that a read(2) holds a complete request that is then thrown away + completely. */ + +struct ldap_message_queue { + struct ldap_message_queue *prev, *next; + struct ldap_message *msg; +}; + +struct rw_buffer { + uint8_t *data; + size_t ofs, length; +}; + +struct winbind_ldap_client { + struct winbind_ldap_client *next, *prev; + int sock; + BOOL finished; + struct rw_buffer in_buffer, out_buffer; +}; + +static struct winbind_ldap_client *ldap_clients; + +struct winbind_ldap_server { + struct winbind_ldap_server *next, *prev; + int sock; + BOOL ready; /* Bind successful? */ + BOOL finished; + struct rw_buffer in_buffer, out_buffer; + int messageid; +}; + +static struct winbind_ldap_server *ldap_servers; + +struct pending_ldap_message { + struct pending_ldap_message *next, *prev; + struct ldap_message *msg; /* The message the client sent us */ + int our_msgid; /* The messageid we used */ + struct winbind_ldap_client *client; +}; + +struct pending_ldap_message *pending_messages; + +static BOOL append_to_buf(struct rw_buffer *buf, uint8_t *data, size_t length) +{ + buf->data = SMB_REALLOC(buf->data, buf->length+length); + + if (buf->data == NULL) + return False; + + memcpy(buf->data+buf->length, data, length); + + buf->length += length; + return True; +} + +static BOOL read_into_buf(int fd, struct rw_buffer *buf) +{ + char tmp_buf[1024]; + int len; + + len = read(fd, tmp_buf, sizeof(tmp_buf)); + if (len == 0) + return False; + + return append_to_buf(buf, tmp_buf, len); +} + +static void peek_into_buf(struct rw_buffer *buf, uint8_t **out, + size_t *out_length) +{ + *out = buf->data; + *out_length = buf->length; +} + +static void consumed_from_buf(struct rw_buffer *buf, size_t length) +{ + uint8_t *new = memdup(buf->data+length, buf->length-length); + free(buf->data); + buf->data = new; + buf->length -= length; +} + +static BOOL write_out_of_buf(int fd, struct rw_buffer *buf) +{ + uint8_t *tmp; + size_t tmp_length, written; + + peek_into_buf(buf, &tmp, &tmp_length); + if (tmp_length == 0) + return True; + + written = write(fd, tmp, tmp_length); + if (written < 0) + return False; + + consumed_from_buf(buf, written); + return True; +} + +static BOOL ldap_append_to_buf(struct ldap_message *msg, struct rw_buffer *buf) +{ + DATA_BLOB blob; + BOOL res; + + if (!ldap_encode(msg, &blob)) + return False; + + res = append_to_buf(buf, blob.data, blob.length); + + data_blob_free(&blob); + return res; +} + +static void new_ldap_client(int listen_sock) +{ + struct sockaddr_un sunaddr; + struct winbind_ldap_client *client; + socklen_t len; + int sock; + + /* Accept connection */ + + len = sizeof(sunaddr); + + do { + sock = accept(listen_sock, (struct sockaddr *)&sunaddr, &len); + } while (sock == -1 && errno == EINTR); + + if (sock == -1) + return; + + DEBUG(6,("accepted socket %d\n", sock)); + + /* Create new connection structure */ + + client = SMB_MALLOC_P(struct winbind_ldap_client); + + if (client == NULL) + return; + + ZERO_STRUCTP(client); + + client->sock = sock; + client->finished = False; + + DLIST_ADD(ldap_clients, client); +} + +static struct ldap_message *get_msg_from_buf(struct rw_buffer *buffer, + BOOL *error) +{ + uint8_t *buf; + int buf_length, msg_length; + DATA_BLOB blob; + ASN1_DATA data; + struct ldap_message *msg; + + DEBUG(10,("ldapsrv_recv\n")); + + *error = False; + + peek_into_buf(buffer, &buf, &buf_length); + + if (buf_length < 8) { + /* Arbitrary heuristics: ldap messages are longer than eight + * bytes, and their tag length fits into the eight bytes */ + return NULL; + } + + /* LDAP Messages are always SEQUENCES */ + + if (!asn1_object_length(buf, buf_length, ASN1_SEQUENCE(0), + &msg_length)) + goto disconnect; + + if (buf_length < msg_length) { + /* Not enough yet */ + return NULL; + } + + /* We've got a complete LDAP request in the in-buffer */ + + blob.data = buf; + blob.length = msg_length; + + if (!asn1_load(&data, blob)) + goto disconnect; + + msg = new_ldap_message(); + + if ((msg == NULL) || !ldap_decode(&data, msg)) { + asn1_free(&data); + goto disconnect; + } + + asn1_free(&data); + + consumed_from_buf(buffer, msg_length); + + return msg; + + disconnect: + + *error = True; + return NULL; +} + +static int send_msg_to_server(struct ldap_message *msg, + struct winbind_ldap_server *server) +{ + int cli_messageid; + + cli_messageid = msg->messageid; + msg->messageid = ldap_servers->messageid; + + if (!ldap_append_to_buf(msg, &ldap_servers->out_buffer)) + return -1; + + msg->messageid = cli_messageid; + return ldap_servers->messageid++; +} + +static int send_msg(struct ldap_message *msg) +{ + /* This is the scheduling routine that should decide where to send + * stuff. The first attempt is easy: We only have one server. This + * will change once we handle referrals etc. */ + + SMB_ASSERT(ldap_servers != NULL); + + if (!ldap_servers->ready) + return -1; + + return send_msg_to_server(msg, ldap_servers); +} + +static void fake_bind_response(struct winbind_ldap_client *client, + int messageid) +{ + struct ldap_message *msg = new_ldap_message(); + + if (msg == NULL) { + client->finished = True; + return; + } + + msg->messageid = messageid; + msg->type = LDAP_TAG_BindResponse; + msg->r.BindResponse.response.resultcode = 0; + msg->r.BindResponse.response.dn = ""; + msg->r.BindResponse.response.dn = ""; + msg->r.BindResponse.response.errormessage = ""; + msg->r.BindResponse.response.referral = ""; + ldap_append_to_buf(msg, &client->out_buffer); + destroy_ldap_message(msg); +} + +static int open_ldap_socket(void) +{ + static int fd = -1; + + if (fd >= 0) + return fd; + + fd = create_pipe_sock(get_winbind_priv_pipe_dir(), "ldapi", 0750); + return fd; +} + +static BOOL do_sigterm = False; + +static void ldap_termination_handler(int signum) +{ + do_sigterm = True; + sys_select_signal(); +} + +static BOOL handled_locally(struct ldap_message *msg, + struct winbind_ldap_server *server) +{ + struct ldap_Result *r = &msg->r.BindResponse.response; + + if (msg->type != LDAP_TAG_BindResponse) + return False; + + if (r->resultcode != 0) { + destroy_ldap_message(msg); + server->finished = True; + } + destroy_ldap_message(msg); + server->ready = True; + return True; +} + +static void client_has_data(struct winbind_ldap_client *client) +{ + + struct ldap_message *msg; + + if (!read_into_buf(client->sock, &client->in_buffer)) { + client->finished = True; + return; + } + + while ((msg = get_msg_from_buf(&client->in_buffer, + &client->finished))) { + struct pending_ldap_message *pending; + + if (msg->type == LDAP_TAG_BindRequest) { + fake_bind_response(client, msg->messageid); + destroy_ldap_message(msg); + continue; + } + + if (msg->type == LDAP_TAG_UnbindRequest) { + destroy_ldap_message(msg); + client->finished = True; + break; + } + + pending = SMB_MALLOC_P(struct pending_ldap_message); + if (pending == NULL) + continue; + + pending->msg = msg; + pending->client = client; + pending->our_msgid = send_msg(msg); + + if (pending->our_msgid < 0) { + /* could not send */ + client->finished = True; + free(pending); + } + DLIST_ADD(pending_messages, pending); + } +} + +static struct ldap_Result *ldap_msg2result(struct ldap_message *msg) +{ + switch(msg->type) { + case LDAP_TAG_BindResponse: + return &msg->r.BindResponse.response; + case LDAP_TAG_SearchResultDone: + return &msg->r.SearchResultDone; + case LDAP_TAG_ModifyResponse: + return &msg->r.ModifyResponse; + case LDAP_TAG_AddResponse: + return &msg->r.AddResponse; + case LDAP_TAG_DelResponse: + return &msg->r.DelResponse; + case LDAP_TAG_ModifyDNResponse: + return &msg->r.ModifyDNResponse; + case LDAP_TAG_CompareResponse: + return &msg->r.CompareResponse; + case LDAP_TAG_ExtendedResponse: + return &msg->r.ExtendedResponse.response; + } + return NULL; +} + +static void server_has_data(struct winbind_ldap_server *server) +{ + struct ldap_message *msg; + + if (!read_into_buf(server->sock, &server->in_buffer)) { + server->finished = True; + return; + } + + while ((msg = get_msg_from_buf(&server->in_buffer, + &server->finished))) { + struct pending_ldap_message *pending; + struct rw_buffer *buf; + struct ldap_Result *res; + + if (handled_locally(msg, server)) + continue; + + res = ldap_msg2result(msg); + + if ( (res != NULL) && (res->resultcode == 10) ) + DEBUG(5, ("Got Referral %s\n", res->referral)); + + for (pending = pending_messages; + pending != NULL; + pending = pending->next) { + if (pending->our_msgid == msg->messageid) + break; + } + + if (pending == NULL) { + talloc_destroy(msg->mem_ctx); + continue; + } + + msg->messageid = pending->msg->messageid; + + buf = &pending->client->out_buffer; + ldap_append_to_buf(msg, buf); + + if ( (msg->type != LDAP_TAG_SearchResultEntry) && + (msg->type != LDAP_TAG_SearchResultReference) ) { + destroy_ldap_message(pending->msg); + DLIST_REMOVE(pending_messages, + pending); + SAFE_FREE(pending); + } + destroy_ldap_message(msg); + } +} + +static void process_ldap_loop(void) +{ + struct winbind_ldap_client *client; + struct winbind_ldap_server *server; + fd_set r_fds, w_fds; + int maxfd, listen_sock, selret; + struct timeval timeout; + + /* Free up temporary memory */ + + lp_talloc_free(); + main_loop_talloc_free(); + + if (do_sigterm) { +#if 0 + TALLOC_CTX *mem_ctx = talloc_init("describe"); + DEBUG(0, ("%s\n", talloc_describe_all(mem_ctx))); + talloc_destroy(mem_ctx); +#endif + exit(0); + } + + /* Initialise fd lists for select() */ + + listen_sock = open_ldap_socket(); + + if (listen_sock == -1) { + perror("open_ldap_socket"); + exit(1); + } + + maxfd = listen_sock; + + FD_ZERO(&r_fds); + FD_ZERO(&w_fds); + FD_SET(listen_sock, &r_fds); + + timeout.tv_sec = WINBINDD_ESTABLISH_LOOP; + timeout.tv_usec = 0; + + /* Set up client readers and writers */ + + client = ldap_clients; + + while (client != NULL) { + + if (client->finished) { + struct winbind_ldap_client *next = client->next; + DLIST_REMOVE(ldap_clients, client); + close(client->sock); + SAFE_FREE(client->in_buffer.data); + SAFE_FREE(client->out_buffer.data); + SAFE_FREE(client); + client = next; + continue; + } + + if (client->sock > maxfd) + maxfd = client->sock; + + FD_SET(client->sock, &r_fds); + + if (client->out_buffer.length > 0) + FD_SET(client->sock, &w_fds); + + client = client->next; + } + + /* And now the servers */ + + server = ldap_servers; + + while (server != NULL) { + + if (server->finished) { + struct winbind_ldap_server *next = server->next; + DLIST_REMOVE(ldap_servers, server); + close(server->sock); + SAFE_FREE(server); + server = next; + continue; + } + + if (server->sock > maxfd) + maxfd = server->sock; + + FD_SET(server->sock, &r_fds); + + if (server->out_buffer.length > 0) + FD_SET(server->sock, &w_fds); + + server = server->next; + } + + selret = sys_select(maxfd + 1, &r_fds, &w_fds, NULL, &timeout); + + if (selret == 0) + return; + + if (selret == -1 && errno != EINTR) { + perror("select"); + exit(1); + } + + if (FD_ISSET(listen_sock, &r_fds)) + new_ldap_client(listen_sock); + + for (client = ldap_clients; client != NULL; client = client->next) { + + if (FD_ISSET(client->sock, &r_fds)) + client_has_data(client); + + if ((!client->finished) && FD_ISSET(client->sock, &w_fds)) + write_out_of_buf(client->sock, &client->out_buffer); + } + + for (server = ldap_servers; server != NULL; server = server->next) { + + if (FD_ISSET(server->sock, &r_fds)) + server_has_data(server); + + if (!server->finished && FD_ISSET(server->sock, &w_fds)) + write_out_of_buf(server->sock, &server->out_buffer); + } +} + +static BOOL setup_ldap_serverconn(void) +{ + char *host; + uint16 port; + BOOL ldaps; + struct hostent *hp; + struct in_addr ip; + TALLOC_CTX *mem_ctx = talloc_init("server"); + struct ldap_message *msg; + char *dn, *pw; + + ldap_servers = SMB_MALLOC_P(struct winbind_ldap_server); + + if ((ldap_servers == NULL) || (mem_ctx == NULL)) + return False; + + if (!ldap_parse_basic_url(mem_ctx, "ldap://192.168.234.1:3899/", + &host, &port, &ldaps)) + return False; + + hp = sys_gethostbyname(host); + + if ((hp == NULL) || (hp->h_addr == NULL)) + return False; + + putip((char *)&ip, (char *)hp->h_addr); + + ZERO_STRUCTP(ldap_servers); + ldap_servers->sock = open_socket_out(SOCK_STREAM, &ip, port, 10000); + ldap_servers->messageid = 1; + + if (!fetch_ldap_pw(&dn, &pw)) + return False; + + msg = new_ldap_simple_bind_msg(dn, pw); + + SAFE_FREE(dn); + SAFE_FREE(pw); + + if (msg == NULL) + return False; + + msg->messageid = ldap_servers->messageid++; + + ldap_append_to_buf(msg, &ldap_servers->out_buffer); + + destroy_ldap_message(msg); + + return (ldap_servers->sock >= 0); +} + +void do_ldap_proxy(void) +{ + int ldap_child; + + ldap_child = sys_fork(); + + if (ldap_child != 0) + return; + + /* tdb needs special fork handling */ + if (tdb_reopen_all() == -1) { + DEBUG(0,("tdb_reopen_all failed.\n")); + _exit(0); + } + + if (!message_init()) { + DEBUG(0, ("message_init failed\n")); + _exit(0); + } + + CatchSignal(SIGINT, ldap_termination_handler); + CatchSignal(SIGQUIT, ldap_termination_handler); + CatchSignal(SIGTERM, ldap_termination_handler); + + if (!setup_ldap_serverconn()) + return; + + while (1) + process_ldap_loop(); + + return; +} diff --git a/source3/nsswitch/winbindd_misc.c b/source3/nsswitch/winbindd_misc.c index bb30a7029e..9cfa4c6fcc 100644 --- a/source3/nsswitch/winbindd_misc.c +++ b/source3/nsswitch/winbindd_misc.c @@ -31,11 +31,20 @@ enum winbindd_result winbindd_check_machine_acct(struct winbindd_cli_state *state) { + DEBUG(3, ("[%5lu]: check machine account\n", + (unsigned long)state->pid)); + + async_domain_request(state->mem_ctx, find_our_domain(), + &state->request, &state->response, + request_finished_cont, state); + return WINBINDD_PENDING; +} + +enum winbindd_result winbindd_dual_check_machine_acct(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ NTSTATUS result = NT_STATUS_UNSUCCESSFUL; - uchar trust_passwd[16]; int num_retries = 0; - struct cli_state *cli; - uint32 sec_channel_type; struct winbindd_domain *contact_domain; DEBUG(3, ("[%5lu]: check machine account\n", (unsigned long)state->pid)); @@ -43,26 +52,22 @@ enum winbindd_result winbindd_check_machine_acct(struct winbindd_cli_state *stat /* Get trust account password */ again: - if (!secrets_fetch_trust_account_password( - lp_workgroup(), trust_passwd, NULL, &sec_channel_type)) { - result = NT_STATUS_INTERNAL_ERROR; - goto done; - } - contact_domain = find_our_domain(); - if (!contact_domain) { - result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; - DEBUG(1, ("Cannot find our own domain!\n")); - goto done; - } /* This call does a cli_nt_setup_creds() which implicitly checks the trust account password. */ - /* Don't shut this down - it belongs to the connection cache code */ - - result = cm_get_netlogon_cli(contact_domain, - trust_passwd, sec_channel_type, True, &cli); + + invalidate_cm_connection(&contact_domain->conn); + + { + struct rpc_pipe_client *cli; + unsigned char *session_key; + DOM_CRED *creds; + + result = cm_connect_netlogon(contact_domain, state->mem_ctx, + &cli, &session_key, &creds); + } if (!NT_STATUS_IS_OK(result)) { DEBUG(3, ("could not open handle to NETLOGON pipe\n")); @@ -100,108 +105,256 @@ enum winbindd_result winbindd_check_machine_acct(struct winbindd_cli_state *stat return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; } -enum winbindd_result winbindd_list_trusted_domains(struct winbindd_cli_state - *state) +enum winbindd_result winbindd_list_trusted_domains(struct winbindd_cli_state *state) { - struct winbindd_domain *domain; - int total_entries = 0, extra_data_len = 0; - char *ted, *extra_data = NULL; + DEBUG(3, ("[%5lu]: list trusted domains\n", + (unsigned long)state->pid)); + + async_domain_request(state->mem_ctx, find_our_domain(), + &state->request, &state->response, + request_finished_cont, state); + return WINBINDD_PENDING; +} - DEBUG(3, ("[%5lu]: list trusted domains\n", (unsigned long)state->pid)); +enum winbindd_result winbindd_dual_list_trusted_domains(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + uint32 i, num_domains; + char **names, **alt_names; + DOM_SID *sids; + int extra_data_len = 0; + char *extra_data; + NTSTATUS result; - /* We need to refresh the trusted domain list as the domains may - have changed since we last looked. There may be a sequence - number or something we should use but I haven't found it yet. */ + DEBUG(3, ("[%5lu]: list trusted domains\n", + (unsigned long)state->pid)); - if (!init_domain_list()) { - DEBUG(1, ("winbindd_list_trusted_domains: could not " - "refresh trusted domain list\n")); - return WINBINDD_ERROR; + result = domain->methods->trusted_domains(domain, state->mem_ctx, + &num_domains, &names, + &alt_names, &sids); + + extra_data = talloc_strdup(state->mem_ctx, ""); + + if (num_domains > 0) + extra_data = talloc_asprintf(state->mem_ctx, "%s\\%s\\%s", + names[0], alt_names[0], + sid_string_static(&sids[0])); + + for (i=1; i<num_domains; i++) + extra_data = talloc_asprintf(state->mem_ctx, "%s\n%s\\%s\\%s", + extra_data, + names[i], alt_names[i], + sid_string_static(&sids[i])); + + /* This is a bit excessive, but the extra data sooner or later will be + talloc'ed */ + + extra_data_len = strlen(extra_data); + + if (extra_data_len > 0) { + state->response.extra_data = SMB_STRDUP(extra_data); + state->response.length += extra_data_len+1; } - for(domain = domain_list(); domain; domain = domain->next) { + return WINBINDD_OK; +} - /* Skip own domain */ +enum winbindd_result winbindd_getdcname(struct winbindd_cli_state *state) +{ + state->request.domain_name + [sizeof(state->request.domain_name)-1] = '\0'; - if (domain->primary) continue; + DEBUG(3, ("[%5lu]: Get DC name for %s\n", (unsigned long)state->pid, + state->request.domain_name)); - /* Add domain to list */ + async_domain_request(state->mem_ctx, find_our_domain(), + &state->request, &state->response, + request_finished_cont, state); + return WINBINDD_PENDING; +} - total_entries++; - ted = SMB_REALLOC(extra_data, sizeof(fstring) * - total_entries); +enum winbindd_result winbindd_dual_getdcname(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + fstring dcname_slash; + char *p; + struct rpc_pipe_client *cli; + NTSTATUS result; - if (!ted) { - DEBUG(0,("winbindd_list_trusted_domains: failed to enlarge buffer!\n")); - SAFE_FREE(extra_data); - return WINBINDD_ERROR; - } else - extra_data = ted; + state->request.domain_name + [sizeof(state->request.domain_name)-1] = '\0'; - memcpy(&extra_data[extra_data_len], domain->name, - strlen(domain->name)); + DEBUG(3, ("[%5lu]: Get DC name for %s\n", (unsigned long)state->pid, + state->request.domain_name)); + + { + /* These var's can be ignored -- we're not requesting + anything in the credential chain here */ + unsigned char *session_key; + DOM_CRED *creds; + result = cm_connect_netlogon(domain, state->mem_ctx, &cli, + &session_key, &creds); + } - extra_data_len += strlen(domain->name); - extra_data[extra_data_len++] = ','; + if (!NT_STATUS_IS_OK(result)) { + DEBUG(1, ("Can't contact our the NETLOGON pipe\n")); + return WINBINDD_ERROR; } - if (extra_data) { - if (extra_data_len > 1) - extra_data[extra_data_len - 1] = '\0'; - state->response.extra_data = extra_data; - state->response.length += extra_data_len; + result = rpccli_netlogon_getdcname(cli, state->mem_ctx, domain->dcname, + state->request.domain_name, + dcname_slash); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(5, ("Error requesting DCname: %s\n", nt_errstr(result))); + return WINBINDD_ERROR; } + p = dcname_slash; + if (*p == '\\') p+=1; + if (*p == '\\') p+=1; + + fstrcpy(state->response.data.dc_name, p); + return WINBINDD_OK; } +struct sequence_state { + TALLOC_CTX *mem_ctx; + struct winbindd_cli_state *cli_state; + struct winbindd_domain *domain; + struct winbindd_request *request; + struct winbindd_response *response; + char *extra_data; +}; + +static void sequence_recv(void *private, BOOL success); enum winbindd_result winbindd_show_sequence(struct winbindd_cli_state *state) { - struct winbindd_domain *domain; - char *extra_data = NULL; - const char *which_domain; + struct sequence_state *seq; + + /* Ensure null termination */ + state->request.domain_name[sizeof(state->request.domain_name)-1]='\0'; + + if (strlen(state->request.domain_name) > 0) { + struct winbindd_domain *domain; + domain = find_domain_from_name_noinit( + state->request.domain_name); + if (domain == NULL) + return WINBINDD_ERROR; + async_domain_request(state->mem_ctx, domain, + &state->request, &state->response, + request_finished_cont, state); + return WINBINDD_PENDING; + } + + /* Ask all domains in sequence, collect the results in sequence_recv */ + seq = TALLOC_P(state->mem_ctx, struct sequence_state); + if (seq == NULL) { + DEBUG(0, ("talloc failed\n")); + return WINBINDD_ERROR; + } + + seq->mem_ctx = state->mem_ctx; + seq->cli_state = state; + seq->domain = domain_list(); + if (seq->domain == NULL) { + DEBUG(0, ("domain list empty\n")); + return WINBINDD_ERROR; + } + seq->request = TALLOC_ZERO_P(state->mem_ctx, + struct winbindd_request); + seq->response = TALLOC_ZERO_P(state->mem_ctx, + struct winbindd_response); + seq->extra_data = talloc_strdup(state->mem_ctx, ""); + + if ((seq->request == NULL) || (seq->response == NULL) || + (seq->extra_data == NULL)) { + DEBUG(0, ("talloc failed\n")); + return WINBINDD_ERROR; + } + + seq->request->length = sizeof(*seq->request); + seq->request->cmd = WINBINDD_SHOW_SEQUENCE; + fstrcpy(seq->request->domain_name, seq->domain->name); + + async_domain_request(state->mem_ctx, seq->domain, + seq->request, seq->response, + sequence_recv, seq); + return WINBINDD_PENDING; +} + +static void sequence_recv(void *private, BOOL success) +{ + struct sequence_state *state = private; + uint32 seq = DOM_SEQUENCE_NONE; + + if ((success) && (state->response->result == WINBINDD_OK)) + seq = state->response->data.domain_info.sequence_number; + + if (seq == DOM_SEQUENCE_NONE) { + state->extra_data = talloc_asprintf(state->mem_ctx, + "%s%s : DISCONNECTED\n", + state->extra_data, + state->domain->name); + } else { + state->extra_data = talloc_asprintf(state->mem_ctx, + "%s%s : %d\n", + state->extra_data, + state->domain->name, seq); + } + + state->domain->sequence_number = seq; + + state->domain = state->domain->next; + + if (state->domain == NULL) { + struct winbindd_cli_state *cli_state = state->cli_state; + cli_state->response.result = WINBINDD_OK; + cli_state->response.length = + sizeof(cli_state->response) + + strlen(state->extra_data) + 1; + cli_state->response.extra_data = + SMB_STRDUP(state->extra_data); + request_finished(cli_state); + return; + } + + /* Ask the next domain */ + fstrcpy(state->request->domain_name, state->domain->name); + async_domain_request(state->mem_ctx, state->domain, + state->request, state->response, + sequence_recv, state); +} + +/* This is the child-only version of --sequence. It only allows for a single + * domain (ie "our" one) to be displayed. */ + +enum winbindd_result winbindd_dual_show_sequence(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ DEBUG(3, ("[%5lu]: show sequence\n", (unsigned long)state->pid)); /* Ensure null termination */ - state->request.domain_name[sizeof(state->request.domain_name)-1]='\0'; - which_domain = state->request.domain_name; - - extra_data = SMB_STRDUP(""); - - /* this makes for a very simple data format, and is easily parsable as well - if that is ever needed */ - for (domain = domain_list(); domain; domain = domain->next) { - char *s; - - /* if we have a domain name restricting the request and this - one in the list doesn't match, then just bypass the remainder - of the loop */ - - if ( *which_domain && !strequal(which_domain, domain->name) ) - continue; - - domain->methods->sequence_number(domain, &domain->sequence_number); - - if (DOM_SEQUENCE_NONE == (unsigned)domain->sequence_number) { - asprintf(&s,"%s%s : DISCONNECTED\n", extra_data, - domain->name); - } else { - asprintf(&s,"%s%s : %u\n", extra_data, - domain->name, (unsigned)domain->sequence_number); - } - free(extra_data); - extra_data = s; - } + state->request.domain_name[sizeof(state->request.domain_name)-1]='\0'; - state->response.extra_data = extra_data; - /* must add one to length to copy the 0 for string termination */ - state->response.length += strlen(extra_data) + 1; + domain->methods->sequence_number(domain, &domain->sequence_number); + + state->response.data.domain_info.sequence_number = + domain->sequence_number; return WINBINDD_OK; } +struct domain_info_state { + struct winbindd_domain *domain; + struct winbindd_cli_state *cli_state; +}; + +static void domain_info_init_recv(void *private, BOOL success); + enum winbindd_result winbindd_domain_info(struct winbindd_cli_state *state) { struct winbindd_domain *domain; @@ -209,7 +362,7 @@ enum winbindd_result winbindd_domain_info(struct winbindd_cli_state *state) DEBUG(3, ("[%5lu]: domain_info [%s]\n", (unsigned long)state->pid, state->request.domain_name)); - domain = find_domain_from_name(state->request.domain_name); + domain = find_domain_from_name_noinit(state->request.domain_name); if (domain == NULL) { DEBUG(3, ("Did not find domain [%s]\n", @@ -217,21 +370,78 @@ enum winbindd_result winbindd_domain_info(struct winbindd_cli_state *state) return WINBINDD_ERROR; } - fstrcpy(state->response.data.domain_info.name, domain->name); - fstrcpy(state->response.data.domain_info.alt_name, domain->alt_name); + if (!domain->initialized) { + struct domain_info_state *istate; + + istate = TALLOC_P(state->mem_ctx, struct domain_info_state); + if (istate == NULL) { + DEBUG(0, ("talloc failed\n")); + return WINBINDD_ERROR; + } + + istate->cli_state = state; + istate->domain = domain; + + init_child_connection(domain, domain_info_init_recv, istate); + + return WINBINDD_PENDING; + } + + fstrcpy(state->response.data.domain_info.name, + domain->name); + fstrcpy(state->response.data.domain_info.alt_name, + domain->alt_name); fstrcpy(state->response.data.domain_info.sid, sid_string_static(&domain->sid)); - state->response.data.domain_info.native_mode = domain->native_mode; - state->response.data.domain_info.active_directory = domain->active_directory; - state->response.data.domain_info.primary = domain->primary; - + state->response.data.domain_info.native_mode = + domain->native_mode; + state->response.data.domain_info.active_directory = + domain->active_directory; + state->response.data.domain_info.primary = + domain->primary; state->response.data.domain_info.sequence_number = domain->sequence_number; return WINBINDD_OK; } +static void domain_info_init_recv(void *private, BOOL success) +{ + struct domain_info_state *istate = private; + struct winbindd_cli_state *state = istate->cli_state; + struct winbindd_domain *domain = istate->domain; + + DEBUG(10, ("Got back from child init: %d\n", success)); + + if ((!success) || (!domain->initialized)) { + DEBUG(5, ("Could not init child for domain %s\n", + domain->name)); + state->response.result = WINBINDD_ERROR; + request_finished_cont(state, False); + return; + } + + fstrcpy(state->response.data.domain_info.name, + domain->name); + fstrcpy(state->response.data.domain_info.alt_name, + domain->alt_name); + fstrcpy(state->response.data.domain_info.sid, + sid_string_static(&domain->sid)); + + state->response.data.domain_info.native_mode = + domain->native_mode; + state->response.data.domain_info.active_directory = + domain->active_directory; + state->response.data.domain_info.primary = + domain->primary; + state->response.data.domain_info.sequence_number = + domain->sequence_number; + + state->response.result = WINBINDD_OK; + request_finished_cont(state, True); +} + enum winbindd_result winbindd_ping(struct winbindd_cli_state *state) { diff --git a/source3/nsswitch/winbindd_nss.h b/source3/nsswitch/winbindd_nss.h index 88645e093b..b249b62d69 100644 --- a/source3/nsswitch/winbindd_nss.h +++ b/source3/nsswitch/winbindd_nss.h @@ -34,7 +34,7 @@ /* Update this when you change the interface. */ -#define WINBIND_INTERFACE_VERSION 10 +#define WINBIND_INTERFACE_VERSION 11 /* Socket commands */ @@ -83,6 +83,7 @@ enum winbindd_cmd { WINBINDD_UID_TO_SID, WINBINDD_GID_TO_SID, WINBINDD_ALLOCATE_RID, + WINBINDD_ALLOCATE_RID_AND_GID, /* Miscellaneous other stuff */ @@ -93,6 +94,7 @@ enum winbindd_cmd { WINBINDD_DOMAIN_INFO, /* Most of what we know from struct winbindd_domain */ + WINBINDD_GETDCNAME, /* Issue a GetDCName Request */ WINBINDD_SHOW_SEQUENCE, /* display sequence numbers of domains */ @@ -110,9 +112,29 @@ enum winbindd_cmd { WINBINDD_PRIV_PIPE_DIR, /* return a list of group sids for a user sid */ - WINBINDD_GETUSERSIDS, + WINBINDD_GETUSERSIDS, + + /* Return the domain groups a user is in */ + WINBINDD_GETUSERDOMGROUPS, + + /* Initialize connection in a child */ + WINBINDD_INIT_CONNECTION, + + /* Blocking calls that are not allowed on the main winbind pipe, only + * between parent and children */ + WINBINDD_DUAL_SID2UID, + WINBINDD_DUAL_SID2GID, + WINBINDD_DUAL_IDMAPSET, + + /* Wrapper around possibly blocking unix nss calls */ + WINBINDD_DUAL_UID2NAME, + WINBINDD_DUAL_NAME2UID, + WINBINDD_DUAL_GID2NAME, + WINBINDD_DUAL_NAME2GID, + + WINBINDD_DUAL_USERINFO, + WINBINDD_DUAL_GETSIDALIASES, - /* Placeholder for end of cmd list */ WINBINDD_NUM_CMDS }; @@ -148,6 +170,9 @@ typedef struct winbindd_gr { #define WBFLAG_PAM_AFS_TOKEN 0x0100 #define WBFLAG_PAM_NT_STATUS_SQUASH 0x0200 +/* This is a flag that can only be sent from parent to child */ +#define WBFLAG_IS_PRIVILEGED 0x0400 + /* Winbind request structure */ struct winbindd_request { @@ -156,6 +181,7 @@ struct winbindd_request { pid_t pid; /* pid of calling process */ uint32 flags; /* flags relavant to a given request */ fstring domain_name; /* name of domain for which the request applies */ + int msgid; union { fstring winsreq; /* WINS request */ @@ -197,6 +223,24 @@ struct winbindd_request { fstring username; fstring groupname; } acct_mgt; + struct { + BOOL is_primary; + fstring dcname; + } init_conn; + struct { + fstring sid; + fstring name; + BOOL alloc; + } dual_sid2id; + struct { + int type; + uid_t uid; + gid_t gid; + fstring sid; + } dual_idmapset; + struct { + fstring cache_key; + } dual_sidaliases; } data; char null_term; }; @@ -205,6 +249,7 @@ struct winbindd_request { enum winbindd_result { WINBINDD_ERROR, + WINBINDD_PENDING, WINBINDD_OK }; @@ -250,6 +295,7 @@ struct winbindd_response { } info; fstring domain_name; fstring netbios_name; + fstring dc_name; struct auth_reply { uint32 nt_status; @@ -261,6 +307,10 @@ struct winbindd_response { } auth; uint32 rid; /* create user or group or allocate rid */ struct { + uint32 rid; + gid_t gid; + } rid_and_gid; + struct { fstring name; fstring alt_name; fstring sid; @@ -269,6 +319,11 @@ struct winbindd_response { BOOL primary; uint32 sequence_number; } domain_info; + struct { + fstring acct_name; + fstring full_name; + uint32 group_rid; + } user_info; } data; /* Variable length return data */ diff --git a/source3/nsswitch/winbindd_pam.c b/source3/nsswitch/winbindd_pam.c index 9061391118..97dc35c0e7 100644 --- a/source3/nsswitch/winbindd_pam.c +++ b/source3/nsswitch/winbindd_pam.c @@ -145,24 +145,99 @@ static NTSTATUS check_info3_in_group(TALLOC_CTX *mem_ctx, return NT_STATUS_LOGON_FAILURE; } +static struct winbindd_domain *find_auth_domain(const char *domain_name) +{ + struct winbindd_domain *domain; + + if (IS_DC) { + domain = find_domain_from_name_noinit(domain_name); + if (domain == NULL) { + DEBUG(3, ("Authentication for domain [%s] " + "as it is not a trusted domain\n", + domain_name)); + } + return domain; + } + + if (is_myname(domain_name)) { + DEBUG(3, ("Authentication for domain %s (local domain " + "to this server) not supported at this " + "stage\n", domain_name)); + return NULL; + } + + return find_our_domain(); +} + +static void set_auth_errors(struct winbindd_response *resp, NTSTATUS result) +{ + resp->data.auth.nt_status = NT_STATUS_V(result); + fstrcpy(resp->data.auth.nt_status_string, nt_errstr(result)); + + /* we might have given a more useful error above */ + if (*resp->data.auth.error_string == '\0') + fstrcpy(resp->data.auth.error_string, + get_friendly_nt_error_msg(result)); + resp->data.auth.pam_error = nt_status_to_pam(result); +} + /********************************************************************** Authenticate a user with a clear text password **********************************************************************/ -enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) +enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) +{ + struct winbindd_domain *domain; + fstring name_domain, name_user; + + /* Ensure null termination */ + state->request.data.auth.user + [sizeof(state->request.data.auth.user)-1]='\0'; + + /* Ensure null termination */ + state->request.data.auth.pass + [sizeof(state->request.data.auth.pass)-1]='\0'; + + DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid, + state->request.data.auth.user)); + + /* Parse domain and username */ + + parse_domain_user(state->request.data.auth.user, + name_domain, name_user); + + domain = find_auth_domain(name_domain); + + if (domain == NULL) { + set_auth_errors(&state->response, NT_STATUS_NO_SUCH_USER); + DEBUG(5, ("Plain text authentication for %s returned %s " + "(PAM: %d)\n", + state->request.data.auth.user, + state->response.data.auth.nt_status_string, + state->response.data.auth.pam_error)); + return WINBINDD_ERROR; + } + + async_domain_request(state->mem_ctx, domain, + &state->request, &state->response, + request_finished_cont, state); + return WINBINDD_PENDING; +} + +enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain, + struct winbindd_cli_state *state) { NTSTATUS result; fstring name_domain, name_user; - unsigned char trust_passwd[16]; - time_t last_change_time; - uint32 sec_channel_type; + const char *srv_name_slash; NET_USER_INFO_3 info3; - struct cli_state *cli = NULL; + unsigned char *session_key; + struct rpc_pipe_client *pipe_cli; uchar chal[8]; - TALLOC_CTX *mem_ctx = NULL; DATA_BLOB lm_resp; DATA_BLOB nt_resp; DOM_CRED ret_creds; + DOM_CRED *credentials; int attempts = 0; unsigned char local_lm_response[24]; unsigned char local_nt_response[24]; @@ -178,12 +253,6 @@ enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) DEBUG(3, ("[%5lu]: pam auth %s\n", (unsigned long)state->pid, state->request.data.auth.user)); - if (!(mem_ctx = talloc_init("winbind pam auth for %s", state->request.data.auth.user))) { - DEBUG(0, ("winbindd_pam_auth: could not talloc_init()!\n")); - result = NT_STATUS_NO_MEMORY; - goto done; - } - /* Parse domain and username */ parse_domain_user(state->request.data.auth.user, name_domain, name_user); @@ -197,7 +266,7 @@ enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) DATA_BLOB names_blob; DATA_BLOB nt_response; DATA_BLOB lm_response; - server_chal = data_blob_talloc(mem_ctx, chal, 8); + server_chal = data_blob_talloc(state->mem_ctx, chal, 8); /* note that the 'workgroup' here is a best guess - we don't know the server's domain at this point. The 'server name' is also @@ -218,8 +287,10 @@ enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) } data_blob_free(&names_blob); data_blob_free(&server_chal); - lm_resp = data_blob_talloc(mem_ctx, lm_response.data, lm_response.length); - nt_resp = data_blob_talloc(mem_ctx, nt_response.data, nt_response.length); + lm_resp = data_blob_talloc(state->mem_ctx, lm_response.data, + lm_response.length); + nt_resp = data_blob_talloc(state->mem_ctx, nt_response.data, + nt_response.length); data_blob_free(&lm_response); data_blob_free(&nt_response); @@ -228,7 +299,7 @@ enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) && SMBencrypt(state->request.data.auth.pass, chal, local_lm_response)) { - lm_resp = data_blob_talloc(mem_ctx, + lm_resp = data_blob_talloc(state->mem_ctx, local_lm_response, sizeof(local_lm_response)); } else { @@ -238,7 +309,7 @@ enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) chal, local_nt_response); - nt_resp = data_blob_talloc(mem_ctx, + nt_resp = data_blob_talloc(state->mem_ctx, local_nt_response, sizeof(local_nt_response)); } @@ -261,80 +332,95 @@ enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) goto done; } - if (!(contact_domain = find_our_domain())) { - DEBUG(1, ("Authentication for [%s] -> [%s]\\[%s] in our domain failed - we can't find our domain!\n", - state->request.data.auth.user, name_domain, name_user)); - result = NT_STATUS_NO_SUCH_USER; - goto done; - } + contact_domain = find_our_domain(); } - if ( !get_trust_pw(contact_domain->name, trust_passwd, &last_change_time, &sec_channel_type) ) { - result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; - goto done; + srv_name_slash = talloc_asprintf(state->mem_ctx, "\\\\%s", + contact_domain->dcname); + if (srv_name_slash == NULL) { + DEBUG(0, ("talloc_asprintf failed\n")); + return WINBINDD_ERROR; } - + /* check authentication loop */ do { + DOM_CRED clnt_creds; + ZERO_STRUCT(info3); ZERO_STRUCT(ret_creds); retry = False; - - /* Don't shut this down - it belongs to the connection cache code */ - result = cm_get_netlogon_cli(contact_domain, trust_passwd, - sec_channel_type, False, &cli); + + result = cm_connect_netlogon(contact_domain, state->mem_ctx, + &pipe_cli, &session_key, + &credentials); if (!NT_STATUS_IS_OK(result)) { DEBUG(3, ("could not open handle to NETLOGON pipe\n")); goto done; } - result = cli_netlogon_sam_network_logon(cli, mem_ctx, - &ret_creds, - name_user, name_domain, - global_myname(), chal, - lm_resp, nt_resp, - &info3); + credentials->timestamp.time = time(NULL); + memcpy(&clnt_creds, credentials, sizeof(clnt_creds)); + + /* Calculate the new credentials. */ + cred_create(session_key, &credentials->challenge, + clnt_creds.timestamp, &(clnt_creds.challenge)); + + result = rpccli_netlogon_sam_network_logon(pipe_cli, + state->mem_ctx, + srv_name_slash, + &clnt_creds, + &ret_creds, + name_user, + name_domain, + global_myname(), + chal, lm_resp, + nt_resp, &info3, + session_key); attempts += 1; - - /* We have to try a second time as cm_get_netlogon_cli + + /* We have to try a second time as cm_connect_netlogon might not yet have noticed that the DC has killed our connection. */ - if ( cli->fd == -1 ) { + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) { retry = True; continue; - } + } - /* if we get access denied, a possible cuase was that we had and open - connection to the DC, but someone changed our machine account password - out from underneath us using 'net rpc changetrustpw' */ + /* if we get access denied, a possible cause was that we had + and open connection to the DC, but someone changed our + machine account password out from underneath us using 'net + rpc changetrustpw' */ - if ( NT_STATUS_V(result) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) ) { - DEBUG(3,("winbindd_pam_auth: sam_logon returned ACCESS_DENIED. Maybe the trust account " - "password was changed and we didn't know it. Killing connections to domain %s\n", + if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) { + DEBUG(3,("winbindd_pam_auth: sam_logon returned " + "ACCESS_DENIED. Maybe the trust account " + "password was changed and we didn't know it. " + "Killing connections to domain %s\n", name_domain)); - winbindd_cm_flush(); + invalidate_cm_connection(&contact_domain->conn); retry = True; - cli = NULL; } } while ( (attempts < 2) && retry ); - if (cli != NULL) { - /* We might have come out of the loop above with cli == NULL, - so don't dereference that. */ - clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred), &ret_creds); + if (NT_STATUS_IS_OK(result) && + (!clnt_deal_with_creds(session_key, credentials, + &ret_creds))) { + DEBUG(3, ("DC %s sent wrong credentials\n", + pipe_cli->cli->srv_name_slash)); + result = NT_STATUS_ACCESS_DENIED; } - + if (NT_STATUS_IS_OK(result)) { - netsamlogon_cache_store( cli->mem_ctx, name_user, &info3 ); + netsamlogon_cache_store(state->mem_ctx, name_user, &info3); wcache_invalidate_samlogon(find_domain_from_name(name_domain), &info3); /* Check if the user is in the right group */ - if (!NT_STATUS_IS_OK(result = check_info3_in_group(mem_ctx, &info3, state->request.data.auth.require_membership_of_sid))) { + if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, &info3, state->request.data.auth.require_membership_of_sid))) { DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n", state->request.data.auth.user, state->request.data.auth.require_membership_of_sid)); @@ -407,9 +493,6 @@ done: SAFE_FREE(afsname); } - if (mem_ctx) - talloc_destroy(mem_ctx); - return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; } @@ -417,15 +500,79 @@ done: Challenge Response Authentication Protocol **********************************************************************/ -enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state) +enum winbindd_result winbindd_crap_auth(struct winbindd_cli_state *state) { + struct winbindd_domain *domain = NULL; + const char *domain_name = NULL; NTSTATUS result; - unsigned char trust_passwd[16]; - time_t last_change_time; - uint32 sec_channel_type; + + if (!state->privileged) { + char *error_string = NULL; + DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access " + "denied. !\n")); + DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions " + "on %s are set correctly.\n", + get_winbind_priv_pipe_dir())); + /* send a better message than ACCESS_DENIED */ + error_string = talloc_asprintf(state->mem_ctx, + "winbind client not authorized " + "to use winbindd_pam_auth_crap." + " Ensure permissions on %s " + "are set correctly.", + get_winbind_priv_pipe_dir()); + fstrcpy(state->response.data.auth.error_string, error_string); + result = NT_STATUS_ACCESS_DENIED; + goto done; + } + + /* Ensure null termination */ + state->request.data.auth_crap.user + [sizeof(state->request.data.auth_crap.user)-1]=0; + state->request.data.auth_crap.domain + [sizeof(state->request.data.auth_crap.domain)-1]=0; + + DEBUG(3, ("[%5lu]: pam auth crap domain: [%s] user: %s\n", + (unsigned long)state->pid, + state->request.data.auth_crap.domain, + state->request.data.auth_crap.user)); + + if (*state->request.data.auth_crap.domain != '\0') { + domain_name = state->request.data.auth_crap.domain; + } else if (lp_winbind_use_default_domain()) { + domain_name = lp_workgroup(); + } + + if (domain_name != NULL) + domain = find_auth_domain(domain_name); + + if (domain != NULL) { + async_domain_request(state->mem_ctx, domain, + &state->request, &state->response, + request_finished_cont, state); + return WINBINDD_PENDING; + } + + result = NT_STATUS_NO_SUCH_USER; + + done: + set_auth_errors(&state->response, result); + DEBUG(5, ("CRAP authentication for %s returned %s (PAM: %d)\n", + state->request.data.auth.user, + state->response.data.auth.nt_status_string, + state->response.data.auth.pam_error)); + return WINBINDD_ERROR; +} + + +enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + NTSTATUS result; + const char *srv_name_slash; NET_USER_INFO_3 info3; - struct cli_state *cli = NULL; - TALLOC_CTX *mem_ctx = NULL; + unsigned char *session_key; + struct rpc_pipe_client *pipe_cli; + DOM_CRED *credentials; const char *name_user = NULL; const char *name_domain = NULL; const char *workstation; @@ -436,30 +583,13 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state) DATA_BLOB lm_resp, nt_resp; - if (!state->privileged) { - char *error_string = NULL; - DEBUG(2, ("winbindd_pam_auth_crap: non-privileged access denied. !\n")); - DEBUGADD(2, ("winbindd_pam_auth_crap: Ensure permissions on %s are set correctly.\n", - get_winbind_priv_pipe_dir())); - /* send a better message than ACCESS_DENIED */ - asprintf(&error_string, "winbind client not authorized to use winbindd_pam_auth_crap. Ensure permissions on %s are set correctly.", - get_winbind_priv_pipe_dir()); - fstrcpy(state->response.data.auth.error_string, error_string); - SAFE_FREE(error_string); - result = NT_STATUS_ACCESS_DENIED; - goto done; - } + /* This is child-only, so no check for privileged access is needed + anymore */ /* Ensure null termination */ state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]=0; state->request.data.auth_crap.domain[sizeof(state->request.data.auth_crap.domain)-1]=0; - if (!(mem_ctx = talloc_init("winbind pam auth crap for %s", state->request.data.auth_crap.user))) { - DEBUG(0, ("winbindd_pam_auth_crap: could not talloc_init()!\n")); - result = NT_STATUS_NO_MEMORY; - goto done; - } - name_user = state->request.data.auth_crap.user; if (*state->request.data.auth_crap.domain) { @@ -491,8 +621,8 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state) goto done; } - lm_resp = data_blob_talloc(mem_ctx, state->request.data.auth_crap.lm_resp, state->request.data.auth_crap.lm_resp_len); - nt_resp = data_blob_talloc(mem_ctx, state->request.data.auth_crap.nt_resp, state->request.data.auth_crap.nt_resp_len); + lm_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.lm_resp, state->request.data.auth_crap.lm_resp_len); + nt_resp = data_blob_talloc(state->mem_ctx, state->request.data.auth_crap.nt_resp, state->request.data.auth_crap.nt_resp_len); /* what domain should we contact? */ @@ -512,26 +642,25 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state) goto done; } - if (!(contact_domain = find_our_domain())) { - DEBUG(1, ("Authenticatoin for [%s] -> [%s]\\[%s] in our domain failed - we can't find our domain!\n", - state->request.data.auth_crap.user, name_domain, name_user)); - result = NT_STATUS_NO_SUCH_USER; - goto done; - } - } - - if ( !get_trust_pw(contact_domain->name, trust_passwd, &last_change_time, &sec_channel_type) ) { - result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; - goto done; + contact_domain = find_our_domain(); } + srv_name_slash = talloc_asprintf(state->mem_ctx, "\\\\%s", + contact_domain->dcname); + if (srv_name_slash == NULL) { + DEBUG(0, ("talloc_asprintf failed\n")); + return WINBINDD_ERROR; + } + do { + DOM_CRED clnt_creds; ZERO_STRUCT(info3); ZERO_STRUCT(ret_creds); retry = False; - /* Don't shut this down - it belongs to the connection cache code */ - result = cm_get_netlogon_cli(contact_domain, trust_passwd, sec_channel_type, False, &cli); + result = cm_connect_netlogon(contact_domain, state->mem_ctx, + &pipe_cli, &session_key, + &credentials); if (!NT_STATUS_IS_OK(result)) { DEBUG(3, ("could not open handle to NETLOGON pipe (error: %s)\n", @@ -539,51 +668,66 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state) goto done; } - result = cli_netlogon_sam_network_logon(cli, mem_ctx, - &ret_creds, - name_user, name_domain, - workstation, - state->request.data.auth_crap.chal, - lm_resp, nt_resp, - &info3); + credentials->timestamp.time = time(NULL); + memcpy(&clnt_creds, credentials, sizeof(clnt_creds)); + + /* Calculate the new credentials. */ + cred_create(session_key, &credentials->challenge, + clnt_creds.timestamp, &(clnt_creds.challenge)); + + result = rpccli_netlogon_sam_network_logon(pipe_cli, + state->mem_ctx, + srv_name_slash, + &clnt_creds, + &ret_creds, + name_user, + name_domain, + global_myname(), + state->request.data.auth_crap.chal, + lm_resp, + nt_resp, &info3, + session_key); attempts += 1; - /* We have to try a second time as cm_get_netlogon_cli + /* We have to try a second time as cm_connect_netlogon might not yet have noticed that the DC has killed our connection. */ - if ( cli->fd == -1 ) { + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) { retry = True; continue; - } + } /* if we get access denied, a possible cause was that we had and open connection to the DC, but someone changed our machine account password out from underneath us using 'net rpc changetrustpw' */ - if ( NT_STATUS_V(result) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) ) { - DEBUG(3,("winbindd_pam_auth_crap: sam_logon returned ACCESS_DENIED. Maybe the trust account " - "password was changed and we didn't know it. Killing connections to domain %s\n", - contact_domain->name)); - winbindd_cm_flush(); + if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) { + DEBUG(3,("winbindd_pam_auth: sam_logon returned " + "ACCESS_DENIED. Maybe the trust account " + "password was changed and we didn't know it. " + "Killing connections to domain %s\n", + name_domain)); + invalidate_cm_connection(&contact_domain->conn); retry = True; - cli = NULL; } - + } while ( (attempts < 2) && retry ); - if (cli != NULL) { - /* We might have come out of the loop above with cli == NULL, - so don't dereference that. */ - clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred), &ret_creds); + if (NT_STATUS_IS_OK(result) && + (!clnt_deal_with_creds(session_key, credentials, + &ret_creds))) { + DEBUG(3, ("DC %s sent wrong credentials\n", + pipe_cli->cli->srv_name_slash)); + result = NT_STATUS_ACCESS_DENIED; } if (NT_STATUS_IS_OK(result)) { - netsamlogon_cache_store( cli->mem_ctx, name_user, &info3 ); + netsamlogon_cache_store( state->mem_ctx, name_user, &info3 ); wcache_invalidate_samlogon(find_domain_from_name(name_domain), &info3); - if (!NT_STATUS_IS_OK(result = check_info3_in_group(mem_ctx, &info3, state->request.data.auth_crap.require_membership_of_sid))) { + if (!NT_STATUS_IS_OK(result = check_info3_in_group(state->mem_ctx, &info3, state->request.data.auth_crap.require_membership_of_sid))) { DEBUG(3, ("User %s is not in the required group (%s), so plaintext authentication is rejected\n", state->request.data.auth_crap.user, state->request.data.auth_crap.require_membership_of_sid)); @@ -591,19 +735,19 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state) } if (state->request.flags & WBFLAG_PAM_INFO3_NDR) { - result = append_info3_as_ndr(mem_ctx, state, &info3); + result = append_info3_as_ndr(state->mem_ctx, state, &info3); } else if (state->request.flags & WBFLAG_PAM_UNIX_NAME) { /* ntlm_auth should return the unix username, per 'winbind use default domain' settings and the like */ fstring username_out; const char *nt_username, *nt_domain; - if (!(nt_username = unistr2_tdup(mem_ctx, &(info3.uni_user_name)))) { + if (!(nt_username = unistr2_tdup(state->mem_ctx, &(info3.uni_user_name)))) { /* If the server didn't give us one, just use the one we sent them */ nt_username = name_user; } - if (!(nt_domain = unistr2_tdup(mem_ctx, &(info3.uni_logon_dom)))) { + if (!(nt_domain = unistr2_tdup(state->mem_ctx, &(info3.uni_logon_dom)))) { /* If the server didn't give us one, just use the one we sent them */ nt_domain = name_domain; } @@ -653,9 +797,6 @@ done: state->response.data.auth.nt_status_string, state->response.data.auth.pam_error)); - if (mem_ctx) - talloc_destroy(mem_ctx); - return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; } @@ -666,20 +807,13 @@ enum winbindd_result winbindd_pam_chauthtok(struct winbindd_cli_state *state) NTSTATUS result; char *oldpass, *newpass; fstring domain, user; - CLI_POLICY_HND *hnd; - TALLOC_CTX *mem_ctx; + POLICY_HND dom_pol; struct winbindd_domain *contact_domain; + struct rpc_pipe_client *cli; DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)state->pid, state->request.data.chauthtok.user)); - if (!(mem_ctx = talloc_init("winbind password change for %s", - state->request.data.chauthtok.user))) { - DEBUG(0, ("winbindd_pam_auth_crap: could not talloc_init()!\n")); - result = NT_STATUS_NO_MEMORY; - goto done; - } - /* Setup crap */ if (state == NULL) @@ -701,12 +835,15 @@ enum winbindd_result winbindd_pam_chauthtok(struct winbindd_cli_state *state) /* Get sam handle */ - if (!NT_STATUS_IS_OK(result = cm_get_sam_handle(contact_domain, &hnd)) ) { + result = cm_connect_sam(contact_domain, state->mem_ctx, &cli, + &dom_pol); + if (!NT_STATUS_IS_OK(result)) { DEBUG(1, ("could not get SAM handle on DC for %s\n", domain)); goto done; } - result = cli_samr_chgpasswd_user(hnd->cli, mem_ctx, user, newpass, oldpass); + result = rpccli_samr_chgpasswd_user(cli, state->mem_ctx, user, newpass, + oldpass); done: state->response.data.auth.nt_status = NT_STATUS_V(result); @@ -721,8 +858,5 @@ done: state->response.data.auth.nt_status_string, state->response.data.auth.pam_error)); - if (mem_ctx) - talloc_destroy(mem_ctx); - return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; } diff --git a/source3/nsswitch/winbindd_passdb.c b/source3/nsswitch/winbindd_passdb.c index f0484d35ee..4b22712e0a 100644 --- a/source3/nsswitch/winbindd_passdb.c +++ b/source3/nsswitch/winbindd_passdb.c @@ -57,7 +57,7 @@ add_expanded_sid(const DOM_SID *sid, char **members, int *num_members) enum SID_NAME_USE type; uint32 num_names; - DOM_SID **sid_mem; + DOM_SID *sid_mem; char **names; uint32 *types; @@ -126,7 +126,7 @@ add_expanded_sid(const DOM_SID *sid, char **members, int *num_members) for (i=0; i<num_names; i++) { DEBUG(10, ("Adding group member SID %s\n", - sid_string_static(sid_mem[i]))); + sid_string_static(&sid_mem[i]))); if (types[i] != SID_NAME_USER) { DEBUG(1, ("Hmmm. Member %s of group %s is no user. " @@ -297,24 +297,29 @@ static NTSTATUS query_user(struct winbindd_domain *domain, static NTSTATUS lookup_usergroups(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, const DOM_SID *user_sid, - uint32 *num_groups, DOM_SID ***user_gids) + uint32 *num_groups, DOM_SID **user_gids) { return NT_STATUS_NO_SUCH_USER; } static NTSTATUS lookup_useraliases(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, - uint32 num_sids, DOM_SID **sids, - uint32 *num_aliases, uint32 **aliases) + uint32 num_sids, const DOM_SID *sids, + uint32 *num_aliases, uint32 **rids) { - return NT_STATUS_NO_SUCH_USER; + BOOL result; + + result = pdb_enum_alias_memberships(mem_ctx, &domain->sid, + sids, num_sids, rids, num_aliases); + + return result ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; } /* Lookup group membership given a rid. */ static NTSTATUS lookup_groupmem(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, const DOM_SID *group_sid, uint32 *num_names, - DOM_SID ***sid_mem, char ***names, + DOM_SID **sid_mem, char ***names, uint32 **name_types) { return NT_STATUS_OK; diff --git a/source3/nsswitch/winbindd_reconnect.c b/source3/nsswitch/winbindd_reconnect.c new file mode 100644 index 0000000000..1a90717db3 --- /dev/null +++ b/source3/nsswitch/winbindd_reconnect.c @@ -0,0 +1,273 @@ +/* + Unix SMB/CIFS implementation. + + Wrapper around winbindd_rpc.c to centralize retry logic. + + Copyright (C) Volker Lendecke 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "winbindd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +extern struct winbindd_methods msrpc_methods; + +/* List all users */ +static NTSTATUS query_user_list(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *num_entries, + WINBIND_USERINFO **info) +{ + NTSTATUS result; + + result = msrpc_methods.query_user_list(domain, mem_ctx, + num_entries, info); + + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) + result = msrpc_methods.query_user_list(domain, mem_ctx, + num_entries, info); + return result; +} + +/* list all domain groups */ +static NTSTATUS enum_dom_groups(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *num_entries, + struct acct_info **info) +{ + NTSTATUS result; + + result = msrpc_methods.enum_dom_groups(domain, mem_ctx, + num_entries, info); + + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) + result = msrpc_methods.enum_dom_groups(domain, mem_ctx, + num_entries, info); + return result; +} + +/* List all domain groups */ + +static NTSTATUS enum_local_groups(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *num_entries, + struct acct_info **info) +{ + NTSTATUS result; + + result = msrpc_methods.enum_local_groups(domain, mem_ctx, + num_entries, info); + + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) + result = msrpc_methods.enum_local_groups(domain, mem_ctx, + num_entries, info); + + return result; +} + +/* convert a single name to a sid in a domain */ +static NTSTATUS name_to_sid(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const char *domain_name, + const char *name, + DOM_SID *sid, + enum SID_NAME_USE *type) +{ + NTSTATUS result; + + result = msrpc_methods.name_to_sid(domain, mem_ctx, + domain_name, name, + sid, type); + + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) + result = msrpc_methods.name_to_sid(domain, mem_ctx, + domain_name, name, + sid, type); + + return result; +} + +/* + convert a domain SID to a user or group name +*/ +static NTSTATUS sid_to_name(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *sid, + char **domain_name, + char **name, + enum SID_NAME_USE *type) +{ + NTSTATUS result; + + result = msrpc_methods.sid_to_name(domain, mem_ctx, sid, + domain_name, name, type); + + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) + result = msrpc_methods.sid_to_name(domain, mem_ctx, sid, + domain_name, name, type); + + return result; +} + +/* Lookup user information from a rid or username. */ +static NTSTATUS query_user(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *user_sid, + WINBIND_USERINFO *user_info) +{ + NTSTATUS result; + + result = msrpc_methods.query_user(domain, mem_ctx, user_sid, + user_info); + + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) + result = msrpc_methods.query_user(domain, mem_ctx, user_sid, + user_info); + + return result; +} + +/* Lookup groups a user is a member of. I wish Unix had a call like this! */ +static NTSTATUS lookup_usergroups(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *user_sid, + uint32 *num_groups, DOM_SID **user_gids) +{ + NTSTATUS result; + + result = msrpc_methods.lookup_usergroups(domain, mem_ctx, + user_sid, num_groups, + user_gids); + + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) + result = msrpc_methods.lookup_usergroups(domain, mem_ctx, + user_sid, num_groups, + user_gids); + + return result; +} + +static NTSTATUS lookup_useraliases(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 num_sids, const DOM_SID *sids, + uint32 *num_aliases, uint32 **alias_rids) +{ + NTSTATUS result; + + result = msrpc_methods.lookup_useraliases(domain, mem_ctx, + num_sids, sids, + num_aliases, + alias_rids); + + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) + result = msrpc_methods.lookup_useraliases(domain, mem_ctx, + num_sids, sids, + num_aliases, + alias_rids); + + return result; +} + +/* Lookup group membership given a rid. */ +static NTSTATUS lookup_groupmem(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + const DOM_SID *group_sid, uint32 *num_names, + DOM_SID **sid_mem, char ***names, + uint32 **name_types) +{ + NTSTATUS result; + + result = msrpc_methods.lookup_groupmem(domain, mem_ctx, + group_sid, num_names, + sid_mem, names, + name_types); + + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) + result = msrpc_methods.lookup_groupmem(domain, mem_ctx, + group_sid, num_names, + sid_mem, names, + name_types); + + return result; +} + +/* find the sequence number for a domain */ +static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq) +{ + NTSTATUS result; + + result = msrpc_methods.sequence_number(domain, seq); + + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) + result = msrpc_methods.sequence_number(domain, seq); + + return result; +} + +/* get a list of trusted domains */ +static NTSTATUS trusted_domains(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + uint32 *num_domains, + char ***names, + char ***alt_names, + DOM_SID **dom_sids) +{ + NTSTATUS result; + + result = msrpc_methods.trusted_domains(domain, mem_ctx, + num_domains, names, + alt_names, dom_sids); + + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) + result = msrpc_methods.trusted_domains(domain, mem_ctx, + num_domains, names, + alt_names, dom_sids); + + return result; +} + +static NTSTATUS alternate_name(struct winbindd_domain *domain) +{ + NTSTATUS result; + + result = msrpc_methods.alternate_name(domain); + + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) + result = msrpc_methods.alternate_name(domain); + + return result; +} + + +/* the rpc backend methods are exposed via this structure */ +struct winbindd_methods reconnect_methods = { + False, + query_user_list, + enum_dom_groups, + enum_local_groups, + name_to_sid, + sid_to_name, + query_user, + lookup_usergroups, + lookup_useraliases, + lookup_groupmem, + sequence_number, + trusted_domains, + alternate_name +}; diff --git a/source3/nsswitch/winbindd_rpc.c b/source3/nsswitch/winbindd_rpc.c index 854688da4e..2b4c020d88 100644 --- a/source3/nsswitch/winbindd_rpc.c +++ b/source3/nsswitch/winbindd_rpc.c @@ -37,37 +37,20 @@ static NTSTATUS query_user_list(struct winbindd_domain *domain, uint32 *num_entries, WINBIND_USERINFO **info) { - CLI_POLICY_HND *hnd; - NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + NTSTATUS result; POLICY_HND dom_pol; - BOOL got_dom_pol = False; - uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED; - unsigned int i, start_idx, retry; + unsigned int i, start_idx; uint32 loop_count; + struct rpc_pipe_client *cli; DEBUG(3,("rpc: query_user_list\n")); *num_entries = 0; *info = NULL; - retry = 0; - do { - /* Get sam handle */ - - if ( !NT_STATUS_IS_OK(result = cm_get_sam_handle(domain, &hnd)) ) - return result; - - /* Get domain handle */ - - result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol, - des_access, &domain->sid, &dom_pol); - - } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1); - + result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); if (!NT_STATUS_IS_OK(result)) - goto done; - - got_dom_pol = True; + return result; i = start_idx = 0; loop_count = 0; @@ -83,28 +66,30 @@ static NTSTATUS query_user_list(struct winbindd_domain *domain, ZERO_STRUCT( info1 ); ctr.sam.info1 = &info1; - if (!(ctx2 = talloc_init("winbindd enum_users"))) { - result = NT_STATUS_NO_MEMORY; - goto done; - } + if (!(ctx2 = talloc_init("winbindd enum_users"))) + return NT_STATUS_NO_MEMORY; /* this next bit is copied from net_user_list_internal() */ - get_query_dispinfo_params( loop_count, &max_entries, &max_size ); + get_query_dispinfo_params(loop_count, &max_entries, + &max_size); - result = cli_samr_query_dispinfo(hnd->cli, mem_ctx, &dom_pol, - &start_idx, 1, &num_dom_users, max_entries, max_size, &ctr); + result = rpccli_samr_query_dispinfo(cli, mem_ctx, &dom_pol, + &start_idx, 1, + &num_dom_users, + max_entries, max_size, + &ctr); loop_count++; *num_entries += num_dom_users; - *info = TALLOC_REALLOC_ARRAY( mem_ctx, *info, WINBIND_USERINFO, *num_entries); + *info = TALLOC_REALLOC_ARRAY(mem_ctx, *info, WINBIND_USERINFO, + *num_entries); if (!(*info)) { - result = NT_STATUS_NO_MEMORY; talloc_destroy(ctx2); - goto done; + return NT_STATUS_NO_MEMORY; } for (j = 0; j < num_dom_users; i++, j++) { @@ -116,7 +101,7 @@ static NTSTATUS query_user_list(struct winbindd_domain *domain, (*info)[i].acct_name = talloc_strdup(mem_ctx, username ); (*info)[i].full_name = talloc_strdup(mem_ctx, fullname ); - (*info)[i].user_sid = rid_to_talloced_sid(domain, mem_ctx, rid ); + sid_compose(&(*info)[i].user_sid, &domain->sid, rid); /* For the moment we set the primary group for every user to be the Domain Users group. @@ -126,19 +111,14 @@ static NTSTATUS query_user_list(struct winbindd_domain *domain, force group' smb.conf parameter or something like that. */ - (*info)[i].group_sid = rid_to_talloced_sid(domain, - mem_ctx, DOMAIN_GROUP_RID_USERS); + sid_compose(&(*info)[i].group_sid, &domain->sid, + DOMAIN_GROUP_RID_USERS); } talloc_destroy(ctx2); } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)); - done: - - if (got_dom_pol) - cli_samr_close(hnd->cli, mem_ctx, &dom_pol); - return result; } @@ -148,28 +128,17 @@ static NTSTATUS enum_dom_groups(struct winbindd_domain *domain, uint32 *num_entries, struct acct_info **info) { - uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED; - CLI_POLICY_HND *hnd; POLICY_HND dom_pol; NTSTATUS status; uint32 start = 0; - int retry; - NTSTATUS result; + struct rpc_pipe_client *cli; *num_entries = 0; *info = NULL; DEBUG(3,("rpc: enum_dom_groups\n")); - retry = 0; - do { - if (!NT_STATUS_IS_OK(result = cm_get_sam_handle(domain, &hnd))) - return result; - - status = cli_samr_open_domain(hnd->cli, mem_ctx, - &hnd->pol, des_access, &domain->sid, &dom_pol); - } while (!NT_STATUS_IS_OK(status) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1); - + status = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); if (!NT_STATUS_IS_OK(status)) return status; @@ -181,10 +150,10 @@ static NTSTATUS enum_dom_groups(struct winbindd_domain *domain, mem_ctx2 = talloc_init("enum_dom_groups[rpc]"); /* start is updated by this call. */ - status = cli_samr_enum_dom_groups(hnd->cli, mem_ctx2, &dom_pol, - &start, - 0xFFFF, /* buffer size? */ - &info2, &count); + status = rpccli_samr_enum_dom_groups(cli, mem_ctx2, &dom_pol, + &start, + 0xFFFF, /* buffer size? */ + &info2, &count); if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { @@ -192,11 +161,13 @@ static NTSTATUS enum_dom_groups(struct winbindd_domain *domain, break; } - (*info) = TALLOC_REALLOC_ARRAY(mem_ctx, *info, struct acct_info, (*num_entries) + count); + (*info) = TALLOC_REALLOC_ARRAY(mem_ctx, *info, + struct acct_info, + (*num_entries) + count); if (! *info) { talloc_destroy(mem_ctx2); - cli_samr_close(hnd->cli, mem_ctx, &dom_pol); - return NT_STATUS_NO_MEMORY; + status = NT_STATUS_NO_MEMORY; + break; } memcpy(&(*info)[*num_entries], info2, count*sizeof(*info2)); @@ -204,9 +175,7 @@ static NTSTATUS enum_dom_groups(struct winbindd_domain *domain, talloc_destroy(mem_ctx2); } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)); - cli_samr_close(hnd->cli, mem_ctx, &dom_pol); - - return status; + return NT_STATUS_OK; } /* List all domain groups */ @@ -216,25 +185,17 @@ static NTSTATUS enum_local_groups(struct winbindd_domain *domain, uint32 *num_entries, struct acct_info **info) { - uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED; - CLI_POLICY_HND *hnd; POLICY_HND dom_pol; NTSTATUS result; - int retry; + struct rpc_pipe_client *cli; *num_entries = 0; *info = NULL; - retry = 0; - do { - if ( !NT_STATUS_IS_OK(result = cm_get_sam_handle(domain, &hnd)) ) - return result; - - result = cli_samr_open_domain( hnd->cli, mem_ctx, &hnd->pol, - des_access, &domain->sid, &dom_pol); - } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1); + DEBUG(3,("rpc: enum_local_groups\n")); - if ( !NT_STATUS_IS_OK(result)) + result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); + if (!NT_STATUS_IS_OK(result)) return result; do { @@ -244,31 +205,32 @@ static NTSTATUS enum_local_groups(struct winbindd_domain *domain, mem_ctx2 = talloc_init("enum_dom_local_groups[rpc]"); - result = cli_samr_enum_als_groups( hnd->cli, mem_ctx2, &dom_pol, - &start, 0xFFFF, &info2, &count); + result = rpccli_samr_enum_als_groups( cli, mem_ctx2, &dom_pol, + &start, 0xFFFF, &info2, + &count); - if ( !NT_STATUS_IS_OK(result) - && !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES) ) + if (!NT_STATUS_IS_OK(result) && + !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES) ) { talloc_destroy(mem_ctx2); - break; + return result; } - (*info) = TALLOC_REALLOC_ARRAY(mem_ctx, *info, struct acct_info, (*num_entries) + count); + (*info) = TALLOC_REALLOC_ARRAY(mem_ctx, *info, + struct acct_info, + (*num_entries) + count); if (! *info) { talloc_destroy(mem_ctx2); - cli_samr_close(hnd->cli, mem_ctx, &dom_pol); return NT_STATUS_NO_MEMORY; } memcpy(&(*info)[*num_entries], info2, count*sizeof(*info2)); (*num_entries) += count; talloc_destroy(mem_ctx2); - } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)); - cli_samr_close(hnd->cli, mem_ctx, &dom_pol); + } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)); - return result; + return NT_STATUS_OK; } /* convert a single name to a sid in a domain */ @@ -279,12 +241,12 @@ NTSTATUS msrpc_name_to_sid(struct winbindd_domain *domain, DOM_SID *sid, enum SID_NAME_USE *type) { - CLI_POLICY_HND *hnd; NTSTATUS result; DOM_SID *sids = NULL; uint32 *types = NULL; const char *full_name; - int retry; + struct rpc_pipe_client *cli; + POLICY_HND lsa_policy; if(name == NULL || *name=='\0') { DEBUG(3,("rpc: name_to_sid name=%s\n", domain_name)); @@ -300,25 +262,22 @@ NTSTATUS msrpc_name_to_sid(struct winbindd_domain *domain, DEBUG(3,("name_to_sid [rpc] %s for domain %s\n", name?name:"", domain_name )); - retry = 0; - do { - if (!NT_STATUS_IS_OK(result = cm_get_lsa_handle(domain, &hnd))) { - return result; - } - - result = cli_lsa_lookup_names(hnd->cli, mem_ctx, &hnd->pol, 1, - &full_name, &sids, &types); - } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && - hnd && hnd->cli && hnd->cli->fd == -1); + result = cm_connect_lsa(domain, mem_ctx, &cli, &lsa_policy); + if (!NT_STATUS_IS_OK(result)) + return result; + + result = rpccli_lsa_lookup_names(cli, mem_ctx, &lsa_policy, 1, + &full_name, &sids, &types); + if (!NT_STATUS_IS_OK(result)) + return result; + /* Return rid and type if lookup successful */ - if (NT_STATUS_IS_OK(result)) { - sid_copy(sid, &sids[0]); - *type = (enum SID_NAME_USE)types[0]; - } + sid_copy(sid, &sids[0]); + *type = (enum SID_NAME_USE)types[0]; - return result; + return NT_STATUS_OK; } /* @@ -331,34 +290,30 @@ NTSTATUS msrpc_sid_to_name(struct winbindd_domain *domain, char **name, enum SID_NAME_USE *type) { - CLI_POLICY_HND *hnd; char **domains; char **names; uint32 *types; NTSTATUS result; - int retry; + struct rpc_pipe_client *cli; + POLICY_HND lsa_policy; DEBUG(3,("sid_to_name [rpc] %s for domain %s\n", sid_string_static(sid), domain->name )); - retry = 0; - do { - if (!NT_STATUS_IS_OK(result = cm_get_lsa_handle(domain, &hnd))) - return result; - - result = cli_lsa_lookup_sids(hnd->cli, mem_ctx, &hnd->pol, - 1, sid, &domains, &names, &types); - } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && - hnd && hnd->cli && hnd->cli->fd == -1); + result = cm_connect_lsa(domain, mem_ctx, &cli, &lsa_policy); + if (!NT_STATUS_IS_OK(result)) + return result; - if (NT_STATUS_IS_OK(result)) { - *type = (enum SID_NAME_USE)types[0]; - *domain_name = domains[0]; - *name = names[0]; - DEBUG(5,("Mapped sid to [%s]\\[%s]\n", domains[0], *name)); - } + result = rpccli_lsa_lookup_sids(cli, mem_ctx, &lsa_policy, + 1, sid, &domains, &names, &types); + if (!NT_STATUS_IS_OK(result)) + return result; - return result; + *type = (enum SID_NAME_USE)types[0]; + *domain_name = domains[0]; + *name = names[0]; + DEBUG(5,("Mapped sid to [%s]\\[%s]\n", domains[0], *name)); + return NT_STATUS_OK; } /* Lookup user information from a rid or username. */ @@ -367,20 +322,19 @@ static NTSTATUS query_user(struct winbindd_domain *domain, const DOM_SID *user_sid, WINBIND_USERINFO *user_info) { - CLI_POLICY_HND *hnd = NULL; NTSTATUS result = NT_STATUS_UNSUCCESSFUL; POLICY_HND dom_pol, user_pol; - BOOL got_dom_pol = False, got_user_pol = False; SAM_USERINFO_CTR *ctr; - int retry; fstring sid_string; uint32 user_rid; NET_USER_INFO_3 *user; + struct rpc_pipe_client *cli; - DEBUG(3,("rpc: query_user rid=%s\n", sid_to_string(sid_string, user_sid))); - if (!sid_peek_check_rid(&domain->sid, user_sid, &user_rid)) { - goto done; - } + DEBUG(3,("rpc: query_user rid=%s\n", + sid_to_string(sid_string, user_sid))); + + if (!sid_peek_check_rid(&domain->sid, user_sid, &user_rid)) + return NT_STATUS_UNSUCCESSFUL; /* try netsamlogon cache first */ @@ -389,12 +343,15 @@ static NTSTATUS query_user(struct winbindd_domain *domain, DEBUG(5,("query_user: Cache lookup succeeded for %s\n", sid_string_static(user_sid))); - - user_info->user_sid = rid_to_talloced_sid( domain, mem_ctx, user_rid ); - user_info->group_sid = rid_to_talloced_sid( domain, mem_ctx, user->group_rid ); + + sid_compose(&user_info->user_sid, &domain->sid, user_rid); + sid_compose(&user_info->group_sid, &domain->sid, + user->group_rid); - user_info->acct_name = unistr2_tdup(mem_ctx, &user->uni_user_name); - user_info->full_name = unistr2_tdup(mem_ctx, &user->uni_full_name); + user_info->acct_name = unistr2_tdup(mem_ctx, + &user->uni_user_name); + user_info->full_name = unistr2_tdup(mem_ctx, + &user->uni_full_name); SAFE_FREE(user); @@ -403,82 +360,59 @@ static NTSTATUS query_user(struct winbindd_domain *domain, /* no cache; hit the wire */ - retry = 0; - do { - /* Get sam handle; if we fail here there is no hope */ - - if (!NT_STATUS_IS_OK(result = cm_get_sam_handle(domain, &hnd))) - goto done; - - /* Get domain handle */ - - result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol, - SEC_RIGHTS_MAXIMUM_ALLOWED, - &domain->sid, &dom_pol); - } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && - hnd && hnd->cli && hnd->cli->fd == -1); - + result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); if (!NT_STATUS_IS_OK(result)) - goto done; - - got_dom_pol = True; + return result; /* Get user handle */ - result = cli_samr_open_user(hnd->cli, mem_ctx, &dom_pol, - SEC_RIGHTS_MAXIMUM_ALLOWED, user_rid, &user_pol); + result = rpccli_samr_open_user(cli, mem_ctx, &dom_pol, + SEC_RIGHTS_MAXIMUM_ALLOWED, user_rid, + &user_pol); if (!NT_STATUS_IS_OK(result)) - goto done; - - got_user_pol = True; + return result; /* Get user info */ - result = cli_samr_query_userinfo(hnd->cli, mem_ctx, &user_pol, - 0x15, &ctr); + result = rpccli_samr_query_userinfo(cli, mem_ctx, &user_pol, + 0x15, &ctr); - if (!NT_STATUS_IS_OK(result)) - goto done; + rpccli_samr_close(cli, mem_ctx, &user_pol); - cli_samr_close(hnd->cli, mem_ctx, &user_pol); - got_user_pol = False; + if (!NT_STATUS_IS_OK(result)) + return result; - user_info->user_sid = rid_to_talloced_sid(domain, mem_ctx, user_rid); - user_info->group_sid = rid_to_talloced_sid(domain, mem_ctx, ctr->info.id21->group_rid); + sid_compose(&user_info->user_sid, &domain->sid, user_rid); + sid_compose(&user_info->group_sid, &domain->sid, + ctr->info.id21->group_rid); user_info->acct_name = unistr2_tdup(mem_ctx, &ctr->info.id21->uni_user_name); user_info->full_name = unistr2_tdup(mem_ctx, &ctr->info.id21->uni_full_name); - done: - /* Clean up policy handles */ - if (got_user_pol) - cli_samr_close(hnd->cli, mem_ctx, &user_pol); - - if (got_dom_pol) - cli_samr_close(hnd->cli, mem_ctx, &dom_pol); - - return result; + return NT_STATUS_OK; } /* Lookup groups a user is a member of. I wish Unix had a call like this! */ static NTSTATUS lookup_usergroups(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, const DOM_SID *user_sid, - uint32 *num_groups, DOM_SID ***user_grpsids) + uint32 *num_groups, DOM_SID **user_grpsids) { - CLI_POLICY_HND *hnd; NTSTATUS result = NT_STATUS_UNSUCCESSFUL; POLICY_HND dom_pol, user_pol; uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED; - BOOL got_dom_pol = False, got_user_pol = False; DOM_GID *user_groups; unsigned int i; - unsigned int retry; fstring sid_string; uint32 user_rid; NET_USER_INFO_3 *user; + struct rpc_pipe_client *cli; + + DEBUG(3,("rpc: lookup_usergroups sid=%s\n", + sid_to_string(sid_string, user_sid))); - DEBUG(3,("rpc: lookup_usergroups sid=%s\n", sid_to_string(sid_string, user_sid))); + if (!sid_peek_check_rid(&domain->sid, user_sid, &user_rid)) + return NT_STATUS_UNSUCCESSFUL; *num_groups = 0; *user_grpsids = NULL; @@ -492,9 +426,11 @@ static NTSTATUS lookup_usergroups(struct winbindd_domain *domain, *num_groups = user->num_groups; - (*user_grpsids) = TALLOC_ARRAY(mem_ctx, DOM_SID*, *num_groups); + (*user_grpsids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups); for (i=0;i<(*num_groups);i++) { - (*user_grpsids)[i] = rid_to_talloced_sid(domain, mem_ctx, user->gids[i].g_rid); + sid_copy(&((*user_grpsids)[i]), &domain->sid); + sid_append_rid(&((*user_grpsids)[i]), + user->gids[i].g_rid); } SAFE_FREE(user); @@ -504,124 +440,73 @@ static NTSTATUS lookup_usergroups(struct winbindd_domain *domain, /* no cache; hit the wire */ - retry = 0; - do { - /* Get sam handle; if we fail here there is no hope */ - - if (!NT_STATUS_IS_OK(result = cm_get_sam_handle(domain, &hnd))) - goto done; - - /* Get domain handle */ - - result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol, - des_access, &domain->sid, &dom_pol); - } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && - hnd && hnd->cli && hnd->cli->fd == -1); - + result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); if (!NT_STATUS_IS_OK(result)) - goto done; - - got_dom_pol = True; - - - if (!sid_peek_check_rid(&domain->sid, user_sid, &user_rid)) { - goto done; - } + return result; /* Get user handle */ - result = cli_samr_open_user(hnd->cli, mem_ctx, &dom_pol, + result = rpccli_samr_open_user(cli, mem_ctx, &dom_pol, des_access, user_rid, &user_pol); if (!NT_STATUS_IS_OK(result)) - goto done; - - got_user_pol = True; + return result; /* Query user rids */ - result = cli_samr_query_usergroups(hnd->cli, mem_ctx, &user_pol, + result = rpccli_samr_query_usergroups(cli, mem_ctx, &user_pol, num_groups, &user_groups); + rpccli_samr_close(cli, mem_ctx, &user_pol); + if (!NT_STATUS_IS_OK(result) || (*num_groups) == 0) - goto done; + return result; - (*user_grpsids) = TALLOC_ARRAY(mem_ctx, DOM_SID *, *num_groups); - if (!(*user_grpsids)) { - result = NT_STATUS_NO_MEMORY; - goto done; - } + (*user_grpsids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups); + if (!(*user_grpsids)) + return NT_STATUS_NO_MEMORY; for (i=0;i<(*num_groups);i++) { - (*user_grpsids)[i] = rid_to_talloced_sid(domain, mem_ctx, user_groups[i].g_rid); + sid_copy(&((*user_grpsids)[i]), &domain->sid); + sid_append_rid(&((*user_grpsids)[i]), + user_groups[i].g_rid); } - done: - /* Clean up policy handles */ - if (got_user_pol) - cli_samr_close(hnd->cli, mem_ctx, &user_pol); - - if (got_dom_pol) - cli_samr_close(hnd->cli, mem_ctx, &dom_pol); - - return result; + return NT_STATUS_OK; } NTSTATUS msrpc_lookup_useraliases(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, - uint32 num_sids, DOM_SID **sids, + uint32 num_sids, const DOM_SID *sids, uint32 *num_aliases, uint32 **alias_rids) { NTSTATUS result = NT_STATUS_UNSUCCESSFUL; - CLI_POLICY_HND *hnd; - BOOL got_dom_pol = False; POLICY_HND dom_pol; DOM_SID2 *sid2; - int i, retry; + int i; + struct rpc_pipe_client *cli; *num_aliases = 0; *alias_rids = NULL; - retry = 0; - do { - /* Get sam handle; if we fail here there is no hope */ - - if (!NT_STATUS_IS_OK(result = cm_get_sam_handle(domain, - &hnd))) - goto done; - - /* Get domain handle */ - - result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol, - SEC_RIGHTS_MAXIMUM_ALLOWED, - &domain->sid, &dom_pol); - } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && - hnd && hnd->cli && hnd->cli->fd == -1); + DEBUG(3,("rpc: lookup_useraliases\n")); + result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); if (!NT_STATUS_IS_OK(result)) - goto done; - - got_dom_pol = True; + return result; sid2 = TALLOC_ARRAY(mem_ctx, DOM_SID2, num_sids); - if (sid2 == NULL) { - result = NT_STATUS_NO_MEMORY; - goto done; - } + if (sid2 == NULL) + return NT_STATUS_NO_MEMORY; for (i=0; i<num_sids; i++) { - sid_copy(&sid2[i].sid, sids[i]); + sid_copy(&sid2[i].sid, &sids[i]); sid2[i].num_auths = sid2[i].sid.num_auths; } - result = cli_samr_query_useraliases(hnd->cli, mem_ctx, &dom_pol, - num_sids, sid2, - num_aliases, alias_rids); - - done: + result = rpccli_samr_query_useraliases(cli, mem_ctx, &dom_pol, + num_sids, sid2, + num_aliases, alias_rids); - if (got_dom_pol) - cli_samr_close(hnd->cli, mem_ctx, &dom_pol); - return result; } @@ -630,71 +515,54 @@ NTSTATUS msrpc_lookup_useraliases(struct winbindd_domain *domain, static NTSTATUS lookup_groupmem(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, const DOM_SID *group_sid, uint32 *num_names, - DOM_SID ***sid_mem, char ***names, + DOM_SID **sid_mem, char ***names, uint32 **name_types) { - CLI_POLICY_HND *hnd = NULL; NTSTATUS result = NT_STATUS_UNSUCCESSFUL; uint32 i, total_names = 0; POLICY_HND dom_pol, group_pol; uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED; - BOOL got_dom_pol = False, got_group_pol = False; uint32 *rid_mem = NULL; uint32 group_rid; - int retry; unsigned int j; fstring sid_string; + struct rpc_pipe_client *cli; - DEBUG(10,("rpc: lookup_groupmem %s sid=%s\n", domain->name, sid_to_string(sid_string, group_sid))); + DEBUG(10,("rpc: lookup_groupmem %s sid=%s\n", domain->name, + sid_to_string(sid_string, group_sid))); - if (!sid_peek_check_rid(&domain->sid, group_sid, &group_rid)) { - goto done; - } + if (!sid_peek_check_rid(&domain->sid, group_sid, &group_rid)) + return NT_STATUS_UNSUCCESSFUL; *num_names = 0; - retry = 0; - do { - /* Get sam handle */ - if (!NT_STATUS_IS_OK(result = cm_get_sam_handle(domain, &hnd))) - goto done; - - /* Get domain handle */ - - result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol, - des_access, &domain->sid, &dom_pol); - } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1); - - if (!NT_STATUS_IS_OK(result)) - goto done; - - got_dom_pol = True; - - /* Get group handle */ + result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); + if (!NT_STATUS_IS_OK(result)) + return result; - result = cli_samr_open_group(hnd->cli, mem_ctx, &dom_pol, - des_access, group_rid, &group_pol); + result = rpccli_samr_open_group(cli, mem_ctx, &dom_pol, + des_access, group_rid, &group_pol); if (!NT_STATUS_IS_OK(result)) - goto done; - - got_group_pol = True; + return result; /* Step #1: Get a list of user rids that are the members of the group. */ - result = cli_samr_query_groupmem(hnd->cli, mem_ctx, - &group_pol, num_names, &rid_mem, - name_types); + result = rpccli_samr_query_groupmem(cli, mem_ctx, + &group_pol, num_names, &rid_mem, + name_types); + + rpccli_samr_close(cli, mem_ctx, &group_pol); if (!NT_STATUS_IS_OK(result)) - goto done; + return result; if (!*num_names) { names = NULL; name_types = NULL; sid_mem = NULL; - goto done; + return NT_STATUS_OK; } /* Step #2: Convert list of rids into list of usernames. Do this @@ -706,16 +574,13 @@ static NTSTATUS lookup_groupmem(struct winbindd_domain *domain, *names = TALLOC_ZERO_ARRAY(mem_ctx, char *, *num_names); *name_types = TALLOC_ZERO_ARRAY(mem_ctx, uint32, *num_names); - *sid_mem = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID *, *num_names); + *sid_mem = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, *num_names); - for (j=0;j<(*num_names);j++) { - (*sid_mem)[j] = rid_to_talloced_sid(domain, mem_ctx, (rid_mem)[j]); - } + for (j=0;j<(*num_names);j++) + sid_compose(&(*sid_mem)[j], &domain->sid, rid_mem[j]); - if (*num_names>0 && (!*names || !*name_types)) { - result = NT_STATUS_NO_MEMORY; - goto done; - } + if (*num_names>0 && (!*names || !*name_types)) + return NT_STATUS_NO_MEMORY; for (i = 0; i < *num_names; i += MAX_LOOKUP_RIDS) { int num_lookup_rids = MIN(*num_names - i, MAX_LOOKUP_RIDS); @@ -725,18 +590,19 @@ static NTSTATUS lookup_groupmem(struct winbindd_domain *domain, /* Lookup a chunk of rids */ - result = cli_samr_lookup_rids(hnd->cli, mem_ctx, - &dom_pol, - num_lookup_rids, - &rid_mem[i], - &tmp_num_names, - &tmp_names, &tmp_types); + result = rpccli_samr_lookup_rids(cli, mem_ctx, + &dom_pol, + num_lookup_rids, + &rid_mem[i], + &tmp_num_names, + &tmp_names, &tmp_types); - /* see if we have a real error (and yes the STATUS_SOME_UNMAPPED is - the one returned from 2k) */ + /* see if we have a real error (and yes the + STATUS_SOME_UNMAPPED is the one returned from 2k) */ - if (!NT_STATUS_IS_OK(result) && NT_STATUS_V(result) != NT_STATUS_V(STATUS_SOME_UNMAPPED)) - goto done; + if (!NT_STATUS_IS_OK(result) && + !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) + return result; /* Copy result into array. The talloc system will take care of freeing the temporary arrays later on. */ @@ -752,16 +618,7 @@ static NTSTATUS lookup_groupmem(struct winbindd_domain *domain, *num_names = total_names; - result = NT_STATUS_OK; - -done: - if (got_group_pol) - cli_samr_close(hnd->cli, mem_ctx, &group_pol); - - if (got_dom_pol) - cli_samr_close(hnd->cli, mem_ctx, &dom_pol); - - return result; + return NT_STATUS_OK; } #ifdef HAVE_LDAP @@ -780,11 +637,12 @@ static int get_ldap_seq(const char *server, int port, uint32 *seq) *seq = DOM_SEQUENCE_NONE; /* - * Parameterised (5) second timeout on open. This is needed as the search timeout - * doesn't seem to apply to doing an open as well. JRA. + * Parameterised (5) second timeout on open. This is needed as the + * search timeout doesn't seem to apply to doing an open as well. JRA. */ - if ((ldp = ldap_open_with_timeout(server, port, lp_ldap_timeout())) == NULL) + ldp = ldap_open_with_timeout(server, port, lp_ldap_timeout()); + if (ldp == NULL) return -1; /* Timeout if no response within 20 seconds. */ @@ -792,7 +650,7 @@ static int get_ldap_seq(const char *server, int port, uint32 *seq) to.tv_usec = 0; if (ldap_search_st(ldp, "", LDAP_SCOPE_BASE, "(objectclass=*)", - CONST_DISCARD(char **, &attrs[0]), 0, &to, &res)) + CONST_DISCARD(char **, attrs), 0, &to, &res)) goto done; if (ldap_count_entries(ldp, res) != 1) @@ -838,8 +696,10 @@ static int get_ldap_sequence_number( const char* domain, uint32 *seq) for (i = 0; i < count; i++) { fstring ipstr; - /* since the is an LDAP lookup, default to the LDAP_PORT is not set */ - port = (ip_list[i].port!= PORT_NONE) ? ip_list[i].port : LDAP_PORT; + /* since the is an LDAP lookup, default to the LDAP_PORT is + * not set */ + port = (ip_list[i].port!= PORT_NONE) ? + ip_list[i].port : LDAP_PORT; fstrcpy( ipstr, inet_ntoa(ip_list[i].ip) ); @@ -850,12 +710,14 @@ static int get_ldap_sequence_number( const char* domain, uint32 *seq) goto done; /* add to failed connection cache */ - add_failed_connection_entry( domain, ipstr, NT_STATUS_UNSUCCESSFUL ); + add_failed_connection_entry( domain, ipstr, + NT_STATUS_UNSUCCESSFUL ); } done: if ( ret == 0 ) { - DEBUG(3, ("get_ldap_sequence_number: Retrieved sequence number for Domain (%s) from DC (%s:%d)\n", + DEBUG(3, ("get_ldap_sequence_number: Retrieved sequence " + "number for Domain (%s) from DC (%s:%d)\n", domain, inet_ntoa(ip_list[i].ip), port)); } @@ -870,14 +732,12 @@ done: static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq) { TALLOC_CTX *mem_ctx; - CLI_POLICY_HND *hnd; SAM_UNK_CTR ctr; NTSTATUS result; POLICY_HND dom_pol; - BOOL got_dom_pol = False; BOOL got_seq_num = False; - uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED; int retry; + struct rpc_pipe_client *cli; DEBUG(10,("rpc: fetch sequence_number for %s\n", domain->name)); @@ -887,41 +747,39 @@ static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq) return NT_STATUS_NO_MEMORY; retry = 0; - do { -#ifdef HAVE_LDAP - if ( domain->native_mode ) - { - DEBUG(8,("using get_ldap_seq() to retrieve the sequence number\n")); - - if ( get_ldap_sequence_number( domain->name, seq ) == 0 ) { - result = NT_STATUS_OK; - DEBUG(10,("domain_sequence_number: LDAP for domain %s is %u\n", - domain->name, *seq)); - goto done; - } - DEBUG(10,("domain_sequence_number: failed to get LDAP sequence number for domain %s\n", - domain->name )); - } -#endif /* HAVE_LDAP */ - /* Get sam handle */ - if (!NT_STATUS_IS_OK(result = cm_get_sam_handle(domain, &hnd))) +#ifdef HAVE_LDAP + if ( domain->native_mode ) + { + int res; + + DEBUG(8,("using get_ldap_seq() to retrieve the " + "sequence number\n")); + + res = get_ldap_sequence_number( domain->name, seq ); + if (res == 0) + { + result = NT_STATUS_OK; + DEBUG(10,("domain_sequence_number: LDAP for " + "domain %s is %u\n", + domain->name, *seq)); goto done; + } - /* Get domain handle */ - result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol, - des_access, &domain->sid, &dom_pol); - } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1); + DEBUG(10,("domain_sequence_number: failed to get LDAP " + "sequence number for domain %s\n", + domain->name )); + } +#endif /* HAVE_LDAP */ - if (!NT_STATUS_IS_OK(result)) + result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol); + if (!NT_STATUS_IS_OK(result)) { goto done; - - got_dom_pol = True; + } /* Query domain info */ - result = cli_samr_query_dom_info(hnd->cli, mem_ctx, &dom_pol, - 8, &ctr); + result = rpccli_samr_query_dom_info(cli, mem_ctx, &dom_pol, 8, &ctr); if (NT_STATUS_IS_OK(result)) { *seq = ctr.info.inf8.seq_num.low; @@ -932,8 +790,7 @@ static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq) /* retry with info-level 2 in case the dc does not support info-level 8 * (like all older samba2 and samba3 dc's - Guenther */ - result = cli_samr_query_dom_info(hnd->cli, mem_ctx, &dom_pol, - 2, &ctr); + result = rpccli_samr_query_dom_info(cli, mem_ctx, &dom_pol, 2, &ctr); if (NT_STATUS_IS_OK(result)) { *seq = ctr.info.inf2.seq_num.low; @@ -942,17 +799,16 @@ static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq) seq_num: if (got_seq_num) { - DEBUG(10,("domain_sequence_number: for domain %s is %u\n", domain->name, (unsigned)*seq)); + DEBUG(10,("domain_sequence_number: for domain %s is %u\n", + domain->name, (unsigned)*seq)); } else { - DEBUG(10,("domain_sequence_number: failed to get sequence number (%u) for domain %s\n", - (unsigned)*seq, domain->name )); + DEBUG(10,("domain_sequence_number: failed to get sequence " + "number (%u) for domain %s\n", + (unsigned)*seq, domain->name )); } done: - if (got_dom_pol) - cli_samr_close(hnd->cli, mem_ctx, &dom_pol); - talloc_destroy(mem_ctx); return result; @@ -966,10 +822,10 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain, char ***alt_names, DOM_SID **dom_sids) { - CLI_POLICY_HND *hnd; NTSTATUS result = NT_STATUS_UNSUCCESSFUL; uint32 enum_ctx = 0; - int retry; + struct rpc_pipe_client *cli; + POLICY_HND lsa_policy; DEBUG(3,("rpc: trusted_domains\n")); @@ -978,46 +834,45 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain, *alt_names = NULL; *dom_sids = NULL; - retry = 0; - do { - if (!NT_STATUS_IS_OK(result = cm_get_lsa_handle(find_our_domain(), &hnd))) - goto done; + result = cm_connect_lsa(domain, mem_ctx, &cli, &lsa_policy); + if (!NT_STATUS_IS_OK(result)) + return result; - result = STATUS_MORE_ENTRIES; - - while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) { - uint32 start_idx, num; - char **tmp_names; - DOM_SID *tmp_sids; - int i; - - result = cli_lsa_enum_trust_dom(hnd->cli, mem_ctx, - &hnd->pol, &enum_ctx, - &num, &tmp_names, - &tmp_sids); - - if (!NT_STATUS_IS_OK(result) && - !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) - break; - - start_idx = *num_domains; - *num_domains += num; - *names = TALLOC_REALLOC_ARRAY(mem_ctx, *names, - char *, *num_domains); - *dom_sids = TALLOC_REALLOC_ARRAY(mem_ctx, *dom_sids, - DOM_SID, - *num_domains); - if ((*names == NULL) || (*dom_sids == NULL)) - return NT_STATUS_NO_MEMORY; - - for (i=0; i<num; i++) { - (*names)[start_idx+i] = tmp_names[i]; - (*dom_sids)[start_idx+i] = tmp_sids[i]; - } - } - } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1); + result = STATUS_MORE_ENTRIES; -done: + while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) { + uint32 start_idx, num; + char **tmp_names; + DOM_SID *tmp_sids; + int i; + + result = rpccli_lsa_enum_trust_dom(cli, mem_ctx, + &lsa_policy, &enum_ctx, + &num, &tmp_names, + &tmp_sids); + + if (!NT_STATUS_IS_OK(result) && + !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) + break; + + start_idx = *num_domains; + *num_domains += num; + *names = TALLOC_REALLOC_ARRAY(mem_ctx, *names, + char *, *num_domains); + *dom_sids = TALLOC_REALLOC_ARRAY(mem_ctx, *dom_sids, + DOM_SID, *num_domains); + *alt_names = TALLOC_REALLOC_ARRAY(mem_ctx, *alt_names, + char *, *num_domains); + if ((*names == NULL) || (*dom_sids == NULL) || + (*alt_names == NULL)) + return NT_STATUS_NO_MEMORY; + + for (i=0; i<num; i++) { + (*names)[start_idx+i] = tmp_names[i]; + (*dom_sids)[start_idx+i] = tmp_sids[i]; + (*alt_names)[start_idx+i] = talloc_strdup(mem_ctx, ""); + } + } return result; } diff --git a/source3/nsswitch/winbindd_sid.c b/source3/nsswitch/winbindd_sid.c index 060e66fbc2..f8fb5e93c2 100644 --- a/source3/nsswitch/winbindd_sid.c +++ b/source3/nsswitch/winbindd_sid.c @@ -28,12 +28,13 @@ /* Convert a string */ +static void lookupsid_recv(void *private, BOOL success, + const char *dom_name, const char *name, + enum SID_NAME_USE type); + enum winbindd_result winbindd_lookupsid(struct winbindd_cli_state *state) { - enum SID_NAME_USE type; DOM_SID sid; - fstring name; - fstring dom_name; /* Ensure null termination */ state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; @@ -41,38 +42,46 @@ enum winbindd_result winbindd_lookupsid(struct winbindd_cli_state *state) DEBUG(3, ("[%5lu]: lookupsid %s\n", (unsigned long)state->pid, state->request.data.sid)); - /* Lookup sid from PDC using lsa_lookup_sids() */ - if (!string_to_sid(&sid, state->request.data.sid)) { DEBUG(5, ("%s not a SID\n", state->request.data.sid)); return WINBINDD_ERROR; } - /* Lookup the sid */ + winbindd_lookupsid_async(state->mem_ctx, &sid, lookupsid_recv, state); + return WINBINDD_PENDING; +} - if (!winbindd_lookup_name_by_sid(&sid, dom_name, name, &type)) { - return WINBINDD_ERROR; +static void lookupsid_recv(void *private, BOOL success, + const char *dom_name, const char *name, + enum SID_NAME_USE type) +{ + struct winbindd_cli_state *state = + talloc_get_type_abort(private, struct winbindd_cli_state); + + if (!success) { + DEBUG(5, ("lookupsid returned an error\n")); + state->response.result = WINBINDD_ERROR; + request_finished(state); + return; } fstrcpy(state->response.data.name.dom_name, dom_name); fstrcpy(state->response.data.name.name, name); - state->response.data.name.type = type; - - return WINBINDD_OK; + state->response.result = WINBINDD_OK; + request_finished(state); } - /** * Look up the SID for a qualified name. **/ + +static void lookupname_recv(void *private, BOOL success, + const DOM_SID *sid, enum SID_NAME_USE type); + enum winbindd_result winbindd_lookupname(struct winbindd_cli_state *state) { - enum SID_NAME_USE type; - fstring sid_str; char *name_domain, *name_user; - DOM_SID sid; - struct winbindd_domain *domain; char *p; /* Ensure null termination */ @@ -95,27 +104,48 @@ enum winbindd_result winbindd_lookupname(struct winbindd_cli_state *state) DEBUG(3, ("[%5lu]: lookupname %s%s%s\n", (unsigned long)state->pid, name_domain, lp_winbind_separator(), name_user)); - if ((domain = find_lookup_domain_from_name(name_domain)) == NULL) { - DEBUG(0, ("could not find domain entry for domain %s\n", - name_domain)); - return WINBINDD_ERROR; - } + winbindd_lookupname_async(state->mem_ctx, name_domain, name_user, + lookupname_recv, state); + return WINBINDD_PENDING; +} - /* Lookup name from PDC using lsa_lookup_names() */ - if (!winbindd_lookup_sid_by_name(domain, name_domain, name_user, &sid, &type)) { - return WINBINDD_ERROR; +static void lookupname_recv(void *private, BOOL success, + const DOM_SID *sid, enum SID_NAME_USE type) +{ + struct winbindd_cli_state *state = + talloc_get_type_abort(private, struct winbindd_cli_state); + + if (!success) { + DEBUG(5, ("lookupname returned an error\n")); + state->response.result = WINBINDD_ERROR; + request_finished(state); + return; } - sid_to_string(sid_str, &sid); - fstrcpy(state->response.data.sid.sid, sid_str); + sid_to_string(state->response.data.sid.sid, sid); state->response.data.sid.type = type; + state->response.result = WINBINDD_OK; + request_finished(state); + return; +} - return WINBINDD_OK; +static struct winbindd_child static_idmap_child; + +void init_idmap_child(void) +{ + setup_domain_child(NULL, &static_idmap_child, "idmap"); +} + +struct winbindd_child *idmap_child(void) +{ + return &static_idmap_child; } /* Convert a sid to a uid. We assume we only have one rid attached to the sid. */ +static void sid2uid_recv(void *private, BOOL success, uid_t uid); + enum winbindd_result winbindd_sid_to_uid(struct winbindd_cli_state *state) { DOM_SID sid; @@ -127,105 +157,53 @@ enum winbindd_result winbindd_sid_to_uid(struct winbindd_cli_state *state) DEBUG(3, ("[%5lu]: sid to uid %s\n", (unsigned long)state->pid, state->request.data.sid)); - if (!string_to_sid(&sid, state->request.data.sid)) { - DEBUG(1, ("Could not get convert sid %s from string\n", state->request.data.sid)); + if (idmap_proxyonly()) { + DEBUG(8, ("IDMAP proxy only\n")); return WINBINDD_ERROR; } - - /* This gets a little tricky. If we assume that usernames are syncd between - /etc/passwd and the windows domain (such as a member of a Samba domain), - the we need to get the uid from the OS and not alocate one ourselves */ - - if ( lp_winbind_trusted_domains_only() ) { - struct winbindd_domain *domain = NULL; - DOM_SID sid2; - uint32 rid; - - domain = find_our_domain(); - if ( !domain ) { - DEBUG(0,("winbindd_sid_to_uid: can't find my own domain!\n")); - return WINBINDD_ERROR; - } - - sid_copy( &sid2, &sid ); - sid_split_rid( &sid2, &rid ); - - if ( sid_equal( &sid2, &domain->sid ) ) { - - fstring domain_name; - fstring user; - enum SID_NAME_USE type; - struct passwd *pw = NULL; - unid_t id; - - /* ok...here's we know that we are dealing with our - own domain (the one to which we are joined). And - we know that there must be a UNIX account for this user. - So we lookup the sid and the call getpwnam().*/ - - - /* But first check and see if we don't already have a mapping */ - - if ( NT_STATUS_IS_OK(idmap_sid_to_uid(&sid, &(state->response.data.uid), ID_QUERY_ONLY)) ) - return WINBINDD_OK; - - /* now fall back to the hard way */ - - if ( !winbindd_lookup_name_by_sid(&sid, domain_name, user, &type) ) - return WINBINDD_ERROR; - - if ( !(pw = getpwnam(user)) ) { - DEBUG(0,("winbindd_sid_to_uid: 'winbind trusted domains only' is " - "set but this user [%s] doesn't exist!\n", user)); - return WINBINDD_ERROR; - } - - state->response.data.uid = pw->pw_uid; - - id.uid = pw->pw_uid; - idmap_set_mapping( &sid, id, ID_USERID ); - - return WINBINDD_OK; - } + if (!string_to_sid(&sid, state->request.data.sid)) { + DEBUG(1, ("Could not get convert sid %s from string\n", + state->request.data.sid)); + return WINBINDD_ERROR; } - - /* Find uid for this sid and return it */ + + /* Query only the local tdb, everything else might possibly block */ result = idmap_sid_to_uid(&sid, &(state->response.data.uid), - ID_QUERY_ONLY); + ID_QUERY_ONLY|ID_CACHE_ONLY); - if (NT_STATUS_IS_OK(result)) + if (NT_STATUS_IS_OK(result)) { return WINBINDD_OK; + } - if (state->request.flags & WBFLAG_QUERY_ONLY) - return WINBINDD_ERROR; - - /* The query-only did not work, allocate a new uid *if* it's a user */ - - { - fstring dom_name, name; - enum SID_NAME_USE type; - - if (!winbindd_lookup_name_by_sid(&sid, dom_name, name, &type)) - return WINBINDD_ERROR; + winbindd_sid2uid_async(state->mem_ctx, &sid, sid2uid_recv, state); + return WINBINDD_PENDING; +} - if ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER)) - return WINBINDD_ERROR; +static void sid2uid_recv(void *private, BOOL success, uid_t uid) +{ + struct winbindd_cli_state *state = + talloc_get_type_abort(private, struct winbindd_cli_state); + + if (!success) { + DEBUG(5, ("Could not convert sid %s\n", + state->request.data.sid)); + state->response.result = WINBINDD_ERROR; + request_finished(state); + return; } - - result = idmap_sid_to_uid(&sid, &(state->response.data.uid), 0); - if (NT_STATUS_IS_OK(result)) - return WINBINDD_OK; - - DEBUG(4, ("Could not get uid for sid %s\n", state->request.data.sid)); - return WINBINDD_ERROR; + state->response.result = WINBINDD_OK; + state->response.data.uid = uid; + request_finished(state); } /* Convert a sid to a gid. We assume we only have one rid attached to the sid.*/ +static void sid2gid_recv(void *private, BOOL success, gid_t gid); + enum winbindd_result winbindd_sid_to_gid(struct winbindd_cli_state *state) { DOM_SID sid; @@ -234,253 +212,301 @@ enum winbindd_result winbindd_sid_to_gid(struct winbindd_cli_state *state) /* Ensure null termination */ state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; - DEBUG(3, ("[%5lu]: sid to gid %s\n", (unsigned long)state->pid, + DEBUG(3, ("[%5lu]: sid to gid %s\n", (unsigned long)state->pid, state->request.data.sid)); - if (!string_to_sid(&sid, state->request.data.sid)) { - DEBUG(1, ("Could not cvt string to sid %s\n", state->request.data.sid)); + if (idmap_proxyonly()) { + DEBUG(8, ("IDMAP proxy only\n")); return WINBINDD_ERROR; } - /* This gets a little tricky. If we assume that usernames are syncd between - /etc/passwd and the windows domain (such as a member of a Samba domain), - the we need to get the uid from the OS and not alocate one ourselves */ - - if ( lp_winbind_trusted_domains_only() ) { - struct winbindd_domain *domain = NULL; - DOM_SID sid2; - uint32 rid; - unid_t id; - - domain = find_our_domain(); - if ( !domain ) { - DEBUG(0,("winbindd_sid_to_uid: can't find my own domain!\n")); - return WINBINDD_ERROR; - } - - sid_copy( &sid2, &sid ); - sid_split_rid( &sid2, &rid ); - - if ( sid_equal( &sid2, &domain->sid ) ) { - - fstring domain_name; - fstring group; - enum SID_NAME_USE type; - struct group *grp = NULL; - - /* ok...here's we know that we are dealing with our - own domain (the one to which we are joined). And - we know that there must be a UNIX account for this group. - So we lookup the sid and the call getpwnam().*/ - - /* But first check and see if we don't already have a mapping */ - - if ( NT_STATUS_IS_OK(idmap_sid_to_gid(&sid, &(state->response.data.gid), ID_QUERY_ONLY)) ) - return WINBINDD_OK; - - /* now fall back to the hard way */ - - if ( !winbindd_lookup_name_by_sid(&sid, domain_name, group, &type) ) - return WINBINDD_ERROR; - - if ( !(grp = getgrnam(group)) ) { - DEBUG(0,("winbindd_sid_to_uid: 'winbind trusted domains only' is " - "set but this group [%s] doesn't exist!\n", group)); - return WINBINDD_ERROR; - } - - state->response.data.gid = grp->gr_gid; - - id.gid = grp->gr_gid; - idmap_set_mapping( &sid, id, ID_GROUPID ); - - return WINBINDD_OK; - } - + if (!string_to_sid(&sid, state->request.data.sid)) { + DEBUG(1, ("Could not get convert sid %s from string\n", + state->request.data.sid)); + return WINBINDD_ERROR; } - - /* Find gid for this sid and return it */ + + /* Query only the local tdb, everything else might possibly block */ result = idmap_sid_to_gid(&sid, &(state->response.data.gid), - ID_QUERY_ONLY); + ID_QUERY_ONLY|ID_CACHE_ONLY); - if (NT_STATUS_IS_OK(result)) + if (NT_STATUS_IS_OK(result)) { return WINBINDD_OK; + } - if (state->request.flags & WBFLAG_QUERY_ONLY) - return WINBINDD_ERROR; + winbindd_sid2gid_async(state->mem_ctx, &sid, sid2gid_recv, state); + return WINBINDD_PENDING; +} - /* The query-only did not work, allocate a new gid *if* it's a group */ - - { - fstring dom_name, name; - enum SID_NAME_USE type; - - if (sid_check_is_in_our_domain(&sid)) { - /* This is for half-created aliases... */ - type = SID_NAME_ALIAS; - } else { - /* Foreign domains need to be looked up by the DC if - * it's the right type */ - if (!winbindd_lookup_name_by_sid(&sid, dom_name, name, - &type)) - return WINBINDD_ERROR; - } - - if ((type != SID_NAME_DOM_GRP) && (type != SID_NAME_ALIAS) && - (type != SID_NAME_WKN_GRP)) - return WINBINDD_ERROR; +static void sid2gid_recv(void *private, BOOL success, gid_t gid) +{ + struct winbindd_cli_state *state = + talloc_get_type_abort(private, struct winbindd_cli_state); + + if (!success) { + DEBUG(5, ("Could not convert sid %s\n", + state->request.data.sid)); + state->response.result = WINBINDD_ERROR; + request_finished(state); + return; } - - result = idmap_sid_to_gid(&sid, &(state->response.data.gid), 0); - - if (NT_STATUS_IS_OK(result)) - return WINBINDD_OK; - DEBUG(4, ("Could not get gid for sid %s\n", state->request.data.sid)); - return WINBINDD_ERROR; + state->response.result = WINBINDD_OK; + state->response.data.gid = gid; + request_finished(state); } /* Convert a uid to a sid */ +struct uid2sid_state { + struct winbindd_cli_state *cli_state; + uid_t uid; + fstring name; + DOM_SID sid; + enum SID_NAME_USE type; +}; + +static void uid2sid_uid2name_recv(void *private, BOOL success, + const char *username); +static void uid2sid_lookupname_recv(void *private, BOOL success, + const DOM_SID *sid, + enum SID_NAME_USE type); +static void uid2sid_idmap_set_mapping_recv(void *private, BOOL success); + enum winbindd_result winbindd_uid_to_sid(struct winbindd_cli_state *state) { DOM_SID sid; + NTSTATUS status; + struct uid2sid_state *uid2sid_state; DEBUG(3, ("[%5lu]: uid to sid %lu\n", (unsigned long)state->pid, (unsigned long)state->request.data.uid)); - if ( (state->request.data.uid < server_state.uid_low ) - || (state->request.data.uid > server_state.uid_high) ) - { - struct passwd *pw = NULL; - enum SID_NAME_USE type; - unid_t id; - struct winbindd_domain *domain; - - /* SPECIAL CASE FOR MEMBERS OF SAMBA DOMAINS */ - - /* if we don't trust /etc/password then when can't know - anything about this uid */ - - if ( !lp_winbind_trusted_domains_only() ) - return WINBINDD_ERROR; - - - /* look for an idmap entry first */ - - if ( NT_STATUS_IS_OK(idmap_uid_to_sid(&sid, state->request.data.uid)) ) - goto done; - - /* if users exist in /etc/passwd, we should try to - use that uid. Get the username and the lookup the SID */ - - if ( !(pw = getpwuid(state->request.data.uid)) ) - return WINBINDD_ERROR; - - if ( !(domain = find_our_domain()) ) { - DEBUG(0,("winbindd_uid_to_sid: can't find my own domain!\n")); - return WINBINDD_ERROR; - } - - if ( !winbindd_lookup_sid_by_name(domain, domain->name, pw->pw_name, &sid, &type) ) - return WINBINDD_ERROR; - - if ( type != SID_NAME_USER ) - return WINBINDD_ERROR; - - /* don't fail if we can't store it */ - - id.uid = pw->pw_uid; - idmap_set_mapping( &sid, id, ID_USERID ); - - goto done; + if (idmap_proxyonly()) { + DEBUG(8, ("IDMAP proxy only\n")); + return WINBINDD_ERROR; } - /* Lookup rid for this uid */ - - if (!NT_STATUS_IS_OK(idmap_uid_to_sid(&sid, state->request.data.uid))) { - DEBUG(1, ("Could not convert uid %lu to rid\n", - (unsigned long)state->request.data.uid)); + status = idmap_uid_to_sid(&sid, state->request.data.uid, + ID_QUERY_ONLY | ID_CACHE_ONLY); + + if (NT_STATUS_IS_OK(status)) { + sid_to_string(state->response.data.sid.sid, &sid); + state->response.data.sid.type = SID_NAME_USER; + return WINBINDD_OK; + } + + if (is_in_uid_range(state->request.data.uid)) { + /* This is winbind's, so we should better have succeeded + * above. */ return WINBINDD_ERROR; } -done: - sid_to_string(state->response.data.sid.sid, &sid); - state->response.data.sid.type = SID_NAME_USER; + /* The only chance that this is correct is that winbind trusted + * domains only = yes, and the user exists in nss and the domain. */ - return WINBINDD_OK; + if (!lp_winbind_trusted_domains_only()) { + return WINBINDD_ERROR; + } + + /* The only chance that this is correct is that winbind trusted + * domains only = yes, and the user exists in nss and the domain. */ + + uid2sid_state = TALLOC_ZERO_P(state->mem_ctx, struct uid2sid_state); + if (uid2sid_state == NULL) { + DEBUG(0, ("talloc failed\n")); + return WINBINDD_ERROR; + } + + uid2sid_state->cli_state = state; + uid2sid_state->uid = state->request.data.uid; + + winbindd_uid2name_async(state->mem_ctx, state->request.data.uid, + uid2sid_uid2name_recv, uid2sid_state); + return WINBINDD_PENDING; +} + +static void uid2sid_uid2name_recv(void *private, BOOL success, + const char *username) +{ + struct uid2sid_state *state = + talloc_get_type_abort(private, struct uid2sid_state); + + DEBUG(10, ("uid2sid: uid %lu has name %s\n", + (unsigned long)state->uid, username)); + + fstrcpy(state->name, username); + + if (!success) { + state->cli_state->response.result = WINBINDD_ERROR; + request_finished(state->cli_state); + return; + } + + winbindd_lookupname_async(state->cli_state->mem_ctx, + find_our_domain()->name, username, + uid2sid_lookupname_recv, state); +} + +static void uid2sid_lookupname_recv(void *private, BOOL success, + const DOM_SID *sid, enum SID_NAME_USE type) +{ + struct uid2sid_state *state = + talloc_get_type_abort(private, struct uid2sid_state); + unid_t id; + + if ((!success) || (type != SID_NAME_USER)) { + state->cli_state->response.result = WINBINDD_ERROR; + request_finished(state->cli_state); + return; + } + + state->sid = *sid; + state->type = type; + + id.uid = state->uid; + idmap_set_mapping_async(state->cli_state->mem_ctx, sid, id, ID_USERID, + uid2sid_idmap_set_mapping_recv, state ); +} + +static void uid2sid_idmap_set_mapping_recv(void *private, BOOL success) +{ + struct uid2sid_state *state = + talloc_get_type_abort(private, struct uid2sid_state); + + /* don't fail if we can't store it */ + + sid_to_string(state->cli_state->response.data.sid.sid, &state->sid); + state->cli_state->response.data.sid.type = state->type; + state->cli_state->response.result = WINBINDD_OK; + request_finished(state->cli_state); } /* Convert a gid to a sid */ +struct gid2sid_state { + struct winbindd_cli_state *cli_state; + gid_t gid; + fstring name; + DOM_SID sid; + enum SID_NAME_USE type; +}; + +static void gid2sid_gid2name_recv(void *private, BOOL success, + const char *groupname); +static void gid2sid_lookupname_recv(void *private, BOOL success, + const DOM_SID *sid, + enum SID_NAME_USE type); +static void gid2sid_idmap_set_mapping_recv(void *private, BOOL success); + enum winbindd_result winbindd_gid_to_sid(struct winbindd_cli_state *state) { DOM_SID sid; + NTSTATUS status; + struct gid2sid_state *gid2sid_state; - DEBUG(3, ("[%5lu]: gid to sid %lu\n", (unsigned long)state->pid, + DEBUG(3, ("[%5lu]: gid to sid %lu\n", (unsigned long)state->pid, (unsigned long)state->request.data.gid)); - - if ( (state->request.data.gid < server_state.gid_low) - || (state->request.data.gid > server_state.gid_high) ) - { - struct group *grp = NULL; - enum SID_NAME_USE type; - unid_t id; - struct winbindd_domain *domain; - - /* SPECIAL CASE FOR MEMBERS OF SAMBA DOMAINS */ - - /* if we don't trust /etc/group then when can't know - anything about this gid */ - - if ( !lp_winbind_trusted_domains_only() ) - return WINBINDD_ERROR; - - /* look for an idmap entry first */ - - if ( NT_STATUS_IS_OK(idmap_gid_to_sid(&sid, state->request.data.gid)) ) - goto done; - - /* if users exist in /etc/group, we should try to - use that gid. Get the username and the lookup the SID */ - - if ( !(grp = getgrgid(state->request.data.gid)) ) - return WINBINDD_ERROR; - - if ( !(domain = find_our_domain()) ) { - DEBUG(0,("winbindd_uid_to_sid: can't find my own domain!\n")); - return WINBINDD_ERROR; - } - - if ( !winbindd_lookup_sid_by_name(domain, domain->name, grp->gr_name, &sid, &type) ) - return WINBINDD_ERROR; - - if ( type!=SID_NAME_DOM_GRP && type!=SID_NAME_ALIAS ) - return WINBINDD_ERROR; - - /* don't fail if we can't store it */ - - id.gid = grp->gr_gid; - idmap_set_mapping( &sid, id, ID_GROUPID ); - - goto done; + + if (idmap_proxyonly()) { + DEBUG(8, ("IDMAP proxy only\n")); + return WINBINDD_ERROR; } - /* Lookup sid for this uid */ - - if (!NT_STATUS_IS_OK(idmap_gid_to_sid(&sid, state->request.data.gid))) { - DEBUG(1, ("Could not convert gid %lu to sid\n", - (unsigned long)state->request.data.gid)); + status = idmap_gid_to_sid(&sid, state->request.data.gid, + ID_QUERY_ONLY | ID_CACHE_ONLY); + + if (NT_STATUS_IS_OK(status)) { + sid_to_string(state->response.data.sid.sid, &sid); + state->response.data.sid.type = SID_NAME_USER; + return WINBINDD_OK; + } + + if (is_in_gid_range(state->request.data.gid)) { + /* This is winbind's, so we should better have succeeded + * above. */ return WINBINDD_ERROR; } -done: - /* Construct sid and return it */ - sid_to_string(state->response.data.sid.sid, &sid); - state->response.data.sid.type = SID_NAME_DOM_GRP; + /* The only chance that this is correct is that winbind trusted + * domains only = yes, and the user exists in nss and the domain. */ - return WINBINDD_OK; + if (!lp_winbind_trusted_domains_only()) { + return WINBINDD_ERROR; + } + + /* The only chance that this is correct is that winbind trusted + * domains only = yes, and the user exists in nss and the domain. */ + + gid2sid_state = TALLOC_ZERO_P(state->mem_ctx, struct gid2sid_state); + if (gid2sid_state == NULL) { + DEBUG(0, ("talloc failed\n")); + return WINBINDD_ERROR; + } + + gid2sid_state->cli_state = state; + gid2sid_state->gid = state->request.data.gid; + + winbindd_gid2name_async(state->mem_ctx, state->request.data.gid, + gid2sid_gid2name_recv, gid2sid_state); + return WINBINDD_PENDING; +} + +static void gid2sid_gid2name_recv(void *private, BOOL success, + const char *username) +{ + struct gid2sid_state *state = + talloc_get_type_abort(private, struct gid2sid_state); + + DEBUG(10, ("gid2sid: gid %lu has name %s\n", + (unsigned long)state->gid, username)); + + fstrcpy(state->name, username); + + if (!success) { + state->cli_state->response.result = WINBINDD_ERROR; + request_finished(state->cli_state); + return; + } + + winbindd_lookupname_async(state->cli_state->mem_ctx, + find_our_domain()->name, username, + gid2sid_lookupname_recv, state); +} + +static void gid2sid_lookupname_recv(void *private, BOOL success, + const DOM_SID *sid, enum SID_NAME_USE type) +{ + struct gid2sid_state *state = + talloc_get_type_abort(private, struct gid2sid_state); + unid_t id; + + if ((!success) || + ((type != SID_NAME_DOM_GRP) && (type!=SID_NAME_ALIAS))) { + state->cli_state->response.result = WINBINDD_ERROR; + request_finished(state->cli_state); + return; + } + + state->sid = *sid; + state->type = type; + + id.gid = state->gid; + idmap_set_mapping_async(state->cli_state->mem_ctx, sid, id, ID_GROUPID, + gid2sid_idmap_set_mapping_recv, state ); +} + +static void gid2sid_idmap_set_mapping_recv(void *private, BOOL success) +{ + struct gid2sid_state *state = private; + + /* don't fail if we can't store it */ + + sid_to_string(state->cli_state->response.data.sid.sid, &state->sid); + state->cli_state->response.data.sid.type = state->type; + state->cli_state->response.result = WINBINDD_OK; + request_finished(state->cli_state); } enum winbindd_result winbindd_allocate_rid(struct winbindd_cli_state *state) @@ -491,6 +517,15 @@ enum winbindd_result winbindd_allocate_rid(struct winbindd_cli_state *state) return WINBINDD_ERROR; } + async_request(state->mem_ctx, idmap_child(), + &state->request, &state->response, + request_finished_cont, state); + return WINBINDD_PENDING; +} + +enum winbindd_result winbindd_dual_allocate_rid(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ /* We tell idmap to always allocate a user RID. There might be a good * reason to keep RID allocation for users to even and groups to * odd. This needs discussion I think. For now only allocate user @@ -502,3 +537,45 @@ enum winbindd_result winbindd_allocate_rid(struct winbindd_cli_state *state) return WINBINDD_OK; } + +enum winbindd_result winbindd_allocate_rid_and_gid(struct winbindd_cli_state *state) +{ + if ( !state->privileged ) { + DEBUG(2, ("winbindd_allocate_rid: non-privileged access " + "denied!\n")); + return WINBINDD_ERROR; + } + + async_request(state->mem_ctx, idmap_child(), + &state->request, &state->response, + request_finished_cont, state); + return WINBINDD_PENDING; +} + +enum winbindd_result winbindd_dual_allocate_rid_and_gid(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + NTSTATUS result; + DOM_SID sid; + + /* We tell idmap to always allocate a user RID. This is really + * historic and needs to be fixed. I *think* this has to do with the + * way winbind determines its free RID space. */ + + result = idmap_allocate_rid(&state->response.data.rid_and_gid.rid, + USER_RID_TYPE); + + if (!NT_STATUS_IS_OK(result)) + return WINBINDD_ERROR; + + sid_copy(&sid, get_global_sam_sid()); + sid_append_rid(&sid, state->response.data.rid_and_gid.rid); + + result = idmap_sid_to_gid(&sid, &state->response.data.rid_and_gid.gid, + 0); + + if (!NT_STATUS_IS_OK(result)) + return WINBINDD_ERROR; + + return WINBINDD_OK; +} diff --git a/source3/nsswitch/winbindd_user.c b/source3/nsswitch/winbindd_user.c index 7a9b5cf96a..d192793993 100644 --- a/source3/nsswitch/winbindd_user.c +++ b/source3/nsswitch/winbindd_user.c @@ -106,174 +106,295 @@ static BOOL winbindd_fill_pwent(char *dom_name, char *user_name, return True; } -/* Return a password structure from a username. */ +/* Wrapper for domain->methods->query_user, only on the parent->child pipe */ -enum winbindd_result winbindd_getpwnam(struct winbindd_cli_state *state) +enum winbindd_result winbindd_dual_userinfo(struct winbindd_domain *domain, + struct winbindd_cli_state *state) { + DOM_SID sid; WINBIND_USERINFO user_info; - DOM_SID user_sid; NTSTATUS status; - fstring name_domain, name_user; - enum SID_NAME_USE name_type; - struct winbindd_domain *domain; - TALLOC_CTX *mem_ctx; - + /* Ensure null termination */ - state->request.data.username[sizeof(state->request.data.username)-1]='\0'; + state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; - DEBUG(3, ("[%5lu]: getpwnam %s\n", (unsigned long)state->pid, - state->request.data.username)); - - /* Parse domain and username */ + DEBUG(3, ("[%5lu]: lookupsid %s\n", (unsigned long)state->pid, + state->request.data.sid)); - parse_domain_user(state->request.data.username, - name_domain, name_user); - - /* should we deal with users for our domain? */ - - if ((domain = find_domain_from_name(name_domain)) == NULL) { - DEBUG(5, ("no such domain: %s\n", name_domain)); + if (!string_to_sid(&sid, state->request.data.sid)) { + DEBUG(5, ("%s not a SID\n", state->request.data.sid)); return WINBINDD_ERROR; } - - if ( domain->primary && lp_winbind_trusted_domains_only()) { - DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getpwnam() for %s\\%s.\n", - name_domain, name_user)); - return WINBINDD_ERROR; - } - - /* Get rid and name type from name */ - if (!winbindd_lookup_sid_by_name(domain, domain->name, name_user, &user_sid, &name_type)) { - DEBUG(1, ("user '%s' does not exist\n", name_user)); + status = domain->methods->query_user(domain, state->mem_ctx, + &sid, &user_info); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("error getting user info for sid %s\n", + sid_string_static(&sid))); return WINBINDD_ERROR; } - if (name_type != SID_NAME_USER && name_type != SID_NAME_COMPUTER) { - DEBUG(1, ("name '%s' is not a user name: %d\n", name_user, - name_type)); - return WINBINDD_ERROR; - } - - /* Get some user info. */ - - if (!(mem_ctx = talloc_init("winbindd_getpwnam([%s]\\[%s])", - name_domain, name_user))) { - DEBUG(1, ("out of memory\n")); + fstrcpy(state->response.data.user_info.acct_name, user_info.acct_name); + fstrcpy(state->response.data.user_info.full_name, user_info.full_name); + if (!sid_peek_check_rid(&domain->sid, &user_info.group_sid, + &state->response.data.user_info.group_rid)) { + DEBUG(1, ("Could not extract group rid out of %s\n", + sid_string_static(&sid))); return WINBINDD_ERROR; } - status = domain->methods->query_user(domain, mem_ctx, &user_sid, - &user_info); + return WINBINDD_OK; +} - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("error getting user info for user '[%s]\\[%s]'\n", - name_domain, name_user)); - talloc_destroy(mem_ctx); - return WINBINDD_ERROR; +struct getpwsid_state { + struct winbindd_cli_state *state; + struct winbindd_domain *domain; + char *username; + char *fullname; + DOM_SID user_sid; + uid_t uid; + DOM_SID group_sid; + gid_t gid; +}; + +static void getpwsid_queryuser_recv(void *private, BOOL success, + const char *acct_name, + const char *full_name, uint32 group_rid); +static void getpwsid_sid2uid_recv(void *private, BOOL success, uid_t uid); +static void getpwsid_sid2gid_recv(void *private, BOOL success, gid_t gid); + +static void winbindd_getpwsid(struct winbindd_cli_state *state, + const DOM_SID *sid) +{ + struct getpwsid_state *s; + + s = TALLOC_P(state->mem_ctx, struct getpwsid_state); + if (s == NULL) { + DEBUG(0, ("talloc failed\n")); + goto error; } - - /* Now take all this information and fill in a passwd structure */ - if (!winbindd_fill_pwent(name_domain, user_info.acct_name, - user_info.user_sid, user_info.group_sid, - user_info.full_name, - &state->response.data.pw)) { - talloc_destroy(mem_ctx); - return WINBINDD_ERROR; + + s->state = state; + s->domain = find_domain_from_sid_noinit(sid); + if (s->domain == NULL) { + DEBUG(3, ("Could not find domain for sid %s\n", + sid_string_static(sid))); + goto error; } - talloc_destroy(mem_ctx); + sid_copy(&s->user_sid, sid); + + query_user_async(s->state->mem_ctx, s->domain, sid, + getpwsid_queryuser_recv, s); + return; + + error: + s->state->response.result = WINBINDD_ERROR; + request_finished(state); +} - return WINBINDD_OK; -} +static void getpwsid_queryuser_recv(void *private, BOOL success, + const char *acct_name, + const char *full_name, uint32 group_rid) +{ + struct getpwsid_state *s = + talloc_get_type_abort(private, struct getpwsid_state); + + if (!success) { + DEBUG(5, ("Could not query user %s\\%s\n", s->domain->name, + s->username)); + s->state->response.result = WINBINDD_ERROR; + request_finished(s->state); + return; + } -/* Return a password structure given a uid number */ + s->username = talloc_strdup(s->state->mem_ctx, acct_name); + s->fullname = talloc_strdup(s->state->mem_ctx, full_name); + sid_copy(&s->group_sid, &s->domain->sid); + sid_append_rid(&s->group_sid, group_rid); -enum winbindd_result winbindd_getpwuid(struct winbindd_cli_state *state) + winbindd_sid2uid_async(s->state->mem_ctx, &s->user_sid, + getpwsid_sid2uid_recv, s); +} + +static void getpwsid_sid2uid_recv(void *private, BOOL success, uid_t uid) { - DOM_SID user_sid; - struct winbindd_domain *domain; - fstring dom_name; - fstring user_name; - enum SID_NAME_USE name_type; - WINBIND_USERINFO user_info; - TALLOC_CTX *mem_ctx; - NTSTATUS status; - gid_t gid; - - /* Bug out if the uid isn't in the winbind range */ + struct getpwsid_state *s = + talloc_get_type_abort(private, struct getpwsid_state); + + if (!success) { + DEBUG(5, ("Could not query user's %s\\%s uid\n", + s->domain->name, s->username)); + s->state->response.result = WINBINDD_ERROR; + request_finished(s->state); + return; + } - if ((state->request.data.uid < server_state.uid_low ) || - (state->request.data.uid > server_state.uid_high)) - return WINBINDD_ERROR; + s->uid = uid; + winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid, + getpwsid_sid2gid_recv, s); +} - DEBUG(3, ("[%5lu]: getpwuid %lu\n", (unsigned long)state->pid, - (unsigned long)state->request.data.uid)); +static void getpwsid_sid2gid_recv(void *private, BOOL success, gid_t gid) +{ + struct getpwsid_state *s = + talloc_get_type_abort(private, struct getpwsid_state); + struct winbindd_pw *pw; + fstring output_username; + char *homedir; + char *shell; - /* Get rid from uid */ + s->state->response.result = WINBINDD_ERROR; - if (!NT_STATUS_IS_OK(idmap_uid_to_sid(&user_sid, state->request.data.uid))) { - DEBUG(1, ("could not convert uid %lu to SID\n", - (unsigned long)state->request.data.uid)); - return WINBINDD_ERROR; + if (!success) { + DEBUG(5, ("Could not query user's %s\\%s\n gid", + s->domain->name, s->username)); + goto done; } + + s->gid = gid; + + pw = &s->state->response.data.pw; + pw->pw_uid = s->uid; + pw->pw_gid = s->gid; + fill_domain_username(output_username, s->domain->name, s->username); + safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1); + safe_strcpy(pw->pw_gecos, s->fullname, 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. */ - /* Get name and name type from rid */ + /* The substitution of %U and %D in the 'template homedir' is done + by alloc_sub_specified() below. */ - if (!winbindd_lookup_name_by_sid(&user_sid, dom_name, user_name, &name_type)) { - fstring temp; - - sid_to_string(temp, &user_sid); - DEBUG(1, ("could not lookup sid %s\n", temp)); - return WINBINDD_ERROR; + fstrcpy(current_user_info.domain, s->domain->name); + + homedir = alloc_sub_specified(lp_template_homedir(), s->username, + s->domain->name, pw->pw_uid, pw->pw_gid); + if (homedir == NULL) { + DEBUG(5, ("Could not compose homedir\n")); + goto done; } + safe_strcpy(pw->pw_dir, homedir, sizeof(pw->pw_dir) - 1); + SAFE_FREE(homedir); - domain = find_domain_from_sid(&user_sid); + shell = alloc_sub_specified(lp_template_shell(), s->username, + s->domain->name, pw->pw_uid, pw->pw_gid); + if (shell == NULL) { + DEBUG(5, ("Could not compose shell\n")); + goto done; + } + safe_strcpy(pw->pw_shell, shell, sizeof(pw->pw_shell) - 1); + SAFE_FREE(shell); + + /* Password - set to "x" as we can't generate anything useful here. + Authentication can be done using the pam_winbind module. */ + + safe_strcpy(pw->pw_passwd, "x", sizeof(pw->pw_passwd) - 1); + + s->state->response.result = WINBINDD_OK; + + done: + request_finished(s->state); +} - if (!domain) { - DEBUG(1,("Can't find domain from sid\n")); +/* Return a password structure from a username. */ + +static void getpwnam_name2sid_recv(void *private, BOOL success, + const DOM_SID *sid, enum SID_NAME_USE type); + +enum winbindd_result winbindd_getpwnam(struct winbindd_cli_state *state) +{ + struct winbindd_domain *domain; + fstring domname, username; + + /* Ensure null termination */ + state->request.data.username[sizeof(state->request.data.username)-1]='\0'; + + DEBUG(3, ("[%5lu]: getpwnam %s\n", (unsigned long)state->pid, + state->request.data.username)); + + if (!parse_domain_user(state->request.data.username, domname, + username)) { + DEBUG(0, ("Could not parse domain user: %s\n", + state->request.data.username)); return WINBINDD_ERROR; } - - /* Get some user info */ - if (!(mem_ctx = talloc_init("winbind_getpwuid(%lu)", - (unsigned long)state->request.data.uid))) { + /* Get info for the domain */ - DEBUG(1, ("out of memory\n")); + domain = find_lookup_domain_from_name(domname); + + if (domain == NULL) { + DEBUG(7, ("could not find domain entry for domain %s\n", + domname)); return WINBINDD_ERROR; } - status = domain->methods->query_user(domain, mem_ctx, &user_sid, - &user_info); - - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("error getting user info for user '%s'\n", - user_name)); - talloc_destroy(mem_ctx); + if ( domain->primary && lp_winbind_trusted_domains_only()) { + DEBUG(7,("winbindd_getpwnam: My domain -- rejecting " + "getgroups() for %s\\%s.\n", domname, username)); return WINBINDD_ERROR; + } + + /* Get rid and name type from name. The following costs 1 packet */ + + winbindd_lookupname_async(state->mem_ctx, domname, username, + getpwnam_name2sid_recv, state); + return WINBINDD_PENDING; +} + +static void getpwnam_name2sid_recv(void *private, BOOL success, + const DOM_SID *sid, enum SID_NAME_USE type) +{ + struct winbindd_cli_state *state = private; + + if (!success) { + DEBUG(5, ("Could not lookup name for user %s\n", + state->request.data.username)); + state->response.result = WINBINDD_ERROR; + request_finished(state); + return; + } + + if ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER)) { + DEBUG(5, ("%s is not a user\n", state->request.data.username)); + state->response.result = WINBINDD_ERROR; + request_finished(state); + return; } + + winbindd_getpwsid(state, sid); +} + +/* Return a password structure given a uid number */ + +enum winbindd_result winbindd_getpwuid(struct winbindd_cli_state *state) +{ + DOM_SID user_sid; + NTSTATUS status; - /* Check group has a gid number */ + /* Bug out if the uid isn't in the winbind range */ - if (!NT_STATUS_IS_OK(idmap_sid_to_gid(user_info.group_sid, &gid, 0))) { - DEBUG(1, ("error getting group id for user %s\n", user_name)); - talloc_destroy(mem_ctx); + if ((state->request.data.uid < server_state.uid_low ) || + (state->request.data.uid > server_state.uid_high)) return WINBINDD_ERROR; - } - /* Fill in password structure */ + DEBUG(3, ("[%5lu]: getpwuid %lu\n", (unsigned long)state->pid, + (unsigned long)state->request.data.uid)); - if (!winbindd_fill_pwent(domain->name, user_info.acct_name, user_info.user_sid, - user_info.group_sid, - user_info.full_name, &state->response.data.pw)) { - talloc_destroy(mem_ctx); + status = idmap_uid_to_sid(&user_sid, state->request.data.uid, + ID_QUERY_ONLY | ID_CACHE_ONLY); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(5, ("Could not find SID for uid %lu\n", + (unsigned long)state->request.data.uid)); return WINBINDD_ERROR; } - - talloc_destroy(mem_ctx); - return WINBINDD_OK; + winbindd_getpwsid(state, &user_sid); + return WINBINDD_PENDING; } /* @@ -365,14 +486,13 @@ enum winbindd_result winbindd_endpwent(struct winbindd_cli_state *state) field is incremented to the index of the next user to fetch. Return True if some users were returned, False otherwise. */ -static BOOL get_sam_user_entries(struct getent_state *ent) +static BOOL get_sam_user_entries(struct getent_state *ent, TALLOC_CTX *mem_ctx) { NTSTATUS status; uint32 num_entries; WINBIND_USERINFO *info; struct getpwent_user *name_list = NULL; BOOL result = False; - TALLOC_CTX *mem_ctx; struct winbindd_domain *domain; struct winbindd_methods *methods; unsigned int i; @@ -380,10 +500,6 @@ static BOOL get_sam_user_entries(struct getent_state *ent) if (ent->num_sam_entries) return False; - if (!(mem_ctx = talloc_init("get_sam_user_entries(%s)", - ent->domain_name))) - return False; - if (!(domain = find_domain_from_name(ent->domain_name))) { DEBUG(3, ("no such domain %s in get_sam_user_entries\n", ent->domain_name)); @@ -433,8 +549,10 @@ static BOOL get_sam_user_entries(struct getent_state *ent) } /* User and group ids */ - sid_copy(&name_list[ent->num_sam_entries+i].user_sid, info[i].user_sid); - sid_copy(&name_list[ent->num_sam_entries+i].group_sid, info[i].group_sid); + sid_copy(&name_list[ent->num_sam_entries+i].user_sid, + &info[i].user_sid); + sid_copy(&name_list[ent->num_sam_entries+i].group_sid, + &info[i].group_sid); } ent->num_sam_entries += num_entries; @@ -447,8 +565,6 @@ static BOOL get_sam_user_entries(struct getent_state *ent) done: - talloc_destroy(mem_ctx); - return result; } @@ -497,7 +613,8 @@ enum winbindd_result winbindd_getpwent(struct winbindd_cli_state *state) if (ent->num_sam_entries == ent->sam_entry_index) { - while(ent && !get_sam_user_entries(ent)) { + while(ent && + !get_sam_user_entries(ent, state->mem_ctx)) { struct getent_state *next_ent; /* Free state information for this domain */ @@ -560,14 +677,10 @@ enum winbindd_result winbindd_list_users(struct winbindd_cli_state *state) uint32 num_entries = 0, total_entries = 0; char *ted, *extra_data = NULL; int extra_data_len = 0; - TALLOC_CTX *mem_ctx; enum winbindd_result rv = WINBINDD_ERROR; DEBUG(3, ("[%5lu]: list users\n", (unsigned long)state->pid)); - if (!(mem_ctx = talloc_init("winbindd_list_users"))) - return WINBINDD_ERROR; - /* Ensure null termination */ state->request.domain_name[sizeof(state->request.domain_name)-1]='\0'; which_domain = state->request.domain_name; @@ -585,14 +698,11 @@ enum winbindd_result winbindd_list_users(struct winbindd_cli_state *state) if ( *which_domain && !strequal(which_domain, domain->name) ) continue; - - if ( !domain->initialized ) - set_dc_type_and_flags( domain ); methods = domain->methods; /* Query display info */ - status = methods->query_user_list(domain, mem_ctx, + status = methods->query_user_list(domain, state->mem_ctx, &num_entries, &info); if (num_entries == 0) @@ -646,7 +756,5 @@ enum winbindd_result winbindd_list_users(struct winbindd_cli_state *state) done: - talloc_destroy(mem_ctx); - return rv; } diff --git a/source3/nsswitch/winbindd_util.c b/source3/nsswitch/winbindd_util.c index 739a7ed2a7..21ae4611c2 100644 --- a/source3/nsswitch/winbindd_util.c +++ b/source3/nsswitch/winbindd_util.c @@ -24,9 +24,6 @@ #include "includes.h" #include "winbindd.h" -extern struct winbindd_methods cache_methods; -extern struct winbindd_methods passdb_methods; - #undef DBGC_CLASS #define DBGC_CLASS DBGC_WINBIND @@ -65,8 +62,7 @@ struct winbindd_domain *domain_list(void) /* Initialise list */ if (!_domain_list) - if (!init_domain_list()) - return NULL; + init_domain_list(); return _domain_list; } @@ -91,12 +87,7 @@ static BOOL is_internal_domain(const DOM_SID *sid) if (sid == NULL) return False; - if ( sid_compare_domain( sid, get_global_sam_sid() ) == 0 ) - return True; - if ( sid_compare_domain( sid, &global_sid_Builtin ) == 0 ) - return True; - - return False; + return (sid_check_is_domain(sid) || sid_check_is_builtin(sid)); } @@ -181,80 +172,115 @@ static struct winbindd_domain *add_trusted_domain(const char *domain_name, const rescan our domains looking for new trusted domains ********************************************************************/ +struct trustdom_state { + TALLOC_CTX *mem_ctx; + struct winbindd_response *response; +}; + +static void trustdom_recv(void *private, BOOL success); + static void add_trusted_domains( struct winbindd_domain *domain ) { TALLOC_CTX *mem_ctx; - NTSTATUS result; - time_t t; - char **names; - char **alt_names; - int num_domains = 0; - DOM_SID *dom_sids, null_sid; - int i; - struct winbindd_domain *new_domain; - - /* trusted domains might be disabled */ - if (!lp_allow_trusted_domains()) { + struct winbindd_request *request; + struct winbindd_response *response; + + struct trustdom_state *state; + + mem_ctx = talloc_init("add_trusted_domains"); + if (mem_ctx == NULL) { + DEBUG(0, ("talloc_init failed\n")); return; } - DEBUG(5, ("scanning trusted domain list\n")); + request = TALLOC_ZERO_P(mem_ctx, struct winbindd_request); + response = TALLOC_P(mem_ctx, struct winbindd_response); + state = TALLOC_P(mem_ctx, struct trustdom_state); - if (!(mem_ctx = talloc_init("init_domain_list"))) + if ((request == NULL) || (response == NULL) || (state == NULL)) { + DEBUG(0, ("talloc failed\n")); + talloc_destroy(mem_ctx); return; - - ZERO_STRUCTP(&null_sid); + } - t = time(NULL); - - /* ask the DC what domains it trusts */ - - result = domain->methods->trusted_domains(domain, mem_ctx, (unsigned int *)&num_domains, - &names, &alt_names, &dom_sids); - - if ( NT_STATUS_IS_OK(result) ) { + state->mem_ctx = mem_ctx; + state->response = response; - /* Add each domain to the trusted domain list */ - - for(i = 0; i < num_domains; i++) { - DEBUG(10,("Found domain %s\n", names[i])); - add_trusted_domain(names[i], alt_names?alt_names[i]:NULL, - &cache_methods, &dom_sids[i]); - - /* if the SID was empty, we better set it now */ - - if ( sid_equal(&dom_sids[i], &null_sid) ) { - enum SID_NAME_USE type; - new_domain = find_domain_from_name(names[i]); - - /* this should never happen */ - if ( !new_domain ) { - DEBUG(0,("rescan_trust_domains: can't find the domain I just added! [%s]\n", - names[i])); - break; - } - - /* call the cache method; which will operate on the winbindd_domain \ - passed in and choose either rpc or ads as appropriate */ - - result = domain->methods->name_to_sid( domain, - mem_ctx, - new_domain->name, - NULL, - &new_domain->sid, - &type); - - if ( NT_STATUS_IS_OK(result) ) - sid_copy( &dom_sids[i], &new_domain->sid ); - } + request->length = sizeof(*request); + request->cmd = WINBINDD_LIST_TRUSTDOM; + + async_domain_request(mem_ctx, domain, request, response, + trustdom_recv, state); +} + +static void trustdom_recv(void *private, BOOL success) +{ + extern struct winbindd_methods cache_methods; + struct trustdom_state *state = + talloc_get_type_abort(private, struct trustdom_state); + struct winbindd_response *response = state->response; + char *p; + + if ((!success) || (response->result != WINBINDD_OK)) { + DEBUG(1, ("Could not receive trustdoms\n")); + talloc_destroy(state->mem_ctx); + return; + } + + p = response->extra_data; + + while ((p != NULL) && (*p != '\0')) { + char *q, *sidstr, *alt_name; + DOM_SID sid; + + alt_name = strchr(p, '\\'); + if (alt_name == NULL) { + DEBUG(0, ("Got invalid trustdom response\n")); + break; + } + + *alt_name = '\0'; + alt_name += 1; + + sidstr = strchr(alt_name, '\\'); + if (sidstr == NULL) { + DEBUG(0, ("Got invalid trustdom response\n")); + break; + } + + *sidstr = '\0'; + sidstr += 1; + + q = strchr(sidstr, '\n'); + if (q != NULL) + *q = '\0'; + + if (!string_to_sid(&sid, sidstr)) { + DEBUG(0, ("Got invalid trustdom response\n")); + break; + } + + if (find_domain_from_name_noinit(p) == NULL) { + struct winbindd_domain *domain; + char *alternate_name = NULL; - /* store trusted domain in the cache */ - trustdom_cache_store(names[i], alt_names ? alt_names[i] : NULL, - &dom_sids[i], t + WINBINDD_RESCAN_FREQ); + /* use the real alt_name if we have one, else pass in NULL */ + + if ( !strequal( alt_name, "(null)" ) ) + alternate_name = alt_name; + + domain = add_trusted_domain(p, alternate_name, + &cache_methods, + &sid); + setup_domain_child(domain, &domain->child, NULL); } + p=q; + if (p != NULL) + p += 1; } - talloc_destroy(mem_ctx); + SAFE_FREE(response->extra_data); + talloc_destroy(state->mem_ctx); } /******************************************************************** @@ -264,30 +290,204 @@ static void add_trusted_domains( struct winbindd_domain *domain ) void rescan_trusted_domains( void ) { time_t now = time(NULL); - struct winbindd_domain *mydomain = NULL; /* see if the time has come... */ - if ( (now > last_trustdom_scan) && ((now-last_trustdom_scan) < WINBINDD_RESCAN_FREQ) ) + if ((now >= last_trustdom_scan) && + ((now-last_trustdom_scan) < WINBINDD_RESCAN_FREQ) ) return; - if ( (mydomain = find_our_domain()) == NULL ) { - DEBUG(0,("rescan_trusted_domains: Can't find my own domain!\n")); - return; - } - /* this will only add new domains we didn't already know about */ - add_trusted_domains( mydomain ); + add_trusted_domains( find_our_domain() ); last_trustdom_scan = now; return; } +struct init_child_state { + TALLOC_CTX *mem_ctx; + struct winbindd_domain *domain; + struct winbindd_request *request; + struct winbindd_response *response; + void (*continuation)(void *private, BOOL success); + void *private; +}; + +static void init_child_recv(void *private, BOOL success); +static void init_child_getdc_recv(void *private, BOOL success); + +enum winbindd_result init_child_connection(struct winbindd_domain *domain, + void (*continuation)(void *private, + BOOL success), + void *private) +{ + TALLOC_CTX *mem_ctx; + struct winbindd_request *request; + struct winbindd_response *response; + struct init_child_state *state; + + mem_ctx = talloc_init("init_child_connection"); + if (mem_ctx == NULL) { + DEBUG(0, ("talloc_init failed\n")); + return WINBINDD_ERROR; + } + + request = TALLOC_ZERO_P(mem_ctx, struct winbindd_request); + response = TALLOC_P(mem_ctx, struct winbindd_response); + state = TALLOC_P(mem_ctx, struct init_child_state); + + if ((request == NULL) || (response == NULL) || (state == NULL)) { + DEBUG(0, ("talloc failed\n")); + continuation(private, False); + return WINBINDD_ERROR; + } + + request->length = sizeof(*request); + + state->mem_ctx = mem_ctx; + state->domain = domain; + state->request = request; + state->response = response; + state->continuation = continuation; + state->private = private; + + if (domain->primary) { + /* The primary domain has to find the DC name itself */ + request->cmd = WINBINDD_INIT_CONNECTION; + fstrcpy(request->domain_name, domain->name); + request->data.init_conn.is_primary = True; + fstrcpy(request->data.init_conn.dcname, ""); + + async_request(mem_ctx, &domain->child, request, response, + init_child_recv, state); + return WINBINDD_PENDING; + } + + /* This is *not* the primary domain, let's ask our DC about a DC + * name */ + + request->cmd = WINBINDD_GETDCNAME; + fstrcpy(request->domain_name, domain->name); + + async_domain_request(mem_ctx, find_our_domain(), request, response, + init_child_getdc_recv, state); + return WINBINDD_PENDING; +} + +static void init_child_getdc_recv(void *private, BOOL success) +{ + struct init_child_state *state = + talloc_get_type_abort(private, struct init_child_state); + const char *dcname = ""; + + DEBUG(10, ("Received getdcname response\n")); + + if (success && (state->response->result == WINBINDD_OK)) { + dcname = state->response->data.dc_name; + } + + state->request->cmd = WINBINDD_INIT_CONNECTION; + fstrcpy(state->request->domain_name, state->domain->name); + state->request->data.init_conn.is_primary = False; + fstrcpy(state->request->data.init_conn.dcname, dcname); + + async_request(state->mem_ctx, &state->domain->child, + state->request, state->response, + init_child_recv, state); +} + +static void init_child_recv(void *private, BOOL success) +{ + struct init_child_state *state = + talloc_get_type_abort(private, struct init_child_state); + + DEBUG(5, ("Received child initialization response for domain %s\n", + state->domain->name)); + + if ((!success) || (state->response->result != WINBINDD_OK)) { + DEBUG(3, ("Could not init child\n")); + state->continuation(state->private, False); + talloc_destroy(state->mem_ctx); + return; + } + + fstrcpy(state->domain->name, + state->response->data.domain_info.name); + fstrcpy(state->domain->alt_name, + state->response->data.domain_info.alt_name); + string_to_sid(&state->domain->sid, + state->response->data.domain_info.sid); + state->domain->native_mode = + state->response->data.domain_info.native_mode; + state->domain->active_directory = + state->response->data.domain_info.active_directory; + state->domain->sequence_number = + state->response->data.domain_info.sequence_number; + + state->domain->initialized = 1; + + if (state->continuation != NULL) + state->continuation(state->private, True); + talloc_destroy(state->mem_ctx); +} + +enum winbindd_result winbindd_dual_init_connection(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + struct in_addr ipaddr; + + /* Ensure null termination */ + state->request.domain_name + [sizeof(state->request.domain_name)-1]='\0'; + state->request.data.init_conn.dcname + [sizeof(state->request.data.init_conn.dcname)-1]='\0'; + + fstrcpy(domain->dcname, state->request.data.init_conn.dcname); + + if (strlen(domain->dcname) > 0) { + if (!resolve_name(domain->dcname, &ipaddr, 0x20)) { + DEBUG(2, ("Could not resolve DC name %s for domain %s\n", + domain->dcname, domain->name)); + return WINBINDD_ERROR; + } + + domain->dcaddr.sin_family = PF_INET; + putip((char *)&(domain->dcaddr.sin_addr), (char *)&ipaddr); + domain->dcaddr.sin_port = 0; + } + + set_dc_type_and_flags(domain); + + if (!domain->initialized) { + DEBUG(1, ("Could not initialize domain %s\n", + state->request.domain_name)); + return WINBINDD_ERROR; + } + + fstrcpy(state->response.data.domain_info.name, domain->name); + fstrcpy(state->response.data.domain_info.alt_name, domain->alt_name); + fstrcpy(state->response.data.domain_info.sid, + sid_string_static(&domain->sid)); + + state->response.data.domain_info.native_mode + = domain->native_mode; + state->response.data.domain_info.active_directory + = domain->active_directory; + state->response.data.domain_info.primary + = domain->primary; + state->response.data.domain_info.sequence_number = + domain->sequence_number; + + return WINBINDD_OK; +} + /* Look up global info for the winbind daemon */ -BOOL init_domain_list(void) +void init_domain_list(void) { + extern struct winbindd_methods cache_methods; + extern struct winbindd_methods passdb_methods; struct winbindd_domain *domain; /* Free existing list */ @@ -297,50 +497,35 @@ BOOL init_domain_list(void) if (IS_DC) { domain = add_trusted_domain(get_global_sam_name(), NULL, - &passdb_methods, get_global_sam_sid()); + &passdb_methods, + get_global_sam_sid()); } else { - - domain = add_trusted_domain( lp_workgroup(), lp_realm(), - &cache_methods, NULL); - - /* set flags about native_mode, active_directory */ - set_dc_type_and_flags(domain); - } - domain->primary = True; + DOM_SID our_sid; - /* get any alternate name for the primary domain */ - - cache_methods.alternate_name(domain); - - /* now we have the correct netbios (short) domain name */ + if (!secrets_fetch_domain_sid(lp_workgroup(), &our_sid)) { + DEBUG(0, ("Could not fetch our SID - did we join?\n")); + } - if ( *domain->name ) - set_global_myworkgroup( domain->name ); - - if (!secrets_fetch_domain_sid(domain->name, &domain->sid)) { - DEBUG(1, ("Could not fetch sid for our domain %s\n", - domain->name)); - return False; + domain = add_trusted_domain( lp_workgroup(), lp_realm(), + &cache_methods, &our_sid); } - /* do an initial scan for trusted domains */ - add_trusted_domains(domain); - + domain->primary = True; + setup_domain_child(domain, &domain->child, NULL); /* Add our local SAM domains */ - add_trusted_domain("BUILTIN", NULL, &passdb_methods, - &global_sid_Builtin); + domain = add_trusted_domain("BUILTIN", NULL, &passdb_methods, + &global_sid_Builtin); + setup_domain_child(domain, &domain->child, NULL); if (!IS_DC) { - add_trusted_domain(get_global_sam_name(), NULL, - &passdb_methods, get_global_sam_sid()); + domain = add_trusted_domain(get_global_sam_name(), NULL, + &passdb_methods, + get_global_sam_sid()); + setup_domain_child(domain, &domain->child, NULL); } - - /* avoid rescanning this right away */ - last_trustdom_scan = time(NULL); - return True; } /** @@ -355,7 +540,7 @@ BOOL init_domain_list(void) * @return The domain structure for the named domain, if it is working. */ -struct winbindd_domain *find_domain_from_name(const char *domain_name) +struct winbindd_domain *find_domain_from_name_noinit(const char *domain_name) { struct winbindd_domain *domain; @@ -363,10 +548,8 @@ struct winbindd_domain *find_domain_from_name(const char *domain_name) for (domain = domain_list(); domain != NULL; domain = domain->next) { if (strequal(domain_name, domain->name) || - (domain->alt_name[0] && strequal(domain_name, domain->alt_name))) { - if (!domain->initialized) - set_dc_type_and_flags(domain); - + (domain->alt_name[0] && + strequal(domain_name, domain->alt_name))) { return domain; } } @@ -376,20 +559,32 @@ struct winbindd_domain *find_domain_from_name(const char *domain_name) return NULL; } +struct winbindd_domain *find_domain_from_name(const char *domain_name) +{ + struct winbindd_domain *domain; + + domain = find_domain_from_name_noinit(domain_name); + + if (domain == NULL) + return NULL; + + if (!domain->initialized) + set_dc_type_and_flags(domain); + + return domain; +} + /* Given a domain sid, return the struct winbindd domain info for it */ -struct winbindd_domain *find_domain_from_sid(const DOM_SID *sid) +struct winbindd_domain *find_domain_from_sid_noinit(const DOM_SID *sid) { struct winbindd_domain *domain; /* Search through list */ for (domain = domain_list(); domain != NULL; domain = domain->next) { - if (sid_compare_domain(sid, &domain->sid) == 0) { - if (!domain->initialized) - set_dc_type_and_flags(domain); + if (sid_compare_domain(sid, &domain->sid) == 0) return domain; - } } /* Not found */ @@ -399,6 +594,21 @@ struct winbindd_domain *find_domain_from_sid(const DOM_SID *sid) /* Given a domain sid, return the struct winbindd domain info for it */ +struct winbindd_domain *find_domain_from_sid(const DOM_SID *sid) +{ + struct winbindd_domain *domain; + + domain = find_domain_from_sid_noinit(sid); + + if (domain == NULL) + return NULL; + + if (!domain->initialized) + set_dc_type_and_flags(domain); + + return domain; +} + struct winbindd_domain *find_our_domain(void) { struct winbindd_domain *domain; @@ -410,11 +620,24 @@ struct winbindd_domain *find_our_domain(void) return domain; } - /* Not found */ - + smb_panic("Could not find our domain\n"); return NULL; } +struct winbindd_domain *find_builtin_domain(void) +{ + DOM_SID sid; + struct winbindd_domain *domain; + + string_to_sid(&sid, "S-1-5-32"); + domain = find_domain_from_sid(&sid); + + if (domain == NULL) + smb_panic("Could not find BUILTIN domain\n"); + + return domain; +} + /* Find the appropriate domain to lookup a name or SID */ struct winbindd_domain *find_lookup_domain_from_sid(const DOM_SID *sid) @@ -436,31 +659,24 @@ struct winbindd_domain *find_lookup_domain_from_name(const char *domain_name) { if (IS_DC || strequal(domain_name, "BUILTIN") || strequal(domain_name, get_global_sam_name())) - return find_domain_from_name(domain_name); + return find_domain_from_name_noinit(domain_name); return find_our_domain(); } /* Lookup a sid in a domain from a name */ -BOOL winbindd_lookup_sid_by_name(struct winbindd_domain *domain, +BOOL winbindd_lookup_sid_by_name(TALLOC_CTX *mem_ctx, + struct winbindd_domain *domain, const char *domain_name, const char *name, DOM_SID *sid, enum SID_NAME_USE *type) { NTSTATUS result; - TALLOC_CTX *mem_ctx; - mem_ctx = talloc_init("lookup_sid_by_name for %s\\%s\n", - domain_name, name); - if (!mem_ctx) - return False; - /* Lookup name */ result = domain->methods->name_to_sid(domain, mem_ctx, domain_name, name, sid, type); - talloc_destroy(mem_ctx); - /* Return rid and type if lookup successful */ if (!NT_STATUS_IS_OK(result)) { *type = SID_NAME_UNKNOWN; @@ -480,7 +696,8 @@ BOOL winbindd_lookup_sid_by_name(struct winbindd_domain *domain, * @retval True if the name exists, in which case @p name and @p type * are set, otherwise False. **/ -BOOL winbindd_lookup_name_by_sid(DOM_SID *sid, +BOOL winbindd_lookup_name_by_sid(TALLOC_CTX *mem_ctx, + DOM_SID *sid, fstring dom_name, fstring name, enum SID_NAME_USE *type) @@ -488,7 +705,6 @@ BOOL winbindd_lookup_name_by_sid(DOM_SID *sid, char *names; char *dom_names; NTSTATUS result; - TALLOC_CTX *mem_ctx; BOOL rv = False; struct winbindd_domain *domain; @@ -501,9 +717,6 @@ BOOL winbindd_lookup_name_by_sid(DOM_SID *sid, /* Lookup name */ - if (!(mem_ctx = talloc_init("winbindd_lookup_name_by_sid"))) - return False; - result = domain->methods->sid_to_name(domain, mem_ctx, sid, &dom_names, &names, type); /* Return name and type if successful */ @@ -516,12 +729,9 @@ BOOL winbindd_lookup_name_by_sid(DOM_SID *sid, fstrcpy(name, name_deadbeef); } - talloc_destroy(mem_ctx); - return rv; } - /* Free state information held for {set,get,end}{pw,gr}ent() functions */ void free_getent_state(struct getent_state *state) @@ -553,31 +763,30 @@ BOOL winbindd_param_init(void) /* Parse winbind uid and winbind_gid parameters */ if (!lp_idmap_uid(&server_state.uid_low, &server_state.uid_high)) { - DEBUG(2, ("winbindd: idmap uid range missing or invalid\n")); + DEBUG(0, ("winbindd: idmap uid range missing or invalid\n")); + DEBUG(0, ("winbindd: cannot continue, exiting.\n")); return False; } if (!lp_idmap_gid(&server_state.gid_low, &server_state.gid_high)) { - DEBUG(2, ("winbindd: idmap gid range missing or invalid\n")); + DEBUG(0, ("winbindd: idmap gid range missing or invalid\n")); + DEBUG(0, ("winbindd: cannot continue, exiting.\n")); return False; } return True; } -/* Check if a domain is present in a comma-separated list of domains */ - -BOOL check_domain_env(char *domain_env, char *domain) +BOOL is_in_uid_range(uid_t uid) { - fstring name; - const char *tmp = domain_env; - - while(next_token(&tmp, name, ",", sizeof(fstring))) { - if (strequal(name, domain)) - return True; - } + return ((uid >= server_state.uid_low) && + (uid <= server_state.uid_high)); +} - return False; +BOOL is_in_gid_range(gid_t gid) +{ + return ((gid >= server_state.gid_low) && + (gid <= server_state.gid_high)); } /* Is this a domain which we may assume no DOMAIN\ prefix? */ @@ -620,6 +829,16 @@ BOOL parse_domain_user(const char *domuser, fstring domain, fstring user) return True; } +BOOL parse_domain_user_talloc(TALLOC_CTX *mem_ctx, const char *domuser, + char **domain, char **user) +{ + fstring fstr_domain, fstr_user; + parse_domain_user(domuser, fstr_domain, fstr_user); + *domain = talloc_strdup(mem_ctx, fstr_domain); + *user = talloc_strdup(mem_ctx, fstr_user); + return ((*domain != NULL) && (*user != NULL)); +} + /* Fill DOMAIN\\USERNAME entry accounting 'winbind use default domain' and 'winbind separator' options. @@ -635,14 +854,16 @@ BOOL parse_domain_user(const char *domuser, fstring domain, fstring user) */ void fill_domain_username(fstring name, const char *domain, const char *user) { - strlower_m(CONST_DISCARD(char *, user)); + fstring tmp_user; + + fstrcpy(tmp_user, user); if (assume_domain(domain)) { strlcpy(name, user, sizeof(fstring)); } else { slprintf(name, sizeof(fstring) - 1, "%s%c%s", domain, *lp_winbind_separator(), - user); + tmp_user); } } @@ -764,22 +985,6 @@ int winbindd_num_clients(void) return _num_clients; } -/* Help with RID -> SID conversion */ - -DOM_SID *rid_to_talloced_sid(struct winbindd_domain *domain, - TALLOC_CTX *mem_ctx, - uint32 rid) -{ - DOM_SID *sid; - sid = TALLOC_P(mem_ctx, DOM_SID); - if (!sid) { - smb_panic("rid_to_to_talloced_sid: talloc for DOM_SID failed!\n"); - } - sid_copy(sid, &domain->sid); - sid_append_rid(sid, rid); - return sid; -} - /***************************************************************************** For idmap conversion: convert one record to new format Ancient versions (eg 2.2.3a) of winbindd_idmap.tdb mapped DOMAINNAME/rid @@ -986,52 +1191,3 @@ BOOL winbindd_upgrade_idmap(void) return idmap_convert(idmap_name); } - -/******************************************************************* - wrapper around retrieving the trust account password -*******************************************************************/ - -BOOL get_trust_pw(const char *domain, uint8 ret_pwd[16], - time_t *pass_last_set_time, uint32 *channel) -{ - DOM_SID sid; - char *pwd; - - /* if we are a DC and this is not our domain, then lookup an account - for the domain trust */ - - if ( IS_DC && !strequal(domain, lp_workgroup()) && lp_allow_trusted_domains() ) - { - if ( !secrets_fetch_trusted_domain_password(domain, &pwd, &sid, - pass_last_set_time) ) - { - DEBUG(0, ("get_trust_pw: could not fetch trust account " - "password for trusted domain %s\n", domain)); - return False; - } - - *channel = SEC_CHAN_DOMAIN; - E_md4hash(pwd, ret_pwd); - SAFE_FREE(pwd); - - return True; - } - else /* just get the account for our domain (covers - ROLE_DOMAIN_MEMBER as well */ - { - /* get the machine trust account for our domain */ - - if ( !secrets_fetch_trust_account_password (lp_workgroup(), ret_pwd, - pass_last_set_time, channel) ) - { - DEBUG(0, ("get_trust_pw: could not fetch trust account " - "password for my domain %s\n", domain)); - return False; - } - - return True; - } - - /* Failure */ -} - |