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
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;
73 struct cli_state *cli;
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));
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 )
122 uchar trust_password[16];
123 uint32 sec_channel_type;
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()) ) {
133 if ( !secrets_fetch_trusted_domain_password( domain,
136 return NT_STATUS_UNSUCCESSFUL;
139 sec_channel_type = SEC_CHAN_DOMAIN;
140 E_md4hash(pass, trust_password);
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);
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;
162 struct winbindd_cm_conn *conn;
174 if ((our_domain = find_our_domain()) == NULL)
177 result = get_connection_from_cache(our_domain, PIPE_NETLOGON, &conn);
178 if (!NT_STATUS_IS_OK(result))
181 if ((mem_ctx = talloc_init("get_dc_name_via_netlogon")) == NULL)
184 result = cli_netlogon_getdcname(conn->cli, mem_ctx, domain->name, tmp);
186 talloc_destroy(mem_ctx);
188 if (!NT_STATUS_IS_OK(result))
191 /* cli_netlogon_getdcname gives us a name with \\ */
193 if (*p == '\\') p+=1;
194 if (*p == '\\') p+=1;
198 if (!resolve_name(dcname, dc_ip, 0x20))
204 /************************************************************************
205 Given a fd with a just-connected TCP connection to a DC, open a connection
207 ************************************************************************/
209 static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
211 const int pipe_index,
212 const char *controller,
213 struct cli_state **cli,
216 char *machine_password, *machine_krb5_principal;
217 char *ipc_username, *ipc_domain, *ipc_password;
218 struct ntuser_creds creds;
221 BOOL add_failed_connection = True;
223 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
225 struct sockaddr peeraddr;
226 socklen_t peeraddr_len;
228 struct sockaddr_in *peeraddr_in = (struct sockaddr_in *)&peeraddr;
230 machine_password = secrets_fetch_machine_password(lp_workgroup(), NULL,
233 if (asprintf(&machine_krb5_principal, "%s$@%s", global_myname(),
235 SAFE_FREE(machine_password);
236 return NT_STATUS_NO_MEMORY;
239 cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
243 got_mutex = secrets_named_mutex(controller,
244 WINBIND_SERVER_MUTEX_WAIT_TIME);
247 DEBUG(0,("cm_open_connection: mutex grab failed for %s\n",
249 result = NT_STATUS_POSSIBLE_DEADLOCK;
253 if ((*cli = cli_initialise(NULL)) == NULL) {
254 DEBUG(1, ("Could not cli_initialize\n"));
255 result = NT_STATUS_NO_MEMORY;
259 (*cli)->timeout = 10000; /* 10 seconds */
261 fstrcpy((*cli)->desthost, controller);
262 (*cli)->use_kerberos = True;
264 peeraddr_len = sizeof(peeraddr);
266 if ((getpeername((*cli)->fd, &peeraddr, &peeraddr_len) != 0) ||
267 (peeraddr_len != sizeof(struct sockaddr_in)) ||
268 (peeraddr_in->sin_family != PF_INET))
270 DEBUG(0,("cm_prepare_connection: %s\n", strerror(errno)));
274 if (ntohs(peeraddr_in->sin_port) == 139) {
275 struct nmb_name calling;
276 struct nmb_name called;
278 make_nmb_name(&calling, global_myname(), 0x0);
279 make_nmb_name(&called, "*SMBSERVER", 0x20);
281 if (!cli_session_request(*cli, &calling, &called)) {
282 DEBUG(8, ("cli_session_request failed for %s\n",
288 cli_setup_signing_state(*cli, Undefined);
290 if (!cli_negprot(*cli)) {
291 DEBUG(1, ("cli_negprot failed\n"));
298 if ((lp_security() == SEC_ADS)
299 && ((*cli)->protocol >= PROTOCOL_NT1 &&
300 (*cli)->capabilities & CAP_EXTENDED_SECURITY)) {
302 ADS_STATUS ads_status;
303 (*cli)->use_kerberos = True;
304 DEBUG(5, ("connecting to %s from %s with kerberos principal "
305 "[%s]\n", controller, global_myname(),
306 machine_krb5_principal));
308 ads_status = cli_session_setup_spnego(*cli,
309 machine_krb5_principal,
313 if (!ADS_ERR_OK(ads_status))
314 DEBUG(4,("failed kerberos session setup with %s\n",
315 ads_errstr(ads_status)));
317 result = ads_ntstatus(ads_status);
320 if (NT_STATUS_IS_OK(result))
321 goto session_setup_done;
323 /* Fall back to non-kerberos session setup */
325 (*cli)->use_kerberos = False;
327 if ((((*cli)->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) != 0) &&
328 (strlen(ipc_username) > 0)) {
330 /* Only try authenticated if we have a username */
332 DEBUG(5, ("connecting to %s from %s with username "
333 "[%s]\\[%s]\n", controller, global_myname(),
334 ipc_domain, ipc_username));
336 if (cli_session_setup(*cli, ipc_username,
337 ipc_password, strlen(ipc_password)+1,
338 ipc_password, strlen(ipc_password)+1,
340 DEBUG(5, ("authenticated session setup failed\n"));
341 goto session_setup_done;
345 /* Fall back to anonymous connection, this might fail later */
347 if (cli_session_setup(*cli, "", NULL, 0, NULL, 0, "")) {
348 DEBUG(5, ("Connected anonymously\n"));
349 goto session_setup_done;
352 result = cli_nt_error(*cli);
354 if (NT_STATUS_IS_OK(result))
355 result = NT_STATUS_UNSUCCESSFUL;
357 /* We can't session setup */
363 if (!cli_send_tconX(*cli, "IPC$", "IPC", "", 0)) {
365 result = cli_nt_error(*cli);
367 DEBUG(1,("failed tcon_X with %s\n", nt_errstr(result)));
369 if (NT_STATUS_IS_OK(result))
370 result = NT_STATUS_UNSUCCESSFUL;
376 init_creds(&creds, ipc_username, ipc_domain, ipc_password);
377 cli_init_creds(*cli, &creds);
379 secrets_named_mutex_release(controller);
383 if (domain->primary || IS_DC) {
384 NTSTATUS status = setup_schannel( *cli, domain->name );
385 if (!NT_STATUS_IS_OK(status)) {
386 DEBUG(3,("schannel refused - continuing without "
387 "schannel (%s)\n", nt_errstr(status)));
391 /* set the domain if empty; needed for schannel connections */
392 if ( !*(*cli)->domain )
393 fstrcpy( (*cli)->domain, domain->name );
395 if ( !cli_nt_session_open (*cli, pipe_index) ) {
397 result = NT_STATUS_PIPE_NOT_AVAILABLE;
399 /* This might be a NT4 DC */
400 if ( is_win2k_pipe(pipe_index) )
401 add_failed_connection = False;
407 result = NT_STATUS_OK;
408 add_failed_connection = False;
412 secrets_named_mutex_release(controller);
414 SAFE_FREE(machine_password);
415 SAFE_FREE(machine_krb5_principal);
416 SAFE_FREE(ipc_username);
417 SAFE_FREE(ipc_domain);
418 SAFE_FREE(ipc_password);
420 if (add_failed_connection)
421 add_failed_connection_entry(domain->name, controller, result);
431 static BOOL add_one_dc_unique(TALLOC_CTX *mem_ctx, const char *domain_name,
432 const char *dcname, struct in_addr ip,
433 struct dc_name_ip **dcs, int *num)
435 if (!NT_STATUS_IS_OK(check_negative_conn_cache(domain_name, dcname)))
438 *dcs = TALLOC_REALLOC_ARRAY(mem_ctx, *dcs, struct dc_name_ip, (*num)+1);
443 fstrcpy((*dcs)[*num].name, dcname);
444 (*dcs)[*num].ip = ip;
449 static BOOL add_sockaddr_to_array(TALLOC_CTX *mem_ctx,
450 struct in_addr ip, uint16 port,
451 struct sockaddr_in **addrs, int *num)
453 *addrs = TALLOC_REALLOC_ARRAY(mem_ctx, *addrs, struct sockaddr_in, (*num)+1);
458 (*addrs)[*num].sin_family = PF_INET;
459 putip((char *)&((*addrs)[*num].sin_addr), (char *)&ip);
460 (*addrs)[*num].sin_port = htons(port);
466 static BOOL get_dcs_1c(TALLOC_CTX *mem_ctx,
467 const struct winbindd_domain *domain,
468 struct dc_name_ip **dcs, int *num_dcs)
470 struct ip_service *iplist = NULL;
473 if (!internal_resolve_name(domain->name, 0x1c, &iplist, &num,
474 lp_name_resolve_order()))
477 /* Now try to find the server names of at least one IP address, hosts
478 * not replying are cached as such */
480 for (i=0; i<num; i++) {
484 if (!name_status_find(domain->name, 0x1c, 0x20, iplist[i].ip,
488 if (add_one_dc_unique(mem_ctx, domain->name, dcname,
489 iplist[i].ip, dcs, num_dcs)) {
490 /* One DC responded, so we assume that he will also
501 static BOOL get_dcs(TALLOC_CTX *mem_ctx, const struct winbindd_domain *domain,
502 struct dc_name_ip **dcs, int *num_dcs)
510 is_our_domain = strequal(domain->name, lp_workgroup());
512 if (!is_our_domain && get_dc_name_via_netlogon(domain, dcname, &ip) &&
513 add_one_dc_unique(mem_ctx, domain->name, dcname, ip, dcs, num_dcs))
516 if (!is_our_domain) {
517 /* NETLOGON to our own domain could not give us a DC name
518 * (which is an error), fall back to looking up domain#1c */
519 return get_dcs_1c(mem_ctx, domain, dcs, num_dcs);
522 if (must_use_pdc(domain->name) && get_pdc_ip(domain->name, &ip)) {
524 if (!name_status_find(domain->name, 0x1b, 0x20, ip, dcname))
527 if (add_one_dc_unique(mem_ctx, domain->name,
528 dcname, ip, dcs, num_dcs))
532 p = lp_passwordserver();
535 return get_dcs_1c(mem_ctx, domain, dcs, num_dcs);
537 while (next_token(&p, dcname, LIST_SEP, sizeof(dcname))) {
539 if (strequal(dcname, "*")) {
540 get_dcs_1c(mem_ctx, domain, dcs, num_dcs);
544 if (!resolve_name(dcname, &ip, 0x20))
547 /* Even if we got the dcname, double check the name to use for
548 * the netlogon auth2 */
550 if (!name_status_find(domain->name, 0x1c, 0x20, ip, dcname))
553 add_one_dc_unique(mem_ctx, domain->name, dcname, ip,
560 static BOOL find_new_dc(TALLOC_CTX *mem_ctx,
561 const struct winbindd_domain *domain,
562 fstring dcname, struct sockaddr_in *addr, int *fd)
564 struct dc_name_ip *dcs = NULL;
567 char **dcnames = NULL;
570 struct sockaddr_in *addrs = NULL;
575 if (!get_dcs(mem_ctx, domain, &dcs, &num_dcs) || (num_dcs == 0))
578 for (i=0; i<num_dcs; i++) {
580 add_string_to_array(mem_ctx, dcs[i].name,
581 &dcnames, &num_dcnames);
582 add_sockaddr_to_array(mem_ctx, dcs[i].ip, 445,
585 add_string_to_array(mem_ctx, dcs[i].name,
586 &dcnames, &num_dcnames);
587 add_sockaddr_to_array(mem_ctx, dcs[i].ip, 139,
591 if ((num_dcnames == 0) || (num_dcnames != num_addrs))
594 if (!open_any_socket_out(addrs, num_addrs, 10000, &fd_index, fd)) {
595 for (i=0; i<num_dcs; i++) {
596 add_failed_connection_entry(domain->name,
598 NT_STATUS_UNSUCCESSFUL);
603 fstrcpy(dcname, dcnames[fd_index]);
604 *addr = addrs[fd_index];
609 static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
610 const int pipe_index,
611 struct winbindd_cm_conn *new_conn)
618 if ((mem_ctx = talloc_init("cm_open_connection")) == NULL)
619 return NT_STATUS_NO_MEMORY;
621 for (retries = 0; retries < 3; retries++) {
626 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
628 if ((strlen(domain->dcname) > 0) &&
629 NT_STATUS_IS_OK(check_negative_conn_cache(domain->name,
632 if (!open_any_socket_out(&domain->dcaddr, 1, 10000,
639 !find_new_dc(mem_ctx, domain, domain->dcname,
640 &domain->dcaddr, &fd))
643 new_conn->cli = NULL;
645 result = cm_prepare_connection(domain, fd, pipe_index,
647 &new_conn->cli, &retry);
649 if (NT_STATUS_IS_OK(result)) {
650 fstrcpy(new_conn->domain, domain->name);
651 /* Initialise SMB connection */
652 fstrcpy(new_conn->pipe_name,
653 get_pipe_name_from_index(pipe_index));
661 talloc_destroy(mem_ctx);
665 /************************************************************************
666 Wrapper around statuc cm_open_connection to retreive a freshly
667 setup cli_state struct
668 ************************************************************************/
670 NTSTATUS cm_fresh_connection(struct winbindd_domain *domain, const int pipe_index,
671 struct cli_state **cli)
674 struct winbindd_cm_conn conn;
676 result = cm_open_connection( domain, pipe_index, &conn );
678 if ( NT_STATUS_IS_OK(result) )
684 /* Return true if a connection is still alive */
686 static BOOL connection_ok(struct winbindd_cm_conn *conn)
689 smb_panic("Invalid parameter passed to connection_ok(): conn was NULL!\n");
694 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n",
695 conn->controller, conn->domain, conn->pipe_name));
699 if (!conn->cli->initialised) {
700 DEBUG(3, ("Connection to %s for domain %s (pipe %s) was never initialised!\n",
701 conn->controller, conn->domain, conn->pipe_name));
705 if (conn->cli->fd == -1) {
706 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n",
707 conn->controller, conn->domain, conn->pipe_name));
714 /* Search the cache for a connection. If there is a broken one,
715 shut it down properly and return NULL. */
717 static void find_cm_connection(struct winbindd_domain *domain, const char *pipe_name,
718 struct winbindd_cm_conn **conn_out)
720 struct winbindd_cm_conn *conn;
722 for (conn = cm_conns; conn; ) {
723 if (strequal(conn->domain, domain->name) &&
724 strequal(conn->pipe_name, pipe_name)) {
725 if (!connection_ok(conn)) {
726 /* Dead connection - remove it. */
727 struct winbindd_cm_conn *conn_temp = conn->next;
729 cli_shutdown(conn->cli);
730 DLIST_REMOVE(cm_conns, conn);
732 conn = conn_temp; /* Keep the loop moving */
744 /* Initialize a new connection up to the RPC BIND. */
746 static NTSTATUS new_cm_connection(struct winbindd_domain *domain, const char *pipe_name,
747 struct winbindd_cm_conn **conn_out)
749 struct winbindd_cm_conn *conn;
752 if (!(conn = SMB_MALLOC_P(struct winbindd_cm_conn)))
753 return NT_STATUS_NO_MEMORY;
757 if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, get_pipe_index(pipe_name), conn))) {
758 DEBUG(3, ("Could not open a connection to %s for %s (%s)\n",
759 domain->name, pipe_name, nt_errstr(result)));
763 DLIST_ADD(cm_conns, conn);
769 /* Get a connection to the remote DC and open the pipe. If there is already a connection, use that */
771 static NTSTATUS get_connection_from_cache(struct winbindd_domain *domain, const char *pipe_name,
772 struct winbindd_cm_conn **conn_out)
774 find_cm_connection(domain, pipe_name, conn_out);
776 if (*conn_out != NULL)
779 return new_cm_connection(domain, pipe_name, conn_out);
782 /**********************************************************************************
783 We can 'sense' certain things about the DC by it's replies to certain questions.
785 This tells us if this particular remote server is Active Directory, and if it is
787 **********************************************************************************/
789 void set_dc_type_and_flags( struct winbindd_domain *domain )
792 struct winbindd_cm_conn conn;
794 TALLOC_CTX *mem_ctx = NULL;
799 domain->native_mode = False;
800 domain->active_directory = False;
802 if (domain->internal) {
803 domain->initialized = True;
807 if ( !NT_STATUS_IS_OK(result = cm_open_connection(domain, PI_LSARPC_DS, &conn)) ) {
808 DEBUG(5, ("set_dc_type_and_flags: Could not open a connection to %s for PIPE_LSARPC (%s)\n",
809 domain->name, nt_errstr(result)));
810 domain->initialized = True;
815 if ( !NT_STATUS_IS_OK(cli_ds_getprimarydominfo( conn.cli,
816 conn.cli->mem_ctx, DsRolePrimaryDomainInfoBasic, &ctr)) ) {
821 if ( (ctr.basic->flags & DSROLE_PRIMARY_DS_RUNNING)
822 && !(ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE) )
823 domain->native_mode = True;
825 /* Cheat - shut down the DS pipe, and open LSA */
827 cli_nt_session_close(conn.cli);
829 if ( cli_nt_session_open (conn.cli, PI_LSARPC) ) {
830 char *domain_name = NULL;
831 char *dns_name = NULL;
832 DOM_SID *dom_sid = NULL;
834 mem_ctx = talloc_init("set_dc_type_and_flags on domain %s\n", domain->name);
836 DEBUG(1, ("set_dc_type_and_flags: talloc_init() failed\n"));
840 result = cli_lsa_open_policy2(conn.cli, mem_ctx, True,
841 SEC_RIGHTS_MAXIMUM_ALLOWED,
844 if (NT_STATUS_IS_OK(result)) {
845 /* This particular query is exactly what Win2k clients use
846 to determine that the DC is active directory */
847 result = cli_lsa_query_info_policy2(conn.cli, mem_ctx,
854 if (NT_STATUS_IS_OK(result)) {
856 fstrcpy(domain->name, domain_name);
859 fstrcpy(domain->alt_name, dns_name);
862 sid_copy(&domain->sid, dom_sid);
864 domain->active_directory = True;
867 result = cli_lsa_open_policy(conn.cli, mem_ctx, True,
868 SEC_RIGHTS_MAXIMUM_ALLOWED,
871 if (!NT_STATUS_IS_OK(result))
874 result = cli_lsa_query_info_policy(conn.cli, mem_ctx,
875 &conn.pol, 5, &domain_name,
878 if (NT_STATUS_IS_OK(result)) {
880 fstrcpy(domain->name, domain_name);
883 sid_copy(&domain->sid, dom_sid);
890 /* close the connection; no other calls use this pipe and it is called only
891 on reestablishing the domain list --jerry */
894 cli_shutdown( conn.cli );
896 talloc_destroy(mem_ctx);
898 domain->initialized = True;
905 /* Return a LSA policy handle on a domain */
907 NTSTATUS cm_get_lsa_handle(struct winbindd_domain *domain, CLI_POLICY_HND **return_hnd)
909 struct winbindd_cm_conn *conn;
910 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
912 static CLI_POLICY_HND hnd;
914 /* Look for existing connections */
916 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn)))
919 /* This *shitty* code needs scrapping ! JRA */
921 if (policy_handle_is_valid(&conn->pol)) {
929 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
930 des_access, &conn->pol);
932 if (!NT_STATUS_IS_OK(result)) {
933 /* Hit the cache code again. This cleans out the old connection and gets a new one */
934 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
935 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn)))
938 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
939 des_access, &conn->pol);
942 if (!NT_STATUS_IS_OK(result)) {
943 cli_shutdown(conn->cli);
944 DLIST_REMOVE(cm_conns, conn);
958 /* Return a SAM policy handle on a domain */
960 NTSTATUS cm_get_sam_handle(struct winbindd_domain *domain, CLI_POLICY_HND **return_hnd)
962 struct winbindd_cm_conn *conn;
963 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
965 static CLI_POLICY_HND hnd;
967 /* Look for existing connections */
969 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
972 /* This *shitty* code needs scrapping ! JRA */
974 if (policy_handle_is_valid(&conn->pol)) {
983 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
984 des_access, &conn->pol);
986 if (!NT_STATUS_IS_OK(result)) {
987 /* Hit the cache code again. This cleans out the old connection and gets a new one */
988 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
990 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
993 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
994 des_access, &conn->pol);
997 if (!NT_STATUS_IS_OK(result)) {
999 cli_shutdown(conn->cli);
1000 DLIST_REMOVE(cm_conns, conn);
1007 hnd.pol = conn->pol;
1008 hnd.cli = conn->cli;
1012 return NT_STATUS_OK;
1015 /* Get a handle on a netlogon pipe. This is a bit of a hack to re-use the
1016 netlogon pipe as no handle is returned. */
1018 NTSTATUS cm_get_netlogon_cli(struct winbindd_domain *domain,
1019 const unsigned char *trust_passwd,
1020 uint32 sec_channel_type,
1022 struct cli_state **cli)
1024 NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
1025 struct winbindd_cm_conn *conn;
1030 return NT_STATUS_INVALID_PARAMETER;
1032 /* Open an initial conection - keep the mutex. */
1034 find_cm_connection(domain, PIPE_NETLOGON, &conn);
1036 if ( fresh && (conn != NULL) ) {
1037 cli_shutdown(conn->cli);
1042 /* purge connection from cache */
1043 find_cm_connection(domain, PIPE_NETLOGON, &conn);
1045 DEBUG(0,("Could not purge connection\n"));
1046 return NT_STATUS_UNSUCCESSFUL;
1052 return NT_STATUS_OK;
1055 result = new_cm_connection(domain, PIPE_NETLOGON, &conn);
1057 if (!NT_STATUS_IS_OK(result))
1060 fstr_sprintf(lock_name, "NETLOGON\\%s", conn->controller);
1062 if (!(got_mutex = secrets_named_mutex(lock_name, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
1063 DEBUG(0,("cm_get_netlogon_cli: mutex grab failed for %s\n", conn->controller));
1066 if ( sec_channel_type == SEC_CHAN_DOMAIN )
1067 fstr_sprintf(conn->cli->mach_acct, "%s$", lp_workgroup());
1069 /* This must be the remote domain (not ours) for schannel */
1071 fstrcpy( conn->cli->domain, domain->name);
1073 result = cli_nt_establish_netlogon(conn->cli, sec_channel_type, trust_passwd);
1076 secrets_named_mutex_release(lock_name);
1078 if (!NT_STATUS_IS_OK(result)) {
1079 cli_shutdown(conn->cli);
1080 DLIST_REMOVE(cm_conns, conn);
1090 /* Dump the current connection status */
1092 static void dump_conn_list(void)
1094 struct winbindd_cm_conn *con;
1096 DEBUG(0, ("\tDomain Controller Pipe\n"));
1098 for(con = cm_conns; con; con = con->next) {
1101 /* Display pipe info */
1103 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
1104 DEBUG(0, ("Error: not enough memory!\n"));
1106 DEBUG(0, ("%s\n", msg));
1112 void winbindd_cm_status(void)
1114 /* List open connections */
1116 DEBUG(0, ("winbindd connection manager status:\n"));
1121 DEBUG(0, ("\tNo active connections\n"));
1124 /* Close all cached connections */
1126 void winbindd_cm_flush(void)
1128 struct winbindd_cm_conn *conn, tmp;
1130 /* Flush connection cache */
1132 for (conn = cm_conns; conn; conn = conn->next) {
1134 if (!connection_ok(conn))
1137 DEBUG(10, ("Closing connection to %s on %s\n",
1138 conn->pipe_name, conn->controller));
1141 cli_shutdown(conn->cli);
1143 tmp.next = conn->next;
1145 DLIST_REMOVE(cm_conns, conn);
1150 /* Flush failed connection cache */
1152 flush_negative_conn_cache();