s3-auth Move map to guest to directly after the check_password calls
[amitay/samba.git] / source3 / smbd / sesssetup.c
index 12d046038c9ce82a9fcaeaa5b2b1a236998d3f76..329b8b6aa5255f1415f3af61f36a7f49c11e75cd 100644 (file)
 */
 
 #include "includes.h"
+#include "../lib/tsocket/tsocket.h"
+#include "smbd/smbd.h"
 #include "smbd/globals.h"
 #include "../libcli/auth/spnego.h"
 #include "../libcli/auth/ntlmssp.h"
 #include "ntlmssp_wrap.h"
-#include "librpc/gen_ndr/messaging.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 {
@@ -40,37 +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
-*/
-NTSTATUS do_map_to_guest(NTSTATUS status,
-                       struct auth_serversupplied_info **server_info,
-                       const char *user, const char *domain)
-{
-       user = user ? user : "";
-       domain = domain ? 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.
 ****************************************************************************/
@@ -135,7 +110,8 @@ static void reply_sesssetup_blob(struct smb_request *req,
  Do a 'guest' logon, getting back the
 ****************************************************************************/
 
-static NTSTATUS check_guest_password(struct 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;
        struct auth_usersupplied_info *user_info = NULL;
@@ -150,7 +126,7 @@ static NTSTATUS check_guest_password(struct auth_serversupplied_info **server_in
                return nt_status;
        }
 
-       if (!make_user_info_guest(&user_info)) {
+       if (!make_user_info_guest(remote_address, &user_info)) {
                TALLOC_FREE(auth_context);
                return NT_STATUS_NO_MEMORY;
        }
@@ -189,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;
        }
@@ -245,7 +221,7 @@ static void reply_spnego_kerberos(struct smb_request *req,
        int sess_vuid = req->vuid;
        NTSTATUS ret = NT_STATUS_OK;
        DATA_BLOB ap_rep, ap_rep_wrapped, response;
-       struct 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;
@@ -341,7 +317,7 @@ static void reply_spnego_kerberos(struct smb_request *req,
        }
 
        ret = get_user_from_kerberos_info(talloc_tos(),
-                                         sconn->client_id.name,
+                                         sconn->remote_hostname,
                                          principal, logon_info,
                                          &username_was_mapped,
                                          &map_domainuser_to_guest,
@@ -366,56 +342,32 @@ static void reply_spnego_kerberos(struct smb_request *req,
        /* reload services so that the new %U is taken into account */
        reload_services(sconn->msg_ctx, sconn->sock, True);
 
-       ret = make_server_info_krb5(mem_ctx,
-                                   user, domain, real_username, pw,
-                                   logon_info, map_domainuser_to_guest,
-                                   &server_info);
+       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);
-               data_blob_free(&session_key);
                TALLOC_FREE(mem_ctx);
                reply_nterror(req, nt_status_squash(ret));
                return;
        }
 
-       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;
-               }
-       }
-
        if (!is_partial_auth_vuid(sconn, sess_vuid)) {
                sess_vuid = register_initial_vuid(sconn);
        }
 
-       data_blob_free(&server_info->user_session_key);
-       /* Set the kerberos-derived session key onto the server_info */
-       server_info->user_session_key = session_key;
-       talloc_steal(server_info, session_key.data);
-
-       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, user);
+                                          session_info, nullblob, user);
 
        reply_outbuf(req, 4, 0);
        SSVAL(req->outbuf,smb_uid,sess_vuid);
@@ -428,7 +380,7 @@ static void reply_spnego_kerberos(struct smb_request *req,
 
                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);
                }
 
@@ -474,21 +426,12 @@ static void reply_spnego_ntlmssp(struct smb_request *req,
 {
        bool do_invalidate = true;
        DATA_BLOB response;
-       struct auth_serversupplied_info *server_info = NULL;
+       struct auth_session_info *session_info = NULL;
        struct smbd_server_connection *sconn = req->sconn;
 
        if (NT_STATUS_IS_OK(nt_status)) {
-               nt_status = auth_ntlmssp_steal_server_info(talloc_tos(),
-                                       (*auth_ntlmssp_state), &server_info);
-       } else {
-               /* Note that this server_info won't have a session
-                * key.  But for map to guest, that's exactly the right
-                * thing - we can't reasonably guess the key the
-                * client wants, as the password was wrong */
-               nt_status = do_map_to_guest(nt_status,
-                                           &server_info,
-                                           auth_ntlmssp_get_username(*auth_ntlmssp_state),
-                                           auth_ntlmssp_get_domain(*auth_ntlmssp_state));
+               nt_status = auth_ntlmssp_steal_session_info(talloc_tos(),
+                                       (*auth_ntlmssp_state), &session_info);
        }
 
        reply_outbuf(req, 4, 0);
@@ -505,7 +448,7 @@ static void reply_spnego_ntlmssp(struct smb_request *req,
 
                /* register_existing_vuid keeps the server info */
                if (register_existing_vuid(sconn, vuid,
-                                          server_info, nullblob,
+                                          session_info, nullblob,
                                           auth_ntlmssp_get_username(*auth_ntlmssp_state)) !=
                                           vuid) {
                        /* The problem is, *auth_ntlmssp_state points
@@ -522,7 +465,7 @@ static void reply_spnego_ntlmssp(struct smb_request *req,
 
                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);
                }
        }
@@ -670,9 +613,7 @@ static void reply_spnego_negotiate(struct smb_request *req,
        }
 #endif
 
-       if (*auth_ntlmssp_state) {
-               TALLOC_FREE(*auth_ntlmssp_state);
-       }
+       TALLOC_FREE(*auth_ntlmssp_state);
 
        if (kerb_mech) {
                data_blob_free(&secblob);
@@ -683,7 +624,8 @@ static void reply_spnego_negotiate(struct smb_request *req,
                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);
@@ -784,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);
@@ -951,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);
+               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);
-               /* Let caller catch. */
                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 ||
@@ -986,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;
        }
 
@@ -1064,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 + blob1.length;
+       p2 = (const char *)req->buf + blob1.length;
 
        p2 += srvstr_pull_req_talloc(talloc_tos(), req, &tmp, p2,
                                     STR_TERMINATE);
@@ -1095,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 ? */
@@ -1170,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);
@@ -1218,7 +1188,7 @@ static int shutdown_other_smbds(const struct connections_key *key,
        struct shutdown_state *state = (struct shutdown_state *)private_data;
 
        DEBUG(10, ("shutdown_other_smbds: %s, %s\n",
-                  procid_str(talloc_tos(), &crec->pid), crec->addr));
+                  server_id_str(talloc_tos(), &crec->pid), crec->addr));
 
        if (!process_exists(crec->pid)) {
                DEBUG(10, ("process does not exist\n"));
@@ -1288,6 +1258,7 @@ void reply_sesssetup_and_X(struct smb_request *req)
        const char *primary_domain;
        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;
@@ -1568,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;
@@ -1583,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(
@@ -1603,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;
                        }
@@ -1622,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)) {
@@ -1635,29 +1609,20 @@ 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 */
@@ -1667,7 +1632,7 @@ void reply_sesssetup_and_X(struct smb_request *req)
                /* 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);
        }
 
@@ -1676,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);
@@ -1688,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) {
@@ -1710,7 +1675,7 @@ void reply_sesssetup_and_X(struct smb_request *req)
        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) {