ProFTPD's Authentication Process
When an FTP client connects to a proftpd
server, it needs to
authenticate itself, as per the FTP protocol. The clients send a
USER
command followed by a PASS
command in most cases.
Upon receiving a USER
command, proftpd
will first
check to see if the requested user is allowed to log in, based on any
<Limit LOGIN>
directives. Then it will check to see if the
login is allowed by any configured MaxClients
,
MaxClientsPerHost
, and MaxHostsPerUser
directives.
If these directives mean that the client connection cannot be accepted,
any configured DisplayGoAway
message is sent to the client.
When proftpd
receives a PASS
command, it will again
check for configured MaxClients
, MaxClientsPerHost
,
and MaxHostsPerUser
limits. The first check, during the handling
of the USER
command, handles UserAlias
ed usernames;
this check handles real usernames. The actual work of authenticating
the user then takes place (more on this below). If the authentication
succeeds, any DisplayLogin
and AccessGrantMsg
messages are sent to the client. Otherwise, any AccessDenyMsg
message is sent.
Authenticating the User
During authentication, the process handling the connection is running under
the identity configured using the User
and Group
configuration directives. Only after successfully authenticating the user
will the process assume the identity and privileges of that user. This
process of authentication is handled by the
_setup_environment()
function, and involves quite a bit.
First, this function determines whether the login request is for an
<Anonymous>
server, or for a real user. Unknown users
are rejected at this point. The determination of "known" user
is done via the auth_getpwnam()
dispatch function, an abstraction of the
getpwnam(3)
function. Modules that wish to supply their
own authentication routines will need to provide all of the
dispatched authentication functions mentioned
here. This dispatch function
"cascades" through each of the registered authentication modules of
the server, asking each module in turn to authenticate the user until an
error is encountered or until a module successfully returns the requested
information. This design allows multiple authentication schemes to be present
simultaneously.
The
auth_getgroups()
dispatch function is similarly used to request the supplemental group
membership information, both group names and group IDs, from authentication
modules. The list of obtained group IDs, if any, will subsequently be
passed on to setgroups(2)
. At this point, I would like to
point out a common error message pertaining to supplemental group
membership:
"unable to set groups: Invalid argument"This message will occur for one of two reasons: negative IDs for the user, or the user is a member of more than the maximum allowable number of groups (NGROUPS, NGROUPS_MAX, _SC_NGROUPS_MAX NGROUPS_MAX Max simultaneous groups to which one may belong )
Next, the function will check to see if the requested user is
root
; logging in as root
requires that the
RootLogin
directive be explicitly set, as this is a very
bad idea. If the requested user is a member of an AnonymousGroup
group, then <Limit>
ed logins are again checked for the
user.
At this point, the given password is checked. If any UserPassword
directives are set, then the configured password will be checked using
auth_check()
abstracted authentication handler. If not, then the
auth_authenticate()
handler will be called. Should user authentication fail, then
groups-based authentication, using the given password as a group password
and/or any GroupPassword
configuration directives, will be
attempted.
If the requested user has succeeded up to this point, the default directory
in which to place the user, either their home directory or any applicable
DefaultChdir
directives, is retrieved. This path is checked
for any applicable <Limit>
directives. Next, after
logging a wtmp
or utmp
log (unless configured
not to), this function will check and apply any DefaultRoot
configuration directives as needed. Last, the identity of the current process
is changed to be that of the authenticated user.
At this point, unless authentication has failed at any point earlier,
the authentication process is done. Any .ftpaccess
files
are parsed, the default transfer mode is set (configurable via the
DefaultTransferMode
directive), and scoreboard files updated.
Authentication is a complex process, and may fail at several points: if
an authentication module encounters an error while authenticating, if
the login is not for a valid user or is not a valid UserAlias
,
if the login is denied by an applicable <Limit LOGIN>
,
if the password given was incorrect or expired, if the account used
is disabled, if the user's account includes an invalid shell, if the login
name is listed in a /etc/ftpusers
file and the server is
configured to honour that file, etc.