s3-auth Move map to guest to directly after the check_password calls
[amitay/samba.git] / source3 / smbd / sesssetup.c
index 16ea2ebfa911b09a7798cdac0a0880abecbf4bf3..329b8b6aa5255f1415f3af61f36a7f49c11e75cd 100644 (file)
 */
 
 #include "includes.h"
+#include "../lib/tsocket/tsocket.h"
+#include "smbd/smbd.h"
 #include "smbd/globals.h"
 #include "../libcli/auth/spnego.h"
-
-extern enum protocol_types Protocol;
+#include "../libcli/auth/ntlmssp.h"
+#include "ntlmssp_wrap.h"
+#include "../librpc/gen_ndr/krb5pac.h"
+#include "libads/kerberos_proto.h"
+#include "../lib/util/asn1.h"
+#include "auth.h"
+#include "messages.h"
+#include "smbprofile.h"
+#include "../libcli/security/security.h"
 
 /* For split krb5 SPNEGO blobs. */
 struct pending_auth_data {
@@ -37,34 +46,6 @@ struct pending_auth_data {
        DATA_BLOB partial_data;
 };
 
-/*
-  on a logon error possibly map the error to success if "map to guest"
-  is set approriately
-*/
-static NTSTATUS do_map_to_guest(NTSTATUS status,
-                               auth_serversupplied_info **server_info,
-                               const char *user, const char *domain)
-{
-       if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
-               if ((lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_USER) ||
-                   (lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_PASSWORD)) {
-                       DEBUG(3,("No such user %s [%s] - using guest account\n",
-                                user, domain));
-                       status = make_server_info_guest(NULL, server_info);
-               }
-       }
-
-       if (NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
-               if (lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_PASSWORD) {
-                       DEBUG(3,("Registered username %s for guest access\n",
-                               user));
-                       status = make_server_info_guest(NULL, server_info);
-               }
-       }
-
-       return status;
-}
-
 /****************************************************************************
  Add the standard 'Samba' signature to the end of the session setup.
 ****************************************************************************/
