compile error
[bbaumbach/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         size_t mutex_ref_count;
76         struct cli_state *cli;
77         POLICY_HND pol;
78 };
79
80 static struct winbindd_cm_conn *cm_conns = NULL;
81
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. */
84
85 #define GET_DC_NAME_CACHE_TIMEOUT 30 /* Seconds between dc lookups */
86
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;
92 };
93
94 /*
95   find the DC for a domain using methods appropriate for a ADS domain
96 */
97 static BOOL cm_ads_find_dc(const char *domain, struct in_addr *dc_ip, fstring srv_name)
98 {
99         ADS_STRUCT *ads;
100         const char *realm = domain;
101
102         if (strcasecmp(realm, lp_workgroup()) == 0)
103                 realm = lp_realm();
104
105         ads = ads_init(realm, domain, NULL);
106         if (!ads)
107                 return False;
108
109         /* we don't need to bind, just connect */
110         ads->auth.flags |= ADS_AUTH_NO_BIND;
111
112         DEBUG(4,("cm_ads_find_dc: domain=%s\n", domain));
113
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
120
121         if (!ads->config.realm)
122                 return False;
123
124         fstrcpy(srv_name, ads->config.ldap_server_name);
125         strupper(srv_name);
126         *dc_ip = ads->ldap_ip;
127         ads_destroy(&ads);
128         
129         DEBUG(4,("cm_ads_find_dc: using server='%s' IP=%s\n",
130                  srv_name, inet_ntoa(*dc_ip)));
131         
132         return True;
133 }
134
135
136
137 static BOOL cm_get_dc_name(const char *domain, fstring srv_name, struct in_addr *ip_out)
138 {
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;
143
144         /* Check the cache for previous lookups */
145
146         for (dcc = get_dc_name_cache; dcc; dcc = dcc->next) {
147
148                 if (!strequal(domain, dcc->domain_name))
149                         continue; /* Not our domain */
150
151                 if ((time(NULL) - dcc->lookup_time) > 
152                     GET_DC_NAME_CACHE_TIMEOUT) {
153
154                         /* Cache entry has expired, delete it */
155
156                         DEBUG(10, ("get_dc_name_cache entry expired for %s\n", domain));
157
158                         DLIST_REMOVE(get_dc_name_cache, dcc);
159                         SAFE_FREE(dcc);
160
161                         break;
162                 }
163
164                 /* Return a positive or negative lookup for this domain */
165
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;
173                 }
174         }
175
176         /* Add cache entry for this lookup. */
177
178         DEBUG(10, ("Creating get_dc_name_cache entry for %s\n", domain));
179
180         if (!(dcc = (struct get_dc_name_cache *) 
181               malloc(sizeof(struct get_dc_name_cache))))
182                 return False;
183
184         ZERO_STRUCTP(dcc);
185
186         fstrcpy(dcc->domain_name, domain);
187         dcc->lookup_time = time(NULL);
188
189         DLIST_ADD(get_dc_name_cache, dcc);
190
191         zero_ip(&dc_ip);
192
193         ret = False;
194         if (lp_security() == SEC_ADS)
195                 ret = cm_ads_find_dc(domain, &dc_ip, srv_name);
196
197         if (!ret) {
198                 /* fall back on rpc methods if the ADS methods fail */
199                 ret = rpc_find_dc(domain, srv_name, &dc_ip);
200         }
201
202         if (!ret)
203                 return False;
204
205         /* We have a name so make the cache entry positive now */
206         fstrcpy(dcc->srv_name, srv_name);
207
208         DEBUG(3, ("cm_get_dc_name: Returning DC %s (%s) for domain %s\n", srv_name,
209                   inet_ntoa(dc_ip), domain));
210
211         *ip_out = dc_ip;
212
213         return True;
214 }
215
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. 
220    
221    Caller to free() result in domain, username, password
222 */
223
224 static void cm_get_ipc_userpass(char **username, char **domain, char **password)
225 {
226         *username = secrets_fetch(SECRETS_AUTH_USER, NULL);
227         *domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
228         *password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
229         
230         if (*username && **username) {
231
232                 if (!*domain || !**domain)
233                         *domain = smb_xstrdup(lp_workgroup());
234                 
235                 if (!*password || !**password)
236                         *password = smb_xstrdup("");
237
238                 DEBUG(3, ("IPC$ connections done by user %s\\%s\n", 
239                           *domain, *username));
240
241         } else {
242                 DEBUG(3, ("IPC$ connections done anonymously\n"));
243                 *username = smb_xstrdup("");
244                 *domain = smb_xstrdup("");
245                 *password = smb_xstrdup("");
246         }
247 }
248
249 /* Open a connction to the remote server, cache failures for 30 seconds */
250
251 static NTSTATUS cm_open_connection(const char *domain, const int pipe_index,
252                                struct winbindd_cm_conn *new_conn)
253 {
254         NTSTATUS result;
255         char *ipc_username, *ipc_domain, *ipc_password;
256         struct in_addr dc_ip;
257         int i;
258         BOOL retry = True;
259
260         ZERO_STRUCT(dc_ip);
261
262         fstrcpy(new_conn->domain, domain);
263         fstrcpy(new_conn->pipe_name, get_pipe_name_from_index(pipe_index));
264         
265         /* connection failure cache has been moved inside of get_dc_name
266            so we can deal with half dead DC's   --jerry */
267
268         if (!cm_get_dc_name(domain, new_conn->controller, &dc_ip)) {
269                 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
270                 add_failed_connection_entry(domain, "", result);
271                 return result;
272         }
273                 
274         /* Initialise SMB connection */
275
276         cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
277
278         DEBUG(5, ("connecting to %s from %s with username [%s]\\[%s]\n", 
279               new_conn->controller, global_myname(), ipc_domain, ipc_username));
280
281         for (i = 0; retry && (i < 3); i++) {
282                 BOOL got_mutex;
283                 if (!(got_mutex = secrets_named_mutex(new_conn->controller, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
284                         DEBUG(0,("cm_open_connection: mutex grab failed for %s\n", new_conn->controller));
285                         result = NT_STATUS_POSSIBLE_DEADLOCK;
286                         continue;
287                 }
288                 
289                 result = cli_full_connection(&new_conn->cli, global_myname(), new_conn->controller, 
290                                              &dc_ip, 0, "IPC$", "IPC", ipc_username, ipc_domain, 
291                                              ipc_password, CLI_FULL_CONNECTION_ANNONYMOUS_FALLBACK, &retry);
292                 
293                 secrets_named_mutex_release(new_conn->controller);
294
295                 if (NT_STATUS_IS_OK(result))
296                         break;
297         }
298
299         SAFE_FREE(ipc_username);
300         SAFE_FREE(ipc_domain);
301         SAFE_FREE(ipc_password);
302
303         if (!NT_STATUS_IS_OK(result)) {
304                 add_failed_connection_entry(domain, new_conn->controller, result);
305                 return result;
306         }
307         
308         if ( !cli_nt_session_open (new_conn->cli, pipe_index) ) {
309                 result = NT_STATUS_PIPE_NOT_AVAILABLE;
310                 /* 
311                  * only cache a failure if we are not trying to open the 
312                  * **win2k** specific lsarpc UUID.  This could be an NT PDC 
313                  * and therefore a failure is normal.  This should probably
314                  * be abstracted to a check for 2k specific pipes and wondering
315                  * if the PDC is an NT4 box.   but since there is only one 2k 
316                  * specific UUID right now, i'm not going to bother.  --jerry
317                  */
318                 if ( !is_win2k_pipe(pipe_index) )
319                         add_failed_connection_entry(domain, new_conn->controller, result);
320                 cli_shutdown(new_conn->cli);
321                 return result;
322         }
323
324         return NT_STATUS_OK;
325 }
326
327 /* Return true if a connection is still alive */
328
329 static BOOL connection_ok(struct winbindd_cm_conn *conn)
330 {
331         if (!conn) {
332                 smb_panic("Invalid parameter passed to connection_ok():  conn was NULL!\n");
333                 return False;
334         }
335
336         if (!conn->cli) {
337                 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n", 
338                           conn->controller, conn->domain, conn->pipe_name));
339                 return False;
340         }
341
342         if (!conn->cli->initialised) {
343                 DEBUG(3, ("Connection to %s for domain %s (pipe %s) was never initialised!\n", 
344                           conn->controller, conn->domain, conn->pipe_name));
345                 return False;
346         }
347
348         if (conn->cli->fd == -1) {
349                 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n", 
350                           conn->controller, conn->domain, conn->pipe_name));
351                 return False;
352         }
353         
354         return True;
355 }
356
357 /* Search the cache for a connection. If there is a broken one,
358    shut it down properly and return NULL. */
359
360 static void find_cm_connection(const char *domain, const char *pipe_name,
361                                struct winbindd_cm_conn **conn_out) 
362 {
363         struct winbindd_cm_conn *conn;
364
365         for (conn = cm_conns; conn; ) {
366                 if (strequal(conn->domain, domain) && 
367                     strequal(conn->pipe_name, pipe_name)) {
368                         if (!connection_ok(conn)) {
369                                 /* Dead connection - remove it. */
370                                 struct winbindd_cm_conn *conn_temp = conn->next;
371                                 if (conn->cli)
372                                         cli_shutdown(conn->cli);
373                                 DLIST_REMOVE(cm_conns, conn);
374                                 SAFE_FREE(conn);
375                                 conn = conn_temp;  /* Keep the loop moving */
376                                 continue;
377                         } else {
378                                 break;
379                         }
380                 }
381                 conn = conn->next;
382         }
383
384         *conn_out = conn;
385 }
386
387 /* Initialize a new connection up to the RPC BIND. */
388
389 static NTSTATUS new_cm_connection(const char *domain, const char *pipe_name,
390                                   struct winbindd_cm_conn **conn_out)
391 {
392         struct winbindd_cm_conn *conn;
393         NTSTATUS result;
394
395         if (!(conn = malloc(sizeof(*conn))))
396                 return NT_STATUS_NO_MEMORY;
397                 
398         ZERO_STRUCTP(conn);
399                 
400         if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, get_pipe_index(pipe_name), conn))) {
401                 DEBUG(3, ("Could not open a connection to %s for %s (%s)\n", 
402                           domain, pipe_name, nt_errstr(result)));
403                 SAFE_FREE(conn);
404                 return result;
405         }
406         DLIST_ADD(cm_conns, conn);
407
408         *conn_out = conn;
409         return NT_STATUS_OK;
410 }
411
412 /* Get a connection to the remote DC and open the pipe.  If there is already a connection, use that */
413
414 static NTSTATUS get_connection_from_cache(const char *domain, const char *pipe_name,
415                                           struct winbindd_cm_conn **conn_out)
416 {
417         find_cm_connection(domain, pipe_name, conn_out);
418
419         if (*conn_out != NULL)
420                 return NT_STATUS_OK;
421
422         return new_cm_connection(domain, pipe_name, conn_out);
423 }
424
425 /**********************************************************************************
426 **********************************************************************************/
427
428 BOOL cm_check_for_native_mode_win2k( const char *domain )
429 {
430         NTSTATUS                result;
431         struct winbindd_cm_conn conn;
432         DS_DOMINFO_CTR          ctr;
433         BOOL                    ret = False;
434         
435         ZERO_STRUCT( conn );
436         ZERO_STRUCT( ctr );
437         
438         
439         if ( !NT_STATUS_IS_OK(result = cm_open_connection(domain, PI_LSARPC_DS, &conn)) ) {
440                 DEBUG(5, ("cm_check_for_native_mode_win2k: Could not open a connection to %s for PIPE_LSARPC (%s)\n", 
441                           domain, nt_errstr(result)));
442                 return False;
443         }
444         
445         if ( conn.cli ) {
446                 if ( !NT_STATUS_IS_OK(cli_ds_getprimarydominfo( conn.cli, 
447                                 conn.cli->mem_ctx, DsRolePrimaryDomainInfoBasic, &ctr)) ) {
448                         ret = False;
449                         goto done;
450                 }
451         }
452                                 
453         if ( (ctr.basic->flags & DSROLE_PRIMARY_DS_RUNNING) 
454                         && !(ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE) )
455                 ret = True;
456
457 done:
458         if ( conn.cli )
459                 cli_shutdown( conn.cli );
460         
461         return ret;
462 }
463
464
465
466 /* Return a LSA policy handle on a domain */
467
468 CLI_POLICY_HND *cm_get_lsa_handle(const char *domain)
469 {
470         struct winbindd_cm_conn *conn;
471         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
472         NTSTATUS result;
473         static CLI_POLICY_HND hnd;
474
475         /* Look for existing connections */
476
477         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn)))
478                 return NULL;
479
480         /* This *shitty* code needs scrapping ! JRA */
481         if (policy_handle_is_valid(&conn->pol)) {
482                 hnd.pol = conn->pol;
483                 hnd.cli = conn->cli;
484                 return &hnd;
485         }
486         
487         result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False, 
488                                      des_access, &conn->pol);
489
490         if (!NT_STATUS_IS_OK(result)) {
491                 /* Hit the cache code again.  This cleans out the old connection and gets a new one */
492                 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
493                         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn)))
494                                 return NULL;
495
496                         result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False, 
497                                                      des_access, &conn->pol);
498                 }
499
500                 if (!NT_STATUS_IS_OK(result)) {
501                         cli_shutdown(conn->cli);
502                         DLIST_REMOVE(cm_conns, conn);
503                         SAFE_FREE(conn);
504                         return NULL;
505                 }
506         }       
507
508         hnd.pol = conn->pol;
509         hnd.cli = conn->cli;
510
511         return &hnd;
512 }
513
514 /* Return a SAM policy handle on a domain */
515
516 CLI_POLICY_HND *cm_get_sam_handle(char *domain)
517
518         struct winbindd_cm_conn *conn;
519         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
520         NTSTATUS result;
521         static CLI_POLICY_HND hnd;
522
523         /* Look for existing connections */
524
525         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
526                 return NULL;
527         
528         /* This *shitty* code needs scrapping ! JRA */
529         if (policy_handle_is_valid(&conn->pol)) {
530                 hnd.pol = conn->pol;
531                 hnd.cli = conn->cli;
532                 return &hnd;
533         }
534         result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
535                                   des_access, &conn->pol);
536
537         if (!NT_STATUS_IS_OK(result)) {
538                 /* Hit the cache code again.  This cleans out the old connection and gets a new one */
539                 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
540                         if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn)))
541                                 return NULL;
542
543                         result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
544                                                   des_access, &conn->pol);
545                 }
546
547                 if (!NT_STATUS_IS_OK(result)) {
548                         cli_shutdown(conn->cli);
549                         DLIST_REMOVE(cm_conns, conn);
550                         SAFE_FREE(conn);
551                         return NULL;
552                 }
553         }       
554
555         hnd.pol = conn->pol;
556         hnd.cli = conn->cli;
557
558         return &hnd;
559 }
560
561 /* Get a handle on a netlogon pipe.  This is a bit of a hack to re-use the
562    netlogon pipe as no handle is returned. */
563
564 NTSTATUS cm_get_netlogon_cli(const char *domain, 
565                              const unsigned char *trust_passwd, 
566                              uint32 sec_channel_type,
567                              BOOL fresh,
568                              struct cli_state **cli)
569 {
570         NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
571         struct winbindd_cm_conn *conn;
572         fstring lock_name;
573         BOOL got_mutex;
574
575         if (!cli)
576                 return NT_STATUS_INVALID_PARAMETER;
577
578         /* Open an initial conection - keep the mutex. */
579
580         find_cm_connection(domain, PIPE_NETLOGON, &conn);
581
582         if ( fresh && (conn != NULL) ) {
583                 cli_shutdown(conn->cli);
584                 conn->cli = NULL;
585
586                 conn = NULL;
587
588                 /* purge connection from cache */
589                 find_cm_connection(domain, PIPE_NETLOGON, &conn);
590                 if (conn != NULL) {
591                         DEBUG(0,("Could not purge connection\n"));
592                         return NT_STATUS_UNSUCCESSFUL;
593                 }
594         }
595
596         if (conn != NULL) {
597                 *cli = conn->cli;
598                 return NT_STATUS_OK;
599         }
600
601         result = new_cm_connection(domain, PIPE_NETLOGON, &conn);
602
603         if (!NT_STATUS_IS_OK(result))
604                 return result;
605         
606         snprintf(lock_name, sizeof(lock_name), "NETLOGON\\%s", conn->controller);
607
608         if (!(got_mutex = secrets_named_mutex(lock_name, WINBIND_SERVER_MUTEX_WAIT_TIME))) {
609                 DEBUG(0,("cm_get_netlogon_cli: mutex grab failed for %s\n", conn->controller));
610         }
611                         
612         result = cli_nt_establish_netlogon(conn->cli, sec_channel_type, trust_passwd);
613         
614         if (got_mutex)
615                 secrets_named_mutex_release(lock_name);
616                                 
617         if (!NT_STATUS_IS_OK(result)) {
618                 cli_shutdown(conn->cli);
619                 DLIST_REMOVE(cm_conns, conn);
620                 SAFE_FREE(conn);
621                 return result;
622         }
623
624         *cli = conn->cli;
625
626         return result;
627 }
628
629 /* Dump the current connection status */
630
631 static void dump_conn_list(void)
632 {
633         struct winbindd_cm_conn *con;
634
635         DEBUG(0, ("\tDomain          Controller      Pipe\n"));
636
637         for(con = cm_conns; con; con = con->next) {
638                 char *msg;
639
640                 /* Display pipe info */
641                 
642                 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
643                         DEBUG(0, ("Error: not enough memory!\n"));
644                 } else {
645                         DEBUG(0, ("%s\n", msg));
646                         SAFE_FREE(msg);
647                 }
648         }
649 }
650
651 void winbindd_cm_status(void)
652 {
653         /* List open connections */
654
655         DEBUG(0, ("winbindd connection manager status:\n"));
656
657         if (cm_conns)
658                 dump_conn_list();
659         else
660                 DEBUG(0, ("\tNo active connections\n"));
661 }