NcFTPd Auth Daemon

Starting with version 2.0.0 of NcFTPd, you can use an external process to authenticate user logins. This can be useful if you use a non-standard method of logging users in, such as querying an internal database or some sort of one-time password scheme such as S/KEY. The standard methods are built-in to NcFTPd, which include looking up the user and password in /etc/passwd and the shadow password file, and also using a NcFTPd Password Database built by ncftpd_passwd. Therefore, most sites will be able to use the standard methods and will not have a use for a custom process.

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_authd=localhost:721. This tells NcFTPd to use the authentication daemon listening on UDP port 721 for user logins instead of the built-in methods.

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.


Init message
The init message is sent by NcFTPd to your process when a new user connects. At this time, the fields rAddr, hostName, ipstr, lAddr, cfname, and connectTime are valid.

The auth daemon init reply should simply be the same message back, optionally setting the denied field.


User message
When NcFTPd has prompted the remote client for a username and received the response from the remote client, it sends the auth daemon a user message with the username.

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.


Password message
After NcFTPd has prompted and received a response for the password, it sends the auth daemon the password message. At this point the only thing left to do is for the auth daemon to verify the user and password. The auth daemon must return the message back, and set allowed to non-zero if the user authenticated. (It should not set denied unless the user should be immediately disconnected.)

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.


Message mode
By default NcFTPd will not use the complete message exchange; it will only send the password message (which is a superset of all the other messages). This is to reduce the number of unnecessary messages back and forth between NcFTPd and the auth daemon. If you do need to use the full set, you can configure NcFTPd to send all the messages by appending a ";2" after auth daemon address to indicate message mode 2. Example: "ncftpd_authd=localhost:721;2"


XOR filter
The message is encoded for transit, by exclusive OR-ing the hex string 7FFFFEFF against the message contents. Upon receipt, the message should then be decoded by the same method. This prevents casual packet sniffers from picking up useful data; however, a determined sniffer would have no problem decoding the messages if they put forth an effort!


Additional security concerns
The auth daemon was designed to run on the same machine as the NcFTPd server. It uses UDP for interprocess communication, but the intent was for the UDP messages to be exchanged by two processes on the same server machine. Nevertheless, you could put the auth daemon on a different machine, although that is definitely not recommended! The messages are essentially sent in the clear, so when the remote client sent its password, it could be intercepted before reaching the auth daemon. It could be argued that it's easier to just snoop for rlogin and telnet sessions, but the point is that the message safety and validity can only be guaranteed when both NcFTPd and the auth daemon are on the same machine.


The software development kit
To get you started, source code for a sample NcFTPd Auth Daemon is available. This example shows implements the above protocol and authenticates users according to the /etc/passwd. It obviously should not be used in production since NcFTPd provides that support built-in, but you can use it to test with to see how the overall process works, and to build in support for your own authentication.