[GLUE] Rsync SAMBA_3_2_0 SVN r25598 in order to create the v3-2-test branch.
[sfrench/samba-autobuild/.git] / source3 / auth / auth_server.c
index 5190d45c20364986d1640f825eb773ecf4cae4e1..b7669e945cfb1f5f6ae6133b3144042ae6e5a8eb 100644 (file)
@@ -6,7 +6,7 @@
 
    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;
 
 /****************************************************************************
@@ -33,10 +34,11 @@ 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 */
@@ -46,8 +48,11 @@ static struct cli_state *server_cryptkey(TALLOC_CTX *mem_ctx)
        p = pserver;
 
         while(next_token( &p, desthost, LIST_SEP, sizeof(desthost))) {
-               standard_sub_basic(current_user_info.smb_name, 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,74 +64,148 @@ static struct cli_state *server_cryptkey(TALLOC_CTX *mem_ctx)
                        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) ));
        }
 
        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);
+       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;
 }
 
 /****************************************************************************
@@ -142,30 +221,32 @@ static DATA_BLOB auth_get_challenge_server(const struct auth_context *auth_conte
        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;
        }
 }
 
@@ -186,21 +267,10 @@ static NTSTATUS check_smbserver_security(const struct auth_context *auth_context
        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;
 
-       /* 
-        * 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;
-       }
-
-       cli = my_private_data;
+       cli = (struct cli_state *)my_private_data;
        
        if (cli) {
        } else {
@@ -213,7 +283,7 @@ static NTSTATUS check_smbserver_security(const struct auth_context *auth_context
                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;         
@@ -239,7 +309,7 @@ static NTSTATUS check_smbserver_security(const struct auth_context *auth_context
 
        if(baduser[0] == 0) {
                fstrcpy(baduser, INVALID_USER_PREFIX);
-               fstrcat(baduser, global_myname);
+               fstrcat(baduser, global_myname());
        }
 
        /*
@@ -249,14 +319,18 @@ static NTSTATUS check_smbserver_security(const struct auth_context *auth_context
         * 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
@@ -302,30 +376,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 */
@@ -336,13 +405,26 @@ 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;
                }
        }
@@ -354,14 +436,18 @@ use this machine as the password server.\n"));
        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);
 }