r6149: Fixes bugs #2498 and 2484.
[samba.git] / source / smbd / sesssetup.c
index 9405c065e182e9af47cc781efeaaa4aac26dfea6..60867df6535a75bd07c746e04174cff2acb90212 100644 (file)
@@ -143,17 +143,17 @@ static int reply_spnego_kerberos(connection_struct *conn,
        DATA_BLOB ticket;
        char *client, *p, *domain;
        fstring netbios_domain_name;
-       const struct passwd *pw;
+       struct passwd *pw;
        char *user;
        int sess_vuid;
        NTSTATUS ret;
        DATA_BLOB auth_data;
        DATA_BLOB ap_rep, ap_rep_wrapped, response;
        auth_serversupplied_info *server_info = NULL;
-       DATA_BLOB session_key;
+       DATA_BLOB session_key = data_blob(NULL, 0);
        uint8 tok_id[2];
-       BOOL foreign = False;
        DATA_BLOB nullblob = data_blob(NULL, 0);
+       fstring real_username;
 
        ZERO_STRUCT(ticket);
        ZERO_STRUCT(auth_data);
@@ -182,6 +182,7 @@ static int reply_spnego_kerberos(connection_struct *conn,
        if (!p) {
                DEBUG(3,("Doesn't look like a valid principal\n"));
                data_blob_free(&ap_rep);
+               data_blob_free(&session_key);
                SAFE_FREE(client);
                return ERROR_NT(NT_STATUS_LOGON_FAILURE);
        }
@@ -191,10 +192,10 @@ static int reply_spnego_kerberos(connection_struct *conn,
                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);
                        SAFE_FREE(client);
                        return ERROR_NT(NT_STATUS_LOGON_FAILURE);
                }
-               foreign = True;
        }
 
        /* this gives a fully qualified user name (ie. with full realm).
@@ -239,26 +240,33 @@ static int reply_spnego_kerberos(connection_struct *conn,
 
        asprintf(&user, "%s%c%s", domain, *lp_winbind_separator(), client);
        
-       pw = smb_getpwnam( user );
+       /* lookup the passwd struct, create a new user if necessary */
+
+       map_username( user );
+
+       pw = smb_getpwnam( user, real_username, True );
        
        if (!pw) {
                DEBUG(1,("Username %s is invalid on this system\n",user));
                SAFE_FREE(user);
                SAFE_FREE(client);
                data_blob_free(&ap_rep);
+               data_blob_free(&session_key);
                return ERROR_NT(NT_STATUS_LOGON_FAILURE);
        }
 
        /* setup the string used by %U */
        
-       sub_set_smb_name(pw->pw_name);
+       sub_set_smb_name( real_username );
        reload_services(True);
        
