r7882: Looks like a large patch - but what it actually does is make Samba
[ira/wip.git] / source3 / smbd / sesssetup.c
index 2c38cd3eb3a93670add53310327a98506d58808f..95fe571cff1a775a2ba6d87bab503de3f9eba0d6 100644 (file)
@@ -143,17 +143,18 @@ static int reply_spnego_kerberos(connection_struct *conn,
        DATA_BLOB ticket;
        char *client, *p, *domain;
        fstring netbios_domain_name;
-       const struct passwd *pw;
-       char *user;
+       struct passwd *pw;
+       fstring 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;
+       BOOL map_domainuser_to_guest = False;
 
        ZERO_STRUCT(ticket);
        ZERO_STRUCT(auth_data);
@@ -182,6 +183,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 +193,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).
@@ -221,7 +223,7 @@ static int reply_spnego_kerberos(connection_struct *conn,
 
                fstrcpy(wb_request.domain_name, domain);
 
-               wb_result = winbindd_request(WINBINDD_DOMAIN_INFO,
+               wb_result = winbindd_request_response(WINBINDD_DOMAIN_INFO,
                                             &wb_request, &wb_response);
 
                if (wb_result == NSS_STATUS_SUCCESS) {
@@ -237,38 +239,75 @@ static int reply_spnego_kerberos(connection_struct *conn,
                }
        }
 
-       asprintf(&user, "%s%c%s", domain, *lp_winbind_separator(), client);
-       
-       pw = smb_getpwnam( user );
+       fstr_sprintf(user, "%s%c%s", domain, *lp_winbind_separator(), client);
        
-       SAFE_FREE(user);
-       SAFE_FREE(client);
+       /* 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));
-               data_blob_free(&ap_rep);
-               return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+
+               /* 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( 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));
+                       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))) {
-               DEBUG(1,("make_server_info_from_pw failed!\n"));
-               data_blob_free(&ap_rep);
-               return ERROR_NT(ret);
+       if ( map_domainuser_to_guest ) {
+               make_server_info_guest(&server_info);
+       } else {
+               ret = make_server_info_pw(&server_info, real_username, pw);
+               if ( !NT_STATUS_IS_OK(ret) ) {
+                       DEBUG(1,("make_server_info_from_pw failed!\n"));
+                       SAFE_FREE(client);
+                       data_blob_free(&ap_rep);
+                       data_blob_free(&session_key);
+                       passwd_free(&pw);
+                       return ERROR_NT(ret);
+               }
        }
+       passwd_free(&pw);
+
+        /* 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);
+        }
 
        /* register_vuid keeps the server info */
-       sess_vuid = register_vuid(server_info, session_key, nullblob, user);
+       /* 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);
 
-       free(user);
+       SAFE_FREE(client);
 
        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);
                        
@@ -278,14 +317,14 @@ 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);
                }
        }
 
@@ -342,6 +381,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);
                        
@@ -351,14 +393,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);
                        }
                }
        }
@@ -391,7 +434,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 */
@@ -408,11 +453,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]));
@@ -421,7 +468,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 && ((lp_security()==SEC_ADS) || lp_use_kerberos_keytab()) ) {
                int ret = reply_spnego_kerberos(conn, inbuf, outbuf, 
                                                length, bufsize, &secblob);
                data_blob_free(&secblob);
@@ -607,7 +654,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;
@@ -647,6 +694,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);
                }
@@ -729,10 +780,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, 
@@ -861,15 +920,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 */
@@ -889,14 +945,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");
        }