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