/* 
   Unix SMB/Netbios implementation.
   Version 1.9.
   Authenticate to a remote server
   Copyright (C) Andrew Tridgell 1992-1998
   Copyright (C) Andrew Bartlett 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.
*/

#include "includes.h"

extern int DEBUGLEVEL;

extern pstring global_myname;

/****************************************************************************
 Return the client state structure.
****************************************************************************/

struct cli_state *server_client(void)
{
	static struct cli_state pw_cli;
	return &pw_cli;
}

/****************************************************************************
 Support for server level security.
****************************************************************************/

struct cli_state *server_cryptkey(void)
{
	struct cli_state *cli;
	fstring desthost;
	struct in_addr dest_ip;
	char *p, *pserver;
	BOOL connected_ok = False;

	cli = server_client();

	if (!cli_initialise(cli))
		return NULL;

        pserver = strdup(lp_passwordserver());
	p = pserver;

        while(next_token( &p, desthost, LIST_SEP, sizeof(desthost))) {
		standard_sub_basic(desthost);
		strupper(desthost);

		if(!resolve_name( desthost, &dest_ip, 0x20)) {
			DEBUG(1,("server_cryptkey: Can't resolve address for %s\n",desthost));
			continue;
		}

		if (ismyip(dest_ip)) {
			DEBUG(1,("Password server loop - disabling password server %s\n",desthost));
			continue;
		}

		if (cli_connect(cli, desthost, &dest_ip)) {
			DEBUG(3,("connected to password server %s\n",desthost));
			connected_ok = True;
			break;
		}
	}

	SAFE_FREE(pserver);

	if (!connected_ok) {
		DEBUG(0,("password server not available\n"));
		cli_shutdown(cli);
		return NULL;
	}

	if (!attempt_netbios_session_request(cli, global_myname, desthost, &dest_ip))
		return NULL;

	DEBUG(3,("got session\n"));

	if (!cli_negprot(cli)) {
		DEBUG(1,("%s rejected the negprot\n",desthost));
		cli_shutdown(cli);
		return NULL;
	}

	if (cli->protocol < PROTOCOL_LANMAN2 ||
	    !(cli->sec_mode & 1)) {
		DEBUG(1,("%s isn't in user level security mode\n",desthost));
		cli_shutdown(cli);
		return NULL;
	}

	DEBUG(3,("password server OK\n"));

	return cli;
}


/****************************************************************************
 Check for a valid username and password in security=server mode.
  - Validate a password with the password server.
****************************************************************************/

NTSTATUS check_server_security(const auth_usersupplied_info *user_info, auth_serversupplied_info *server_info)
{
	struct cli_state *cli;
	static unsigned char badpass[24];
	static fstring baduser; 
	static BOOL tested_password_server = False;
	static BOOL bad_password_server = False;
	NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;

	cli = server_client();

	if (!cli->initialised) {
		DEBUG(1,("password server %s is not connected\n", cli->desthost));
		return(NT_STATUS_LOGON_FAILURE);
	}  

	if(badpass[0] == 0)
		memset(badpass, 0x1f, sizeof(badpass));

	if((user_info->nt_resp.len == sizeof(badpass)) && 
	   !memcmp(badpass, user_info->nt_resp.buffer, sizeof(badpass))) {
		/* 
		 * Very unlikely, our random bad password is the same as the users
		 * password.
		 */
		memset(badpass, badpass[0]+1, sizeof(badpass));
	}

	if(baduser[0] == 0) {
		fstrcpy(baduser, INVALID_USER_PREFIX);
		fstrcat(baduser, global_myname);
	}

	/*
	 * Attempt a session setup with a totally incorrect password.
	 * If this succeeds with the guest bit *NOT* set then the password
	 * server is broken and is not correctly setting the guest bit. We
	 * need to detect this as some versions of NT4.x are broken. JRA.
	 */

	/* I sure as hell hope that there arn't servers out there that take 
	 * NTLMv2 and have this bug, as we don't test for that... 
	 *  - abartlet@samba.org
	 */

	if ((!tested_password_server) && (lp_paranoid_server_security())) {
		if (cli_session_setup(cli, baduser, (char *)badpass, sizeof(badpass), 
					(char *)badpass, sizeof(badpass), user_info->domain.str)) {

			/*
			 * We connected to the password server so we
			 * can say we've tested it.
			 */
			tested_password_server = True;

			if ((SVAL(cli->inbuf,smb_vwv2) & 1) == 0) {
				DEBUG(0,("server_validate: password server %s allows users as non-guest \
with a bad password.\n", cli->desthost));
				DEBUG(0,("server_validate: This is broken (and insecure) behaviour. Please do not \
use this machine as the password server.\n"));
				cli_ulogoff(cli);

				/*
				 * Password server has the bug.
				 */
				bad_password_server = True;
				return NT_STATUS_LOGON_FAILURE;
			}
			cli_ulogoff(cli);
		}
	} else {

		/*
		 * We have already tested the password server.
		 * Fail immediately if it has the bug.
		 */

		if(bad_password_server) {
			DEBUG(0,("server_validate: [1] password server %s allows users as non-guest \
with a bad password.\n", cli->desthost));
			DEBUG(0,("server_validate: [1] This is broken (and insecure) behaviour. Please do not \
use this machine as the password server.\n"));
			return NT_STATUS_LOGON_FAILURE;
		}
	}

	/*
	 * Now we know the password server will correctly set the guest bit, or is
	 * not guest enabled, we can try with the real password.
	 */

	if (!cli_session_setup(cli, user_info->smb_username.str, 
			       (char *)user_info->lm_resp.buffer, 
			       user_info->lm_resp.len, 
			       (char *)user_info->nt_resp.buffer, 
			       user_info->nt_resp.len, 
			       user_info->domain.str)) {
		DEBUG(1,("password server %s rejected the password\n", cli->desthost));
		/* Make this cli_nt_error() when the conversion is in */
		nt_status = cli_nt_error(cli);
	} else {
		nt_status = NT_STATUS_OK;
	}

	/* if logged in as guest then reject */
	if ((SVAL(cli->inbuf,smb_vwv2) & 1) != 0) {
		DEBUG(1,("password server %s gave us guest only\n", cli->desthost));
		nt_status = NT_STATUS_LOGON_FAILURE;
	}

	cli_ulogoff(cli);

	return(nt_status);
}