2 Unix SMB/CIFS implementation.
4 Winbind daemon connection manager
6 Copyright (C) Tim Potter 2001
7 Copyright (C) Andrew Bartlett 2002
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 We need to manage connections to domain controllers without having to
26 mess up the main winbindd code with other issues. The aim of the
27 connection manager is to:
29 - make connections to domain controllers and cache them
30 - re-establish connections when networks or servers go down
31 - centralise the policy on connection timeouts, domain controller
33 - manage re-entrancy for when winbindd becomes able to handle
34 multiple outstanding rpc requests
36 Why not have connection management as part of the rpc layer like tng?
37 Good question. This code may morph into libsmb/rpc_cache.c or something
38 like that but at the moment it's simply staying as part of winbind. I
39 think the TNG architecture of forcing every user of the rpc layer to use
40 the connection caching system is a bad idea. It should be an optional
41 method of using the routines.
43 The TNG design is quite good but I disagree with some aspects of the
51 - I'm pretty annoyed by all the make_nmb_name() stuff. It should be
52 moved down into another function.
54 - Take care when destroying cli_structs as they can be shared between
62 #define DBGC_CLASS DBGC_WINBIND
64 /* Global list of connections. Initially a DLIST but can become a hash
65 table or whatever later. */
67 struct winbindd_cm_conn {
68 struct winbindd_cm_conn *prev, *next;
72 size_t mutex_ref_count;
73 struct cli_state *cli;
77 static struct winbindd_cm_conn *cm_conns = NULL;
80 /* Choose between anonymous or authenticated connections. We need to use
81 an authenticated connection if DCs have the RestrictAnonymous registry
82 entry set > 0, or the "Additional restrictions for anonymous
83 connections" set in the win2k Local Security Policy.
85 Caller to free() result in domain, username, password
88 static void cm_get_ipc_userpass(char **username, char **domain, char **password)
90 *username = secrets_fetch(SECRETS_AUTH_USER, NULL);
91 *domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
92 *password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
94 if (*username && **username) {
96 if (!*domain || !**domain)
97 *domain = smb_xstrdup(lp_workgroup());
99 if (!*password || !**password)
100 *password = smb_xstrdup("");
102 DEBUG(3, ("IPC$ connections done by user %s\\%s\n",
103 *domain, *username));
106 DEBUG(3, ("IPC$ connections done anonymously\n"));
107 *username = smb_xstrdup("");
108 *domain = smb_xstrdup("");
109 *password = smb_xstrdup("");
113 /* Open a connction to the remote server, cache failures for 30 seconds */
115 static NTSTATUS cm_open_connection(const char *domain, const int pipe_index,
116 struct winbindd_cm_conn *new_conn)
119 char *machine_password;
120 char *machine_krb5_principal, *ipc_username, *ipc_domain, *ipc_password;
121 struct in_addr dc_ip;
127 fstrcpy(new_conn->domain, domain);
128 fstrcpy(new_conn->pipe_name, get_pipe_name_from_index(pipe_index));
130 /* connection failure cache has been moved inside of get_dc_name
131 so we can deal with half dead DC's --jerry */
133 if (!get_dc_name(domain, new_conn->controller, &dc_ip)) {
134 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
135 add_failed_connection_entry(domain, "", result);
139 /* Initialise SMB connection */
141 /* grab stored passwords */
142 machine_password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
144 if (asprintf(&machine_krb5_principal, "%s$@%s", global_myname(), lp_realm()) == -1) {
145 SAFE_FREE(machine_password);
146 return NT_STATUS_NO_MEMORY;
149 cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
151 for (i = 0; retry && (i < 3); i++) {
153 if (!(got_mutex = secrets_named_mutex(new_conn->controller, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
154 DEBUG(0,("cm_open_connection: mutex grab failed for %s\n", new_conn->controller));
155 result = NT_STATUS_POSSIBLE_DEADLOCK;
159 new_conn->cli = NULL;
160 result = cli_start_connection(&new_conn->cli, global_myname(),
161 new_conn->controller,
162 &dc_ip, 0, Undefined,
163 CLI_FULL_CONNECTION_USE_KERBEROS,
166 if (NT_STATUS_IS_OK(result)) {
167 if ((lp_security() == SEC_ADS)
168 && (new_conn->cli->protocol >= PROTOCOL_NT1 && new_conn->cli->capabilities & CAP_EXTENDED_SECURITY)) {
169 new_conn->cli->use_kerberos = True;
170 DEBUG(5, ("connecting to %s from %s with kerberos principal [%s]\n",
171 new_conn->controller, global_myname(), machine_krb5_principal));
173 if (!cli_session_setup_spnego(new_conn->cli, machine_krb5_principal,
176 result = cli_nt_error(new_conn->cli);
177 DEBUG(4,("failed kerberos session setup with %s\n", nt_errstr(result)));
178 if (NT_STATUS_IS_OK(result))
179 result = NT_STATUS_UNSUCCESSFUL;
182 new_conn->cli->use_kerberos = False;
183 if (!NT_STATUS_IS_OK(result)
184 && new_conn->cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
185 DEBUG(5, ("connecting to %s from %s with username [%s]\\[%s]\n",
186 new_conn->controller, global_myname(), ipc_domain, ipc_username));
188 if (!cli_session_setup(new_conn->cli, ipc_username,
189 ipc_password, strlen(ipc_password)+1,
190 ipc_password, strlen(ipc_password)+1,
192 result = cli_nt_error(new_conn->cli);
193 DEBUG(4,("failed kerberos session setup with %s\n", nt_errstr(result)));
194 if (NT_STATUS_IS_OK(result))
195 result = NT_STATUS_UNSUCCESSFUL;
198 if (!NT_STATUS_IS_OK(result)) {
199 if (!cli_session_setup(new_conn->cli, "", NULL, 0,
202 result = cli_nt_error(new_conn->cli);
203 DEBUG(4,("failed kerberos session setup with %s\n", nt_errstr(result)));
204 if (NT_STATUS_IS_OK(result))
205 result = NT_STATUS_UNSUCCESSFUL;
209 if (NT_STATUS_IS_OK(result) && !cli_send_tconX(new_conn->cli, "IPC$", "IPC",
211 result = cli_nt_error(new_conn->cli);
212 DEBUG(1,("failed tcon_X with %s\n", nt_errstr(result)));
213 cli_shutdown(new_conn->cli);
214 if (NT_STATUS_IS_OK(result)) {
215 result = NT_STATUS_UNSUCCESSFUL;
220 if (NT_STATUS_IS_OK(result)) {
221 struct ntuser_creds creds;
222 init_creds(&creds, ipc_username, ipc_domain, ipc_password);
223 cli_init_creds(new_conn->cli, &creds);
227 secrets_named_mutex_release(new_conn->controller);
229 if (NT_STATUS_IS_OK(result))
233 SAFE_FREE(ipc_username);
234 SAFE_FREE(ipc_domain);
235 SAFE_FREE(ipc_password);
236 SAFE_FREE(machine_password);
238 if (!NT_STATUS_IS_OK(result)) {
239 add_failed_connection_entry(domain, new_conn->controller, result);
243 /* set the domain if empty; needed for schannel connections */
244 if ( !*new_conn->cli->domain )
245 fstrcpy( new_conn->cli->domain, domain );
248 if ( !cli_nt_session_open (new_conn->cli, pipe_index) ) {
249 result = NT_STATUS_PIPE_NOT_AVAILABLE;
251 * only cache a failure if we are not trying to open the
252 * **win2k** specific lsarpc UUID. This could be an NT PDC
253 * and therefore a failure is normal. This should probably
254 * be abstracted to a check for 2k specific pipes and wondering
255 * if the PDC is an NT4 box. but since there is only one 2k
256 * specific UUID right now, i'm not going to bother. --jerry
258 if ( !is_win2k_pipe(pipe_index) )
259 add_failed_connection_entry(domain, new_conn->controller, result);
260 cli_shutdown(new_conn->cli);
267 /************************************************************************
268 Wrapper around statuc cm_open_connection to retreive a freshly
269 setup cli_state struct
270 ************************************************************************/
272 NTSTATUS cm_fresh_connection(const char *domain, const int pipe_index,
273 struct cli_state **cli)
276 struct winbindd_cm_conn conn;
278 result = cm_open_connection( domain, pipe_index, &conn );
280 if ( NT_STATUS_IS_OK(result) )
286 /* Return true if a connection is still alive */
288 static BOOL connection_ok(struct winbindd_cm_conn *conn)
291 smb_panic("Invalid parameter passed to connection_ok(): conn was NULL!\n");
296 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n",
297 conn->controller, conn->domain, conn->pipe_name));
301 if (!conn->cli->initialised) {
302 DEBUG(3, ("Connection to %s for domain %s (pipe %s) was never initialised!\n",
303 conn->controller, conn->domain, conn->pipe_name));
307 if (conn->cli->fd == -1) {
308 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n",
309 conn->controller, conn->domain, conn->pipe_name));
316 /* Search the cache for a connection. If there is a broken one,
317 shut it down properly and return NULL. */
319 static void find_cm_connection(const char *domain, const char *pipe_name,
320 struct winbindd_cm_conn **conn_out)
322 struct winbindd_cm_conn *conn;
324 for (conn = cm_conns; conn; ) {
325 if (strequal(conn->domain, domain) &&
326 strequal(conn->pipe_name, pipe_name)) {
327 if (!connection_ok(conn)) {
328 /* Dead connection - remove it. */
329 struct winbindd_cm_conn *conn_temp = conn->next;
331 cli_shutdown(conn->cli);
332 DLIST_REMOVE(cm_conns, conn);
334 conn = conn_temp; /* Keep the loop moving */
346 /* Initialize a new connection up to the RPC BIND. */
348 static NTSTATUS new_cm_connection(const char *domain, const char *pipe_name,
349 struct winbindd_cm_conn **conn_out)
351 struct winbindd_cm_conn *conn;
354 if (!(conn = malloc(sizeof(*conn))))
355 return NT_STATUS_NO_MEMORY;
359 if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, get_pipe_index(pipe_name), conn))) {
360 DEBUG(3, ("Could not open a connection to %s for %s (%s)\n",
361 domain, pipe_name, nt_errstr(result)));
365 DLIST_ADD(cm_conns, conn);
371 /* Get a connection to the remote DC and open the pipe. If there is already a connection, use that */
373 static NTSTATUS get_connection_from_cache(const char *domain, const char *pipe_name,
374 struct winbindd_cm_conn **conn_out)
376 find_cm_connection(domain, pipe_name, conn_out);
378 if (*conn_out != NULL)
381 return new_cm_connection(domain, pipe_name, conn_out);
384 /**********************************************************************************
385 **********************************************************************************/
387 BOOL cm_check_for_native_mode_win2k( const char *domain )
390 struct winbindd_cm_conn conn;
398 if ( !NT_STATUS_IS_OK(result = cm_open_connection(domain, PI_LSARPC_DS, &conn)) ) {
399 DEBUG(5, ("cm_check_for_native_mode_win2k: Could not open a connection to %s for PIPE_LSARPC (%s)\n",
400 domain, nt_errstr(result)));
405 if ( !NT_STATUS_IS_OK(cli_ds_getprimarydominfo( conn.cli,
406 conn.cli->mem_ctx, DsRolePrimaryDomainInfoBasic, &ctr)) ) {
412 if ( (ctr.basic->flags & DSROLE_PRIMARY_DS_RUNNING)
413 && !(ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE) )
418 /* close the connection; no other cals use this pipe and it is called only
419 on reestablishing the domain list --jerry */
422 cli_shutdown( conn.cli );
429 /* Return a LSA policy handle on a domain */
431 NTSTATUS cm_get_lsa_handle(const char *domain, CLI_POLICY_HND **return_hnd)
433 struct winbindd_cm_conn *conn;
434 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
436 static CLI_POLICY_HND hnd;
438 /* Look for existing connections */
440 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn)))
443 /* This *shitty* code needs scrapping ! JRA */
445 if (policy_handle_is_valid(&conn->pol)) {
453 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
454 des_access, &conn->pol);
456 if (!NT_STATUS_IS_OK(result)) {
457 /* Hit the cache code again. This cleans out the old connection and gets a new one */
458 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
459 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn)))
462 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
463 des_access, &conn->pol);
466 if (!NT_STATUS_IS_OK(result)) {
467 cli_shutdown(conn->cli);
468 DLIST_REMOVE(cm_conns, conn);
482 /* Return a SAM policy handle on a domain */
484 NTSTATUS cm_get_sam_handle(char *domain, CLI_POLICY_HND **return_hnd)
486 struct winbindd_cm_conn *conn;
487 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
489 static CLI_POLICY_HND hnd;
491 /* Look for existing connections */
493 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
496 /* This *shitty* code needs scrapping ! JRA */
498 if (policy_handle_is_valid(&conn->pol)) {
507 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
508 des_access, &conn->pol);
510 if (!NT_STATUS_IS_OK(result)) {
511 /* Hit the cache code again. This cleans out the old connection and gets a new one */
512 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
514 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
517 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
518 des_access, &conn->pol);
521 if (!NT_STATUS_IS_OK(result)) {
523 cli_shutdown(conn->cli);
524 DLIST_REMOVE(cm_conns, conn);
539 /* Get a handle on a netlogon pipe. This is a bit of a hack to re-use the
540 netlogon pipe as no handle is returned. */
542 NTSTATUS cm_get_netlogon_cli(const char *domain,
543 const unsigned char *trust_passwd,
544 uint32 sec_channel_type,
546 struct cli_state **cli)
548 NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
549 struct winbindd_cm_conn *conn;
554 return NT_STATUS_INVALID_PARAMETER;
556 /* Open an initial conection - keep the mutex. */
558 find_cm_connection(domain, PIPE_NETLOGON, &conn);
560 if ( fresh && (conn != NULL) ) {
561 cli_shutdown(conn->cli);
566 /* purge connection from cache */
567 find_cm_connection(domain, PIPE_NETLOGON, &conn);
569 DEBUG(0,("Could not purge connection\n"));
570 return NT_STATUS_UNSUCCESSFUL;
579 result = new_cm_connection(domain, PIPE_NETLOGON, &conn);
581 if (!NT_STATUS_IS_OK(result))
584 fstr_sprintf(lock_name, "NETLOGON\\%s", conn->controller);
586 if (!(got_mutex = secrets_named_mutex(lock_name, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
587 DEBUG(0,("cm_get_netlogon_cli: mutex grab failed for %s\n", conn->controller));
590 if ( sec_channel_type == SEC_CHAN_DOMAIN )
591 fstr_sprintf(conn->cli->mach_acct, "%s$", lp_workgroup());
593 result = cli_nt_establish_netlogon(conn->cli, sec_channel_type, trust_passwd);
596 secrets_named_mutex_release(lock_name);
598 if (!NT_STATUS_IS_OK(result)) {
599 cli_shutdown(conn->cli);
600 DLIST_REMOVE(cm_conns, conn);
610 /* Dump the current connection status */
612 static void dump_conn_list(void)
614 struct winbindd_cm_conn *con;
616 DEBUG(0, ("\tDomain Controller Pipe\n"));
618 for(con = cm_conns; con; con = con->next) {
621 /* Display pipe info */
623 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
624 DEBUG(0, ("Error: not enough memory!\n"));
626 DEBUG(0, ("%s\n", msg));
632 void winbindd_cm_status(void)
634 /* List open connections */
636 DEBUG(0, ("winbindd connection manager status:\n"));
641 DEBUG(0, ("\tNo active connections\n"));
644 /* Close all cached connections */
646 void winbindd_cm_flush(void)
648 struct winbindd_cm_conn *conn, tmp;
650 /* Flush connection cache */
652 for (conn = cm_conns; conn; conn = conn->next) {
654 if (!connection_ok(conn))
657 DEBUG(10, ("Closing connection to %s on %s\n",
658 conn->pipe_name, conn->controller));
661 cli_shutdown(conn->cli);
663 tmp.next = conn->next;
665 DLIST_REMOVE(cm_conns, conn);
670 /* Flush failed connection cache */
672 flush_negative_conn_cache();