1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
|
ProFTPD Application Programmer Interface
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
NOTE: This API documentation ONLY applies to proftpd versions 1.1.5 and
greater, wherein the module interface was significantly enhanced.
ProFTPD modules are designed to handle and respond to configuration
directives (at startup), FTP commands sent over the control connection by
FTP clients, and authentication requests.
Module are prioritized in the *inverse* order of which they were loaded.
In other words, the last loaded module is the FIRST to receive calls for a
particular configuration directive, ftp command or authentication request.
This can be used to allow later loaded modules (higher priority) to
(optionally) "override" lower priority modules. Thus, load order of the
modules can be VERY important.
Module handler functions are _always_ declared as:
MODRET my_handler_func(cmd_rec *cmd);
In include/modules.h, MODRET is defined as 'static modret_t *'. cmd_rec
is a structure created by the engine and passed to the handler which
contains all information regarding the command, etc. modret_t is a
special structure that is created by the handler and returned to the
engine in order to tell the engine how to proceed. There are a few macros
in include/modules.h which allow the module programmer to easily create
return structures. The `cmd' argument to each macro is a pointer to
the current module function's cmd_rec structure.
HANDLED(cmd)
- indicates that the handler properly handled the command, and that the
engine should consider the command completed and continue processing.
Note that if a module handler returns HANDLED, POST_CMD handlers
will be called for the command (see below for handler types).
DECLINED(cmd)
- indicates that the engine should act as though the handler was never
called and continue processing. POST_CMD type handlers will NOT
be called in this case.
ERROR(cmd)
- A protocol error occured. POST_CMD type handlers are NOT
called.
ERROR_MSG(cmd,numeric,message)
- Same as above, however the string composed of "numeric message" is sent
to the client. In the case of directive configuration handlers, the
"numeric" argument is ignored, "message" is displayed to the user (or
logged via syslog), and proftpd terminates.
ERROR_INT(cmd,n)
- Same as above, however this indicates a single integer numeric error.
(Used only in authentication/mapping handlers [see mod_unixpw.c])
Handler Tables
--------------
Each module can define up to three different handler tables (which are
registered via the module structure). These need not all be supplied.
They are grouped by handler class:
conftable[]: For configuration directive handlers.
cmdtable[]: For command handlers, including POST_CMD and PRE_CMD
(see below)
authtable[]: For authentication handlers.
The "conftable" is a structure which lists all configuration directive
handlers for the module.
The "cmdtable" is a structure which lists all FTP command handlers for the
module. Each entry in this table contains a special "command type" field
which determines the general 'type' of handler. There are currently four
types: CMD, PRE_CMD, and POST_CMD and LOG_CMD. When proftpd receives a
command for a client, it enters a five (eight?) step command->module
cascading delivery process:
1. Call any PRE_CMD type handlers which match the special "*"
command wildcard
1a. Call any PRE_CMD type handlers which match the command.
2. Call any CMD type handlers which match the special "*"
command wildcard
2a. Call any CMD type handlers which match the command.
3. Call any POST_CMD type handlers which match the special "*"
command wildcard
3a. Call any POST_CMD type handlers which match the command.
4. Call any LOG_CMD type handlers which match the special "*" wildcard.
5. Call any LOG_CMD type handlers which match the command.
Further more, the return type of a particular command can allow or
disallow further dispatching. Rules:
PRE_CMD:
1. If DECLINED is returned, all other PRE_CMD handlers are called.
2. If ERROR is returned, command handler dispatching stops completely.
CMD:
1. If DECLINED is returned, all other CMD handlers are called.
2. If ERROR is returned, command handler dispatching stops completely.
POST_CMD:
1. If ERROR is returned, the message (if any) is sent to syslog,
however because the CMD handler already executed, nothing further
can be done.
As you can no doubt see, this allows a higher priority module to
"overload" a PRE_CMD handler for a particular command and allow/disallow
the command as desired.
As a general rule of thumb, PRE_CMD handlers should check syntax/basic
applicability of the command, while CMD type handlers should actually
do the 'work'. POST_CMD handlers generally handle any kind of cleanup,
while LOG_CMD handlers handle logging successful completion of the
command.
Responding to client requests in command handlers
=================================================
There are two main ways to respond to a client command inside a command
handler. The first way is incompatible with other other handlers, and
should only be used if the handler is about to terminate the current
connection (and thus kill the child, usually with end_login()). This
first method, using one of the core functions outlined below, must be used
because in the event that a handler is about to terminate proftpd, the
internal response lists will never be processed by the proftpd engine.
Method 1 (Use only in conjunction with end_login() or similar
termination)
send_response(char *numeric, char *format, ...);
-- immediately sends the given response with the indicated numeric
to the client
send_response_ml_start(char *numeric, char *format, ...);
-- starts a multiline ftp protocol with the given numeric
send_response_ml(char *format, ...);
-- continues a multiline response using the numeric specified
in send_response_ml_start();
send_response_ml_end(char *format, ...);
-- completes a multiline response
send_response_async(char *numeric, char *format, ...);
-- send an asyncronous message to the client, this function is
suitable for use inside signal handlers
The second, and prefered, method of transmitting numeric + text message
responses to clients is via the internal response chain. Using this
allows all handlers to add their own individual responses which will all
be sent en masse after the command successfully completes (or fails).
Proftpd maintains two such chains, one response chain for success
messages, and one for error messages. However, when all handlers for a
given command have been called, proftpd evaluates the final condition of
the command. If it has failed (caused by a handler returning one of the
available ERROR* macros), all responses pending in the error response
chain are sent. If the command has successfully completed, the normal
(success) response chain is sent. If a command has neither completed
successfully nor resulted in an error, proftpd assumes the command is
invalid and informs the client appropriately. Either way, once the
"lifetime" of the command is over, and one (or none) of the response
chains has been sent, BOTH chains are destroyed.
Method 2 (using response chains)
add_response(char *numeric, char *format, ...);
-- adds the given numeric + message to the end of the _success_
chain, to be sent if the command successfully completes.
add_response_err(char *numeric, char *format, ...);
-- adds the given numeric + message to the end of the _error_
chain, to be sent if the command results in a final error.
As you can see, with method 2, there is no corresponding _ml* functions
for multiline replies. This is because proftpd automatically generates a
multiline format reply if it detects that there are two or more responses
with the same numeric waiting to be sent to the client. In many cases,
you may be writing a handler (post_cmd, for example) that neither cares
nor specifically knows what numerics other handlers are using during their
responses. In this case, you can use the special R_DUP response numeric,
which tells proftpd that your response should be _assumed_ to be part of a
multiline response started by another handler. For example, if the
following add_response() calls were made from different modules:
module a:
add_response(R_200,"Command successfully completed.");
module b:
add_response(R_DUP,"Statistics for command '%s': none.",cmd->argv[0]);
module c:
add_response(R_DUP,"XFOO post_cmd handler ran.");
The final output sent to the client (assuming the command was successfully
handled) would be (assuming your command is named 'XFOO'):
200-Command successfully completed.
Statistics for command 'XFOO': none.
200 XFOO post_cmd handler ran.
Authentication Handlers
=======================
Authentication handlers allow a module to provide (or overload)
authentication, uid/gid to name and name to uid/gid mappings.
In order for a module to provide this functionality, it must export an
authtable (via the module structure), and define one or more of the
following handler "names" (authtable.name).
Each handler accepts data in the cmd structure and should return it's
data in the modret->data field. The core function mod_create_data()
can create this data structure properly for return to the caller.
Defined 'names' are:
"setpwent" : low-level emulation of setpwent libc function
"setgrent" : low-level emulation of setgrent libc function
"endpwent" : low-level emulation of endpwent libc function
"endgrent" : low-level emulation of endgrent libc function
"getpwent" : low-level emulation of getpwent libc function
"getgrent" : low-level emulation of getgrent libc function
"getpwnam" : low-level emulation of getpwnam libc function
"getgrnam" : low-level emulation of getgrnam libc function
"getpwuid" : low-level emulation of getpwuid libc function
"getgrgid" : low-level emulation of getgrgid libc function
"auth" : authenticate user
(cmd->argv[0] = user,
cmd->argv[1] = cleartext password)
"check" : compare supplied passwords
(cmd->argv[0] = hashed password,
cmd->argv[1] = user,
cmd->argv[2] = cleartext password)
"uid_name" : map uid to name
(cmd->argv[0] = (char*)uid)
"gid_name" : map gid to group
(cmd->argv[0] = (char*)gid)
"name_uid" : map name to uid
(cmd->argv[0] = name)
"name_gid" : map group to gid
(cmd->argv[0] = name)
The cascaded order of authentication handlers is similar to command
handlers, but slightly different. Rules:
1. If an authentication handler returns DECLINED, handlers in other
modules are called (in priority load order, just as with command
handlers). If ALL handlers for a particular function return
DECLINED, proftpd will assume that the operation failed.
2. If an authentication handler returns HANDLED, proftpd assumes
that the operation completed successfully, and will stop calling
auth handlers. Most auth handlers MUST return data if they complete
successfully (note that some void-style handlers do not have this
requirement; i.e. "setpwent" and friends).
3. If an authentication handler returns ERROR, proftpd assumes an
error has occured and discontinues calling other handlers. Some
functions ("auth", for example), should use the ERROR_INT macro
to return a numeric error code (one of the AUTH_* macros in
include/modules.h) indicating the reason for failure.
|