r4231: commiting changes to 3.0.10
[Samba.git] / source / nsswitch / winbindd_cm.c
blob139efec2141da4230e05f0b3749d6ae10afbf461
1 /*
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
32 selection etc
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
44 implementation. -tpot
49 TODO:
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
55 various sam handles.
59 #include "includes.h"
60 #include "winbindd.h"
62 #undef DBGC_CLASS
63 #define DBGC_CLASS DBGC_WINBIND
65 /* Global list of connections. Initially a DLIST but can become a hash
66 table or whatever later. */
68 struct winbindd_cm_conn {
69 struct winbindd_cm_conn *prev, *next;
70 fstring domain;
71 fstring controller;
72 fstring pipe_name;
73 struct cli_state *cli;
74 POLICY_HND pol;
77 static struct winbindd_cm_conn *cm_conns = NULL;
79 static NTSTATUS get_connection_from_cache(struct winbindd_domain *domain,
80 const char *pipe_name,
81 struct winbindd_cm_conn **conn_out);
83 /* Choose between anonymous or authenticated connections. We need to use
84 an authenticated connection if DCs have the RestrictAnonymous registry
85 entry set > 0, or the "Additional restrictions for anonymous
86 connections" set in the win2k Local Security Policy.
88 Caller to free() result in domain, username, password
91 static void cm_get_ipc_userpass(char **username, char **domain, char **password)
93 *username = secrets_fetch(SECRETS_AUTH_USER, NULL);
94 *domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
95 *password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
97 if (*username && **username) {
99 if (!*domain || !**domain)
100 *domain = smb_xstrdup(lp_workgroup());
102 if (!*password || !**password)
103 *password = smb_xstrdup("");
105 DEBUG(3, ("IPC$ connections done by user %s\\%s\n",
106 *domain, *username));
108 } else {
109 DEBUG(3, ("IPC$ connections done anonymously\n"));
110 *username = smb_xstrdup("");
111 *domain = smb_xstrdup("");
112 *password = smb_xstrdup("");
117 setup for schannel on any pipes opened on this connection
119 static NTSTATUS setup_schannel( struct cli_state *cli, const char *domain )
121 NTSTATUS ret;
122 uchar trust_password[16];
123 uint32 sec_channel_type;
124 DOM_SID sid;
125 time_t lct;
127 /* use the domain trust password if we're on a DC
128 and this is not our domain */
130 if ( IS_DC && !strequal(domain, lp_workgroup()) ) {
131 char *pass = NULL;
133 if ( !secrets_fetch_trusted_domain_password( domain,
134 &pass, &sid, &lct) )
136 return NT_STATUS_UNSUCCESSFUL;
139 sec_channel_type = SEC_CHAN_DOMAIN;
140 E_md4hash(pass, trust_password);
141 SAFE_FREE( pass );
143 } else {
144 if (!secrets_fetch_trust_account_password(lp_workgroup(),
145 trust_password, NULL, &sec_channel_type))
147 return NT_STATUS_UNSUCCESSFUL;
151 ret = cli_nt_setup_netsec(cli, sec_channel_type,
152 AUTH_PIPE_NETSEC | AUTH_PIPE_SIGN, trust_password);
154 return ret;
157 static BOOL get_dc_name_via_netlogon(const struct winbindd_domain *domain,
158 fstring dcname, struct in_addr *dc_ip)
160 struct winbindd_domain *our_domain;
161 NTSTATUS result;
162 struct winbindd_cm_conn *conn;
163 TALLOC_CTX *mem_ctx;
165 fstring tmp;
166 char *p;
168 if (IS_DC)
169 return False;
171 if (domain->primary)
172 return False;
174 if ((our_domain = find_our_domain()) == NULL)
175 return False;
177 result = get_connection_from_cache(our_domain, PIPE_NETLOGON, &conn);
178 if (!NT_STATUS_IS_OK(result))
179 return False;
181 if ((mem_ctx = talloc_init("get_dc_name_via_netlogon")) == NULL)
182 return False;
184 result = cli_netlogon_getdcname(conn->cli, mem_ctx, domain->name, tmp);
186 talloc_destroy(mem_ctx);
188 if (!NT_STATUS_IS_OK(result))
189 return False;
191 /* cli_netlogon_getdcname gives us a name with \\ */
192 p = tmp;
193 if (*p == '\\') p+=1;
194 if (*p == '\\') p+=1;
196 fstrcpy(dcname, p);
198 if (!resolve_name(dcname, dc_ip, 0x20))
199 return False;
201 return True;
204 /* Open a connction to the remote server, cache failures for 30 seconds */
206 static NTSTATUS cm_open_connection(const struct winbindd_domain *domain, const int pipe_index,
207 struct winbindd_cm_conn *new_conn)
209 NTSTATUS result;
210 char *machine_password;
211 char *machine_krb5_principal, *ipc_username, *ipc_domain, *ipc_password;
212 struct in_addr dc_ip;
213 int i;
214 BOOL retry = True;
216 ZERO_STRUCT(dc_ip);
218 fstrcpy(new_conn->domain, domain->name);
220 if (!get_dc_name_via_netlogon(domain, new_conn->controller, &dc_ip)) {
222 /* connection failure cache has been moved inside of
223 get_dc_name so we can deal with half dead DC's --jerry */
225 if (!get_dc_name(domain->name, domain->alt_name[0] ?
226 domain->alt_name : NULL,
227 new_conn->controller, &dc_ip)) {
228 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
229 add_failed_connection_entry(domain->name, "", result);
230 return result;
234 /* Initialise SMB connection */
235 fstrcpy(new_conn->pipe_name, get_pipe_name_from_index(pipe_index));
237 /* grab stored passwords */
239 machine_password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
241 if (asprintf(&machine_krb5_principal, "%s$@%s", global_myname(), lp_realm()) == -1) {
242 SAFE_FREE(machine_password);
243 return NT_STATUS_NO_MEMORY;
246 cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
248 for (i = 0; retry && (i < 3); i++) {
249 BOOL got_mutex;
250 if (!(got_mutex = secrets_named_mutex(new_conn->controller, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
251 DEBUG(0,("cm_open_connection: mutex grab failed for %s\n", new_conn->controller));
252 result = NT_STATUS_POSSIBLE_DEADLOCK;
253 continue;
256 new_conn->cli = NULL;
257 result = cli_start_connection(&new_conn->cli, global_myname(),
258 new_conn->controller,
259 &dc_ip, 0, Undefined,
260 CLI_FULL_CONNECTION_USE_KERBEROS,
261 &retry);
263 if (NT_STATUS_IS_OK(result)) {
265 /* reset the error code */
266 result = NT_STATUS_UNSUCCESSFUL;
268 /* Krb5 session */
270 if ((lp_security() == SEC_ADS)
271 && (new_conn->cli->protocol >= PROTOCOL_NT1 && new_conn->cli->capabilities & CAP_EXTENDED_SECURITY)) {
272 ADS_STATUS ads_status;
273 new_conn->cli->use_kerberos = True;
274 DEBUG(5, ("connecting to %s from %s with kerberos principal [%s]\n",
275 new_conn->controller, global_myname(), machine_krb5_principal));
277 ads_status = cli_session_setup_spnego(new_conn->cli, machine_krb5_principal,
278 machine_password,
279 lp_workgroup());
280 if (!ADS_ERR_OK(ads_status)) {
281 DEBUG(4,("failed kerberos session setup with %s\n", ads_errstr(ads_status)));
282 result = ads_ntstatus(ads_status);
283 } else {
284 result = NT_STATUS_OK;
287 new_conn->cli->use_kerberos = False;
289 /* only do this is we have a username/password for thr IPC$ connection */
291 if ( !NT_STATUS_IS_OK(result)
292 && new_conn->cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE
293 && strlen(ipc_username) )
295 DEBUG(5, ("connecting to %s from %s with username [%s]\\[%s]\n",
296 new_conn->controller, global_myname(), ipc_domain, ipc_username));
298 result = NT_STATUS_OK;
300 if (!cli_session_setup(new_conn->cli, ipc_username,
301 ipc_password, strlen(ipc_password)+1,
302 ipc_password, strlen(ipc_password)+1,
303 ipc_domain)) {
304 result = cli_nt_error(new_conn->cli);
305 DEBUG(4,("failed authenticated session setup with %s\n", nt_errstr(result)));
306 if (NT_STATUS_IS_OK(result))
307 result = NT_STATUS_UNSUCCESSFUL;
311 /* anonymous is all that is left if we get to here */
313 if (!NT_STATUS_IS_OK(result)) {
315 DEBUG(5, ("anonymous connection attempt to %s from %s\n",
316 new_conn->controller, global_myname()));
318 result = NT_STATUS_OK;
320 if (!cli_session_setup(new_conn->cli, "", NULL, 0, NULL, 0, ""))
322 result = cli_nt_error(new_conn->cli);
323 DEBUG(4,("failed anonymous session setup with %s\n", nt_errstr(result)));
324 if (NT_STATUS_IS_OK(result))
325 result = NT_STATUS_UNSUCCESSFUL;
330 if (NT_STATUS_IS_OK(result) && !cli_send_tconX(new_conn->cli, "IPC$", "IPC",
331 "", 0)) {
332 result = cli_nt_error(new_conn->cli);
333 DEBUG(1,("failed tcon_X with %s\n", nt_errstr(result)));
334 cli_shutdown(new_conn->cli);
335 if (NT_STATUS_IS_OK(result)) {
336 result = NT_STATUS_UNSUCCESSFUL;
341 if (NT_STATUS_IS_OK(result)) {
342 struct ntuser_creds creds;
343 init_creds(&creds, ipc_username, ipc_domain, ipc_password);
344 cli_init_creds(new_conn->cli, &creds);
347 if (got_mutex)
348 secrets_named_mutex_release(new_conn->controller);
350 if (NT_STATUS_IS_OK(result))
351 break;
354 /* try and use schannel if possible, but continue anyway if it
355 failed. This allows existing setups to continue working,
356 while solving the win2003 '100 user' limit for systems that
357 are joined properly.
359 Only do this for our own domain or perhaps a trusted domain
360 if we are on a Samba DC */
362 if (NT_STATUS_IS_OK(result) && (domain->primary || IS_DC) ) {
363 NTSTATUS status = setup_schannel( new_conn->cli, domain->name );
364 if (!NT_STATUS_IS_OK(status)) {
365 DEBUG(3,("schannel refused - continuing without schannel (%s)\n",
366 nt_errstr(status)));
370 SAFE_FREE(ipc_username);
371 SAFE_FREE(ipc_domain);
372 SAFE_FREE(ipc_password);
373 SAFE_FREE(machine_password);
374 SAFE_FREE(machine_krb5_principal);
376 if (!NT_STATUS_IS_OK(result)) {
377 add_failed_connection_entry(domain->name, new_conn->controller, result);
378 return result;
381 /* set the domain if empty; needed for schannel connections */
382 if ( !*new_conn->cli->domain )
383 fstrcpy( new_conn->cli->domain, domain->name );
386 if ( !cli_nt_session_open (new_conn->cli, pipe_index) ) {
387 result = NT_STATUS_PIPE_NOT_AVAILABLE;
389 * only cache a failure if we are not trying to open the
390 * **win2k** specific lsarpc UUID. This could be an NT PDC
391 * and therefore a failure is normal. This should probably
392 * be abstracted to a check for 2k specific pipes and wondering
393 * if the PDC is an NT4 box. but since there is only one 2k
394 * specific UUID right now, i'm not going to bother. --jerry
396 if ( !is_win2k_pipe(pipe_index) )
397 add_failed_connection_entry(domain->name, new_conn->controller, result);
398 cli_shutdown(new_conn->cli);
399 return result;
402 return NT_STATUS_OK;
405 /************************************************************************
406 Wrapper around statuc cm_open_connection to retreive a freshly
407 setup cli_state struct
408 ************************************************************************/
410 NTSTATUS cm_fresh_connection(struct winbindd_domain *domain, const int pipe_index,
411 struct cli_state **cli)
413 NTSTATUS result;
414 struct winbindd_cm_conn conn;
416 result = cm_open_connection( domain, pipe_index, &conn );
418 if ( NT_STATUS_IS_OK(result) )
419 *cli = conn.cli;
421 return result;
424 /* Return true if a connection is still alive */
426 static BOOL connection_ok(struct winbindd_cm_conn *conn)
428 if (!conn) {
429 smb_panic("Invalid parameter passed to connection_ok(): conn was NULL!\n");
430 return False;
433 if (!conn->cli) {
434 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n",
435 conn->controller, conn->domain, conn->pipe_name));
436 return False;
439 if (!conn->cli->initialised) {
440 DEBUG(3, ("Connection to %s for domain %s (pipe %s) was never initialised!\n",
441 conn->controller, conn->domain, conn->pipe_name));
442 return False;
445 if (conn->cli->fd == -1) {
446 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n",
447 conn->controller, conn->domain, conn->pipe_name));
448 return False;
451 return True;
454 /* Search the cache for a connection. If there is a broken one,
455 shut it down properly and return NULL. */
457 static void find_cm_connection(struct winbindd_domain *domain, const char *pipe_name,
458 struct winbindd_cm_conn **conn_out)
460 struct winbindd_cm_conn *conn;
462 for (conn = cm_conns; conn; ) {
463 if (strequal(conn->domain, domain->name) &&
464 strequal(conn->pipe_name, pipe_name)) {
465 if (!connection_ok(conn)) {
466 /* Dead connection - remove it. */
467 struct winbindd_cm_conn *conn_temp = conn->next;
468 if (conn->cli)
469 cli_shutdown(conn->cli);
470 DLIST_REMOVE(cm_conns, conn);
471 SAFE_FREE(conn);
472 conn = conn_temp; /* Keep the loop moving */
473 continue;
474 } else {
475 break;
478 conn = conn->next;
481 *conn_out = conn;
484 /* Initialize a new connection up to the RPC BIND. */
486 static NTSTATUS new_cm_connection(struct winbindd_domain *domain, const char *pipe_name,
487 struct winbindd_cm_conn **conn_out)
489 struct winbindd_cm_conn *conn;
490 NTSTATUS result;
492 if (!(conn = SMB_MALLOC_P(struct winbindd_cm_conn)))
493 return NT_STATUS_NO_MEMORY;
495 ZERO_STRUCTP(conn);
497 if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, get_pipe_index(pipe_name), conn))) {
498 DEBUG(3, ("Could not open a connection to %s for %s (%s)\n",
499 domain->name, pipe_name, nt_errstr(result)));
500 SAFE_FREE(conn);
501 return result;
503 DLIST_ADD(cm_conns, conn);
505 *conn_out = conn;
506 return NT_STATUS_OK;
509 /* Get a connection to the remote DC and open the pipe. If there is already a connection, use that */
511 static NTSTATUS get_connection_from_cache(struct winbindd_domain *domain, const char *pipe_name,
512 struct winbindd_cm_conn **conn_out)
514 find_cm_connection(domain, pipe_name, conn_out);
516 if (*conn_out != NULL)
517 return NT_STATUS_OK;
519 return new_cm_connection(domain, pipe_name, conn_out);
522 /**********************************************************************************
523 We can 'sense' certain things about the DC by it's replies to certain questions.
525 This tells us if this particular remote server is Active Directory, and if it is
526 native mode.
527 **********************************************************************************/
529 void set_dc_type_and_flags( struct winbindd_domain *domain )
531 NTSTATUS result;
532 struct winbindd_cm_conn conn;
533 DS_DOMINFO_CTR ctr;
534 TALLOC_CTX *mem_ctx = NULL;
536 ZERO_STRUCT( conn );
537 ZERO_STRUCT( ctr );
539 domain->native_mode = False;
540 domain->active_directory = False;
542 if (domain->internal) {
543 domain->initialized = True;
544 return;
547 if ( !NT_STATUS_IS_OK(result = cm_open_connection(domain, PI_LSARPC_DS, &conn)) ) {
548 DEBUG(5, ("set_dc_type_and_flags: Could not open a connection to %s for PIPE_LSARPC (%s)\n",
549 domain->name, nt_errstr(result)));
550 domain->initialized = True;
551 return;
554 if ( conn.cli ) {
555 if ( !NT_STATUS_IS_OK(cli_ds_getprimarydominfo( conn.cli,
556 conn.cli->mem_ctx, DsRolePrimaryDomainInfoBasic, &ctr)) ) {
557 goto done;
561 if ( (ctr.basic->flags & DSROLE_PRIMARY_DS_RUNNING)
562 && !(ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE) )
563 domain->native_mode = True;
565 /* Cheat - shut down the DS pipe, and open LSA */
567 cli_nt_session_close(conn.cli);
569 if ( cli_nt_session_open (conn.cli, PI_LSARPC) ) {
570 char *domain_name = NULL;
571 char *dns_name = NULL;
572 DOM_SID *dom_sid = NULL;
574 mem_ctx = talloc_init("set_dc_type_and_flags on domain %s\n", domain->name);
575 if (!mem_ctx) {
576 DEBUG(1, ("set_dc_type_and_flags: talloc_init() failed\n"));
577 return;
580 result = cli_lsa_open_policy2(conn.cli, mem_ctx, True,
581 SEC_RIGHTS_MAXIMUM_ALLOWED,
582 &conn.pol);
584 if (NT_STATUS_IS_OK(result)) {
585 /* This particular query is exactly what Win2k clients use
586 to determine that the DC is active directory */
587 result = cli_lsa_query_info_policy2(conn.cli, mem_ctx,
588 &conn.pol,
589 12, &domain_name,
590 &dns_name, NULL,
591 NULL, &dom_sid);
594 if (NT_STATUS_IS_OK(result)) {
595 if (domain_name)
596 fstrcpy(domain->name, domain_name);
598 if (dns_name)
599 fstrcpy(domain->alt_name, dns_name);
601 if (dom_sid)
602 sid_copy(&domain->sid, dom_sid);
604 domain->active_directory = True;
605 } else {
607 result = cli_lsa_open_policy(conn.cli, mem_ctx, True,
608 SEC_RIGHTS_MAXIMUM_ALLOWED,
609 &conn.pol);
611 if (!NT_STATUS_IS_OK(result))
612 goto done;
614 result = cli_lsa_query_info_policy(conn.cli, mem_ctx,
615 &conn.pol, 5, &domain_name,
616 &dom_sid);
618 if (NT_STATUS_IS_OK(result)) {
619 if (domain_name)
620 fstrcpy(domain->name, domain_name);
622 if (dom_sid)
623 sid_copy(&domain->sid, dom_sid);
628 done:
630 /* close the connection; no other calls use this pipe and it is called only
631 on reestablishing the domain list --jerry */
633 if ( conn.cli )
634 cli_shutdown( conn.cli );
636 talloc_destroy(mem_ctx);
638 domain->initialized = True;
640 return;
645 /* Return a LSA policy handle on a domain */
647 NTSTATUS cm_get_lsa_handle(struct winbindd_domain *domain, CLI_POLICY_HND **return_hnd)
649 struct winbindd_cm_conn *conn;
650 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
651 NTSTATUS result;
652 static CLI_POLICY_HND hnd;
654 /* Look for existing connections */
656 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn)))
657 return result;
659 /* This *shitty* code needs scrapping ! JRA */
661 if (policy_handle_is_valid(&conn->pol)) {
662 hnd.pol = conn->pol;
663 hnd.cli = conn->cli;
664 *return_hnd = &hnd;
666 return NT_STATUS_OK;
669 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
670 des_access, &conn->pol);
672 if (!NT_STATUS_IS_OK(result)) {
673 /* Hit the cache code again. This cleans out the old connection and gets a new one */
674 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
675 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn)))
676 return result;
678 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
679 des_access, &conn->pol);
682 if (!NT_STATUS_IS_OK(result)) {
683 cli_shutdown(conn->cli);
684 DLIST_REMOVE(cm_conns, conn);
685 SAFE_FREE(conn);
686 return result;
690 hnd.pol = conn->pol;
691 hnd.cli = conn->cli;
693 *return_hnd = &hnd;
695 return NT_STATUS_OK;
698 /* Return a SAM policy handle on a domain */
700 NTSTATUS cm_get_sam_handle(struct winbindd_domain *domain, CLI_POLICY_HND **return_hnd)
702 struct winbindd_cm_conn *conn;
703 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
704 NTSTATUS result;
705 static CLI_POLICY_HND hnd;
707 /* Look for existing connections */
709 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
710 return result;
712 /* This *shitty* code needs scrapping ! JRA */
714 if (policy_handle_is_valid(&conn->pol)) {
715 hnd.pol = conn->pol;
716 hnd.cli = conn->cli;
718 *return_hnd = &hnd;
720 return NT_STATUS_OK;
723 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
724 des_access, &conn->pol);
726 if (!NT_STATUS_IS_OK(result)) {
727 /* Hit the cache code again. This cleans out the old connection and gets a new one */
728 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
730 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
731 return result;
733 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
734 des_access, &conn->pol);
737 if (!NT_STATUS_IS_OK(result)) {
739 cli_shutdown(conn->cli);
740 DLIST_REMOVE(cm_conns, conn);
741 SAFE_FREE(conn);
743 return result;
747 hnd.pol = conn->pol;
748 hnd.cli = conn->cli;
750 *return_hnd = &hnd;
752 return NT_STATUS_OK;
755 /* Get a handle on a netlogon pipe. This is a bit of a hack to re-use the
756 netlogon pipe as no handle is returned. */
758 NTSTATUS cm_get_netlogon_cli(struct winbindd_domain *domain,
759 const unsigned char *trust_passwd,
760 uint32 sec_channel_type,
761 BOOL fresh,
762 struct cli_state **cli)
764 NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
765 struct winbindd_cm_conn *conn;
766 fstring lock_name;
767 BOOL got_mutex;
769 if (!cli)
770 return NT_STATUS_INVALID_PARAMETER;
772 /* Open an initial conection - keep the mutex. */
774 find_cm_connection(domain, PIPE_NETLOGON, &conn);
776 if ( fresh && (conn != NULL) ) {
777 cli_shutdown(conn->cli);
778 conn->cli = NULL;
780 conn = NULL;
782 /* purge connection from cache */
783 find_cm_connection(domain, PIPE_NETLOGON, &conn);
784 if (conn != NULL) {
785 DEBUG(0,("Could not purge connection\n"));
786 return NT_STATUS_UNSUCCESSFUL;
790 if (conn != NULL) {
791 *cli = conn->cli;
792 return NT_STATUS_OK;
795 result = new_cm_connection(domain, PIPE_NETLOGON, &conn);
797 if (!NT_STATUS_IS_OK(result))
798 return result;
800 fstr_sprintf(lock_name, "NETLOGON\\%s", conn->controller);
802 if (!(got_mutex = secrets_named_mutex(lock_name, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
803 DEBUG(0,("cm_get_netlogon_cli: mutex grab failed for %s\n", conn->controller));
806 if ( sec_channel_type == SEC_CHAN_DOMAIN )
807 fstr_sprintf(conn->cli->mach_acct, "%s$", lp_workgroup());
809 /* This must be the remote domain (not ours) for schannel */
811 fstrcpy( conn->cli->domain, domain->name);
813 result = cli_nt_establish_netlogon(conn->cli, sec_channel_type, trust_passwd);
815 if (got_mutex)
816 secrets_named_mutex_release(lock_name);
818 if (!NT_STATUS_IS_OK(result)) {
819 cli_shutdown(conn->cli);
820 DLIST_REMOVE(cm_conns, conn);
821 SAFE_FREE(conn);
822 return result;
825 *cli = conn->cli;
827 return result;
830 /* Dump the current connection status */
832 static void dump_conn_list(void)
834 struct winbindd_cm_conn *con;
836 DEBUG(0, ("\tDomain Controller Pipe\n"));
838 for(con = cm_conns; con; con = con->next) {
839 char *msg;
841 /* Display pipe info */
843 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
844 DEBUG(0, ("Error: not enough memory!\n"));
845 } else {
846 DEBUG(0, ("%s\n", msg));
847 SAFE_FREE(msg);
852 void winbindd_cm_status(void)
854 /* List open connections */
856 DEBUG(0, ("winbindd connection manager status:\n"));
858 if (cm_conns)
859 dump_conn_list();
860 else
861 DEBUG(0, ("\tNo active connections\n"));
864 /* Close all cached connections */
866 void winbindd_cm_flush(void)
868 struct winbindd_cm_conn *conn, tmp;
870 /* Flush connection cache */
872 for (conn = cm_conns; conn; conn = conn->next) {
874 if (!connection_ok(conn))
875 continue;
877 DEBUG(10, ("Closing connection to %s on %s\n",
878 conn->pipe_name, conn->controller));
880 if (conn->cli)
881 cli_shutdown(conn->cli);
883 tmp.next = conn->next;
885 DLIST_REMOVE(cm_conns, conn);
886 SAFE_FREE(conn);
887 conn = &tmp;
890 /* Flush failed connection cache */
892 flush_negative_conn_cache();