If you do have a need for custom user authentication, then you need
to build a process and have NcFTPd configured to communicate with
your process. After building your process, you set the ncftpd_authd
option in the general.cf file to point to your process, i.e.
NcFTPd communicates with your process with a series of messages, all of whom share the same structure, which is documented below.
/* authd.h */ #define kAuthdMaxSupplementaryGroups 32 typedef struct FTPAuthState { long mType; long state; long duplicate; /* Init message. */ struct sockaddr_in rAddr; /* of remote client. */ char hostName[64]; /* of remote client. */ char ipstr[32]; /* of remote client. */ struct sockaddr_in lAddr; /* of FTP server. */ char cfname[64]; /* of FTP server. */ time_t connectTime; /* Init reply message. */ /* Username message. */ char username[32]; /* Username reply message. */ char passPrompt[256]; /* Password message. */ char passwd[64]; /* Password reply message. */ long uid; long gids[1 + kAuthdMaxSupplementaryGroups]; /* pri + supp */ unsigned long ngids; long restrictedUser; char homedir[128]; long denied; long allowed; char ncftpdPrivate[256]; char authdPrivate[256]; } FTPAuthState, *FTPAuthStatePtr; #define kFTPAuthStateMType 1 #define kFTPAuthStateInitMessage 1 #define kFTPAuthStateInitReply 2 #define kFTPAuthStateUserMessage 3 #define kFTPAuthStateUserReply 4 #define kFTPAuthStatePassMessage 5 #define kFTPAuthStatePassReply 6
The mType field of the message is defined to be 1, in network byte order. All integral fields in the structure are to be converted to network byte order before sending. The state field tells which state of the authentication process is in effect.
Each message is cumulative, so that at any state, the results of the previous states are also known. For example, when the password message is received, the message also contains the username field from the previous user message.
Whenever your process sends a message back to NcFTPd, it may set the denied field to non-zero to indicate that NcFTPd should immediately deny access and disconnect the user. It should also set the allowed field to non-zero as soon as the user has supplied enough information such that the user should be logged in. It is possible that a message exchange will never set denied nor allowed to non-zero, if the user does not supply a valid username and password.
Whenever a message is received, it must preserve the private field of the sender. For example, if NcFTPd sends a message to the auth daemon, the auth daemon must preserve the contents of the ncftpdPrivate field when it sends the reply for that message.
The auth daemon init reply should simply be the same message back, optionally setting the denied field.
The auth daemon should either set the denied field, or return back a user reply message, which is the user message. You may also give the remote client a custom prompt, if you set the passPrompt field to other than an empty string. The default value is "331 User okay, need password.\r\n". You may find the passPrompt field is useful for a one-time password scheme, where you would have to present the remote user with the challenge string so that the user could enter the correct password.
Along with the authentication result, the auth daemon returns the user information if the user is to be logged in. This includes the uid (user ID), gids (group IDs), ngids (number of group IDs in the array), the homedir (directory tree to access), and the restrictedUser flag (indicating if the user is confined to the homedir tree).
The only restriction is that NcFTPd will not accept a root login (uid equal to 0) from the auth daemon.