For NTLMSSP authentication, the authentication is performed through two session setup requests. The smbd allocates a vuid for each request, but fails to free the first vuid (vuid allocated in the first request). As a result, both vuid and memory of struct user is leaked. This bug also occurs during Kerberos authentication. The best way to reproduce the bug is to write a smbtoture test that opens a TCP connection and performs NTLMSSP session setups and logoff operation in a loop. After a while, the smbd fails session setup request with STATUS_INVALID_PARAMETER and logs "Too many vuids" in the log file. If there is rlimit on smbd, then smbd might reach that limit and fail with memory allocation error. Without writing a new smbtoture test, the bug can be proved by looking at the variable num_validated_vuids in smbd after one successful NTLMSSP authentication. The correct value should be 1, but the actual value would be 2. The bug is in reply_spnego_ntlmssp()/invalidate_vuid(). The reply_spnego_ntlmssp() calls invalidate_vuid() to free the first allocated vuid, but invalidate_vuid() does not free the vuid because the vuid is not "valid" according to get_valid_user_struct(), since the server_info is NULL for this user structure. The bug is fixed by the following code changes identified by #ifdef BUGFIX: void invalidate_vuid(uint16 vuid) { user_struct *vuser = get_valid_user_struct(vuid); #ifdef BUGFIX /* * For NTLMSPP, the second session setup request allocates a new vuid * and invalidates the first vuid. But the first vuid is not valid * (does not have server_info), hence get_valid_user_struct won't find * it. So use get_partial_auth_user_struct to find the vuser structure * for such vuid. Otherwise, both memory and vuid is leaked. */ if (vuser == NULL) { vuser = get_partial_auth_user_struct(vuid); } #endif /* BUGFIX */ if (vuser == NULL) return; SAFE_FREE(vuser->homedir); SAFE_FREE(vuser->unix_homedir); SAFE_FREE(vuser->logon_script); session_yield(vuser); SAFE_FREE(vuser->session_keystr); TALLOC_FREE(vuser->server_info); data_blob_free(&vuser->session_key); DLIST_REMOVE(validated_users, vuser); /* clear the vuid from the 'cache' on each connection, and from the vuid 'owner' of connections */ conn_clear_vuid_cache(vuid); SAFE_FREE(vuser->groups); TALLOC_FREE(vuser->nt_user_token); SAFE_FREE(vuser); num_validated_vuids--; }
Correct the bug summary.
This bug has already been fixed in 3.0.30 and 3.2.x (actually I think it was fixed around 3.0.28). Thanks for the report though. Please let me know if you can reproduce in a later version. Jeremy.