summaryrefslogtreecommitdiff
path: root/source3/nsswitch/winbindd_cm.c
blob: ec1db826dde9b905ccfdb3695bca736ffb65b7f8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
/* 
   Unix SMB/Netbios implementation.
   Version 3.0

   Winbind daemon connection manager

   Copyright (C) Tim Potter 2001
   
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/*
   We need to manage connections to domain controllers without having to
   mess up the main winbindd code with other issues.  The aim of the
   connection manager is to:
  
       - make connections to domain controllers and cache them
       - re-establish connections when networks or servers go down
       - centralise the policy on connection timeouts, domain controller
         selection etc
       - manage re-entrancy for when winbindd becomes able to handle
         multiple outstanding rpc requests
  
   We can also throw away the CLI_POLICY_HND stuff as all this information
   will be stored within this module.
  
   Why not have connection management as part of the rpc layer like tng?
   Good question.  This code may morph into libsmb/rpc_cache.c or something
   like that but at the moment it's simply staying as part of winbind.  I
   think the TNG architecture of forcing every user of the rpc layer to use
   the connection caching system is a bad idea.  It should be an optional
   method of using the routines.

   The TNG design is quite good but I disagree with some aspects of the
   implementation. -tpot

 */

/*
   TODO:

     - I'm pretty annoyed by all the make_nmb_name() stuff.  It should be
       moved down into another function.

     - There needs to be a utility function in libsmb/namequery.c that does
       get_any_dc_name() 

 */

#include "winbindd.h"

/* We store lists of connections here */

struct winbindd_cm_conn {
        struct winbindd_cm_conn *prev, *next;
        fstring domain;
        fstring controller;
        fstring pipe_name;
        struct cli_state cli;
        POLICY_HND pol;
};

/* Global list of connections.  Initially a DLIST but can become a hash
   table or whatever later. */

struct winbindd_cm_conn *cm_conns = NULL;

/* Get a domain controller name */

BOOL cm_get_dc_name(char *domain, fstring srv_name)
{
	struct in_addr *ip_list, dc_ip;
	extern pstring global_myname;
	int count, i;

	/* Lookup domain controller name */
		
	if (!get_dc_list(False, domain, &ip_list, &count))
		return False;
		
	/* Firstly choose a PDC/BDC who has the same network address as any
	   of our interfaces. */
	
	for (i = 0; i < count; i++) {
		if(!is_local_net(ip_list[i]))
			goto got_ip;
	}
	
	i = (sys_random() % count);
	
 got_ip:
	dc_ip = ip_list[i];
	SAFE_FREE(ip_list);
		
	if (!lookup_pdc_name(global_myname, domain, &dc_ip, srv_name))
		return False;

	return True;
}

/* Open a new smb pipe connection to a DC on a given domain */

static BOOL cm_open_connection(char *domain, char *pipe_name,
                               struct winbindd_cm_conn *new_conn)
{
	struct nmb_name calling, called;
        extern pstring global_myname;
        fstring dest_host;
        struct in_addr dest_ip;
        BOOL result = False;
        struct ntuser_creds creds;

        ZERO_STRUCT(new_conn->cli);

        fstrcpy(new_conn->domain, domain);
        fstrcpy(new_conn->pipe_name, pipe_name);
        
        /* Look for a domain controller for this domain */

        if (!cm_get_dc_name(lp_workgroup(), new_conn->controller))
                goto done;

        /* Initialise SMB connection */

        if (!cli_initialise(&new_conn->cli))
                goto done;

	if (!resolve_srv_name(new_conn->controller, dest_host, &dest_ip))
		goto done;

	make_nmb_name(&called, dns_to_netbios_name(new_conn->controller), 
                      0x20);
	make_nmb_name(&calling, dns_to_netbios_name(global_myname), 0);

	ZERO_STRUCT(creds);
	creds.pwd.null_pwd = 1;

	cli_init_creds(&new_conn->cli, &creds);

	if (!cli_establish_connection(&new_conn->cli, new_conn->controller, 
                                      &dest_ip, &calling, &called, "IPC$", 
                                      "IPC", False, True))
		goto done;

	if (!cli_nt_session_open (&new_conn->cli, pipe_name))
		goto done;

        result = True;

 done:
        if (!result)
                cli_shutdown(&new_conn->cli);

        return result;
}

/* Return a LSA policy handle on a domain */

CLI_POLICY_HND *cm_get_lsa_handle(char *domain)
{
        struct winbindd_cm_conn *conn;
        uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
        NTSTATUS result;
        static CLI_POLICY_HND hnd;

        /* Look for existing connections */

        for (conn = cm_conns; conn; conn = conn->next) {
                if (strequal(conn->domain, domain) &&
                    strequal(conn->pipe_name, PIPE_LSARPC))
                        goto ok;
        }

        /* Create a new one */

        if (!(conn = (struct winbindd_cm_conn *)
              malloc(sizeof(struct winbindd_cm_conn))))
                return NULL;

        if (!cm_open_connection(domain, PIPE_LSARPC, conn)) {
                DEBUG(3, ("Could not connect to a dc for domain %s\n",
                          domain));
                return NULL;
        }

        result = cli_lsa_open_policy(&conn->cli, conn->cli.mem_ctx, False, 
                                     des_access, &conn->pol);

        if (!NT_STATUS_IS_OK(result))
                return NULL;

        /* Add to list */

        DLIST_ADD(cm_conns, conn);

 ok:
        hnd.pol = conn->pol;
        hnd.cli = &conn->cli;

        return &hnd;
}

/* Return a SAM policy handle on a domain */

CLI_POLICY_HND *cm_get_sam_handle(char *domain)
{ 
        DEBUG(0, ("get_sam_handle(): not implemented\n"));
        return NULL;
}

/* Return a SAM domain policy handle on a domain */

CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain)
{
        DEBUG(0, ("get_sam_dom_handle(): not implemented\n"));
        return NULL;
}

/* Return a SAM policy handle on a domain user */

CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, char *user)
{
        DEBUG(0, ("get_sam_user_handle(): not implemented\n"));
        return NULL;
}

/* Return a SAM policy handle on a domain group */

CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, char *group)
{
        DEBUG(0, ("get_sam_group_handle(): not implemented\n"));
        return NULL;
}