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
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
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.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "includes.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_AUTH
-extern pstring global_myname;
extern userdom_struct current_user_info;
/****************************************************************************
static struct cli_state *server_cryptkey(TALLOC_CTX *mem_ctx)
{
struct cli_state *cli = NULL;
- fstring desthost;
- struct in_addr dest_ip;
- char *p, *pserver;
- BOOL connected_ok = False;
-
- if (!(cli = cli_initialise(cli)))
+ char *desthost = NULL;
+ struct sockaddr_storage dest_ss;
+ const char *p;
+ char *pserver = NULL;
+ bool connected_ok = False;
+ struct named_mutex *mutex = NULL;
+
+ if (!(cli = cli_initialise()))
return NULL;
/* security = server just can't function with spnego */
pserver = talloc_strdup(mem_ctx, lp_passwordserver());
p = pserver;
- while(next_token( &p, desthost, LIST_SEP, sizeof(desthost))) {
- standard_sub_basic(current_user_info.smb_name, desthost);
- strupper(desthost);
+ while(next_token_talloc(mem_ctx, &p, &desthost, LIST_SEP)) {
+ NTSTATUS status;
+
+ desthost = talloc_sub_basic(mem_ctx,
+ current_user_info.smb_name,
+ current_user_info.domain,
+ desthost);
+ if (!desthost) {
+ return NULL;
+ }
+ strupper_m(desthost);
- if(!resolve_name( desthost, &dest_ip, 0x20)) {
+ if(!resolve_name( desthost, &dest_ss, 0x20)) {
DEBUG(1,("server_cryptkey: Can't resolve address for %s\n",desthost));
continue;
}
- if (ismyip(dest_ip)) {
+ if (ismyaddr((struct sockaddr *)&dest_ss)) {
DEBUG(1,("Password server loop - disabling password server %s\n",desthost));
continue;
}
- if (cli_connect(cli, desthost, &dest_ip)) {
+ /* we use a mutex to prevent two connections at once - when a
+ Win2k PDC get two connections where one hasn't completed a
+ session setup yet it will send a TCP reset to the first
+ connection (tridge) */
+
+ mutex = grab_named_mutex(talloc_tos(), desthost, 10);
+ if (mutex == NULL) {
+ cli_shutdown(cli);
+ return NULL;
+ }
+
+ status = cli_connect(cli, desthost, &dest_ss);
+ if (NT_STATUS_IS_OK(status)) {
DEBUG(3,("connected to password server %s\n",desthost));
connected_ok = True;
break;
}
+ DEBUG(10,("server_cryptkey: failed to connect to server %s. Error %s\n",
+ desthost, nt_errstr(status) ));
+ TALLOC_FREE(mutex);
}
if (!connected_ok) {
return NULL;
}
- if (!attempt_netbios_session_request(cli, global_myname, desthost, &dest_ip))
+ if (!attempt_netbios_session_request(&cli, global_myname(),
+ desthost, &dest_ss)) {
+ TALLOC_FREE(mutex);
+ DEBUG(1,("password server fails session request\n"));
+ cli_shutdown(cli);
return NULL;
-
+ }
+
if (strequal(desthost,myhostname())) {
- exit_server("Password server loop!");
+ exit_server_cleanly("Password server loop!");
}
-
+
DEBUG(3,("got session\n"));
if (!cli_negprot(cli)) {
+ TALLOC_FREE(mutex);
DEBUG(1,("%s rejected the negprot\n",desthost));
cli_shutdown(cli);
return NULL;
}
if (cli->protocol < PROTOCOL_LANMAN2 ||
- !(cli->sec_mode & 1)) {
+ !(cli->sec_mode & NEGOTIATE_SECURITY_USER_LEVEL)) {
+ TALLOC_FREE(mutex);
DEBUG(1,("%s isn't in user level security mode\n",desthost));
cli_shutdown(cli);
return NULL;
}
+ /* Get the first session setup done quickly, to avoid silly
+ Win2k bugs. (The next connection to the server will kill
+ this one...
+ */
+
+ if (!NT_STATUS_IS_OK(cli_session_setup(cli, "", "", 0, "", 0,
+ ""))) {
+ TALLOC_FREE(mutex);
+ DEBUG(0,("%s rejected the initial session setup (%s)\n",
+ desthost, cli_errstr(cli)));
+ cli_shutdown(cli);
+ return NULL;
+ }
+
+ TALLOC_FREE(mutex);
+
DEBUG(3,("password server OK\n"));
return cli;
}
+struct server_security_state {
+ struct cli_state *cli;
+};
+
/****************************************************************************
- Clean up our allocated cli.
+ Send a 'keepalive' packet down the cli pipe.
****************************************************************************/
-static void free_server_private_data(void **private_data_pointer)
+static bool send_server_keepalive(const struct timeval *now,
+ void *private_data)
{
- struct cli_state **cli = (struct cli_state **)private_data_pointer;
- if (*cli && (*cli)->initialised) {
- cli_shutdown(*cli);
+ struct server_security_state *state = talloc_get_type_abort(
+ private_data, struct server_security_state);
+
+ if (!state->cli || !state->cli->initialised) {
+ return False;
+ }
+
+ if (send_keepalive(state->cli->fd)) {
+ return True;
}
+
+ DEBUG( 2, ( "send_server_keepalive: password server keepalive "
+ "failed.\n"));
+ cli_shutdown(state->cli);
+ state->cli = NULL;
+ return False;
}
-/****************************************************************************
- Send a 'keepalive' packet down the cli pipe.
-****************************************************************************/
+static int destroy_server_security(struct server_security_state *state)
+{
+ if (state->cli) {
+ cli_shutdown(state->cli);
+ }
+ return 0;
+}
-static void send_server_keepalive(void **private_data_pointer)
+static struct server_security_state *make_server_security_state(struct cli_state *cli)
{
- struct cli_state **cli = (struct cli_state **)private_data_pointer;
-
- /* also send a keepalive to the password server if its still
- connected */
- if (cli && *cli && (*cli)->initialised) {
- if (!send_keepalive((*cli)->fd)) {
- DEBUG( 2, ( "password server keepalive failed.\n"));
- cli_shutdown(*cli);
+ struct server_security_state *result;
+
+ if (!(result = talloc(NULL, struct server_security_state))) {
+ DEBUG(0, ("talloc failed\n"));
+ cli_shutdown(cli);
+ return NULL;
+ }
+
+ result->cli = cli;
+ talloc_set_destructor(result, destroy_server_security);
+
+ if (lp_keepalive() != 0) {
+ struct timeval interval;
+ interval.tv_sec = lp_keepalive();
+ interval.tv_usec = 0;
+
+ if (event_add_idle(smbd_event_context(), result, interval,
+ "server_security_keepalive",
+ send_server_keepalive,
+ result) == NULL) {
+ DEBUG(0, ("event_add_idle failed\n"));
+ TALLOC_FREE(result);
+ return NULL;
}
}
+
+ return result;
}
/****************************************************************************
if (cli) {
DEBUG(3,("using password server validation\n"));
- if ((cli->sec_mode & 2) == 0) {
+ if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
/* We can't work with unencrypted password servers
unless 'encrypt passwords = no' */
DEBUG(5,("make_auth_info_server: Server is unencrypted, no challenge available..\n"));
/* However, it is still a perfectly fine connection
to pass that unencrypted password over */
- *my_private_data = (void *)cli;
- return data_blob(NULL, 0);
-
+ *my_private_data =
+ (void *)make_server_security_state(cli);
+ return data_blob_null;
} else if (cli->secblob.length < 8) {
/* We can't do much if we don't get a full challenge */
DEBUG(2,("make_auth_info_server: Didn't receive a full challenge from server\n"));
cli_shutdown(cli);
- return data_blob(NULL, 0);
+ return data_blob_null;
}
- *my_private_data = (void *)cli;
+ if (!(*my_private_data = (void *)make_server_security_state(cli))) {
+ return data_blob(NULL,0);
+ }
/* The return must be allocated on the caller's mem_ctx, as our own will be
destoyed just after the call. */
return data_blob_talloc(auth_context->mem_ctx, cli->secblob.data,8);
} else {
- return data_blob(NULL, 0);
+ return data_blob_null;
}
}
const auth_usersupplied_info *user_info,
auth_serversupplied_info **server_info)
{
+ struct server_security_state *state = talloc_get_type_abort(
+ my_private_data, struct server_security_state);
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;
- BOOL locally_made_cli = False;
-
- /*
- * Check that the requested domain is not our own machine name.
- * If it is, we should never check the PDC here, we use our own local
- * password file.
- */
-
- if(is_netbios_alias_or_name(user_info->domain.str)) {
- DEBUG(3,("check_smbserver_security: Requested domain was for this machine.\n"));
- return NT_STATUS_LOGON_FAILURE;
- }
+ static bool tested_password_server = False;
+ static bool bad_password_server = False;
+ NTSTATUS nt_status = NT_STATUS_NOT_IMPLEMENTED;
+ bool locally_made_cli = False;
- cli = my_private_data;
+ cli = state->cli;
if (cli) {
} else {
}
if (!cli || !cli->initialised) {
- DEBUG(1,("password server is not connected (cli not initilised)\n"));
+ DEBUG(1,("password server is not connected (cli not initialised)\n"));
return NT_STATUS_LOGON_FAILURE;
}
- if ((cli->sec_mode & 2) == 0) {
+ if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
if (user_info->encrypted) {
DEBUG(1,("password server %s is plaintext, but we are encrypted. This just can't work :-(\n", cli->desthost));
return NT_STATUS_LOGON_FAILURE;
}
}
- if(badpass[0] == 0)
- memset(badpass, 0x1f, sizeof(badpass));
-
- if((user_info->nt_resp.length == sizeof(badpass)) &&
- !memcmp(badpass, user_info->nt_resp.data, 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
* 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
+ /* I sure as hell hope that there aren'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)) {
+ unsigned char badpass[24];
+ char *baduser = NULL;
+
+ memset(badpass, 0x1f, sizeof(badpass));
+
+ if((user_info->nt_resp.length == sizeof(badpass)) &&
+ !memcmp(badpass, user_info->nt_resp.data, sizeof(badpass))) {
+ /*
+ * Very unlikely, our random bad password is the same as the users
+ * password.
+ */
+ memset(badpass, badpass[0]+1, sizeof(badpass));
+ }
+
+ baduser = talloc_asprintf(mem_ctx,
+ "%s%s",
+ INVALID_USER_PREFIX,
+ global_myname());
+ if (!baduser) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (NT_STATUS_IS_OK(cli_session_setup(cli, baduser,
+ (char *)badpass,
+ sizeof(badpass),
+ (char *)badpass,
+ sizeof(badpass),
+ user_info->domain))) {
/*
* We connected to the password server so we
if (!user_info->encrypted) {
/* Plaintext available */
- if (!cli_session_setup(cli, user_info->smb_name.str,
- (char *)user_info->plaintext_password.data,
- user_info->plaintext_password.length,
- NULL, 0,
- 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;
- }
+ nt_status = cli_session_setup(
+ cli, user_info->smb_name,
+ (char *)user_info->plaintext_password.data,
+ user_info->plaintext_password.length,
+ NULL, 0, user_info->domain);
+
} else {
- if (!cli_session_setup(cli, user_info->smb_name.str,
- (char *)user_info->lm_resp.data,
- user_info->lm_resp.length,
- (char *)user_info->nt_resp.data,
- user_info->nt_resp.length,
- 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;
- }
+ nt_status = cli_session_setup(
+ cli, user_info->smb_name,
+ (char *)user_info->lm_resp.data,
+ user_info->lm_resp.length,
+ (char *)user_info->nt_resp.data,
+ user_info->nt_resp.length,
+ user_info->domain);
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1,("password server %s rejected the password: %s\n",
+ cli->desthost, nt_errstr(nt_status)));
}
/* if logged in as guest then reject */
cli_ulogoff(cli);
- if NT_STATUS_IS_OK(nt_status) {
- struct passwd *pass = Get_Pwnam(user_info->internal_username.str);
- if (pass) {
- if (!make_server_info_pw(server_info, pass)) {
- nt_status = NT_STATUS_NO_MEMORY;
+ if (NT_STATUS_IS_OK(nt_status)) {
+ fstring real_username;
+ struct passwd *pass;
+
+ if ( (pass = smb_getpwnam( NULL, user_info->internal_username,
+ real_username, True )) != NULL )
+ {
+ /* if a real user check pam account restrictions */
+ /* only really perfomed if "obey pam restriction" is true */
+ nt_status = smb_pam_accountcheck(pass->pw_name);
+ if ( !NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1, ("PAM account restriction prevents user login\n"));
+ } else {
+
+ nt_status = make_server_info_pw(server_info, pass->pw_name, pass);
}
- } else {
+ TALLOC_FREE(pass);
+ }
+ else
+ {
nt_status = NT_STATUS_NO_SUCH_USER;
}
}
return(nt_status);
}
-BOOL auth_init_smbserver(struct auth_context *auth_context, auth_methods **auth_method)
+static NTSTATUS auth_init_smbserver(struct auth_context *auth_context, const char* param, auth_methods **auth_method)
{
if (!make_auth_methods(auth_context, auth_method)) {
- return False;
+ return NT_STATUS_NO_MEMORY;
}
+ (*auth_method)->name = "smbserver";
(*auth_method)->auth = check_smbserver_security;
(*auth_method)->get_chal = auth_get_challenge_server;
- (*auth_method)->send_keepalive = send_server_keepalive;
- (*auth_method)->free_private_data = free_server_private_data;
- return True;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS auth_server_init(void)
+{
+ return smb_register_auth(AUTH_INTERFACE_VERSION, "smbserver", auth_init_smbserver);
}