Actually, first_valid_uid only affects Dovecot's userdb lookup in passwd database and does not impact the passdb lookup. After delving into the source files, it becomes clear that the validity check takes place in the passwd_iterate_want_pw function located in the file core/src/auth/userdb-passwd.c:
static bool passwd_iterate_want_pw(struct passwd *pw, const struct auth_settings *set) {
/* Skip entries not in the valid UID range.
They're users for daemons and such. */
if (pw->pw_uid < (uid_t)set->first_valid_uid)
return FALSE;
if (pw->pw_uid > (uid_t)set->last_valid_uid && set->last_valid_uid != 0)
return FALSE;
if (pw->pw_gid < (gid_t)set->first_valid_gid)
return FALSE;
if (pw->pw_gid > (gid_t)set->last_valid_gid && set->last_valid_gid != 0)
return FALSE;
return TRUE;
}
If the function returns FALSE, the userdb lookup will skip that user, resulting in an error stating that the user is not found.
However, during the passdb lookup, a separate database is used to verify the username and password. Taking the default driver pam as an example, its configuration file is located at /etc/pam.d/dovecot, and lookup is performed in /etc/shadow. The passdb lookup does not involve checking the validity of the username with the passwd database for userdb.
When the auth_debug = yes setting is enabled in Dovecot's configuration file, here is an example of logs generated during an smtpd authentication:
1111-11-11T11:11:41.122920+00:00 myserver dovecot: auth: Debug: client in: AUTH#0114#011plain#011service=smtp#011nologin#011lip=127.0.0.1#011rip=127.0.0.1.87#011secured
1111-11-11T11:11:41.123831+00:00 myserver dovecot: auth: Debug: client passdb out: CONT#0114#011
1111-11-11T11:11:43.185585+00:00 myserver dovecot: auth: Debug: client in: CONT<hidden>
1111-11-11T11:11:43.185703+00:00 myserver dovecot: auth: Debug: pam(root,127.0.0.1.87): Performing passdb lookup
1111-11-11T11:11:43.194570+00:00 myserver dovecot: auth-worker(37592): Debug: Loading modules from directory: /usr/lib/dovecot/modules/auth
1111-11-11T11:11:43.195187+00:00 myserver dovecot: auth-worker(37592): Debug: Module loaded: /usr/lib/dovecot/modules/auth/lib20_auth_var_expand_crypt.so
1111-11-11T11:11:43.195807+00:00 myserver dovecot: auth-worker(37592): Debug: conn unix:auth-worker (pid=37547,uid=107): Server accepted connection (fd=13)
1111-11-11T11:11:43.196014+00:00 myserver dovecot: auth-worker(37592): Debug: conn unix:auth-worker (pid=37547,uid=107): Sending version handshake
1111-11-11T11:11:43.196326+00:00 myserver dovecot: auth-worker(37592): Debug: conn unix:auth-worker (pid=37547,uid=107): auth-worker<1>: Handling PASSV request
1111-11-11T11:11:43.196575+00:00 myserver dovecot: auth-worker(37592): Debug: conn unix:auth-worker (pid=37547,uid=107): auth-worker<1>: pam(root,127.0.0.1.87): Performing passdb lookup
1111-11-11T11:11:43.196757+00:00 myserver dovecot: auth-worker(37592): Debug: conn unix:auth-worker (pid=37547,uid=107): auth-worker<1>: pam(root,127.0.0.1.87): lookup service=dovecot
1111-11-11T11:11:43.199784+00:00 myserver dovecot: auth-worker(37592): Debug: conn unix:auth-worker (pid=37547,uid=107): auth-worker<1>: pam(root,127.0.0.1.87): #1/1 style=1 msg=Password:
1111-11-11T11:11:45.306854+00:00 myserver dovecot: auth: Debug: pam(root,127.0.0.1.87): Finished passdb lookup
As you can see, only the pam module is called throughout the process, and there is no module to determine the validity of the user like the passwd module. Consequently, the root user can pass the SMTPD authentication as long as the password is correct and can send emails without any issues, although they won't be able to receive emails.
Considering the security vulnerabilities that enabling password login for the root user brings, it is advisable to run the command passwd -l root to disable the password for root. This command sets an impossible value for the root user's password in the /etc/shadow file. Consequently, no matter what password is attempted for root in SASL, the pam module will return an error Authentication failure (Password mismatch?), thus preventing root from logging in via SASL.