Fixed up mutex protection around winbindd logon code. Sync with APP-HEAD.
[Samba/gbeck.git] / source3 / nsswitch / winbindd_cm.c
blob54096c0c1d8a712357cf16e7105162a6ab8a740f
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 - There needs to be a utility function in libsmb/namequery.c that does
55 cm_get_dc_name()
57 - Take care when destroying cli_structs as they can be shared between
58 various sam handles.
62 #include "winbindd.h"
64 #undef DBGC_CLASS
65 #define DBGC_CLASS DBGC_WINBIND
67 /* Global list of connections. Initially a DLIST but can become a hash
68 table or whatever later. */
70 struct winbindd_cm_conn {
71 struct winbindd_cm_conn *prev, *next;
72 fstring domain;
73 fstring controller;
74 fstring pipe_name;
75 size_t mutex_ref_count;
76 struct cli_state *cli;
77 POLICY_HND pol;
80 static struct winbindd_cm_conn *cm_conns = NULL;
82 /* Get a domain controller name. Cache positive and negative lookups so we
83 don't go to the network too often when something is badly broken. */
85 #define GET_DC_NAME_CACHE_TIMEOUT 30 /* Seconds between dc lookups */
87 struct get_dc_name_cache {
88 fstring domain_name;
89 fstring srv_name;
90 time_t lookup_time;
91 struct get_dc_name_cache *prev, *next;
95 find the DC for a domain using methods appropriate for a ADS domain
97 static BOOL cm_ads_find_dc(const char *domain, struct in_addr *dc_ip, fstring srv_name)
99 ADS_STRUCT *ads;
100 const char *realm = domain;
102 if (strcasecmp(realm, lp_workgroup()) == 0)
103 realm = lp_realm();
105 ads = ads_init(realm, domain, NULL);
106 if (!ads)
107 return False;
109 /* we don't need to bind, just connect */
110 ads->auth.flags |= ADS_AUTH_NO_BIND;
112 DEBUG(4,("cm_ads_find_dc: domain=%s\n", domain));
114 #ifdef HAVE_ADS
115 /* a full ads_connect() is actually overkill, as we don't srictly need
116 to do the SASL auth in order to get the info we need, but libads
117 doesn't offer a better way right now */
118 ads_connect(ads);
119 #endif
121 if (!ads->config.realm)
122 return False;
124 fstrcpy(srv_name, ads->config.ldap_server_name);
125 strupper(srv_name);
126 *dc_ip = ads->ldap_ip;
127 ads_destroy(&ads);
129 DEBUG(4,("cm_ads_find_dc: using server='%s' IP=%s\n",
130 srv_name, inet_ntoa(*dc_ip)));
132 return True;
137 static BOOL cm_get_dc_name(const char *domain, fstring srv_name, struct in_addr *ip_out)
139 static struct get_dc_name_cache *get_dc_name_cache;
140 struct get_dc_name_cache *dcc;
141 struct in_addr dc_ip;
142 BOOL ret;
144 /* Check the cache for previous lookups */
146 for (dcc = get_dc_name_cache; dcc; dcc = dcc->next) {
148 if (!strequal(domain, dcc->domain_name))
149 continue; /* Not our domain */
151 if ((time(NULL) - dcc->lookup_time) >
152 GET_DC_NAME_CACHE_TIMEOUT) {
154 /* Cache entry has expired, delete it */
156 DEBUG(10, ("get_dc_name_cache entry expired for %s\n", domain));
158 DLIST_REMOVE(get_dc_name_cache, dcc);
159 SAFE_FREE(dcc);
161 break;
164 /* Return a positive or negative lookup for this domain */
166 if (dcc->srv_name[0]) {
167 DEBUG(10, ("returning positive get_dc_name_cache entry for %s\n", domain));
168 fstrcpy(srv_name, dcc->srv_name);
169 return True;
170 } else {
171 DEBUG(10, ("returning negative get_dc_name_cache entry for %s\n", domain));
172 return False;
176 /* Add cache entry for this lookup. */
178 DEBUG(10, ("Creating get_dc_name_cache entry for %s\n", domain));
180 if (!(dcc = (struct get_dc_name_cache *)
181 malloc(sizeof(struct get_dc_name_cache))))
182 return False;
184 ZERO_STRUCTP(dcc);
186 fstrcpy(dcc->domain_name, domain);
187 dcc->lookup_time = time(NULL);
189 DLIST_ADD(get_dc_name_cache, dcc);
191 zero_ip(&dc_ip);
193 ret = False;
194 if (lp_security() == SEC_ADS)
195 ret = cm_ads_find_dc(domain, &dc_ip, srv_name);
197 if (!ret) {
198 /* fall back on rpc methods if the ADS methods fail */
199 ret = rpc_find_dc(domain, srv_name, &dc_ip);
202 if (!ret)
203 return False;
205 /* We have a name so make the cache entry positive now */
206 fstrcpy(dcc->srv_name, srv_name);
208 DEBUG(3, ("cm_get_dc_name: Returning DC %s (%s) for domain %s\n", srv_name,
209 inet_ntoa(dc_ip), domain));
211 *ip_out = dc_ip;
213 return True;
216 /* Choose between anonymous or authenticated connections. We need to use
217 an authenticated connection if DCs have the RestrictAnonymous registry
218 entry set > 0, or the "Additional restrictions for anonymous
219 connections" set in the win2k Local Security Policy.
221 Caller to free() result in domain, username, password
224 static void cm_get_ipc_userpass(char **username, char **domain, char **password)
226 *username = secrets_fetch(SECRETS_AUTH_USER, NULL);
227 *domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
228 *password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
230 if (*username && **username) {
232 if (!*domain || !**domain)
233 *domain = smb_xstrdup(lp_workgroup());
235 if (!*password || !**password)
236 *password = smb_xstrdup("");
238 DEBUG(3, ("IPC$ connections done by user %s\\%s\n",
239 *domain, *username));
241 } else {
242 DEBUG(3, ("IPC$ connections done anonymously\n"));
243 *username = smb_xstrdup("");
244 *domain = smb_xstrdup("");
245 *password = smb_xstrdup("");
249 /* Open a new smb pipe connection to a DC on a given domain. Cache
250 negative creation attempts so we don't try and connect to broken
251 machines too often. */
253 #define FAILED_CONNECTION_CACHE_TIMEOUT 30 /* Seconds between attempts */
255 struct failed_connection_cache {
256 fstring domain_name;
257 fstring controller;
258 time_t lookup_time;
259 NTSTATUS nt_status;
260 struct failed_connection_cache *prev, *next;
263 static struct failed_connection_cache *failed_connection_cache;
265 /* Add an entry to the failed conneciton cache */
267 static void add_failed_connection_entry(struct winbindd_cm_conn *new_conn,
268 NTSTATUS result)
270 struct failed_connection_cache *fcc;
272 SMB_ASSERT(!NT_STATUS_IS_OK(result));
274 /* Check we already aren't in the cache */
276 for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
277 if (strequal(fcc->domain_name, new_conn->domain)) {
278 DEBUG(10, ("domain %s already tried and failed\n",
279 fcc->domain_name));
280 return;
284 /* Create negative lookup cache entry for this domain and controller */
286 if (!(fcc = (struct failed_connection_cache *)
287 malloc(sizeof(struct failed_connection_cache)))) {
288 DEBUG(0, ("malloc failed in add_failed_connection_entry!\n"));
289 return;
292 ZERO_STRUCTP(fcc);
294 fstrcpy(fcc->domain_name, new_conn->domain);
295 fstrcpy(fcc->controller, new_conn->controller);
296 fcc->lookup_time = time(NULL);
297 fcc->nt_status = result;
299 DLIST_ADD(failed_connection_cache, fcc);
302 /* Open a connction to the remote server, cache failures for 30 seconds */
304 static NTSTATUS cm_open_connection(const char *domain, const int pipe_index,
305 struct winbindd_cm_conn *new_conn, BOOL keep_mutex)
307 struct failed_connection_cache *fcc;
308 NTSTATUS result;
309 char *ipc_username, *ipc_domain, *ipc_password;
310 struct in_addr dc_ip;
311 int i;
312 BOOL retry = True;
313 BOOL got_mutex = False;
315 ZERO_STRUCT(dc_ip);
317 fstrcpy(new_conn->domain, domain);
318 fstrcpy(new_conn->pipe_name, get_pipe_name_from_index(pipe_index));
320 /* Look for a domain controller for this domain. Negative results
321 are cached so don't bother applying the caching for this
322 function just yet. */
324 if (!cm_get_dc_name(domain, new_conn->controller, &dc_ip)) {
325 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
326 add_failed_connection_entry(new_conn, result);
327 return result;
330 /* Return false if we have tried to look up this domain and netbios
331 name before and failed. */
333 for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
335 if (!(strequal(domain, fcc->domain_name) &&
336 strequal(new_conn->controller, fcc->controller)))
337 continue; /* Not our domain */
339 if ((time(NULL) - fcc->lookup_time) >
340 FAILED_CONNECTION_CACHE_TIMEOUT) {
342 /* Cache entry has expired, delete it */
344 DEBUG(10, ("cm_open_connection cache entry expired for %s, %s\n", domain, new_conn->controller));
346 DLIST_REMOVE(failed_connection_cache, fcc);
347 free(fcc);
349 break;
352 /* The timeout hasn't expired yet so return false */
354 DEBUG(10, ("returning negative open_connection_cache entry for %s, %s\n", domain, new_conn->controller));
356 result = fcc->nt_status;
357 SMB_ASSERT(!NT_STATUS_IS_OK(result));
358 return result;
361 /* Initialise SMB connection */
363 cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
365 DEBUG(5, ("connecting to %s from %s with username [%s]\\[%s]\n",
366 new_conn->controller, global_myname(), ipc_domain, ipc_username));
368 for (i = 0; retry && (i < 3); i++) {
370 if (!secrets_named_mutex(new_conn->controller, WINBIND_SERVER_MUTEX_WAIT_TIME, &new_conn->mutex_ref_count)) {
371 DEBUG(0,("cm_open_connection: mutex grab failed for %s\n", new_conn->controller));
372 result = NT_STATUS_POSSIBLE_DEADLOCK;
373 continue;
376 got_mutex = True;
378 result = cli_full_connection(&new_conn->cli, global_myname(), new_conn->controller,
379 &dc_ip, 0, "IPC$", "IPC", ipc_username, ipc_domain,
380 ipc_password, 0, &retry);
382 if (NT_STATUS_IS_OK(result))
383 break;
385 secrets_named_mutex_release(new_conn->controller, &new_conn->mutex_ref_count);
386 got_mutex = False;
389 SAFE_FREE(ipc_username);
390 SAFE_FREE(ipc_domain);
391 SAFE_FREE(ipc_password);
393 if (!NT_STATUS_IS_OK(result)) {
394 if (got_mutex)
395 secrets_named_mutex_release(new_conn->controller, &new_conn->mutex_ref_count);
396 add_failed_connection_entry(new_conn, result);
397 return result;
400 if ( !cli_nt_session_open (new_conn->cli, pipe_index) ) {
401 result = NT_STATUS_PIPE_NOT_AVAILABLE;
403 * only cache a failure if we are not trying to open the
404 * **win2k** specific lsarpc UUID. This could be an NT PDC
405 * and therefore a failure is normal. This should probably
406 * be abstracted to a check for 2k specific pipes and wondering
407 * if the PDC is an NT4 box. but since there is only one 2k
408 * specific UUID right now, i'm not going to bother. --jerry
410 if (got_mutex)
411 secrets_named_mutex_release(new_conn->controller, &new_conn->mutex_ref_count);
412 if ( !is_win2k_pipe(pipe_index) )
413 add_failed_connection_entry(new_conn, result);
414 cli_shutdown(new_conn->cli);
415 return result;
418 if ((got_mutex) && !keep_mutex)
419 secrets_named_mutex_release(new_conn->controller, &new_conn->mutex_ref_count);
420 return NT_STATUS_OK;
423 /* Return true if a connection is still alive */
425 static BOOL connection_ok(struct winbindd_cm_conn *conn)
427 if (!conn) {
428 smb_panic("Invalid paramater passed to conneciton_ok(): conn was NULL!\n");
429 return False;
432 if (!conn->cli) {
433 DEBUG(0, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n",
434 conn->controller, conn->domain, conn->pipe_name));
435 smb_panic("connection_ok: conn->cli was null!");
436 return False;
439 if (!conn->cli->initialised) {
440 DEBUG(0, ("Connection to %s for domain %s (pipe %s) was never initialised!\n",
441 conn->controller, conn->domain, conn->pipe_name));
442 smb_panic("connection_ok: conn->cli->initialised is False!");
443 return False;
446 if (conn->cli->fd == -1) {
447 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n",
448 conn->controller, conn->domain, conn->pipe_name));
449 return False;
452 return True;
455 /* Get a connection to the remote DC and open the pipe. If there is already a connection, use that */
457 static NTSTATUS get_connection_from_cache(const char *domain, const char *pipe_name,
458 struct winbindd_cm_conn **conn_out, BOOL keep_mutex)
460 struct winbindd_cm_conn *conn, conn_temp;
461 NTSTATUS result;
463 for (conn = cm_conns; conn; conn = conn->next) {
464 if (strequal(conn->domain, domain) &&
465 strequal(conn->pipe_name, pipe_name)) {
466 if (!connection_ok(conn)) {
467 if (conn->cli)
468 cli_shutdown(conn->cli);
469 ZERO_STRUCT(conn_temp);
470 conn_temp.next = conn->next;
471 DLIST_REMOVE(cm_conns, conn);
472 SAFE_FREE(conn);
473 conn = &conn_temp; /* Just to keep the loop moving */
474 } else {
475 if (keep_mutex) {
476 if (!secrets_named_mutex(conn->controller,
477 WINBIND_SERVER_MUTEX_WAIT_TIME, &conn->mutex_ref_count))
478 DEBUG(0,("get_connection_from_cache: mutex grab failed for %s\n",
479 conn->controller));
481 break;
486 if (!conn) {
487 if (!(conn = malloc(sizeof(*conn))))
488 return NT_STATUS_NO_MEMORY;
490 ZERO_STRUCTP(conn);
492 if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, get_pipe_index(pipe_name), conn, keep_mutex))) {
493 DEBUG(3, ("Could not open a connection to %s for %s (%s)\n",
494 domain, pipe_name, nt_errstr(result)));
495 SAFE_FREE(conn);
496 return result;
498 DLIST_ADD(cm_conns, conn);
501 *conn_out = conn;
502 return NT_STATUS_OK;
506 /**********************************************************************************
507 **********************************************************************************/
509 BOOL cm_check_for_native_mode_win2k( const char *domain )
511 NTSTATUS result;
512 struct winbindd_cm_conn conn;
513 DS_DOMINFO_CTR ctr;
514 BOOL ret = False;
516 ZERO_STRUCT( conn );
517 ZERO_STRUCT( ctr );
520 if ( !NT_STATUS_IS_OK(result = cm_open_connection(domain, PI_LSARPC_DS, &conn, False)) ) {
521 DEBUG(5, ("cm_check_for_native_mode_win2k: Could not open a connection to %s for PIPE_LSARPC (%s)\n",
522 domain, nt_errstr(result)));
523 return False;
526 if ( conn.cli ) {
527 if ( !NT_STATUS_IS_OK(cli_ds_getprimarydominfo( conn.cli,
528 conn.cli->mem_ctx, DsRolePrimaryDomainInfoBasic, &ctr)) ) {
529 ret = False;
530 goto done;
534 if ( (ctr.basic->flags & DSROLE_PRIMARY_DS_RUNNING)
535 && !(ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE) )
536 ret = True;
538 done:
539 if ( conn.cli )
540 cli_shutdown( conn.cli );
542 return ret;
547 /* Return a LSA policy handle on a domain */
549 CLI_POLICY_HND *cm_get_lsa_handle(const char *domain)
551 struct winbindd_cm_conn *conn;
552 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
553 NTSTATUS result;
554 static CLI_POLICY_HND hnd;
556 /* Look for existing connections */
558 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn, False)))
559 return NULL;
561 /* This *shitty* code needs scrapping ! JRA */
562 if (policy_handle_is_valid(&conn->pol)) {
563 hnd.pol = conn->pol;
564 hnd.cli = conn->cli;
565 return &hnd;
568 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
569 des_access, &conn->pol);
571 if (!NT_STATUS_IS_OK(result)) {
572 /* Hit the cache code again. This cleans out the old connection and gets a new one */
573 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
574 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn, False)))
575 return NULL;
577 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
578 des_access, &conn->pol);
581 if (!NT_STATUS_IS_OK(result)) {
582 cli_shutdown(conn->cli);
583 DLIST_REMOVE(cm_conns, conn);
584 SAFE_FREE(conn);
585 return NULL;
589 hnd.pol = conn->pol;
590 hnd.cli = conn->cli;
592 return &hnd;
595 /* Return a SAM policy handle on a domain */
597 CLI_POLICY_HND *cm_get_sam_handle(char *domain)
599 struct winbindd_cm_conn *conn;
600 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
601 NTSTATUS result;
602 static CLI_POLICY_HND hnd;
604 /* Look for existing connections */
606 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn, False)))
607 return NULL;
609 /* This *shitty* code needs scrapping ! JRA */
610 if (policy_handle_is_valid(&conn->pol)) {
611 hnd.pol = conn->pol;
612 hnd.cli = conn->cli;
613 return &hnd;
615 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
616 des_access, &conn->pol);
618 if (!NT_STATUS_IS_OK(result)) {
619 /* Hit the cache code again. This cleans out the old connection and gets a new one */
620 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
621 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn, False)))
622 return NULL;
624 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
625 des_access, &conn->pol);
628 if (!NT_STATUS_IS_OK(result)) {
629 cli_shutdown(conn->cli);
630 DLIST_REMOVE(cm_conns, conn);
631 SAFE_FREE(conn);
632 return NULL;
636 hnd.pol = conn->pol;
637 hnd.cli = conn->cli;
639 return &hnd;
642 #if 0 /* This code now *well* out of date */
644 /* Return a SAM domain policy handle on a domain */
646 CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain, DOM_SID *domain_sid)
648 struct winbindd_cm_conn *conn, *basic_conn = NULL;
649 static CLI_POLICY_HND hnd;
650 NTSTATUS result;
651 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
653 /* Look for existing connections */
655 for (conn = cm_conns; conn; conn = conn->next) {
656 if (strequal(conn->domain, domain) &&
657 strequal(conn->pipe_name, PIPE_SAMR) &&
658 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM) {
660 if (!connection_ok(conn)) {
661 /* Shutdown cli? Free conn? Allow retry of DC? */
662 DLIST_REMOVE(cm_conns, conn);
663 return NULL;
666 goto ok;
670 /* Create a basic handle to open a domain handle from */
672 if (!cm_get_sam_handle(domain))
673 return False;
675 for (conn = cm_conns; conn; conn = conn->next) {
676 if (strequal(conn->domain, domain) &&
677 strequal(conn->pipe_name, PIPE_SAMR) &&
678 conn->pipe_data.samr.pipe_type == SAM_PIPE_BASIC)
679 basic_conn = conn;
682 if (!(conn = (struct winbindd_cm_conn *)
683 malloc(sizeof(struct winbindd_cm_conn))))
684 return NULL;
686 ZERO_STRUCTP(conn);
688 fstrcpy(conn->domain, basic_conn->domain);
689 fstrcpy(conn->controller, basic_conn->controller);
690 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
692 conn->pipe_data.samr.pipe_type = SAM_PIPE_DOM;
693 conn->cli = basic_conn->cli;
695 result = cli_samr_open_domain(conn->cli, conn->cli->mem_ctx,
696 &basic_conn->pol, des_access,
697 domain_sid, &conn->pol);
699 if (!NT_STATUS_IS_OK(result))
700 return NULL;
702 /* Add to list */
704 DLIST_ADD(cm_conns, conn);
707 hnd.pol = conn->pol;
708 hnd.cli = conn->cli;
710 return &hnd;
713 /* Return a SAM policy handle on a domain user */
715 CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, DOM_SID *domain_sid,
716 uint32 user_rid)
718 struct winbindd_cm_conn *conn, *basic_conn = NULL;
719 static CLI_POLICY_HND hnd;
720 NTSTATUS result;
721 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
723 /* Look for existing connections */
725 for (conn = cm_conns; conn; conn = conn->next) {
726 if (strequal(conn->domain, domain) &&
727 strequal(conn->pipe_name, PIPE_SAMR) &&
728 conn->pipe_data.samr.pipe_type == SAM_PIPE_USER &&
729 conn->pipe_data.samr.rid == user_rid) {
731 if (!connection_ok(conn)) {
732 /* Shutdown cli? Free conn? Allow retry of DC? */
733 DLIST_REMOVE(cm_conns, conn);
734 return NULL;
737 goto ok;
741 /* Create a domain handle to open a user handle from */
743 if (!cm_get_sam_dom_handle(domain, domain_sid))
744 return NULL;
746 for (conn = cm_conns; conn; conn = conn->next) {
747 if (strequal(conn->domain, domain) &&
748 strequal(conn->pipe_name, PIPE_SAMR) &&
749 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
750 basic_conn = conn;
753 if (!basic_conn) {
754 DEBUG(0, ("No domain sam handle was created!\n"));
755 return NULL;
758 if (!(conn = (struct winbindd_cm_conn *)
759 malloc(sizeof(struct winbindd_cm_conn))))
760 return NULL;
762 ZERO_STRUCTP(conn);
764 fstrcpy(conn->domain, basic_conn->domain);
765 fstrcpy(conn->controller, basic_conn->controller);
766 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
768 conn->pipe_data.samr.pipe_type = SAM_PIPE_USER;
769 conn->cli = basic_conn->cli;
770 conn->pipe_data.samr.rid = user_rid;
772 result = cli_samr_open_user(conn->cli, conn->cli->mem_ctx,
773 &basic_conn->pol, des_access, user_rid,
774 &conn->pol);
776 if (!NT_STATUS_IS_OK(result))
777 return NULL;
779 /* Add to list */
781 DLIST_ADD(cm_conns, conn);
784 hnd.pol = conn->pol;
785 hnd.cli = conn->cli;
787 return &hnd;
790 /* Return a SAM policy handle on a domain group */
792 CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, DOM_SID *domain_sid,
793 uint32 group_rid)
795 struct winbindd_cm_conn *conn, *basic_conn = NULL;
796 static CLI_POLICY_HND hnd;
797 NTSTATUS result;
798 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
800 /* Look for existing connections */
802 for (conn = cm_conns; conn; conn = conn->next) {
803 if (strequal(conn->domain, domain) &&
804 strequal(conn->pipe_name, PIPE_SAMR) &&
805 conn->pipe_data.samr.pipe_type == SAM_PIPE_GROUP &&
806 conn->pipe_data.samr.rid == group_rid) {
808 if (!connection_ok(conn)) {
809 /* Shutdown cli? Free conn? Allow retry of DC? */
810 DLIST_REMOVE(cm_conns, conn);
811 return NULL;
814 goto ok;
818 /* Create a domain handle to open a user handle from */
820 if (!cm_get_sam_dom_handle(domain, domain_sid))
821 return NULL;
823 for (conn = cm_conns; conn; conn = conn->next) {
824 if (strequal(conn->domain, domain) &&
825 strequal(conn->pipe_name, PIPE_SAMR) &&
826 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
827 basic_conn = conn;
830 if (!basic_conn) {
831 DEBUG(0, ("No domain sam handle was created!\n"));
832 return NULL;
835 if (!(conn = (struct winbindd_cm_conn *)
836 malloc(sizeof(struct winbindd_cm_conn))))
837 return NULL;
839 ZERO_STRUCTP(conn);
841 fstrcpy(conn->domain, basic_conn->domain);
842 fstrcpy(conn->controller, basic_conn->controller);
843 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
845 conn->pipe_data.samr.pipe_type = SAM_PIPE_GROUP;
846 conn->cli = basic_conn->cli;
847 conn->pipe_data.samr.rid = group_rid;
849 result = cli_samr_open_group(conn->cli, conn->cli->mem_ctx,
850 &basic_conn->pol, des_access, group_rid,
851 &conn->pol);
853 if (!NT_STATUS_IS_OK(result))
854 return NULL;
856 /* Add to list */
858 DLIST_ADD(cm_conns, conn);
861 hnd.pol = conn->pol;
862 hnd.cli = conn->cli;
864 return &hnd;
867 #endif
869 /* Get a handle on a netlogon pipe. This is a bit of a hack to re-use the
870 netlogon pipe as no handle is returned. */
872 NTSTATUS cm_get_netlogon_cli(const char *domain, const unsigned char *trust_passwd,
873 struct cli_state **cli)
875 NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
876 struct winbindd_cm_conn *conn;
877 uint32 neg_flags = 0x000001ff;
879 if (!cli)
880 return NT_STATUS_INVALID_PARAMETER;
882 /* Open an initial conection - keep the mutex. */
884 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn, True)))
885 return result;
887 result = cli_nt_setup_creds(conn->cli, get_sec_chan(), trust_passwd, &neg_flags, 2);
889 if (conn->mutex_ref_count)
890 secrets_named_mutex_release(conn->controller, &conn->mutex_ref_count);
892 if (!NT_STATUS_IS_OK(result)) {
893 DEBUG(0, ("error connecting to domain password server: %s\n",
894 nt_errstr(result)));
896 /* Hit the cache code again. This cleans out the old connection and gets a new one */
897 if (conn->cli->fd == -1) {
899 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn, True)))
900 return result;
902 /* Try again */
903 result = cli_nt_setup_creds( conn->cli, get_sec_chan(),trust_passwd, &neg_flags, 2);
905 if (conn->mutex_ref_count)
906 secrets_named_mutex_release(conn->controller, &conn->mutex_ref_count);
909 if (!NT_STATUS_IS_OK(result)) {
910 cli_shutdown(conn->cli);
911 DLIST_REMOVE(cm_conns, conn);
912 SAFE_FREE(conn);
913 return result;
917 *cli = conn->cli;
919 return result;
922 /* Dump the current connection status */
924 static void dump_conn_list(void)
926 struct winbindd_cm_conn *con;
928 DEBUG(0, ("\tDomain Controller Pipe\n"));
930 for(con = cm_conns; con; con = con->next) {
931 char *msg;
933 /* Display pipe info */
935 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
936 DEBUG(0, ("Error: not enough memory!\n"));
937 } else {
938 DEBUG(0, ("%s\n", msg));
939 SAFE_FREE(msg);
944 void winbindd_cm_status(void)
946 /* List open connections */
948 DEBUG(0, ("winbindd connection manager status:\n"));
950 if (cm_conns)
951 dump_conn_list();
952 else
953 DEBUG(0, ("\tNo active connections\n"));