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