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