@@ -129,32 +110,31 @@ static void reply_sesssetup_blob(struct smb_request *req,
  Do a 'guest' logon, getting back the
 ****************************************************************************/
 
-static NTSTATUS check_guest_password(auth_serversupplied_info **server_info)
+static NTSTATUS check_guest_password(const struct tsocket_address *remote_address,
+                                    struct auth_serversupplied_info **server_info)
 {
        struct auth_context *auth_context;
-       auth_usersupplied_info *user_info = NULL;
+       struct auth_usersupplied_info *user_info = NULL;
 
        NTSTATUS nt_status;
-       unsigned char chal[8];
-
-       ZERO_STRUCT(chal);
+       static unsigned char chal[8] = { 0, };
 
        DEBUG(3,("Got anonymous request\n"));
 
-       if (!NT_STATUS_IS_OK(nt_status = make_auth_context_fixed(&auth_context,
-                                       chal))) {
+       nt_status = make_auth_context_fixed(talloc_tos(), &auth_context, chal);
+       if (!NT_STATUS_IS_OK(nt_status)) {
                return nt_status;
        }
 
-       if (!make_user_info_guest(&user_info)) {
-               (auth_context->free)(&auth_context);
+       if (!make_user_info_guest(remote_address, &user_info)) {
+               TALLOC_FREE(auth_context);
                return NT_STATUS_NO_MEMORY;
        }
 
        nt_status = auth_context->check_ntlm_password(auth_context,
                                                user_info,
                                                server_info);
-       (auth_context->free)(&auth_context);
+       TALLOC_FREE(auth_context);
        free_user_info(&user_info);
        return nt_status;
 }
@@ -185,7 +165,7 @@ static bool make_krb5_skew_error(DATA_BLOB *pblob_out)
                return False;
        }
        /* Create server principal. */
-       asprintf(&host_princ_s, "%s$@%s", global_myname(), lp_realm());
+       asprintf(&host_princ_s, "%s$@%s", lp_netbios_name(), lp_realm());
        if (!host_princ_s) {
                goto out;
        }
@@ -237,23 +217,22 @@ static void reply_spnego_kerberos(struct smb_request *req,
 {
        TALLOC_CTX *mem_ctx;
        DATA_BLOB ticket;
-       char *client, *p, *domain;
-       fstring netbios_domain_name;
        struct passwd *pw;
-       fstring user;
        int sess_vuid = req->vuid;
        NTSTATUS ret = NT_STATUS_OK;
-       struct PAC_DATA *pac_data = NULL;
        DATA_BLOB ap_rep, ap_rep_wrapped, response;
-       auth_serversupplied_info *server_info = NULL;
+       struct auth_session_info *session_info = NULL;
        DATA_BLOB session_key = data_blob_null;
        uint8 tok_id[2];
        DATA_BLOB nullblob = data_blob_null;
-       fstring real_username;
        bool map_domainuser_to_guest = False;
        bool username_was_mapped;
        struct PAC_LOGON_INFO *logon_info = NULL;
-       struct smbd_server_connection *sconn = smbd_server_conn;
+       struct smbd_server_connection *sconn = req->sconn;
+       char *principal;
+       char *user;
+       char *domain;
+       char *real_username;
 
        ZERO_STRUCT(ticket);
        ZERO_STRUCT(ap_rep);
@@ -269,14 +248,14 @@ static void reply_spnego_kerberos(struct smb_request *req,
                return;
        }
 
-       if (!spnego_parse_krb5_wrap(*secblob, &ticket, tok_id)) {
+       if (!spnego_parse_krb5_wrap(mem_ctx, *secblob, &ticket, tok_id)) {
                talloc_destroy(mem_ctx);
                reply_nterror(req, nt_status_squash(NT_STATUS_LOGON_FAILURE));
                return;
        }
 
        ret = ads_verify_ticket(mem_ctx, lp_realm(), 0, &ticket,
-                               &client, &pac_data, &ap_rep,
+                               &principal, &logon_info, &ap_rep,
                                &session_key, True);
 
        data_blob_free(&ticket);
@@ -337,11 +316,14 @@ static void reply_spnego_kerberos(struct smb_request *req,
                return;
        }
 
-       DEBUG(3,("Ticket name is [%s]\n", client));
-
-       p = strchr_m(client, '@');
-       if (!p) {
-               DEBUG(3,("Doesn't look like a valid principal\n"));
+       ret = get_user_from_kerberos_info(talloc_tos(),
+                                         sconn->remote_hostname,
+                                         principal, logon_info,
+                                         &username_was_mapped,
+                                         &map_domainuser_to_guest,
+                                         &user, &domain,
+                                         &real_username, &pw);
+       if (!NT_STATUS_IS_OK(ret)) {
                data_blob_free(&ap_rep);
                data_blob_free(&session_key);
                talloc_destroy(mem_ctx);
@@ -349,203 +331,43 @@ static void reply_spnego_kerberos(struct smb_request *req,
                return;
        }
 
-       *p = 0;
-
        /* save the PAC data if we have it */
-
-       if (pac_data) {
-               logon_info = get_logon_info_from_pac(pac_data);
-               if (logon_info) {
-                       netsamlogon_cache_store( client, &logon_info->info3 );
-               }
-       }
-
-       if (!strequal(p+1, lp_realm())) {
-               DEBUG(3,("Ticket for foreign realm %s@%s\n", client, p+1));
-               if (!lp_allow_trusted_domains()) {
-                       data_blob_free(&ap_rep);
-                       data_blob_free(&session_key);
-                       talloc_destroy(mem_ctx);
-                       reply_nterror(req, nt_status_squash(
-                                             NT_STATUS_LOGON_FAILURE));
-                       return;
-               }
-       }
-
-       /* this gives a fully qualified user name (ie. with full realm).
-          that leads to very long usernames, but what else can we do? */
-
-       domain = p+1;
-
-       if (logon_info && logon_info->info3.base.domain.string) {
-               fstrcpy(netbios_domain_name,
-                       logon_info->info3.base.domain.string);
-               domain = netbios_domain_name;
-               DEBUG(10, ("Mapped to [%s] (using PAC)\n", domain));
-
-       } else {
-
-               /* If we have winbind running, we can (and must) shorten the
-                  username by using the short netbios name. Otherwise we will
-                  have inconsistent user names. With Kerberos, we get the
-                  fully qualified realm, with ntlmssp we get the short
-                  name. And even w2k3 does use ntlmssp if you for example
-                  connect to an ip address. */
-
-               wbcErr wbc_status;
-               struct wbcDomainInfo *info = NULL;
-
-               DEBUG(10, ("Mapping [%s] to short name\n", domain));
-
-               wbc_status = wbcDomainInfo(domain, &info);
-
-               if (WBC_ERROR_IS_OK(wbc_status)) {
-
-                       fstrcpy(netbios_domain_name,
-                               info->short_name);
-
-                       wbcFreeMemory(info);
-                       domain = netbios_domain_name;
-                       DEBUG(10, ("Mapped to [%s] (using Winbind)\n", domain));
-               } else {
-                       DEBUG(3, ("Could not find short name: %s\n",
-                               wbcErrorString(wbc_status)));
-               }
-       }
-
-       fstr_sprintf(user, "%s%c%s", domain, *lp_winbind_separator(), client);
-
-       /* lookup the passwd struct, create a new user if necessary */
-
-       username_was_mapped = map_username(sconn, user);
-
-       pw = smb_getpwnam( mem_ctx, user, real_username, True );
-
-       if (pw) {
-               /* if a real user check pam account restrictions */
-               /* only really perfomed if "obey pam restriction" is true */
-               /* do this before an eventual mapping to guest occurs */
-               ret = smb_pam_accountcheck(pw->pw_name);
-               if (  !NT_STATUS_IS_OK(ret)) {
-                       DEBUG(1,("PAM account restriction "
-                               "prevents user login\n"));
-                       data_blob_free(&ap_rep);
-                       data_blob_free(&session_key);
-                       TALLOC_FREE(mem_ctx);
-                       reply_nterror(req, nt_status_squash(ret));
-                       return;
-               }
-       }
-
-       if (!pw) {
-
-               /* this was originally the behavior of Samba 2.2, if a user
-                  did not have a local uid but has been authenticated, then
-                  map them to a guest account */
-
-               if (lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_UID){
-                       map_domainuser_to_guest = True;
-                       fstrcpy(user,lp_guestaccount());
-                       pw = smb_getpwnam( mem_ctx, user, real_username, True );
-               }
-
-               /* extra sanity check that the guest account is valid */
-
-               if ( !pw ) {
-                       DEBUG(1,("Username %s is invalid on this system\n",
-                               user));
-                       data_blob_free(&ap_rep);
-                       data_blob_free(&session_key);
-                       TALLOC_FREE(mem_ctx);
-                       reply_nterror(req, nt_status_squash(
-                                             NT_STATUS_LOGON_FAILURE));
-                       return;
-               }
+       if (logon_info) {
+               netsamlogon_cache_store(user, &logon_info->info3);
        }
 
        /* setup the string used by %U */
-
-       sub_set_smb_name( real_username );
-       reload_services(True);
-
-       if ( map_domainuser_to_guest ) {
-               make_server_info_guest(NULL, &server_info);
-       } else if (logon_info) {
-               /* pass the unmapped username here since map_username()
-                  will be called again from inside make_server_info_info3() */
-
-               ret = make_server_info_info3(mem_ctx, client, domain,
-                                            &server_info, &logon_info->info3);
-               if ( !NT_STATUS_IS_OK(ret) ) {
-                       DEBUG(1,("make_server_info_info3 failed: %s!\n",
-                                nt_errstr(ret)));
-                       data_blob_free(&ap_rep);
-                       data_blob_free(&session_key);
-                       TALLOC_FREE(mem_ctx);
-                       reply_nterror(req, nt_status_squash(ret));
-                       return;
-               }
-
-       } else {
-               ret = make_server_info_pw(&server_info, real_username, pw);
-
-               if ( !NT_STATUS_IS_OK(ret) ) {
-                       DEBUG(1,("make_server_info_pw failed: %s!\n",
-                                nt_errstr(ret)));
-                       data_blob_free(&ap_rep);
-                       data_blob_free(&session_key);
-                       TALLOC_FREE(mem_ctx);
-                       reply_nterror(req, nt_status_squash(ret));
-                       return;
-               }
-
-               /* make_server_info_pw does not set the domain. Without this
-                * we end up with the local netbios name in substitutions for
-                * %D. */
-
-               if (server_info->sam_account != NULL) {
-                       pdb_set_domain(server_info->sam_account,
-                                       domain, PDB_SET);
-               }
-       }
-
-       server_info->nss_token |= username_was_mapped;
-
-       /* we need to build the token for the user. make_server_info_guest()
-          already does this */
-
-       if ( !server_info->ptok ) {
-               ret = create_local_token( server_info );
-               if ( !NT_STATUS_IS_OK(ret) ) {
-                       DEBUG(10,("failed to create local token: %s\n",
-                               nt_errstr(ret)));
-                       data_blob_free(&ap_rep);
-                       data_blob_free(&session_key);
-                       TALLOC_FREE( mem_ctx );
-                       TALLOC_FREE( server_info );
-                       reply_nterror(req, nt_status_squash(ret));
-                       return;
-               }
+       sub_set_smb_name(real_username);
+
+       /* reload services so that the new %U is taken into account */
+       reload_services(sconn->msg_ctx, sconn->sock, True);
+
+       ret = make_session_info_krb5(mem_ctx,
+                                    user, domain, real_username, pw,
+                                    logon_info, map_domainuser_to_guest,
+                                    username_was_mapped,
+                                    &session_key,
+                                    &session_info);
+       data_blob_free(&session_key);
+       if (!NT_STATUS_IS_OK(ret)) {
+               DEBUG(1, ("make_server_info_krb5 failed!\n"));
+               data_blob_free(&ap_rep);
+               TALLOC_FREE(mem_ctx);
+               reply_nterror(req, nt_status_squash(ret));
+               return;
        }
 
        if (!is_partial_auth_vuid(sconn, sess_vuid)) {
                sess_vuid = register_initial_vuid(sconn);
        }
 
-       data_blob_free(&server_info->user_session_key);
-       server_info->user_session_key = session_key;
-       session_key = data_blob_null;
-
        /* register_existing_vuid keeps the server info */
        /* register_existing_vuid takes ownership of session_key on success,
         * no need to free after this on success. A better interface would copy
         * it.... */
 
-       sess_vuid = register_existing_vuid(sconn,
-                                       sess_vuid,
-                                       server_info,
-                                       nullblob,
-                                       client);
+       sess_vuid = register_existing_vuid(sconn, sess_vuid,
+                                          session_info, nullblob, user);
 
        reply_outbuf(req, 4, 0);
        SSVAL(req->outbuf,smb_uid,sess_vuid);
@@ -554,11 +376,11 @@ static void reply_spnego_kerberos(struct smb_request *req,
                ret = NT_STATUS_LOGON_FAILURE;
        } else {
                /* current_user_info is changed on new vuid */
-               reload_services( True );
+               reload_services(sconn->msg_ctx, sconn->sock, True);
 
                SSVAL(req->outbuf, smb_vwv3, 0);
 
-               if (server_info->guest) {
+               if (security_session_user_level(session_info, NULL) < SECURITY_USER) {
                        SSVAL(req->outbuf,smb_vwv2,1);
                }
 
@@ -570,12 +392,12 @@ static void reply_spnego_kerberos(struct smb_request *req,
 
         /* wrap that up in a nice GSS-API wrapping */
        if (NT_STATUS_IS_OK(ret)) {
-               ap_rep_wrapped = spnego_gen_krb5_wrap(ap_rep,
+               ap_rep_wrapped = spnego_gen_krb5_wrap(talloc_tos(), ap_rep,
                                TOK_ID_KRB_AP_REP);
        } else {
                ap_rep_wrapped = data_blob_null;
        }
-       response = spnego_gen_auth_response(&ap_rep_wrapped, ret,
+       response = spnego_gen_auth_response(talloc_tos(), &ap_rep_wrapped, ret,
                        mechOID);
        reply_sesssetup_blob(req, response, ret);
 
@@ -597,22 +419,19 @@ static void reply_spnego_kerberos(struct smb_request *req,
 
 static void reply_spnego_ntlmssp(struct smb_request *req,
                                 uint16 vuid,
-                                AUTH_NTLMSSP_STATE **auth_ntlmssp_state,
+                                struct auth_ntlmssp_state **auth_ntlmssp_state,
                                 DATA_BLOB *ntlmssp_blob, NTSTATUS nt_status,
                                 const char *OID,
                                 bool wrap)
 {
+       bool do_invalidate = true;
        DATA_BLOB response;
-       struct auth_serversupplied_info *server_info = NULL;
-       struct smbd_server_connection *sconn = smbd_server_conn;
+       struct auth_session_info *session_info = NULL;
+       struct smbd_server_connection *sconn = req->sconn;
 
        if (NT_STATUS_IS_OK(nt_status)) {
-               server_info = (*auth_ntlmssp_state)->server_info;
-       } else {
-               nt_status = do_map_to_guest(nt_status,
-                           &server_info,
-                           (*auth_ntlmssp_state)->ntlmssp_state->user,
-                           (*auth_ntlmssp_state)->ntlmssp_state->domain);
+               nt_status = auth_ntlmssp_steal_session_info(talloc_tos(),
+                                       (*auth_ntlmssp_state), &session_info);
        }
 
        reply_outbuf(req, 4, 0);
@@ -627,30 +446,26 @@ static void reply_spnego_ntlmssp(struct smb_request *req,
                        goto out;
                }
 
-               data_blob_free(&server_info->user_session_key);
-               server_info->user_session_key =
-                       data_blob_talloc(
-                       server_info,
-                       (*auth_ntlmssp_state)->ntlmssp_state->session_key.data,
-                       (*auth_ntlmssp_state)->ntlmssp_state->session_key.length);
-
                /* register_existing_vuid keeps the server info */
                if (register_existing_vuid(sconn, vuid,
-                               server_info, nullblob,
-                               (*auth_ntlmssp_state)->ntlmssp_state->user) !=
-                                       vuid) {
+                                          session_info, nullblob,
+                                          auth_ntlmssp_get_username(*auth_ntlmssp_state)) !=
+                                          vuid) {
+                       /* The problem is, *auth_ntlmssp_state points
+                        * into the vuser this will have
+                        * talloc_free()'ed in
+                        * register_existing_vuid() */
+                       do_invalidate = false;
                        nt_status = NT_STATUS_LOGON_FAILURE;
                        goto out;
                }
 
-               (*auth_ntlmssp_state)->server_info = NULL;
-
                /* current_user_info is changed on new vuid */
-               reload_services( True );
+               reload_services(sconn->msg_ctx, sconn->sock, True);
 
                SSVAL(req->outbuf, smb_vwv3, 0);
 
-               if (server_info->guest) {
+               if (security_session_user_level(session_info, NULL) < SECURITY_USER) {
                        SSVAL(req->outbuf,smb_vwv2,1);
                }
        }
@@ -658,7 +473,8 @@ static void reply_spnego_ntlmssp(struct smb_request *req,
   out:
 
        if (wrap) {
-               response = spnego_gen_auth_response(ntlmssp_blob,
+               response = spnego_gen_auth_response(talloc_tos(),
+                               ntlmssp_blob,
                                nt_status, OID);
        } else {
                response = *ntlmssp_blob;
@@ -674,10 +490,12 @@ static void reply_spnego_ntlmssp(struct smb_request *req,
 
        if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
                /* NB. This is *NOT* an error case. JRA */
-               auth_ntlmssp_end(auth_ntlmssp_state);
-               if (!NT_STATUS_IS_OK(nt_status)) {
-                       /* Kill the intermediate vuid */
-                       invalidate_vuid(sconn, vuid);
+               if (do_invalidate) {
+                       TALLOC_FREE(*auth_ntlmssp_state);
+                       if (!NT_STATUS_IS_OK(nt_status)) {
+                               /* Kill the intermediate vuid */
+                               invalidate_vuid(sconn, vuid);
+                       }
                }
        }
 }
@@ -686,7 +504,8 @@ static void reply_spnego_ntlmssp(struct smb_request *req,
  Is this a krb5 mechanism ?
 ****************************************************************************/
 
-NTSTATUS parse_spnego_mechanisms(DATA_BLOB blob_in,
+NTSTATUS parse_spnego_mechanisms(TALLOC_CTX *ctx,
+               DATA_BLOB blob_in,
                DATA_BLOB *pblob_out,
                char **kerb_mechOID)
 {
@@ -697,7 +516,8 @@ NTSTATUS parse_spnego_mechanisms(DATA_BLOB blob_in,
        *kerb_mechOID = NULL;
 
        /* parse out the OIDs and the first sec blob */
-       if (!parse_negTokenTarg(blob_in, OIDs, pblob_out)) {
+       if (!spnego_parse_negTokenInit(ctx, blob_in, OIDs, NULL, pblob_out) ||
+                       (OIDs[0] == NULL)) {
                return NT_STATUS_LOGON_FAILURE;
        }
 
@@ -714,7 +534,7 @@ NTSTATUS parse_spnego_mechanisms(DATA_BLOB blob_in,
 #ifdef HAVE_KRB5
        if (strcmp(OID_KERBEROS5, OIDs[0]) == 0 ||
            strcmp(OID_KERBEROS5_OLD, OIDs[0]) == 0) {
-               *kerb_mechOID = SMB_STRDUP(OIDs[0]);
+               *kerb_mechOID = talloc_strdup(ctx, OIDs[0]);
                if (*kerb_mechOID == NULL) {
                        ret = NT_STATUS_NO_MEMORY;
                }
@@ -743,7 +563,7 @@ static void reply_spnego_downgrade_to_ntlmssp(struct smb_request *req,
        DEBUG(3,("reply_spnego_downgrade_to_ntlmssp: Got krb5 ticket in SPNEGO "
                "but set to downgrade to NTLMSSP\n"));
 
-       response = spnego_gen_auth_response(NULL,
+       response = spnego_gen_auth_response(talloc_tos(), NULL,
                        NT_STATUS_MORE_PROCESSING_REQUIRED,
                        OID_NTLMSSP);
        reply_sesssetup_blob(req, response, NT_STATUS_MORE_PROCESSING_REQUIRED);
@@ -757,15 +577,16 @@ static void reply_spnego_downgrade_to_ntlmssp(struct smb_request *req,
 static void reply_spnego_negotiate(struct smb_request *req,
                                   uint16 vuid,
                                   DATA_BLOB blob1,
-                                  AUTH_NTLMSSP_STATE **auth_ntlmssp_state)
+                                  struct auth_ntlmssp_state **auth_ntlmssp_state)
 {
        DATA_BLOB secblob;
        DATA_BLOB chal;
        char *kerb_mech = NULL;
        NTSTATUS status;
-       struct smbd_server_connection *sconn = smbd_server_conn;
+       struct smbd_server_connection *sconn = req->sconn;
 
-       status = parse_spnego_mechanisms(blob1, &secblob, &kerb_mech);
+       status = parse_spnego_mechanisms(talloc_tos(),
+                       blob1, &secblob, &kerb_mech);
        if (!NT_STATUS_IS_OK(status)) {
                /* Kill the intermediate vuid */
                invalidate_vuid(sconn, vuid);
@@ -787,25 +608,24 @@ static void reply_spnego_negotiate(struct smb_request *req,
                        /* Kill the intermediate vuid */
                        invalidate_vuid(sconn, vuid);
                }
-               SAFE_FREE(kerb_mech);
+               TALLOC_FREE(kerb_mech);
                return;
        }
 #endif
 
-       if (*auth_ntlmssp_state) {
-               auth_ntlmssp_end(auth_ntlmssp_state);
-       }
+       TALLOC_FREE(*auth_ntlmssp_state);
 
        if (kerb_mech) {
                data_blob_free(&secblob);
                /* The mechtoken is a krb5 ticket, but
                 * we need to fall back to NTLM. */
                reply_spnego_downgrade_to_ntlmssp(req, vuid);
-               SAFE_FREE(kerb_mech);
+               TALLOC_FREE(kerb_mech);
                return;
        }
 
-       status = auth_ntlmssp_start(auth_ntlmssp_state);
+       status = auth_ntlmssp_start(sconn->remote_address,
+                                   auth_ntlmssp_state);
        if (!NT_STATUS_IS_OK(status)) {
                /* Kill the intermediate vuid */
                invalidate_vuid(sconn, vuid);
@@ -834,15 +654,15 @@ static void reply_spnego_negotiate(struct smb_request *req,
 static void reply_spnego_auth(struct smb_request *req,
                              uint16 vuid,
                              DATA_BLOB blob1,
-                             AUTH_NTLMSSP_STATE **auth_ntlmssp_state)
+                             struct auth_ntlmssp_state **auth_ntlmssp_state)
 {
        DATA_BLOB auth = data_blob_null;
        DATA_BLOB auth_reply = data_blob_null;
        DATA_BLOB secblob = data_blob_null;
        NTSTATUS status = NT_STATUS_LOGON_FAILURE;
-       struct smbd_server_connection *sconn = smbd_server_conn;
+       struct smbd_server_connection *sconn = req->sconn;
 
-       if (!spnego_parse_auth(blob1, &auth)) {
+       if (!spnego_parse_auth(talloc_tos(), blob1, &auth)) {
 #if 0
                file_save("auth.dat", blob1.data, blob1.length);
 #endif
@@ -858,7 +678,8 @@ static void reply_spnego_auth(struct smb_request *req,
                /* Might be a second negTokenTarg packet */
                char *kerb_mech = NULL;
 
-               status = parse_spnego_mechanisms(auth, &secblob, &kerb_mech);
+               status = parse_spnego_mechanisms(talloc_tos(),
+                               auth, &secblob, &kerb_mech);
 
                if (!NT_STATUS_IS_OK(status)) {
                        /* Kill the intermediate vuid */
@@ -881,7 +702,7 @@ static void reply_spnego_auth(struct smb_request *req,
                                /* Kill the intermediate vuid */
                                invalidate_vuid(sconn, vuid);
                        }
-                       SAFE_FREE(kerb_mech);
+                       TALLOC_FREE(kerb_mech);
                        return;
                }
 #endif
@@ -897,7 +718,7 @@ static void reply_spnego_auth(struct smb_request *req,
                                "not enabled\n"));
                        reply_nterror(req, nt_status_squash(
                                        NT_STATUS_LOGON_FAILURE));
-                       SAFE_FREE(kerb_mech);
+                       TALLOC_FREE(kerb_mech);
                }
        }
 
@@ -905,7 +726,8 @@ static void reply_spnego_auth(struct smb_request *req,
        data_blob_free(&secblob);
 
        if (!*auth_ntlmssp_state) {
-               status = auth_ntlmssp_start(auth_ntlmssp_state);
+               status = auth_ntlmssp_start(sconn->remote_address,
+                                           auth_ntlmssp_state);
                if (!NT_STATUS_IS_OK(status)) {
                        /* Kill the intermediate vuid */
                        invalidate_vuid(sconn, vuid);
@@ -1072,13 +894,28 @@ static NTSTATUS check_spnego_blob_complete(struct smbd_server_connection *sconn,
        }
 
        asn1_load(data, *pblob);
-       asn1_start_tag(data, pblob->data[0]);
-       if (data->has_error || data->nesting == NULL) {
+       if (asn1_start_tag(data, pblob->data[0])) {
+               /* asn1_start_tag checks if the given
+                  length of the blob is enough to complete
+                  the tag. If it returns true we know
+                  there is nothing to do - the blob is
+                  complete. */
                asn1_free(data);
-               /* Let caller catch. */
                return NT_STATUS_OK;
        }
 
+       if (data->nesting == NULL) {
+               /* Incorrect tag, allocation failed,
+                  or reading the tag length failed.
+                  Let the caller catch. */
+               asn1_free(data);
+               return NT_STATUS_OK;
+       }
+
+       /* Here we know asn1_start_tag() has set data->has_error to true.
+          asn1_tag_remaining() will have failed due to the given blob
+          being too short. We need to work out how short. */
+
        /* Integer wrap paranoia.... */
 
        if (data->nesting->taglen + data->nesting->start < data->nesting->taglen ||
@@ -1107,6 +944,13 @@ static NTSTATUS check_spnego_blob_complete(struct smbd_server_connection *sconn,
 
        if (needed_len <= pblob->length) {
                /* Nothing to do - blob is complete. */
+               /* THIS SHOULD NOT HAPPEN - asn1_start_tag()
+                  above should have caught this !!! */
+               DEBUG(0,("check_spnego_blob_complete: logic "
+                       "error (needed_len = %u, "
+                       "pblob->length = %u).\n",
+                       (unsigned int)needed_len,
+                       (unsigned int)pblob->length ));
                return NT_STATUS_OK;
        }
 
@@ -1156,7 +1000,7 @@ static void reply_sesssetup_and_X_spnego(struct smb_request *req)
        user_struct *vuser = NULL;
        NTSTATUS status = NT_STATUS_OK;
        uint16 smbpid = req->smbpid;
-       struct smbd_server_connection *sconn = smbd_server_conn;
+       struct smbd_server_connection *sconn = req->sconn;
 
        DEBUG(3,("Doing spnego session setup\n"));
 
@@ -1185,7 +1029,7 @@ static void reply_sesssetup_and_X_spnego(struct smb_request *req)
        file_save("negotiate.dat", blob1.data, blob1.length);
 #endif
 
-       p2 = (char *)req->buf + data_blob_len;
+       p2 = (const char *)req->buf + blob1.length;
 
        p2 += srvstr_pull_req_talloc(talloc_tos(), req, &tmp, p2,
                                     STR_TERMINATE);
@@ -1216,6 +1060,10 @@ static void reply_sesssetup_and_X_spnego(struct smb_request *req)
                } else {
                        ra_lanman_string( native_lanman );
                }
+       } else if ( ra_type == RA_VISTA ) {
+               if ( strncmp(native_os, "Mac OS X", 8) == 0 ) {
+                       set_remote_arch(RA_OSX);
+               }
        }
 
        /* Did we get a valid vuid ? */
@@ -1291,7 +1139,8 @@ static void reply_sesssetup_and_X_spnego(struct smb_request *req)
                DATA_BLOB chal;
 
                if (!vuser->auth_ntlmssp_state) {
-                       status = auth_ntlmssp_start(&vuser->auth_ntlmssp_state);
+                       status = auth_ntlmssp_start(sconn->remote_address,
+                                                   &vuser->auth_ntlmssp_state);
                        if (!NT_STATUS_IS_OK(status)) {
                                /* Kill the intermediate vuid */
                                invalidate_vuid(sconn, vuid);
@@ -1327,37 +1176,46 @@ static void reply_sesssetup_and_X_spnego(struct smb_request *req)
  a new session setup with VC==0 is ignored.
 ****************************************************************************/
 
-static int shutdown_other_smbds(struct db_record *rec,
-                               const struct connections_key *key,
+struct shutdown_state {
+       const char *ip;
+       struct messaging_context *msg_ctx;
+};
+
+static int shutdown_other_smbds(const struct connections_key *key,
                                const struct connections_data *crec,
                                void *private_data)
 {
-       const char *ip = (const char *)private_data;
+       struct shutdown_state *state = (struct shutdown_state *)private_data;
+
+       DEBUG(10, ("shutdown_other_smbds: %s, %s\n",
+                  server_id_str(talloc_tos(), &crec->pid), crec->addr));
 
        if (!process_exists(crec->pid)) {
+               DEBUG(10, ("process does not exist\n"));
                return 0;
        }
 
        if (procid_is_me(&crec->pid)) {
+               DEBUG(10, ("It's me\n"));
                return 0;
        }
 
-       if (strcmp(ip, crec->addr) != 0) {
+       if (strcmp(state->ip, crec->addr) != 0) {
+               DEBUG(10, ("%s does not match %s\n", state->ip, crec->addr));
                return 0;
        }
 
-       DEBUG(0,("shutdown_other_smbds: shutting down pid %u "
-                "(IP %s)\n", (unsigned int)procid_to_pid(&crec->pid), ip));
+       DEBUG(1, ("shutdown_other_smbds: shutting down pid %u "
+                 "(IP %s)\n", (unsigned int)procid_to_pid(&crec->pid),
+                 state->ip));
 
-       messaging_send(smbd_messaging_context(), crec->pid, MSG_SHUTDOWN,
+       messaging_send(state->msg_ctx, crec->pid, MSG_SHUTDOWN,
                       &data_blob_null);
        return 0;
 }
 
-static void setup_new_vc_session(void)
+static void setup_new_vc_session(struct smbd_server_connection *sconn)
 {
-       char addr[INET6_ADDRSTRLEN];
-
        DEBUG(2,("setup_new_vc_session: New VC == 0, if NT4.x "
                "compatible we would close all old resources.\n"));
 #if 0
@@ -1365,9 +1223,18 @@ static void setup_new_vc_session(void)
        invalidate_all_vuids();
 #endif
        if (lp_reset_on_zero_vc()) {
-               connections_forall(shutdown_other_smbds,
-                       CONST_DISCARD(void *,
-                       client_addr(get_client_fd(),addr,sizeof(addr))));
+               char *addr;
+               struct shutdown_state state;
+
+               addr = tsocket_address_inet_addr_string(
+                       sconn->remote_address, talloc_tos());
+               if (addr == NULL) {
+                       return;
+               }
+               state.ip = addr;
+               state.msg_ctx = sconn->msg_ctx;
+               connections_forall_read(shutdown_other_smbds, &state);
+               TALLOC_FREE(addr);
        }
 }
 
@@ -1384,17 +1251,18 @@ void reply_sesssetup_and_X(struct smb_request *req)
        DATA_BLOB plaintext_password;
        char *tmp;
        const char *user;
-       fstring sub_user; /* Sainitised username for substituion */
+       fstring sub_user; /* Sanitised username for substituion */
        const char *domain;
        const char *native_os;
        const char *native_lanman;
        const char *primary_domain;
-       auth_usersupplied_info *user_info = NULL;
-       auth_serversupplied_info *server_info = NULL;
+       struct auth_usersupplied_info *user_info = NULL;
+       struct auth_serversupplied_info *server_info = NULL;
+       struct auth_session_info *session_info = NULL;
        uint16 smb_flag2 = req->flags2;
 
        NTSTATUS nt_status;
-       struct smbd_server_connection *sconn = smbd_server_conn;
+       struct smbd_server_connection *sconn = req->sconn;
 
        bool doencrypt = sconn->smb1.negprot.encrypted_passwords;
 
@@ -1422,7 +1290,7 @@ void reply_sesssetup_and_X(struct smb_request *req)
                }
 
                if (SVAL(req->vwv+4, 0) == 0) {
-                       setup_new_vc_session();
+                       setup_new_vc_session(req->sconn);
                }
 
                reply_sesssetup_and_X_spnego(req);
@@ -1432,7 +1300,7 @@ void reply_sesssetup_and_X(struct smb_request *req)
 
        smb_bufsize = SVAL(req->vwv+2, 0);
 
-       if (Protocol < PROTOCOL_NT1) {
+       if (get_Protocol() < PROTOCOL_NT1) {
                uint16 passlen1 = SVAL(req->vwv+7, 0);
 
                /* Never do NT status codes with protocols before NT1 as we
@@ -1619,7 +1487,7 @@ void reply_sesssetup_and_X(struct smb_request *req)
        }
 
        if (SVAL(req->vwv+4, 0) == 0) {
-               setup_new_vc_session();
+               setup_new_vc_session(req->sconn);
        }
 
        DEBUG(3,("sesssetupX:name=[%s]\\[%s]@[%s]\n",
@@ -1646,16 +1514,23 @@ void reply_sesssetup_and_X(struct smb_request *req)
 
        sub_set_smb_name(sub_user);
 
-       reload_services(True);
+       reload_services(sconn->msg_ctx, sconn->sock, True);
 
        if (lp_security() == SEC_SHARE) {
+               char *sub_user_mapped = NULL;
                /* In share level we should ignore any passwords */
 
                data_blob_free(&lm_resp);
                data_blob_free(&nt_resp);
                data_blob_clear_free(&plaintext_password);
 
-               map_username(sconn, sub_user);
+               (void)map_username(talloc_tos(), sub_user, &sub_user_mapped);
+               if (!sub_user_mapped) {
+                       reply_nterror(req, NT_STATUS_NO_MEMORY);
+                       END_PROFILE(SMBsesssetupX);
+                       return;
+               }
+               fstrcpy(sub_user, sub_user_mapped);
                add_session_user(sconn, sub_user);
                add_session_workgroup(sconn, domain);
                /* Then force it to null for the benfit of the code below */
@@ -1664,7 +1539,7 @@ void reply_sesssetup_and_X(struct smb_request *req)
 
        if (!*user) {
 
-               nt_status = check_guest_password(&server_info);
+               nt_status = check_guest_password(sconn->remote_address, &server_info);
 
        } else if (doencrypt) {
                struct auth_context *negprot_auth_context = NULL;
@@ -1679,6 +1554,7 @@ void reply_sesssetup_and_X(struct smb_request *req)
                }
                nt_status = make_user_info_for_reply_enc(&user_info, user,
                                                domain,
+                                               sconn->remote_address,
                                                lm_resp, nt_resp);
                if (NT_STATUS_IS_OK(nt_status)) {
                        nt_status = negprot_auth_context->check_ntlm_password(
@@ -1690,7 +1566,7 @@ void reply_sesssetup_and_X(struct smb_request *req)
                struct auth_context *plaintext_auth_context = NULL;
 
                nt_status = make_auth_context_subsystem(
-                               &plaintext_auth_context);
+                       talloc_tos(), &plaintext_auth_context);
 
                if (NT_STATUS_IS_OK(nt_status)) {
                        uint8_t chal[8];
@@ -1699,7 +1575,9 @@ void reply_sesssetup_and_X(struct smb_request *req)
                                        plaintext_auth_context, chal);
 
                        if (!make_user_info_for_reply(&user_info,
-                                                     user, domain, chal,
+                                                     user, domain,
+                                                     sconn->remote_address,
+                                                     chal,
                                                      plaintext_password)) {
                                nt_status = NT_STATUS_NO_MEMORY;
                        }
@@ -1710,8 +1588,7 @@ void reply_sesssetup_and_X(struct smb_request *req)
                                                user_info,
                                                &server_info);
 
-                               (plaintext_auth_context->free)(
-                                               &plaintext_auth_context);
+                               TALLOC_FREE(plaintext_auth_context);
                        }
                }
        }
@@ -1719,8 +1596,8 @@ void reply_sesssetup_and_X(struct smb_request *req)
        free_user_info(&user_info);
 
        if (!NT_STATUS_IS_OK(nt_status)) {
-               nt_status = do_map_to_guest(nt_status, &server_info,
-                               user, domain);
+               nt_status = do_map_to_guest_server_info(nt_status, &server_info,
+                                                       user, domain);
        }
 
        if (!NT_STATUS_IS_OK(nt_status)) {
@@ -1732,39 +1609,30 @@ void reply_sesssetup_and_X(struct smb_request *req)
                return;
        }
 
-       /* Ensure we can't possible take a code path leading to a
-        * null defref. */
-       if (!server_info) {
-               reply_nterror(req, nt_status_squash(NT_STATUS_LOGON_FAILURE));
+       nt_status = create_local_token(req, server_info, NULL, &session_info);
+       TALLOC_FREE(server_info);
+
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DEBUG(10, ("create_local_token failed: %s\n",
+                          nt_errstr(nt_status)));
+               data_blob_free(&nt_resp);
+               data_blob_free(&lm_resp);
+               data_blob_clear_free(&plaintext_password);
+               reply_nterror(req, nt_status_squash(nt_status));
                END_PROFILE(SMBsesssetupX);
                return;
        }
 
-       if (!server_info->ptok) {
-               nt_status = create_local_token(server_info);
-
-               if (!NT_STATUS_IS_OK(nt_status)) {
-                       DEBUG(10, ("create_local_token failed: %s\n",
-                                  nt_errstr(nt_status)));
-                       data_blob_free(&nt_resp);
-                       data_blob_free(&lm_resp);
-                       data_blob_clear_free(&plaintext_password);
-                       reply_nterror(req, nt_status_squash(nt_status));
-                       END_PROFILE(SMBsesssetupX);
-                       return;
-               }
-       }
-
        data_blob_clear_free(&plaintext_password);
 
        /* it's ok - setup a reply */
        reply_outbuf(req, 3, 0);
-       if (Protocol >= PROTOCOL_NT1) {
+       if (get_Protocol() >= PROTOCOL_NT1) {
                push_signature(&req->outbuf);
                /* perhaps grab OS version here?? */
        }
 
-       if (server_info->guest) {
+       if (security_session_user_level(session_info, NULL) < SECURITY_USER) {
                SSVAL(req->outbuf,smb_vwv2,1);
        }
 
@@ -1773,7 +1641,7 @@ void reply_sesssetup_and_X(struct smb_request *req)
 
        if (lp_security() == SEC_SHARE) {
                sess_vuid = UID_FIELD_INVALID;
-               TALLOC_FREE(server_info);
+               TALLOC_FREE(session_info);
        } else {
                /* Ignore the initial vuid. */
                sess_vuid = register_initial_vuid(sconn);
@@ -1785,9 +1653,9 @@ void reply_sesssetup_and_X(struct smb_request *req)
                        END_PROFILE(SMBsesssetupX);
                        return;
                }
-               /* register_existing_vuid keeps the server info */
+               /* register_existing_vuid keeps the session_info */
                sess_vuid = register_existing_vuid(sconn, sess_vuid,
-                                       server_info,
+                                       session_info,
                                        nt_resp.data ? nt_resp : lm_resp,
                                        sub_user);
                if (sess_vuid == UID_FIELD_INVALID) {
@@ -1800,14 +1668,14 @@ void reply_sesssetup_and_X(struct smb_request *req)
                }
 
                /* current_user_info is changed on new vuid */
-               reload_services( True );
+               reload_services(sconn->msg_ctx, sconn->sock, True);
        }
 
        data_blob_free(&nt_resp);
        data_blob_free(&lm_resp);
 
        SSVAL(req->outbuf,smb_uid,sess_vuid);
-       SSVAL(req->inbuf,smb_uid,sess_vuid);
+       SSVAL(discard_const_p(char, req->inbuf),smb_uid,sess_vuid);
        req->vuid = sess_vuid;
 
        if (!sconn->smb1.sessions.done_sesssetup) {