Add mutex protection around auth calls.
[nivanova/samba-autobuild/.git] / source3 / nsswitch / winbindd_cm.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind daemon connection manager
5
6    Copyright (C) Tim Potter 2001
7    Copyright (C) Andrew Bartlett 2002
8    
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.
13    
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.
18    
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.
22 */
23
24 /*
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:
28   
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
35   
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.
42
43    The TNG design is quite good but I disagree with some aspects of the
44    implementation. -tpot
45
46  */
47
48 /*
49    TODO:
50
51      - I'm pretty annoyed by all the make_nmb_name() stuff.  It should be
52        moved down into another function.
53
54      - There needs to be a utility function in libsmb/namequery.c that does
55        cm_get_dc_name() 
56
57      - Take care when destroying cli_structs as they can be shared between
58        various sam handles.
59
60  */
61
62 #include "winbindd.h"
63
64 #undef DBGC_CLASS
65 #define DBGC_CLASS DBGC_WINBIND
66
67 /* Global list of connections.  Initially a DLIST but can become a hash
68    table or whatever later. */
69
70 struct winbindd_cm_conn {
71         struct winbindd_cm_conn *prev, *next;
72         fstring domain;
73         fstring controller;
74         fstring pipe_name;
75         struct cli_state *cli;
76         POLICY_HND pol;
77 };
78
79 static struct winbindd_cm_conn *cm_conns = NULL;
80
81 /* Get a domain controller name.  Cache positive and negative lookups so we
82    don't go to the network too often when something is badly broken. */
83
84 #define GET_DC_NAME_CACHE_TIMEOUT 30 /* Seconds between dc lookups */
85
86 struct get_dc_name_cache {
87         fstring domain_name;
88         fstring srv_name;
89         time_t lookup_time;
90         struct get_dc_name_cache *prev, *next;
91 };
92
93 /*
94   find the DC for a domain using methods appropriate for a ADS domain
95 */
96 static BOOL cm_ads_find_dc(const char *domain, struct in_addr *dc_ip, fstring srv_name)
97 {
98         ADS_STRUCT *ads;
99         const char *realm = domain;
100
101         if (strcasecmp(realm, lp_workgroup()) == 0)
102                 realm = lp_realm();
103
104         ads = ads_init(realm, domain, NULL);
105         if (!ads)
106                 return False;
107
108         /* we don't need to bind, just connect */
109         ads->auth.flags |= ADS_AUTH_NO_BIND;
110
111         DEBUG(4,("cm_ads_find_dc: domain=%s\n", domain));
112
113 #ifdef HAVE_ADS
114         /* a full ads_connect() is actually overkill, as we don't srictly need
115            to do the SASL auth in order to get the info we need, but libads
116            doesn't offer a better way right now */
117         ads_connect(ads);
118 #endif
119
120         if (!ads->config.realm)
121                 return False;
122
123         fstrcpy(srv_name, ads->config.ldap_server_name);
124         strupper(srv_name);
125         *dc_ip = ads->ldap_ip;
126         ads_destroy(&ads);
127         
128         DEBUG(4,("cm_ads_find_dc: using server='%s' IP=%s\n",
129                  srv_name, inet_ntoa(*dc_ip)));
130         
131         return True;
132 }
133
134
135
136 static BOOL cm_get_dc_name(const char *domain, fstring srv_name, struct in_addr *ip_out)
137 {
138         static struct get_dc_name_cache *get_dc_name_cache;
139         struct get_dc_name_cache *dcc;
140         struct in_addr dc_ip;
141         BOOL ret;
142
143         /* Check the cache for previous lookups */
144
145         for (dcc = get_dc_name_cache; dcc; dcc = dcc->next) {
146
147                 if (!strequal(domain, dcc->domain_name))
148                         continue; /* Not our domain */
149
150                 if ((time(NULL) - dcc->lookup_time) > 
151                     GET_DC_NAME_CACHE_TIMEOUT) {
152
153                         /* Cache entry has expired, delete it */
154
155                         DEBUG(10, ("get_dc_name_cache entry expired for %s\n", domain));
156
157                         DLIST_REMOVE(get_dc_name_cache, dcc);
158                         SAFE_FREE(dcc);
159
160                         break;
161                 }
162
163                 /* Return a positive or negative lookup for this domain */
164
165                 if (dcc->srv_name[0]) {
166                         DEBUG(10, ("returning positive get_dc_name_cache entry for %s\n", domain));
167                         fstrcpy(srv_name, dcc->srv_name);
168                         return True;
169                 } else {
170                         DEBUG(10, ("returning negative get_dc_name_cache entry for %s\n", domain));
171                         return False;
172                 }
173         }
174
175         /* Add cache entry for this lookup. */
176
177         DEBUG(10, ("Creating get_dc_name_cache entry for %s\n", domain));
178
179         if (!(dcc = (struct get_dc_name_cache *) 
180               malloc(sizeof(struct get_dc_name_cache))))
181                 return False;
182
183         ZERO_STRUCTP(dcc);
184
185         fstrcpy(dcc->domain_name, domain);
186         dcc->lookup_time = time(NULL);
187
188         DLIST_ADD(get_dc_name_cache, dcc);
189
190         zero_ip(&dc_ip);
191
192         ret = False;
193         if (lp_security() == SEC_ADS)
194                 ret = cm_ads_find_dc(domain, &dc_ip, srv_name);
195
196         if (!ret) {
197                 /* fall back on rpc methods if the ADS methods fail */
198                 ret = rpc_find_dc(domain, srv_name, &dc_ip);
199         }
200
201         if (!ret)
202                 return False;
203
204         /* We have a name so make the cache entry positive now */
205         fstrcpy(dcc->srv_name, srv_name);
206
207         DEBUG(3, ("cm_get_dc_name: Returning DC %s (%s) for domain %s\n", srv_name,
208                   inet_ntoa(dc_ip), domain));
209
210         *ip_out = dc_ip;
211
212         return True;
213 }
214
215 /* Choose between anonymous or authenticated connections.  We need to use
216    an authenticated connection if DCs have the RestrictAnonymous registry
217    entry set > 0, or the "Additional restrictions for anonymous
218    connections" set in the win2k Local Security Policy. 
219    
220    Caller to free() result in domain, username, password
221 */
222
223 static void cm_get_ipc_userpass(char **username, char **domain, char **password)
224 {
225         *username = secrets_fetch(SECRETS_AUTH_USER, NULL);
226         *domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
227         *password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
228         
229         if (*username && **username) {
230
231                 if (!*domain || !**domain)
232                         *domain = smb_xstrdup(lp_workgroup());
233                 
234                 if (!*password || !**password)
235                         *password = smb_xstrdup("");
236
237                 DEBUG(3, ("IPC$ connections done by user %s\\%s\n", 
238                           *domain, *username));
239
240         } else {
241                 DEBUG(3, ("IPC$ connections done anonymously\n"));
242                 *username = smb_xstrdup("");
243                 *domain = smb_xstrdup("");
244                 *password = smb_xstrdup("");
245         }
246 }
247
248 /* Open a new smb pipe connection to a DC on a given domain.  Cache
249    negative creation attempts so we don't try and connect to broken
250    machines too often. */
251
252 #define FAILED_CONNECTION_CACHE_TIMEOUT 30 /* Seconds between attempts */
253
254 struct failed_connection_cache {
255         fstring domain_name;
256         fstring controller;
257         time_t lookup_time;
258         NTSTATUS nt_status;
259         struct failed_connection_cache *prev, *next;
260 };
261
262 static struct failed_connection_cache *failed_connection_cache;
263
264 /* Add an entry to the failed conneciton cache */
265
266 static void add_failed_connection_entry(struct winbindd_cm_conn *new_conn, 
267                                         NTSTATUS result) 
268 {
269         struct failed_connection_cache *fcc;
270
271         SMB_ASSERT(!NT_STATUS_IS_OK(result));
272
273         /* Check we already aren't in the cache */
274
275         for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
276                 if (strequal(fcc->domain_name, new_conn->domain)) {
277                         DEBUG(10, ("domain %s already tried and failed\n",
278                                    fcc->domain_name));
279                         return;
280                 }
281         }
282
283         /* Create negative lookup cache entry for this domain and controller */
284
285         if (!(fcc = (struct failed_connection_cache *)
286               malloc(sizeof(struct failed_connection_cache)))) {
287                 DEBUG(0, ("malloc failed in add_failed_connection_entry!\n"));
288                 return;
289         }
290         
291         ZERO_STRUCTP(fcc);
292         
293         fstrcpy(fcc->domain_name, new_conn->domain);
294         fstrcpy(fcc->controller, new_conn->controller);
295         fcc->lookup_time = time(NULL);
296         fcc->nt_status = result;
297         
298         DLIST_ADD(failed_connection_cache, fcc);
299 }
300         
301 /* Open a connction to the remote server, cache failures for 30 seconds */
302
303 static NTSTATUS cm_open_connection(const char *domain, const int pipe_index,
304                                struct winbindd_cm_conn *new_conn)
305 {
306         struct failed_connection_cache *fcc;
307         NTSTATUS result;
308         char *ipc_username, *ipc_domain, *ipc_password;
309         struct in_addr dc_ip;
310         int i;
311         BOOL retry = True;
312
313         ZERO_STRUCT(dc_ip);
314
315         fstrcpy(new_conn->domain, domain);
316         fstrcpy(new_conn->pipe_name, get_pipe_name_from_index(pipe_index));
317         
318         /* Look for a domain controller for this domain.  Negative results
319            are cached so don't bother applying the caching for this
320            function just yet.  */
321
322         if (!cm_get_dc_name(domain, new_conn->controller, &dc_ip)) {
323                 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
324                 add_failed_connection_entry(new_conn, result);
325                 return result;
326         }
327                 
328         /* Return false if we have tried to look up this domain and netbios
329            name before and failed. */
330
331         for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
332                 
333                 if (!(strequal(domain, fcc->domain_name) &&
334                       strequal(new_conn->controller, fcc->controller)))
335                         continue; /* Not our domain */
336
337                 if ((time(NULL) - fcc->lookup_time) > 
338                     FAILED_CONNECTION_CACHE_TIMEOUT) {
339
340                         /* Cache entry has expired, delete it */
341
342                         DEBUG(10, ("cm_open_connection cache entry expired for %s, %s\n", domain, new_conn->controller));
343
344                         DLIST_REMOVE(failed_connection_cache, fcc);
345                         free(fcc);
346
347                         break;
348                 }
349
350                 /* The timeout hasn't expired yet so return false */
351
352                 DEBUG(10, ("returning negative open_connection_cache entry for %s, %s\n", domain, new_conn->controller));
353
354                 result = fcc->nt_status;
355                 SMB_ASSERT(!NT_STATUS_IS_OK(result));
356                 return result;
357         }
358
359         /* Initialise SMB connection */
360
361         cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
362
363         DEBUG(5, ("connecting to %s from %s with username [%s]\\[%s]\n", 
364               new_conn->controller, global_myname(), ipc_domain, ipc_username));
365
366         for (i = 0; retry && (i < 3); i++) {
367                 
368                 result = cli_full_connection(&new_conn->cli, global_myname(), new_conn->controller, 
369                         &dc_ip, 0, "IPC$", "IPC", ipc_username, ipc_domain, 
370                         ipc_password, 0, &retry);
371
372                 if (NT_STATUS_IS_OK(result))
373                         break;
374         }
375
376         SAFE_FREE(ipc_username);
377         SAFE_FREE(ipc_domain);
378         SAFE_FREE(ipc_password);
379
380         if (!NT_STATUS_IS_OK(result)) {
381                 add_failed_connection_entry(new_conn, result);
382                 return result;
383         }
384         
385         if ( !cli_nt_session_open (new_conn->cli, pipe_index) ) {
386                 result = NT_STATUS_PIPE_NOT_AVAILABLE;
387                 /* 
388                  * only cache a failure if we are not trying to open the 
389                  * **win2k** specific lsarpc UUID.  This could be an NT PDC 
390                  * and therefore a failure is normal.  This should probably
391                  * be abstracted to a check for 2k specific pipes and wondering
392                  * if the PDC is an NT4 box.   but since there is only one 2k 
393                  * specific UUID right now, i'm not going to bother.  --jerry
394                  */
395                 if ( !is_win2k_pipe(pipe_index) )
396                         add_failed_connection_entry(new_conn, result);
397                 cli_shutdown(new_conn->cli);
398                 return result;
399         }
400
401         return NT_STATUS_OK;
402 }
403
404 /* Return true if a connection is still alive */
405
406 static BOOL connection_ok(struct winbindd_cm_conn *conn)
407 {
408         if (!conn) {
409                 smb_panic("Invalid paramater passed to conneciton_ok():  conn was NULL!\n");
410                 return False;
411         }
412
413         if (!conn->cli) {
414                 DEBUG(0, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n", 
415                           conn->controller, conn->domain, conn->pipe_name));
416                 smb_panic("connection_ok: conn->cli was null!");
417                 return False;
418         }
419
420         if (!conn->cli->initialised) {
421                 DEBUG(0, ("Connection to %s for domain %s (pipe %s) was never initialised!\n", 
422                           conn->controller, conn->domain, conn->pipe_name));
423                 smb_panic("connection_ok: conn->cli->initialised is False!");
424                 return False;
425         }
426
427         if (conn->cli->fd == -1) {
428                 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n", 
429                           conn->controller, conn->domain, conn->pipe_name));
430                 return False;
431         }
432         
433         return True;
434 }
435
436 /* Get a connection to the remote DC and open the pipe.  If there is already a connection, use that */
437
438 static NTSTATUS get_connection_from_cache(const char *domain, const char *pipe_name, struct winbindd_cm_conn **conn_out) 
439 {
440         struct winbindd_cm_conn *conn, conn_temp;
441         NTSTATUS result;
442
443         for (conn = cm_conns; conn; conn = conn->next) {
444                 if (strequal(conn->domain, domain) && 
445                     strequal(conn->pipe_name, pipe_name)) {
446                         if (!connection_ok(conn)) {
447                                 if (conn->cli)
448                                         cli_shutdown(conn->cli);
449                                 ZERO_STRUCT(conn_temp);
450                                 conn_temp.next = conn->next;
451                                 DLIST_REMOVE(cm_conns, conn);
452                                 SAFE_FREE(conn);
453                                 conn = &conn_temp;  /* Just to keep the loop moving */
454                         } else {
455                                 break;
456                         }
457                 }
458         }
459         
460         if (!conn) {
461                 if (!(conn = malloc(sizeof(*conn))))
462                         return NT_STATUS_NO_MEMORY;
463                 
464                 ZERO_STRUCTP(conn);
465                 
466                 if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, get_pipe_index(pipe_name), conn))) {
467                         DEBUG(3, ("Could not open a connection to %s for %s (%s)\n", 
468                                   domain, pipe_name, nt_errstr(result)));
469                         SAFE_FREE(conn);
470                         return result;
471                 }
472                 DLIST_ADD(cm_conns, conn);              
473         }
474         
475         *conn_out = conn;
476         return NT_STATUS_OK;
477 }
478
479
480 /**********************************************************************************
481 **********************************************************************************/
482
483 BOOL cm_check_for_native_mode_win2k( const char *domain )
484 {
485         NTSTATUS                result;
486         struct winbindd_cm_conn conn;
487         DS_DOMINFO_CTR          ctr;
488         BOOL                    ret = False;
489         
490         ZERO_STRUCT( conn );
491         ZERO_STRUCT( ctr );
492         
493         
494         if ( !NT_STATUS_IS_OK(result = cm_open_connection(domain, PI_LSARPC_DS, &conn)) ) {
495                 DEBUG(5, ("cm_check_for_native_mode_win2k: Could not open a connection to %s for PIPE_LSARPC (%s)\n", 
496                           domain, nt_errstr(result)));
497                 return False;
498         }
499         
500         if ( conn.cli ) {
501                 if ( !NT_STATUS_IS_OK(cli_ds_getprimarydominfo( conn.cli, 
502                                 conn.cli->mem_ctx, DsRolePrimaryDomainInfoBasic, &ctr)) ) {
503                         ret = False;
504                         goto done;
505                 }
506         }
507                                 
508         if ( (ctr.basic->flags & DSROLE_PRIMARY_DS_RUNNING) 
509                         && !(ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE) )
510                 ret = True;
511
512 done:
513         if ( conn.cli )
514                 cli_shutdown( conn.cli );
515         
516         return ret;
517 }
518
519
520
521 /* Return a LSA policy handle on a domain */
522
523 CLI_POLICY_HND *cm_get_lsa_handle(const char *domain)
524 {
525         struct winbindd_cm_conn *conn;
526         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
527         NTSTATUS result;
528         static CLI_POLICY_HND hnd;
529
530         /* Look for existing connections */
531
532         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn)))
533                 return NULL;
534
535         /* This *shitty* code needs scrapping ! JRA */
536         if (policy_handle_is_valid(&conn->pol)) {
537                 hnd.pol = conn->pol;
538                 hnd.cli = conn->cli;
539                 return &hnd;
540         }
541         
542         result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False, 
543                                      des_access, &conn->pol);
544
545         if (!NT_STATUS_IS_OK(result)) {
546                 /* Hit the cache code again.  This cleans out the old connection and gets a new one */
547                 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
548                         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn)))
549                                 return NULL;
550
551                         result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False, 
552                                                      des_access, &conn->pol);
553                 }
554
555                 if (!NT_STATUS_IS_OK(result)) {
556                         cli_shutdown(conn->cli);
557                         DLIST_REMOVE(cm_conns, conn);
558                         SAFE_FREE(conn);
559                         return NULL;
560                 }
561         }       
562
563         hnd.pol = conn->pol;
564         hnd.cli = conn->cli;
565
566         return &hnd;
567 }
568
569 /* Return a SAM policy handle on a domain */
570
571 CLI_POLICY_HND *cm_get_sam_handle(char *domain)
572
573         struct winbindd_cm_conn *conn;
574         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
575         NTSTATUS result;
576         static CLI_POLICY_HND hnd;
577
578         /* Look for existing connections */
579
580         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
581                 return NULL;
582         
583         /* This *shitty* code needs scrapping ! JRA */
584         if (policy_handle_is_valid(&conn->pol)) {
585                 hnd.pol = conn->pol;
586                 hnd.cli = conn->cli;
587                 return &hnd;
588         }
589         result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
590                                   des_access, &conn->pol);
591
592         if (!NT_STATUS_IS_OK(result)) {
593                 /* Hit the cache code again.  This cleans out the old connection and gets a new one */
594                 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
595                         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
596                                 return NULL;
597
598                         result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
599                                                   des_access, &conn->pol);
600                 }
601
602                 if (!NT_STATUS_IS_OK(result)) {
603                         cli_shutdown(conn->cli);
604                         DLIST_REMOVE(cm_conns, conn);
605                         SAFE_FREE(conn);
606                         return NULL;
607                 }
608         }       
609
610         hnd.pol = conn->pol;
611         hnd.cli = conn->cli;
612
613         return &hnd;
614 }
615
616 #if 0  /* This code now *well* out of date */
617
618 /* Return a SAM domain policy handle on a domain */
619
620 CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain, DOM_SID *domain_sid)
621 {
622         struct winbindd_cm_conn *conn, *basic_conn = NULL;
623         static CLI_POLICY_HND hnd;
624         NTSTATUS result;
625         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
626
627         /* Look for existing connections */
628
629         for (conn = cm_conns; conn; conn = conn->next) {
630                 if (strequal(conn->domain, domain) &&
631                     strequal(conn->pipe_name, PIPE_SAMR) &&
632                     conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM) {
633
634                         if (!connection_ok(conn)) {
635                                 /* Shutdown cli?  Free conn?  Allow retry of DC? */
636                                 DLIST_REMOVE(cm_conns, conn);
637                                 return NULL;
638                         }
639
640                         goto ok;
641                 }
642         }
643
644         /* Create a basic handle to open a domain handle from */
645
646         if (!cm_get_sam_handle(domain))
647                 return False;
648
649         for (conn = cm_conns; conn; conn = conn->next) {
650                 if (strequal(conn->domain, domain) &&
651                     strequal(conn->pipe_name, PIPE_SAMR) &&
652                     conn->pipe_data.samr.pipe_type == SAM_PIPE_BASIC)
653                         basic_conn = conn;
654         }
655         
656         if (!(conn = (struct winbindd_cm_conn *)
657               malloc(sizeof(struct winbindd_cm_conn))))
658                 return NULL;
659         
660         ZERO_STRUCTP(conn);
661
662         fstrcpy(conn->domain, basic_conn->domain);
663         fstrcpy(conn->controller, basic_conn->controller);
664         fstrcpy(conn->pipe_name, basic_conn->pipe_name);
665
666         conn->pipe_data.samr.pipe_type = SAM_PIPE_DOM;
667         conn->cli = basic_conn->cli;
668
669         result = cli_samr_open_domain(conn->cli, conn->cli->mem_ctx,
670                                       &basic_conn->pol, des_access, 
671                                       domain_sid, &conn->pol);
672
673         if (!NT_STATUS_IS_OK(result))
674                 return NULL;
675
676         /* Add to list */
677
678         DLIST_ADD(cm_conns, conn);
679
680  ok:
681         hnd.pol = conn->pol;
682         hnd.cli = conn->cli;
683
684         return &hnd;
685 }
686
687 /* Return a SAM policy handle on a domain user */
688
689 CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, DOM_SID *domain_sid,
690                                        uint32 user_rid)
691 {
692         struct winbindd_cm_conn *conn, *basic_conn = NULL;
693         static CLI_POLICY_HND hnd;
694         NTSTATUS result;
695         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
696
697         /* Look for existing connections */
698
699         for (conn = cm_conns; conn; conn = conn->next) {
700                 if (strequal(conn->domain, domain) &&
701                     strequal(conn->pipe_name, PIPE_SAMR) &&
702                     conn->pipe_data.samr.pipe_type == SAM_PIPE_USER &&
703                     conn->pipe_data.samr.rid == user_rid) {
704
705                         if (!connection_ok(conn)) {
706                                 /* Shutdown cli?  Free conn?  Allow retry of DC? */
707                                 DLIST_REMOVE(cm_conns, conn);
708                                 return NULL;
709                         }
710                 
711                         goto ok;
712                 }
713         }
714
715         /* Create a domain handle to open a user handle from */
716
717         if (!cm_get_sam_dom_handle(domain, domain_sid))
718                 return NULL;
719
720         for (conn = cm_conns; conn; conn = conn->next) {
721                 if (strequal(conn->domain, domain) &&
722                     strequal(conn->pipe_name, PIPE_SAMR) &&
723                     conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
724                         basic_conn = conn;
725         }
726         
727         if (!basic_conn) {
728                 DEBUG(0, ("No domain sam handle was created!\n"));
729                 return NULL;
730         }
731
732         if (!(conn = (struct winbindd_cm_conn *)
733               malloc(sizeof(struct winbindd_cm_conn))))
734                 return NULL;
735         
736         ZERO_STRUCTP(conn);
737
738         fstrcpy(conn->domain, basic_conn->domain);
739         fstrcpy(conn->controller, basic_conn->controller);
740         fstrcpy(conn->pipe_name, basic_conn->pipe_name);
741         
742         conn->pipe_data.samr.pipe_type = SAM_PIPE_USER;
743         conn->cli = basic_conn->cli;
744         conn->pipe_data.samr.rid = user_rid;
745
746         result = cli_samr_open_user(conn->cli, conn->cli->mem_ctx,
747                                     &basic_conn->pol, des_access, user_rid,
748                                     &conn->pol);
749
750         if (!NT_STATUS_IS_OK(result))
751                 return NULL;
752
753         /* Add to list */
754
755         DLIST_ADD(cm_conns, conn);
756
757  ok:
758         hnd.pol = conn->pol;
759         hnd.cli = conn->cli;
760
761         return &hnd;
762 }
763
764 /* Return a SAM policy handle on a domain group */
765
766 CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, DOM_SID *domain_sid,
767                                         uint32 group_rid)
768 {
769         struct winbindd_cm_conn *conn, *basic_conn = NULL;
770         static CLI_POLICY_HND hnd;
771         NTSTATUS result;
772         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
773
774         /* Look for existing connections */
775
776         for (conn = cm_conns; conn; conn = conn->next) {
777                 if (strequal(conn->domain, domain) &&
778                     strequal(conn->pipe_name, PIPE_SAMR) &&
779                     conn->pipe_data.samr.pipe_type == SAM_PIPE_GROUP &&
780                     conn->pipe_data.samr.rid == group_rid) {
781
782                         if (!connection_ok(conn)) {
783                                 /* Shutdown cli?  Free conn?  Allow retry of DC? */
784                                 DLIST_REMOVE(cm_conns, conn);
785                                 return NULL;
786                         }
787                 
788                         goto ok;
789                 }
790         }
791
792         /* Create a domain handle to open a user handle from */
793
794         if (!cm_get_sam_dom_handle(domain, domain_sid))
795                 return NULL;
796
797         for (conn = cm_conns; conn; conn = conn->next) {
798                 if (strequal(conn->domain, domain) &&
799                     strequal(conn->pipe_name, PIPE_SAMR) &&
800                     conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
801                         basic_conn = conn;
802         }
803         
804         if (!basic_conn) {
805                 DEBUG(0, ("No domain sam handle was created!\n"));
806                 return NULL;
807         }
808
809         if (!(conn = (struct winbindd_cm_conn *)
810               malloc(sizeof(struct winbindd_cm_conn))))
811                 return NULL;
812         
813         ZERO_STRUCTP(conn);
814
815         fstrcpy(conn->domain, basic_conn->domain);
816         fstrcpy(conn->controller, basic_conn->controller);
817         fstrcpy(conn->pipe_name, basic_conn->pipe_name);
818         
819         conn->pipe_data.samr.pipe_type = SAM_PIPE_GROUP;
820         conn->cli = basic_conn->cli;
821         conn->pipe_data.samr.rid = group_rid;
822
823         result = cli_samr_open_group(conn->cli, conn->cli->mem_ctx,
824                                     &basic_conn->pol, des_access, group_rid,
825                                     &conn->pol);
826
827         if (!NT_STATUS_IS_OK(result))
828                 return NULL;
829
830         /* Add to list */
831
832         DLIST_ADD(cm_conns, conn);
833
834  ok:
835         hnd.pol = conn->pol;
836         hnd.cli = conn->cli;
837
838         return &hnd;
839 }
840
841 #endif
842
843 /* Get a handle on a netlogon pipe.  This is a bit of a hack to re-use the
844    netlogon pipe as no handle is returned. */
845
846 NTSTATUS cm_get_netlogon_cli(const char *domain, const unsigned char *trust_passwd,
847                              struct cli_state **cli)
848 {
849         NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
850         struct winbindd_cm_conn *conn;
851         uint32 neg_flags = 0x000001ff;
852         fstring srv_name;
853         struct in_addr dc_ip;
854
855         if (!cli)
856                 return NT_STATUS_INVALID_PARAMETER;
857
858         if (!cm_get_dc_name(domain, srv_name, &dc_ip))
859                 return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
860
861         if (!secrets_named_mutex(srv_name, 10)) {
862                 DEBUG(0,("cm_get_netlogon_cli: mutex grab failed for %s\n", srv_name));
863                 return NT_STATUS_POSSIBLE_DEADLOCK;
864         }
865
866         /* Open an initial conection */
867
868         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn)))
869                 return result;
870         
871         result = cli_nt_setup_creds(conn->cli, get_sec_chan(), trust_passwd, &neg_flags, 2);
872
873         secrets_named_mutex_release(srv_name);
874
875         if (!NT_STATUS_IS_OK(result)) {
876                 DEBUG(0, ("error connecting to domain password server: %s\n",
877                           nt_errstr(result)));
878                 
879                 /* Hit the cache code again.  This cleans out the old connection and gets a new one */
880                 if (conn->cli->fd == -1) {
881
882                         if (!secrets_named_mutex(srv_name, 10)) {
883                                 DEBUG(0,("cm_get_netlogon_cli: mutex grab failed for %s\n", srv_name));
884                                 return NT_STATUS_POSSIBLE_DEADLOCK;
885                         }
886
887                         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn)))
888                                 return result;
889                         
890                         /* Try again */
891                         result = cli_nt_setup_creds( conn->cli, get_sec_chan(),trust_passwd, &neg_flags, 2);
892
893                         secrets_named_mutex_release(srv_name);
894                 }
895                 
896                 if (!NT_STATUS_IS_OK(result)) {
897                         cli_shutdown(conn->cli);
898                         DLIST_REMOVE(cm_conns, conn);
899                         SAFE_FREE(conn);
900                         return result;
901                 }
902         }
903
904         *cli = conn->cli;
905
906         return result;
907 }
908
909 /* Dump the current connection status */
910
911 static void dump_conn_list(void)
912 {
913         struct winbindd_cm_conn *con;
914
915         DEBUG(0, ("\tDomain          Controller      Pipe\n"));
916
917         for(con = cm_conns; con; con = con->next) {
918                 char *msg;
919
920                 /* Display pipe info */
921                 
922                 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
923                         DEBUG(0, ("Error: not enough memory!\n"));
924                 } else {
925                         DEBUG(0, ("%s\n", msg));
926                         SAFE_FREE(msg);
927                 }
928         }
929 }
930
931 void winbindd_cm_status(void)
932 {
933         /* List open connections */
934
935         DEBUG(0, ("winbindd connection manager status:\n"));
936
937         if (cm_conns)
938                 dump_conn_list();
939         else
940                 DEBUG(0, ("\tNo active connections\n"));
941 }