-       if (!NT_STATUS_IS_OK(ret = make_server_info_pw(&server_info,pw))) {
+       if (!NT_STATUS_IS_OK(ret = make_server_info_pw(&server_info, real_username, pw))) 
+       {
                DEBUG(1,("make_server_info_from_pw failed!\n"));
                SAFE_FREE(user);
                SAFE_FREE(client);
                data_blob_free(&ap_rep);
+               data_blob_free(&session_key);
                return ERROR_NT(ret);
        }
 
@@ -270,6 +278,8 @@ static int reply_spnego_kerberos(connection_struct *conn,
         }
 
        /* register_vuid keeps the server info */
+       /* register_vuid takes ownership of session_key, no need to free after this.
+          A better interface would copy it.... */
        sess_vuid = register_vuid(server_info, session_key, nullblob, client);
 
        SAFE_FREE(user);
@@ -278,6 +288,9 @@ static int reply_spnego_kerberos(connection_struct *conn,
        if (sess_vuid == -1) {
                ret = NT_STATUS_LOGON_FAILURE;
        } else {
+               /* current_user_info is changed on new vuid */
+               reload_services( True );
+
                set_message(outbuf,4,0,True);
                SSVAL(outbuf, smb_vwv3, 0);
                        
@@ -287,20 +300,22 @@ static int reply_spnego_kerberos(connection_struct *conn,
                
                SSVAL(outbuf, smb_uid, sess_vuid);
 
-               if (!server_info->guest) {
+               if (!server_info->guest && !srv_signing_started()) {
                        /* We need to start the signing engine
                         * here but a W2K client sends the old
                         * "BSRSPYL " signature instead of the
                         * correct one. Subsequent packets will
                         * be correct.
                         */
-                       srv_check_sign_mac(inbuf);
+                       srv_check_sign_mac(inbuf, False);
                }
        }
 
         /* wrap that up in a nice GSS-API wrapping */
        if (NT_STATUS_IS_OK(ret)) {
-               ap_rep_wrapped = spnego_gen_krb5_wrap(ap_rep, TOK_ID_KRB_AP_REP);
+               ap_rep_wrapped = spnego_gen_krb5_wrap(
+                        ap_rep,
+                        CONST_ADD(const uint8 *, TOK_ID_KRB_AP_REP));
        } else {
                ap_rep_wrapped = data_blob(NULL, 0);
        }
@@ -351,6 +366,9 @@ static BOOL reply_spnego_ntlmssp(connection_struct *conn, char *inbuf, char *out
                        nt_status = NT_STATUS_LOGON_FAILURE;
                } else {
                        
+                       /* current_user_info is changed on new vuid */
+                       reload_services( True );
+
                        set_message(outbuf,4,0,True);
                        SSVAL(outbuf, smb_vwv3, 0);
                        
@@ -360,14 +378,15 @@ static BOOL reply_spnego_ntlmssp(connection_struct *conn, char *inbuf, char *out
                        
                        SSVAL(outbuf,smb_uid,sess_vuid);
 
-                       if (!server_info->guest) {
+                       if (!server_info->guest && !srv_signing_started()) {
                                /* We need to start the signing engine
                                 * here but a W2K client sends the old
                                 * "BSRSPYL " signature instead of the
                                 * correct one. Subsequent packets will
                                 * be correct.
                                 */
-                               srv_check_sign_mac(inbuf);
+
+                               srv_check_sign_mac(inbuf, False);
                        }
                }
        }
@@ -400,7 +419,9 @@ static int reply_spnego_negotiate(connection_struct *conn,
        DATA_BLOB secblob;
        int i;
        DATA_BLOB chal;
-       BOOL got_kerberos = False;
+#ifdef HAVE_KRB5
+       BOOL got_kerberos_mechanism = False;
+#endif
        NTSTATUS nt_status;
 
        /* parse out the OIDs and the first sec blob */
@@ -417,11 +438,13 @@ static int reply_spnego_negotiate(connection_struct *conn,
           server sent back krb5/mskrb5/ntlmssp as mechtypes, but the 
           client (2ksp3) replied with ntlmssp/mskrb5/krb5 and an 
           NTLMSSP mechtoken.                 --jerry              */
-       
+
+#ifdef HAVE_KRB5       
        if (strcmp(OID_KERBEROS5, OIDs[0]) == 0 ||
            strcmp(OID_KERBEROS5_OLD, OIDs[0]) == 0) {
-               got_kerberos = True;
+               got_kerberos_mechanism = True;
        }
+#endif
                
        for (i=0;OIDs[i];i++) {
                DEBUG(3,("Got OID %s\n", OIDs[i]));
@@ -430,7 +453,7 @@ static int reply_spnego_negotiate(connection_struct *conn,
        DEBUG(3,("Got secblob of size %lu\n", (unsigned long)secblob.length));
 
 #ifdef HAVE_KRB5
-       if (got_kerberos && (SEC_ADS == lp_security())) {
+       if (got_kerberos_mechanism && (SEC_ADS == lp_security())) {
                int ret = reply_spnego_kerberos(conn, inbuf, outbuf, 
                                                length, bufsize, &secblob);
                data_blob_free(&secblob);
@@ -616,7 +639,7 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf,
        static BOOL done_sesssetup = False;
        extern BOOL global_encrypted_passwords_negotiated;
        extern BOOL global_spnego_negotiated;
-       extern int Protocol;
+       extern enum protocol_types Protocol;
        extern int max_send;
 
        auth_usersupplied_info *user_info = NULL;
@@ -656,6 +679,10 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf,
 
        if (Protocol < PROTOCOL_NT1) {
                uint16 passlen1 = SVAL(inbuf,smb_vwv7);
+
+               /* Never do NT status codes with protocols before NT1 as we don't get client caps. */
+               remove_from_common_flags2(FLAGS2_32_BIT_ERROR_CODES);
+
                if ((passlen1 > MAX_PASS_LEN) || (passlen1 > smb_bufrem(inbuf, smb_buf(inbuf)))) {
                        return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
                }
@@ -738,10 +765,18 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf,
                        pstring pass;
                        BOOL unic=SVAL(inbuf, smb_flg2) & FLAGS2_UNICODE_STRINGS;
 
+#if 0
+                       /* This was the previous fix. Not sure if it's still valid. JRA. */
                        if ((ra_type == RA_WINNT) && (passlen2 == 0) && unic && passlen1) {
                                /* NT4.0 stuffs up plaintext unicode password lengths... */
                                srvstr_pull(inbuf, pass, smb_buf(inbuf) + 1,
                                        sizeof(pass), passlen1, STR_TERMINATE);
+#endif
+
+                       if (unic && (passlen2 == 0) && passlen1) {
+                               /* Only a ascii plaintext password was sent. */
+                               srvstr_pull(inbuf, pass, smb_buf(inbuf), sizeof(pass),
+                                       passlen1, STR_TERMINATE|STR_ASCII);
                        } else {
                                srvstr_pull(inbuf, pass, smb_buf(inbuf), 
                                        sizeof(pass),  unic ? passlen2 : passlen1, 
@@ -870,15 +905,12 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf,
                return ERROR_NT(nt_status_squash(nt_status));
        }
 
-       if (server_info->nt_session_key.data) {
-               session_key = data_blob(server_info->nt_session_key.data, server_info->nt_session_key.length);
-       } else if (server_info->lm_session_key.length >= 8 && lm_resp.length == 24) {
-               session_key = data_blob(NULL, 16);
-               SMBsesskeygen_lmv1(server_info->lm_session_key.data, lm_resp.data, 
-                                  session_key.data);
+       if (server_info->user_session_key.data) {
+               session_key = data_blob(server_info->user_session_key.data, server_info->user_session_key.length);
+       } else {
+               session_key = data_blob(NULL, 0);
        }
 
-       data_blob_free(&lm_resp);
        data_blob_clear_free(&plaintext_password);
        
        /* it's ok - setup a reply */
@@ -898,14 +930,18 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf,
           to a uid can get through without a password, on the same VC */
 
        /* register_vuid keeps the server info */
-       sess_vuid = register_vuid(server_info, session_key, nt_resp, sub_user);
+       sess_vuid = register_vuid(server_info, session_key, nt_resp.data ? nt_resp : lm_resp, sub_user);
        data_blob_free(&nt_resp);
+       data_blob_free(&lm_resp);
 
        if (sess_vuid == -1) {
                return ERROR_NT(NT_STATUS_LOGON_FAILURE);
        }
 
-       if (!server_info->guest && !srv_check_sign_mac(inbuf)) {
+       /* current_user_info is changed on new vuid */
+       reload_services( True );
+
+       if (!server_info->guest && !srv_signing_started() && !srv_check_sign_mac(inbuf, True)) {
                exit_server("reply_sesssetup_and_X: bad smb signature");
        }