1/*
2   Unix SMB/CIFS implementation.
3   Authenticate against a remote domain
4   Copyright (C) Andrew Tridgell 1992-1998
5   Copyright (C) Andrew Bartlett 2001
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20*/
21
22#include "includes.h"
23
24#undef DBGC_CLASS
25#define DBGC_CLASS DBGC_AUTH
26
27extern BOOL global_machine_password_needs_changing;
28
29/**
30 * Connect to a remote server for (inter)domain security authenticaion.
31 *
32 * @param cli the cli to return containing the active connection
33 * @param server either a machine name or text IP address to
34 *               connect to.
35 * @param setup_creds_as domain account to setup credentials as
36 * @param sec_chan a switch value to distinguish between domain
37 *                 member and interdomain authentication
38 * @param trust_passwd the trust password to establish the
39 *                     credentials with.
40 *
41 **/
42
43static NTSTATUS connect_to_domain_password_server(struct cli_state **cli,
44						  const char *domain, const char *dc_name,
45						  struct in_addr dc_ip,
46						  const char *setup_creds_as,
47						  uint16 sec_chan,
48						  const unsigned char *trust_passwd,
49						  BOOL *retry)
50{
51        NTSTATUS result;
52
53	/* TODO: Send a SAMLOGON request to determine whether this is a valid
54	   logonserver.  We can avoid a 30-second timeout if the DC is down
55	   if the SAMLOGON request fails as it is only over UDP. */
56
57	/* we use a mutex to prevent two connections at once - when a
58	   Win2k PDC get two connections where one hasn't completed a
59	   session setup yet it will send a TCP reset to the first
60	   connection (tridge) */
61
62	/*
63	 * With NT4.x DC's *all* authentication must be serialized to avoid
64	 * ACCESS_DENIED errors if 2 auths are done from the same machine. JRA.
65	 */
66
67	if (!grab_server_mutex(dc_name))
68		return NT_STATUS_NO_LOGON_SERVERS;
69
70	/* Attempt connection */
71	*retry = True;
72	result = cli_full_connection(cli, global_myname(), dc_name, &dc_ip, 0,
73		"IPC$", "IPC", "", "", "", 0, Undefined, retry);
74
75	if (!NT_STATUS_IS_OK(result)) {
76		/* map to something more useful */
77		if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
78			result = NT_STATUS_NO_LOGON_SERVERS;
79		}
80
81		release_server_mutex();
82		return result;
83	}
84
85	/*
86	 * We now have an anonymous connection to IPC$ on the domain password server.
87	 */
88
89	/*
90	 * Even if the connect succeeds we need to setup the netlogon
91	 * pipe here. We do this as we may just have changed the domain
92	 * account password on the PDC and yet we may be talking to
93	 * a BDC that doesn't have this replicated yet. In this case
94	 * a successful connect to a DC needs to take the netlogon connect
95	 * into account also. This patch from "Bjart Kvarme" <bjart.kvarme@usit.uio.no>.
96	 */
97
98	if(cli_nt_session_open(*cli, PI_NETLOGON) == False) {
99		DEBUG(0,("connect_to_domain_password_server: unable to open the domain client session to \
100machine %s. Error was : %s.\n", dc_name, cli_errstr(*cli)));
101		cli_nt_session_close(*cli);
102		cli_ulogoff(*cli);
103		cli_shutdown(*cli);
104		release_server_mutex();
105		return NT_STATUS_NO_LOGON_SERVERS;
106	}
107
108	fstr_sprintf((*cli)->mach_acct, "%s$", setup_creds_as);
109
110	/* This must be the remote domain (not ours) for schannel */
111
112	fstrcpy( (*cli)->domain, domain );
113
114	result = cli_nt_establish_netlogon(*cli, sec_chan, trust_passwd);
115
116        if (!NT_STATUS_IS_OK(result)) {
117		DEBUG(0,("connect_to_domain_password_server: unable to setup the NETLOGON credentials to machine \
118%s. Error was : %s.\n", dc_name, nt_errstr(result)));
119		cli_nt_session_close(*cli);
120		cli_ulogoff(*cli);
121		cli_shutdown(*cli);
122		release_server_mutex();
123		return result;
124	}
125
126	/* We exit here with the mutex *locked*. JRA */
127
128	return NT_STATUS_OK;
129}
130
131/***********************************************************************
132 Do the same as security=server, but using NT Domain calls and a session
133 key from the machine password.  If the server parameter is specified
134 use it, otherwise figure out a server from the 'password server' param.
135************************************************************************/
136
137static NTSTATUS domain_client_validate(TALLOC_CTX *mem_ctx,
138				       const auth_usersupplied_info *user_info,
139				       const char *domain,
140				       uchar chal[8],
141				       auth_serversupplied_info **server_info,
142				       const char *dc_name, struct in_addr dc_ip,
143				       const char *setup_creds_as,
144				       uint16 sec_chan,
145				       unsigned char trust_passwd[16],
146				       time_t last_change_time)
147{
148	NET_USER_INFO_3 info3;
149	struct cli_state *cli = NULL;
150	NTSTATUS nt_status = NT_STATUS_NO_LOGON_SERVERS;
151	int i;
152	BOOL retry = True;
153
154	/*
155	 * At this point, smb_apasswd points to the lanman response to
156	 * the challenge in local_challenge, and smb_ntpasswd points to
157	 * the NT response to the challenge in local_challenge. Ship
158	 * these over the secure channel to a domain controller and
159	 * see if they were valid.
160	 */
161
162	/* rety loop for robustness */
163
164	for (i = 0; !NT_STATUS_IS_OK(nt_status) && retry && (i < 3); i++) {
165		nt_status = connect_to_domain_password_server(&cli, domain, dc_name,
166			dc_ip, setup_creds_as, sec_chan, trust_passwd, &retry);
167	}
168
169	if ( !NT_STATUS_IS_OK(nt_status) ) {
170		DEBUG(0,("domain_client_validate: Domain password server not available.\n"));
171		if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
172			return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
173		}
174		return nt_status;
175	}
176
177	ZERO_STRUCT(info3);
178
179        /*
180         * If this call succeeds, we now have lots of info about the user
181         * in the info3 structure.
182         */
183
184	nt_status = cli_netlogon_sam_network_logon(cli, mem_ctx,
185		NULL, user_info->smb_name.str, user_info->domain.str,
186		user_info->wksta_name.str, chal, user_info->lm_resp,
187		user_info->nt_resp, &info3);
188
189	/* let go as soon as possible so we avoid any potential deadlocks
190	   with winbind lookup up users or groups */
191
192	release_server_mutex();
193
194	if (!NT_STATUS_IS_OK(nt_status)) {
195		DEBUG(0,("domain_client_validate: unable to validate password "
196                         "for user %s in domain %s to Domain controller %s. "
197                         "Error was %s.\n", user_info->smb_name.str,
198                         user_info->domain.str, cli->srv_name_slash,
199                         nt_errstr(nt_status)));
200
201		/* map to something more useful */
202		if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
203			nt_status = NT_STATUS_NO_LOGON_SERVERS;
204		}
205	} else {
206		nt_status = make_server_info_info3(mem_ctx, user_info->internal_username.str,
207						   user_info->smb_name.str, domain, server_info, &info3);
208		netsamlogon_cache_store( mem_ctx, user_info->smb_name.str, &info3 );
209	}
210
211#if 0
212	/*
213	 * We don't actually need to do this - plus it fails currently with
214	 * NT_STATUS_INVALID_INFO_CLASS - we need to know *exactly* what to
215	 * send here. JRA.
216	 */
217
218	if (NT_STATUS_IS_OK(status)) {
219		if(cli_nt_logoff(&cli, &ctr) == False) {
220			DEBUG(0,("domain_client_validate: unable to log off user %s in domain \
221%s to Domain controller %s. Error was %s.\n", user, domain, dc_name, cli_errstr(&cli)));
222			nt_status = NT_STATUS_LOGON_FAILURE;
223		}
224	}
225#endif /* 0 */
226
227	/* Note - once the cli stream is shutdown the mem_ctx used
228	   to allocate the other_sids and gids structures has been deleted - so
229	   these pointers are no longer valid..... */
230
231	cli_nt_session_close(cli);
232	cli_ulogoff(cli);
233	cli_shutdown(cli);
234	return nt_status;
235}
236
237/****************************************************************************
238 Check for a valid username and password in security=domain mode.
239****************************************************************************/
240
241static NTSTATUS check_ntdomain_security(const struct auth_context *auth_context,
242					void *my_private_data,
243					TALLOC_CTX *mem_ctx,
244					const auth_usersupplied_info *user_info,
245					auth_serversupplied_info **server_info)
246{
247	NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
248	unsigned char trust_passwd[16];
249	time_t last_change_time;
250	const char *domain = lp_workgroup();
251	uint32 sec_channel_type = 0;
252	fstring dc_name;
253	struct in_addr dc_ip;
254
255	if ( lp_server_role() != ROLE_DOMAIN_MEMBER ) {
256		DEBUG(0,("check_ntdomain_security: Configuration error!  Cannot use "
257			"ntdomain auth method when not a member of a domain.\n"));
258		return NT_STATUS_NOT_IMPLEMENTED;
259	}
260
261	if (!user_info || !server_info || !auth_context) {
262		DEBUG(1,("check_ntdomain_security: Critical variables not present.  Failing.\n"));
263		return NT_STATUS_INVALID_PARAMETER;
264	}
265
266	/*
267	 * Check that the requested domain is not our own machine name.
268	 * If it is, we should never check the PDC here, we use our own local
269	 * password file.
270	 */
271
272	if(strequal(get_global_sam_name(), user_info->domain.str)) {
273		DEBUG(3,("check_ntdomain_security: Requested domain was for this machine.\n"));
274		return NT_STATUS_NOT_IMPLEMENTED;
275	}
276
277	/*
278	 * Get the machine account password for our primary domain
279	 * No need to become_root() as secrets_init() is done at startup.
280	 */
281
282	if (!secrets_fetch_trust_account_password(domain, trust_passwd, &last_change_time, &sec_channel_type))
283	{
284		DEBUG(0, ("check_ntdomain_security: could not fetch trust account password for domain '%s'\n", domain));
285		return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
286	}
287
288	/* Test if machine password has expired and needs to be changed */
289	if (lp_machine_password_timeout()) {
290		if (last_change_time > 0 &&
291		    time(NULL) > (last_change_time +
292				  lp_machine_password_timeout())) {
293			global_machine_password_needs_changing = True;
294		}
295	}
296
297	/* we need our DC to send the net_sam_logon() request to */
298
299	if ( !get_dc_name(domain, NULL, dc_name, &dc_ip) ) {
300		DEBUG(5,("check_ntdomain_security: unable to locate a DC for domain %s\n",
301			user_info->domain.str));
302		return NT_STATUS_NO_LOGON_SERVERS;
303	}
304
305	nt_status = domain_client_validate(mem_ctx, user_info, domain,
306		(uchar *)auth_context->challenge.data, server_info, dc_name, dc_ip,
307		global_myname(), sec_channel_type,trust_passwd, last_change_time);
308
309	return nt_status;
310}
311
312/* module initialisation */
313static NTSTATUS auth_init_ntdomain(struct auth_context *auth_context, const char* param, auth_methods **auth_method)
314{
315	if (!make_auth_methods(auth_context, auth_method)) {
316		return NT_STATUS_NO_MEMORY;
317	}
318
319	(*auth_method)->name = "ntdomain";
320	(*auth_method)->auth = check_ntdomain_security;
321	return NT_STATUS_OK;
322}
323
324
325/****************************************************************************
326 Check for a valid username and password in a trusted domain
327****************************************************************************/
328
329static NTSTATUS check_trustdomain_security(const struct auth_context *auth_context,
330					   void *my_private_data,
331					   TALLOC_CTX *mem_ctx,
332					   const auth_usersupplied_info *user_info,
333					   auth_serversupplied_info **server_info)
334{
335	NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
336	unsigned char trust_md4_password[16];
337	char *trust_password;
338	time_t last_change_time;
339	DOM_SID sid;
340	fstring dc_name;
341	struct in_addr dc_ip;
342
343	if (!user_info || !server_info || !auth_context) {
344		DEBUG(1,("check_trustdomain_security: Critical variables not present.  Failing.\n"));
345		return NT_STATUS_INVALID_PARAMETER;
346	}
347
348	/*
349	 * Check that the requested domain is not our own machine name or domain name.
350	 */
351
352	if( strequal(get_global_sam_name(), user_info->domain.str)) {
353		DEBUG(3,("check_trustdomain_security: Requested domain [%s] was for this machine.\n",
354			user_info->domain.str));
355		return NT_STATUS_NOT_IMPLEMENTED;
356	}
357
358	/* No point is bothering if this is not a trusted domain.
359	   This return makes "map to guest = bad user" work again.
360	   The logic is that if we know nothing about the domain, that
361	   user is known to us and does not exist */
362
363	if ( !is_trusted_domain( user_info->domain.str ) )
364		return NT_STATUS_NOT_IMPLEMENTED;
365
366	/*
367	 * Get the trusted account password for the trusted domain
368	 * No need to become_root() as secrets_init() is done at startup.
369	 */
370
371	if (!secrets_fetch_trusted_domain_password(user_info->domain.str, &trust_password, &sid, &last_change_time))
372	{
373		DEBUG(0, ("check_trustdomain_security: could not fetch trust account password for domain %s\n", user_info->domain.str));
374		return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
375	}
376
377#ifdef DEBUG_PASSWORD
378	DEBUG(100, ("Trust password for domain %s is %s\n", user_info->domain.str, trust_password));
379#endif
380	E_md4hash(trust_password, trust_md4_password);
381	SAFE_FREE(trust_password);
382
383#if 0
384	/* Test if machine password is expired and need to be changed */
385	if (time(NULL) > last_change_time + lp_machine_password_timeout())
386	{
387		global_machine_password_needs_changing = True;
388	}
389#endif
390
391	/* use get_dc_name() for consistency even through we know that it will be
392	   a netbios name */
393
394	if ( !get_dc_name(user_info->domain.str, NULL, dc_name, &dc_ip) ) {
395		DEBUG(5,("check_trustdomain_security: unable to locate a DC for domain %s\n",
396			user_info->domain.str));
397		return NT_STATUS_NO_LOGON_SERVERS;
398	}
399
400	nt_status = domain_client_validate(mem_ctx, user_info, user_info->domain.str,
401		(uchar *)auth_context->challenge.data, server_info, dc_name, dc_ip,
402		lp_workgroup(), SEC_CHAN_DOMAIN, trust_md4_password, last_change_time);
403
404	return nt_status;
405}
406
407/* module initialisation */
408static NTSTATUS auth_init_trustdomain(struct auth_context *auth_context, const char* param, auth_methods **auth_method)
409{
410	if (!make_auth_methods(auth_context, auth_method)) {
411		return NT_STATUS_NO_MEMORY;
412	}
413
414	(*auth_method)->name = "trustdomain";
415	(*auth_method)->auth = check_trustdomain_security;
416	return NT_STATUS_OK;
417}
418
419NTSTATUS auth_domain_init(void)
420{
421	smb_register_auth(AUTH_INTERFACE_VERSION, "trustdomain", auth_init_trustdomain);
422	smb_register_auth(AUTH_INTERFACE_VERSION, "ntdomain", auth_init_ntdomain);
423	return NT_STATUS_OK;
424}
425