1/*
2   Unix SMB/CIFS implementation.
3   Authenticate to a remote server
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 userdom_struct current_user_info;
28
29/****************************************************************************
30 Support for server level security.
31****************************************************************************/
32
33static struct cli_state *server_cryptkey(TALLOC_CTX *mem_ctx)
34{
35	struct cli_state *cli = NULL;
36	fstring desthost;
37	struct in_addr dest_ip;
38	const char *p;
39	char *pserver;
40	BOOL connected_ok = False;
41
42	if (!(cli = cli_initialise(cli)))
43		return NULL;
44
45	/* security = server just can't function with spnego */
46	cli->use_spnego = False;
47
48        pserver = talloc_strdup(mem_ctx, lp_passwordserver());
49	p = pserver;
50
51        while(next_token( &p, desthost, LIST_SEP, sizeof(desthost))) {
52		standard_sub_basic(current_user_info.smb_name, desthost, sizeof(desthost));
53		strupper_m(desthost);
54
55		if(!resolve_name( desthost, &dest_ip, 0x20)) {
56			DEBUG(1,("server_cryptkey: Can't resolve address for %s\n",desthost));
57			continue;
58		}
59
60		if (ismyip(dest_ip)) {
61			DEBUG(1,("Password server loop - disabling password server %s\n",desthost));
62			continue;
63		}
64
65		/* we use a mutex to prevent two connections at once - when a
66		   Win2k PDC get two connections where one hasn't completed a
67		   session setup yet it will send a TCP reset to the first
68		   connection (tridge) */
69
70		if (!grab_server_mutex(desthost)) {
71			return NULL;
72		}
73
74		if (cli_connect(cli, desthost, &dest_ip)) {
75			DEBUG(3,("connected to password server %s\n",desthost));
76			connected_ok = True;
77			break;
78		}
79	}
80
81	if (!connected_ok) {
82		release_server_mutex();
83		DEBUG(0,("password server not available\n"));
84		cli_shutdown(cli);
85		return NULL;
86	}
87
88	if (!attempt_netbios_session_request(cli, global_myname(),
89					     desthost, &dest_ip)) {
90		release_server_mutex();
91		DEBUG(1,("password server fails session request\n"));
92		cli_shutdown(cli);
93		return NULL;
94	}
95
96	if (strequal(desthost,myhostname())) {
97		exit_server("Password server loop!");
98	}
99
100	DEBUG(3,("got session\n"));
101
102	if (!cli_negprot(cli)) {
103		DEBUG(1,("%s rejected the negprot\n",desthost));
104		release_server_mutex();
105		cli_shutdown(cli);
106		return NULL;
107	}
108
109	if (cli->protocol < PROTOCOL_LANMAN2 ||
110	    !(cli->sec_mode & NEGOTIATE_SECURITY_USER_LEVEL)) {
111		DEBUG(1,("%s isn't in user level security mode\n",desthost));
112		release_server_mutex();
113		cli_shutdown(cli);
114		return NULL;
115	}
116
117	/* Get the first session setup done quickly, to avoid silly
118	   Win2k bugs.  (The next connection to the server will kill
119	   this one...
120	*/
121
122	if (!cli_session_setup(cli, "", "", 0, "", 0,
123			       "")) {
124		DEBUG(0,("%s rejected the initial session setup (%s)\n",
125			 desthost, cli_errstr(cli)));
126		release_server_mutex();
127		cli_shutdown(cli);
128		return NULL;
129	}
130
131	release_server_mutex();
132
133	DEBUG(3,("password server OK\n"));
134
135	return cli;
136}
137
138/****************************************************************************
139 Clean up our allocated cli.
140****************************************************************************/
141
142static void free_server_private_data(void **private_data_pointer)
143{
144	struct cli_state **cli = (struct cli_state **)private_data_pointer;
145	if (*cli && (*cli)->initialised) {
146		DEBUG(10, ("Shutting down smbserver connection\n"));
147		cli_shutdown(*cli);
148	}
149	*private_data_pointer = NULL;
150}
151
152/****************************************************************************
153 Send a 'keepalive' packet down the cli pipe.
154****************************************************************************/
155
156static void send_server_keepalive(void **private_data_pointer)
157{
158	/* also send a keepalive to the password server if its still
159	   connected */
160	if (private_data_pointer) {
161		struct cli_state *cli = (struct cli_state *)(*private_data_pointer);
162		if (cli && cli->initialised) {
163			if (!send_keepalive(cli->fd)) {
164				DEBUG( 2, ( "send_server_keepalive: password server keepalive failed.\n"));
165				cli_shutdown(cli);
166				*private_data_pointer = NULL;
167			}
168		}
169	}
170}
171
172/****************************************************************************
173 Get the challenge out of a password server.
174****************************************************************************/
175
176static DATA_BLOB auth_get_challenge_server(const struct auth_context *auth_context,
177					   void **my_private_data,
178					   TALLOC_CTX *mem_ctx)
179{
180	struct cli_state *cli = server_cryptkey(mem_ctx);
181
182	if (cli) {
183		DEBUG(3,("using password server validation\n"));
184
185		if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
186			/* We can't work with unencrypted password servers
187			   unless 'encrypt passwords = no' */
188			DEBUG(5,("make_auth_info_server: Server is unencrypted, no challenge available..\n"));
189
190			/* However, it is still a perfectly fine connection
191			   to pass that unencrypted password over */
192			*my_private_data = (void *)cli;
193			return data_blob(NULL, 0);
194
195		} else if (cli->secblob.length < 8) {
196			/* We can't do much if we don't get a full challenge */
197			DEBUG(2,("make_auth_info_server: Didn't receive a full challenge from server\n"));
198			cli_shutdown(cli);
199			return data_blob(NULL, 0);
200		}
201
202		*my_private_data = (void *)cli;
203
204		/* The return must be allocated on the caller's mem_ctx, as our own will be
205		   destoyed just after the call. */
206		return data_blob_talloc(auth_context->mem_ctx, cli->secblob.data,8);
207	} else {
208		return data_blob(NULL, 0);
209	}
210}
211
212
213/****************************************************************************
214 Check for a valid username and password in security=server mode.
215  - Validate a password with the password server.
216****************************************************************************/
217
218static NTSTATUS check_smbserver_security(const struct auth_context *auth_context,
219					 void *my_private_data,
220					 TALLOC_CTX *mem_ctx,
221					 const auth_usersupplied_info *user_info,
222					 auth_serversupplied_info **server_info)
223{
224	struct cli_state *cli;
225	static unsigned char badpass[24];
226	static fstring baduser;
227	static BOOL tested_password_server = False;
228	static BOOL bad_password_server = False;
229	NTSTATUS nt_status = NT_STATUS_NOT_IMPLEMENTED;
230	BOOL locally_made_cli = False;
231
232	/*
233	 * Check that the requested domain is not our own machine name.
234	 * If it is, we should never check the PDC here, we use our own local
235	 * password file.
236	 */
237
238	if(is_myname(user_info->domain.str)) {
239		DEBUG(3,("check_smbserver_security: Requested domain was for this machine.\n"));
240		return nt_status;
241	}
242
243	cli = my_private_data;
244
245	if (cli) {
246	} else {
247		cli = server_cryptkey(mem_ctx);
248		locally_made_cli = True;
249	}
250
251	if (!cli || !cli->initialised) {
252		DEBUG(1,("password server is not connected (cli not initilised)\n"));
253		return NT_STATUS_LOGON_FAILURE;
254	}
255
256	if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
257		if (user_info->encrypted) {
258			DEBUG(1,("password server %s is plaintext, but we are encrypted. This just can't work :-(\n", cli->desthost));
259			return NT_STATUS_LOGON_FAILURE;
260		}
261	} else {
262		if (memcmp(cli->secblob.data, auth_context->challenge.data, 8) != 0) {
263			DEBUG(1,("the challenge that the password server (%s) supplied us is not the one we gave our client. This just can't work :-(\n", cli->desthost));
264			return NT_STATUS_LOGON_FAILURE;
265		}
266	}
267
268	if(badpass[0] == 0)
269		memset(badpass, 0x1f, sizeof(badpass));
270
271	if((user_info->nt_resp.length == sizeof(badpass)) &&
272	   !memcmp(badpass, user_info->nt_resp.data, sizeof(badpass))) {
273		/*
274		 * Very unlikely, our random bad password is the same as the users
275		 * password.
276		 */
277		memset(badpass, badpass[0]+1, sizeof(badpass));
278	}
279
280	if(baduser[0] == 0) {
281		fstrcpy(baduser, INVALID_USER_PREFIX);
282		fstrcat(baduser, global_myname());
283	}
284
285	/*
286	 * Attempt a session setup with a totally incorrect password.
287	 * If this succeeds with the guest bit *NOT* set then the password
288	 * server is broken and is not correctly setting the guest bit. We
289	 * need to detect this as some versions of NT4.x are broken. JRA.
290	 */
291
292	/* I sure as hell hope that there aren't servers out there that take
293	 * NTLMv2 and have this bug, as we don't test for that...
294	 *  - abartlet@samba.org
295	 */
296
297	if ((!tested_password_server) && (lp_paranoid_server_security())) {
298		if (cli_session_setup(cli, baduser, (char *)badpass, sizeof(badpass),
299					(char *)badpass, sizeof(badpass), user_info->domain.str)) {
300
301			/*
302			 * We connected to the password server so we
303			 * can say we've tested it.
304			 */
305			tested_password_server = True;
306
307			if ((SVAL(cli->inbuf,smb_vwv2) & 1) == 0) {
308				DEBUG(0,("server_validate: password server %s allows users as non-guest \
309with a bad password.\n", cli->desthost));
310				DEBUG(0,("server_validate: This is broken (and insecure) behaviour. Please do not \
311use this machine as the password server.\n"));
312				cli_ulogoff(cli);
313
314				/*
315				 * Password server has the bug.
316				 */
317				bad_password_server = True;
318				return NT_STATUS_LOGON_FAILURE;
319			}
320			cli_ulogoff(cli);
321		}
322	} else {
323
324		/*
325		 * We have already tested the password server.
326		 * Fail immediately if it has the bug.
327		 */
328
329		if(bad_password_server) {
330			DEBUG(0,("server_validate: [1] password server %s allows users as non-guest \
331with a bad password.\n", cli->desthost));
332			DEBUG(0,("server_validate: [1] This is broken (and insecure) behaviour. Please do not \
333use this machine as the password server.\n"));
334			return NT_STATUS_LOGON_FAILURE;
335		}
336	}
337
338	/*
339	 * Now we know the password server will correctly set the guest bit, or is
340	 * not guest enabled, we can try with the real password.
341	 */
342
343	if (!user_info->encrypted) {
344		/* Plaintext available */
345		if (!cli_session_setup(cli, user_info->smb_name.str,
346				       (char *)user_info->plaintext_password.data,
347				       user_info->plaintext_password.length,
348				       NULL, 0,
349				       user_info->domain.str)) {
350			DEBUG(1,("password server %s rejected the password\n", cli->desthost));
351			/* Make this cli_nt_error() when the conversion is in */
352			nt_status = cli_nt_error(cli);
353		} else {
354			nt_status = NT_STATUS_OK;
355		}
356	} else {
357		if (!cli_session_setup(cli, user_info->smb_name.str,
358				       (char *)user_info->lm_resp.data,
359				       user_info->lm_resp.length,
360				       (char *)user_info->nt_resp.data,
361				       user_info->nt_resp.length,
362				       user_info->domain.str)) {
363			DEBUG(1,("password server %s rejected the password\n", cli->desthost));
364			/* Make this cli_nt_error() when the conversion is in */
365			nt_status = cli_nt_error(cli);
366		} else {
367			nt_status = NT_STATUS_OK;
368		}
369	}
370
371	/* if logged in as guest then reject */
372	if ((SVAL(cli->inbuf,smb_vwv2) & 1) != 0) {
373		DEBUG(1,("password server %s gave us guest only\n", cli->desthost));
374		nt_status = NT_STATUS_LOGON_FAILURE;
375	}
376
377	cli_ulogoff(cli);
378
379	if (NT_STATUS_IS_OK(nt_status)) {
380		fstring real_username;
381		struct passwd *pass;
382
383		if ( (pass = smb_getpwnam( user_info->internal_username.str,
384			real_username, True )) != NULL )
385		{
386			nt_status = make_server_info_pw(server_info, pass->pw_name, pass);
387		}
388		else
389		{
390			nt_status = NT_STATUS_NO_SUCH_USER;
391		}
392	}
393
394	if (locally_made_cli) {
395		cli_shutdown(cli);
396	}
397
398	return(nt_status);
399}
400
401static NTSTATUS auth_init_smbserver(struct auth_context *auth_context, const char* param, auth_methods **auth_method)
402{
403	if (!make_auth_methods(auth_context, auth_method)) {
404		return NT_STATUS_NO_MEMORY;
405	}
406	(*auth_method)->name = "smbserver";
407	(*auth_method)->auth = check_smbserver_security;
408	(*auth_method)->get_chal = auth_get_challenge_server;
409	(*auth_method)->send_keepalive = send_server_keepalive;
410	(*auth_method)->free_private_data = free_server_private_data;
411	return NT_STATUS_OK;
412}
413
414NTSTATUS auth_server_init(void)
415{
416	return smb_register_auth(AUTH_INTERFACE_VERSION, "smbserver", auth_init_smbserver);
417}
418