r23784: use the GPLv3 boilerplate as recommended by the FSF and the license text
[tprouty/samba.git] / source / auth / auth_server.c
index a3cfc3a0e609fe8464d91f78102d2bb55b8efb18..ba7507ef4f1f4584fa9558abdb61415ebaf8c939 100644 (file)
@@ -1,13 +1,12 @@
 /* 
-   Unix SMB/Netbios implementation.
-   Version 1.9.
+   Unix SMB/CIFS implementation.
    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
+   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"
 
-extern pstring global_myname;
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+extern userdom_struct current_user_info;
 
 /****************************************************************************
  Support for server level security.
 ****************************************************************************/
 
-static struct cli_state *server_cryptkey(void)
+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;
+       const char *p;
+       char *pserver;
        BOOL connected_ok = False;
 
-       if (!(cli = cli_initialise(cli)))
+       if (!(cli = cli_initialise()))
                return NULL;
 
        /* security = server just can't function with spnego */
        cli->use_spnego = False;
 
-        pserver = strdup(lp_passwordserver());
+        pserver = talloc_strdup(mem_ctx, lp_passwordserver());
        p = pserver;
 
         while(next_token( &p, desthost, LIST_SEP, sizeof(desthost))) {
-               standard_sub_basic(desthost);
-               strupper(desthost);
+               NTSTATUS status;
+
+               standard_sub_basic(current_user_info.smb_name, current_user_info.domain,
+                                  desthost, sizeof(desthost));
+               strupper_m(desthost);
 
                if(!resolve_name( desthost, &dest_ip, 0x20)) {
                        DEBUG(1,("server_cryptkey: Can't resolve address for %s\n",desthost));
@@ -59,111 +64,190 @@ static struct cli_state *server_cryptkey(void)
                        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) */
+
+               if (!grab_server_mutex(desthost)) {
+                       return NULL;
+               }
+
+               status = cli_connect(cli, desthost, &dest_ip);
+               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) ));
        }
 
-       SAFE_FREE(pserver);
-
        if (!connected_ok) {
+               release_server_mutex();
                DEBUG(0,("password server not available\n"));
                cli_shutdown(cli);
                return NULL;
        }
-
-       if (!attempt_netbios_session_request(cli, global_myname, desthost, &dest_ip))
+       
+       if (!attempt_netbios_session_request(&cli, global_myname(), 
+                                            desthost, &dest_ip)) {
+               release_server_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)) {
                DEBUG(1,("%s rejected the negprot\n",desthost));
+               release_server_mutex();
                cli_shutdown(cli);
                return NULL;
        }
 
        if (cli->protocol < PROTOCOL_LANMAN2 ||
-           !(cli->sec_mode & 1)) {
+           !(cli->sec_mode & NEGOTIATE_SECURITY_USER_LEVEL)) {
                DEBUG(1,("%s isn't in user level security mode\n",desthost));
+               release_server_mutex();
                cli_shutdown(cli);
                return NULL;
        }
 
-       DEBUG(3,("password server OK\n"));
+       /* 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,
+                                              ""))) {
+               DEBUG(0,("%s rejected the initial session setup (%s)\n",
+                        desthost, cli_errstr(cli)));
+               release_server_mutex();
+               cli_shutdown(cli);
+               return NULL;
+       }
+       
+       release_server_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);
-               
-               SAFE_FREE(*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);
-                       SAFE_FREE(*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;
 }
 
 /****************************************************************************
  Get the challenge out of a password server.
 ****************************************************************************/
 
-static DATA_BLOB auth_get_challenge_server(void **my_private_data, const struct authsupplied_info *auth_info) 
+static DATA_BLOB auth_get_challenge_server(const struct auth_context *auth_context,
+                                          void **my_private_data, 
+                                          TALLOC_CTX *mem_ctx)
 {
-       struct cli_state *cli = server_cryptkey();
+       struct cli_state *cli = server_cryptkey(mem_ctx);
        
        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"));
-
-                       *my_private_data = (void *)cli;
-                       return data_blob(NULL, 0);
+                       
+                       /* However, it is still a perfectly fine connection
+                          to pass that unencrypted password over */
+                       *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;
-               
-               return data_blob(cli->secblob.data,8);
+               if (!(*my_private_data = (void *)make_server_security_state(cli))) {
+                       return data_blob_null;
+               }
+
+               /* 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;
        }
 }
 
@@ -173,39 +257,44 @@ static DATA_BLOB auth_get_challenge_server(void **my_private_data, const struct
   - Validate a password with the password server.
 ****************************************************************************/
 
-static NTSTATUS check_smbserver_security(void *my_private_data, 
-                                 const auth_usersupplied_info *user_info, 
-                                 const auth_authsupplied_info *auth_info,
-                                 auth_serversupplied_info **server_info)
+static NTSTATUS check_smbserver_security(const struct auth_context *auth_context,
+                                        void *private_data, 
+                                        TALLOC_CTX *mem_ctx,
+                                        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;
+       NTSTATUS nt_status = NT_STATUS_NOT_IMPLEMENTED;
        BOOL locally_made_cli = False;
+       struct server_security_state *state;
 
-       cli = my_private_data;
+       state = talloc_get_type_abort(
+               private_data, struct server_security_state);
+
+       cli = state->cli;
        
        if (cli) {
        } else {
-               cli = server_cryptkey();
+               cli = server_cryptkey(mem_ctx);
                locally_made_cli = True;
        }
 
        if (!cli || !cli->initialised) {
-               DEBUG(1,("password server %s is not connected\n", cli->desthost));
+               DEBUG(1,("password server is not connected (cli not initilised)\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;         
                }
        } else {
-               if (memcmp(cli->secblob.data, auth_info->challenge.data, 8) != 0) {
+               if (memcmp(cli->secblob.data, auth_context->challenge.data, 8) != 0) {
                        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));
                        return NT_STATUS_LOGON_FAILURE;         
                }
@@ -225,7 +314,7 @@ static NTSTATUS check_smbserver_security(void *my_private_data,
 
        if(baduser[0] == 0) {
                fstrcpy(baduser, INVALID_USER_PREFIX);
-               fstrcat(baduser, global_myname);
+               fstrcat(baduser, global_myname());
        }
 
        /*
@@ -235,14 +324,18 @@ static NTSTATUS check_smbserver_security(void *my_private_data,
         * 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)) {
+               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
@@ -288,30 +381,25 @@ use this machine as the password server.\n"));
 
        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 */
@@ -322,33 +410,49 @@ use this machine as the password server.\n"));
 
        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;
                }
        }
 
        if (locally_made_cli) {
                cli_shutdown(cli);
-               SAFE_FREE(cli);
        }
 
        return(nt_status);
 }
 
-BOOL auth_init_smbserver(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_method)) {
-               return False;
+       if (!make_auth_methods(auth_context, auth_method)) {
+               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);
 }