diff --git a/contrib/mod_sftp_pam.c b/contrib/mod_sftp_pam.c new file mode 100644 index 0000000000..d6896677e0 --- /dev/null +++ b/contrib/mod_sftp_pam.c @@ -0,0 +1,716 @@ +/* + * ProFTPD: mod_sftp_pam -- a module which provides an SSH2 + * "keyboard-interactive" driver using PAM + * + * Copyright (c) 2008 TJ Saunders + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + * As a special exemption, TJ Saunders and other respective copyright holders + * give permission to link this program with OpenSSL, and distribute the + * resulting executable, without including the source code for OpenSSL in the + * source distribution. + * + * This is mod_sftp_pam, contrib software for proftpd 1.3.x and above. + * For more information contact TJ Saunders . + * + * $Id: mod_sftp_pam.c,v 1.1 2009-02-13 21:45:12 castaglia Exp $ + * $Libraries: -lpam $ + */ + +#include "conf.h" +#include "privs.h" +#include "mod_sftp.h" + +#ifndef HAVE_PAM +# error "mod_sftp_pam requires PAM support on your system" +#endif + +#define MOD_SFTP_PAM_VERSION "mod_sftp_pam/0.0" + +/* Make sure the version of proftpd is as necessary. */ +#if PROFTPD_VERSION_NUMBER < 0x0001030202 +# error "ProFTPD 1.3.2rc2 or later required" +#endif + +#ifdef HAVE_SECURITY_PAM_APPL_H +# ifdef HPUX11 +# ifndef COMSEC +# define COMSEC 1 +# endif +# endif /* HPUX11 */ +# include +#endif /* HAVE_SECURITY_PAM_APPL_H */ + +#ifdef HAVE_SECURITY_PAM_MODULES_H +# include +#endif /* HAVE_SECURITY_PAM_MODULES_H */ + +/* Needed for the MAXLOGNAME restriction. */ +#ifdef HAVE_SYS_PARAM_H +# include +#endif + +#ifdef HAVE_PAM_PAM_APPL_H +#include +#endif /* HAVE_PAM_PAM_APPL_H */ + +/* There is ambiguity in the PAM spec, with regard to the list of + * struct pam_message that is passed to the conversation callback. Is it + * a pointer to an array, or an array of pointers? + */ +#if defined(SOLARIS2) || defined(HPUX11) +# define SFTP_PAM_MSG_MEMBER(msg, n, member) ((*(msg))[(n)].member) +#else +# define SFTP_PAM_MSG_MEMBER(msg, n, member) ((msg)[(n)]->member) +#endif + +/* Same as from mod_auth_pam.c */ +#define SFTP_PAM_OPT_NO_TTY 0x001 + +module sftp_pam_module; + +static void sftppam_exit_ev(const void *, void *); +MODRET sftppam_auth(cmd_rec *); + +static sftp_kbdint_driver_t sftppam_driver; +static authtable sftppam_authtab[]; + +static pam_handle_t *sftppam_pamh = NULL; +static const char *sftppam_service = "sshd"; + +static int sftppam_authoritative = FALSE; +static int sftppam_auth_code = PR_AUTH_OK; +static int sftppam_handle_auth = FALSE; +static char *sftppam_user = NULL; +static size_t sftppam_userlen = 0; +static char sftppam_tty[32]; + +static const char *trace_channel = "ssh2"; + +/* PAM interaction + */ + +static int sftppam_converse(int nmsgs, const struct pam_message **msgs, + struct pam_response **resps, void *app_data) { + register unsigned int i; + array_header *list; + unsigned int recvd_count = 0; + const char **recvd_responses = NULL; + struct pam_response *res = NULL; + + if (nmsgs <= 0 || + nmsgs > PAM_MAX_NUM_MSG) { + pr_trace_msg(trace_channel, 3, "bad number of PAM messages (%d)", nmsgs); + return PAM_CONV_ERR; + } + + /* First, send these messages to the client. */ + + list = make_array(sftppam_driver.driver_pool, 1, + sizeof(sftp_kbdint_challenge_t)); + for (i = 0; i < nmsgs; i++) { + sftp_kbdint_challenge_t *challenge; + + /* Skip PAM_TEXT_INFO and PAM_ERROR_MSG messages; we don't want to send + * these to the client. + */ + if (SFTP_PAM_MSG_MEMBER(msgs, i, msg_style) == PAM_TEXT_INFO || + SFTP_PAM_MSG_MEMBER(msgs, i, msg_style) == PAM_ERROR_MSG) { + continue; + } + + challenge = push_array(list); + challenge->challenge = pstrdup(sftppam_driver.driver_pool, + SFTP_PAM_MSG_MEMBER(msgs, i, msg)); + + if (SFTP_PAM_MSG_MEMBER(msgs, i, msg_style) != PAM_PROMPT_ECHO_OFF) { + challenge->display_response = TRUE; + + } else { + challenge->display_response = FALSE; + } + } + + if (sftp_kbdint_send_challenge(NULL, NULL, list->nelts, list->elts) < 0) { + pr_trace_msg(trace_channel, 3, + "error sending keyboard-interactive challenges: %s", strerror(errno)); + return PAM_CONV_ERR; + } + + if (sftp_kbdint_recv_response(sftppam_driver.driver_pool, &recvd_count, + &recvd_responses) < 0) { + pr_trace_msg(trace_channel, 3, + "error receiving keyboard-interactive responses: %s", strerror(errno)); + return PAM_CONV_ERR; + } + + /* Make sure that the count of responses matches the challenge count. */ + if (recvd_count != list->nelts) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_PAM_VERSION, + "sent %d %s, but received %u %s", nmsgs, + list->nelts != 1 ? "challenges" : "challenge", recvd_count, + recvd_count != 1 ? "responses" : "response"); + return PAM_CONV_ERR; + } + + res = calloc(nmsgs, sizeof(struct pam_response)); + if (res == NULL) { + pr_log_pri(PR_LOG_CRIT, "Out of memory!"); + return PAM_BUF_ERR; + } + + for (i = 0; i < nmsgs; i++) { + res[i].resp_retcode = 0; + + switch (SFTP_PAM_MSG_MEMBER(msgs, i, msg_style)) { + case PAM_PROMPT_ECHO_ON: + pr_trace_msg(trace_channel, 9, + "received PAM_PROMPT_ECHO_ON message '%s', responding with '%s'", + SFTP_PAM_MSG_MEMBER(msgs, i, msg), recvd_responses[i]); + res[i].resp = strdup(recvd_responses[i]); + break; + + case PAM_PROMPT_ECHO_OFF: + pr_trace_msg(trace_channel, 9, + "received PAM_PROMPT_ECHO_OFF message '%s', responding with text", + SFTP_PAM_MSG_MEMBER(msgs, i, msg)); + res[i].resp = strdup(recvd_responses[i]); + break; + + case PAM_TEXT_INFO: + case PAM_ERROR_MSG: + pr_trace_msg(trace_channel, 9, "received %s message: %s", + msgs[i]->msg_style == PAM_TEXT_INFO ? "PAM_TEXT_INFO" : + "PAM_ERROR_MSG", SFTP_PAM_MSG_MEMBER(msgs, i, msg)); + res[i].resp = NULL; + break; + + default: + pr_trace_msg(trace_channel, 3, + "received unknown PAM message style (%d), treating it as an error", + SFTP_PAM_MSG_MEMBER(msgs, i, msg_style)); + free(res); + + return PAM_CONV_ERR; + } + } + + *resps = res; + return PAM_SUCCESS; +} + +static const struct pam_conv sftppam_conv = { &sftppam_converse, NULL }; + +/* Driver callbacks + */ + +static int sftppam_driver_open(sftp_kbdint_driver_t *driver, const char *user) { + int res; + unsigned long opts = 0; + config_rec *c; + + /* Figure out our default return style: whether or not PAM should allow + * other auth modules a shot at this user or not is controlled by adding + * '*' to a module name in the AuthOrder directive. By default, auth + * modules are not authoritative, and allow other auth modules a chance at + * authenticating the user. This is not the most secure configuration, but + * it allows things like AuthUserFile to work "out of the box". + */ + if (sftppam_authtab[0].auth_flags & PR_AUTH_FL_REQUIRED) { + sftppam_authoritative = TRUE; + } + + sftppam_userlen = strlen(user) + 1; + if (sftppam_userlen > (PAM_MAX_MSG_SIZE + 1)) { + sftppam_userlen = PAM_MAX_MSG_SIZE + 1; + } + +#ifdef MAXLOGNAME + /* Some platforms' PAM libraries do not handle login strings that exceed + * this length. + */ + if (sftppam_userlen > MAXLOGNAME) { + pr_log_pri(PR_LOG_NOTICE, + "PAM(%s): Name exceeds maximum login length (%u)", user, MAXLOGNAME); + pr_trace_msg(trace_channel, 1, + "user name '%s' exceeds maximum login length %u, declining", user, + MAXLOGNAME); + errno = EPERM; + return -1; + } +#endif + + sftppam_user = malloc(sftppam_userlen); + if (sftppam_user == NULL) { + pr_log_pri(PR_LOG_CRIT, "Out of memory!"); + exit(1); + } + + memset(sftppam_user, '\0', sizeof(sftppam_user)); + sstrncpy(sftppam_user, user, sftppam_userlen); + + c = find_config(main_server->conf, CONF_PARAM, "SFTPPAMOptions", FALSE); + if (c != NULL) { + opts = *((unsigned long *) c->argv[0]); + } + +#ifdef SOLARIS2 + /* For Solaris environments, the TTY environment will always be set, + * in order to workaround a bug (Solaris Bug ID 4250887) where + * pam_open_session() will crash unless both PAM_RHOST and PAM_TTY are + * set, and the PAM_TTY setting is at least greater than the length of + * the string "/dev/". + */ + opts &= ~SFTP_PAM_OPT_NO_TTY; +#endif /* SOLARIS2 */ + + pr_signals_block(); + PRIVS_ROOT + + res = pam_start(sftppam_service, sftppam_user, &sftppam_conv, &sftppam_pamh); + if (res != PAM_SUCCESS) { + free(sftppam_user); + sftppam_user = NULL; + sftppam_userlen = 0; + + PRIVS_RELINQUISH + pr_signals_unblock(); + + switch (res) { + case PAM_SYSTEM_ERR: + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_PAM_VERSION, + "error starting PAM service: %s", strerror(errno)); + break; + + case PAM_BUF_ERR: + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_PAM_VERSION, + "error starting PAM service: Memory buffer error"); + break; + } + + return -1; + } + + pam_set_item(sftppam_pamh, PAM_RUSER, sftppam_user); + pam_set_item(sftppam_pamh, PAM_RHOST, session.c->remote_name); + + if (!(opts & SFTP_PAM_OPT_NO_TTY)) { + memset(sftppam_tty, '\0', sizeof(sftppam_tty)); + snprintf(sftppam_tty, sizeof(sftppam_tty), "/dev/ftpd%02lu", + (unsigned long) (session.pid ? session.pid : getpid())); + sftppam_tty[sizeof(sftppam_tty)-1] = '\0'; + + pr_trace_msg(trace_channel, 9, "setting PAM_TTY to '%s'", sftppam_tty); + pam_set_item(sftppam_pamh, PAM_TTY, sftppam_tty); + } + + PRIVS_RELINQUISH + pr_signals_unblock(); + + /* We need to disable mod_auth_pam, since both mod_auth_pam and us want + * to talk to the PAM API, just in different fashions. + */ + c = add_config_param_set(&(main_server->conf), "AuthPAM", 1, NULL); + c->argv[0] = palloc(c->pool, sizeof(unsigned char)); + *((unsigned char *) c->argv[0]) = FALSE; + + pr_auth_add_auth_only_module("mod_sftp_pam.c"); + sftppam_handle_auth = TRUE; + + driver->driver_pool = make_sub_pool(permanent_pool); + pr_pool_tag(driver->driver_pool, "PAM keyboard-interactive driver pool"); + + return 0; +} + +static int sftppam_driver_authenticate(sftp_kbdint_driver_t *driver, + const char *user) { + int res; + + pr_signals_block(); + PRIVS_ROOT + + res = pam_authenticate(sftppam_pamh, PAM_SILENT); + if (res != PAM_SUCCESS) { + switch (res) { + case PAM_USER_UNKNOWN: + sftppam_auth_code = PR_AUTH_NOPWD; + break; + + default: + sftppam_auth_code = PR_AUTH_BADPWD; + } + + pr_trace_msg(trace_channel, 1, + "PAM authentication error (%d) for user '%s': %s", res, user, + pam_strerror(sftppam_pamh, res)); + + PRIVS_RELINQUISH + pr_signals_unblock(); + + errno = EPERM; + return -1; + } + + res = pam_acct_mgmt(sftppam_pamh, PAM_SILENT); + if (res != PAM_SUCCESS) { + switch (res) { +#ifdef PAM_AUTHTOKEN_REQD + case PAM_AUTHTOKEN_REQD: + pr_trace_msg(trace_channel, 8, + "PAM account mgmt error: PAM_AUTHTOKEN_REQD"); + break; +#endif + + case PAM_ACCT_EXPIRED: + pr_trace_msg(trace_channel, 8, + "PAM account mgmt error: PAM_ACCT_EXPIRED"); + sftppam_auth_code = PR_AUTH_DISABLEDPWD; + break; + +#ifdef PAM_ACCT_DISABLED + case PAM_ACCT_DISABLED: + pr_trace_msg(trace_channel, 8, + "PAM account mgmt error: PAM_ACCT_DISABLED"); + sftppam_auth_code = PR_AUTH_DISABLEDPWD; + break; +#endif + + case PAM_USER_UNKNOWN: + pr_trace_msg(trace_channel, 8, + "PAM account mgmt error: PAM_USER_UNKNOWN"); + sftppam_auth_code = PR_AUTH_NOPWD; + break; + + default: + sftppam_auth_code = PR_AUTH_BADPWD; + break; + } + + pr_trace_msg(trace_channel, 1, + "PAM account mgmt error (%d) for user '%s': %s", res, user, + pam_strerror(sftppam_pamh, res)); + + PRIVS_RELINQUISH + pr_signals_unblock(); + + errno = EPERM; + return -1; + } + + res = pam_open_session(sftppam_pamh, PAM_SILENT); + if (res != PAM_SUCCESS) { + sftppam_auth_code = PR_AUTH_DISABLEDPWD; + + pr_trace_msg(trace_channel, 1, + "PAM session error (%d) for user '%s': %s", res, user, + pam_strerror(sftppam_pamh, res)); + + PRIVS_RELINQUISH + pr_signals_unblock(); + + errno = EPERM; + return -1; + } + +#ifdef PAM_CRED_ESTABLISH + res = pam_setcred(sftppam_pamh, PAM_CRED_ESTABLISH); +#else + res = pam_setcred(sftppam_pamh, PAM_ESTABLISH_CRED); +#endif /* !PAM_CRED_ESTABLISH */ + if (res != PAM_SUCCESS) { + switch (res) { + case PAM_CRED_EXPIRED: + pr_trace_msg(trace_channel, 8, + "PAM credentials error: PAM_CRED_EXPIRED"); + sftppam_auth_code = PR_AUTH_AGEPWD; + break; + + case PAM_USER_UNKNOWN: + pr_trace_msg(trace_channel, 8, + "PAM credentials error: PAM_USER_UNKNOWN"); + sftppam_auth_code = PR_AUTH_NOPWD; + break; + + default: + sftppam_auth_code = PR_AUTH_BADPWD; + break; + } + + pr_trace_msg(trace_channel, 1, + "PAM credentials error (%d) for user '%s': %s", res, user, + pam_strerror(sftppam_pamh, res)); + + PRIVS_RELINQUISH + pr_signals_unblock(); + + errno = EPERM; + return -1; + } + + /* XXX Not sure why these platforms have different treatment...? */ +#if defined(SOLARIS2) || defined(HPUX10) || defined(HPUX11) + res = pam_close_session(sftppam_pamh, 0); + if (sftppam_pamh) { + pam_end(sftppam_pamh, res); + sftppam_pamh = NULL; + } +#endif + + PRIVS_RELINQUISH + pr_signals_unblock(); + + return 0; +} + +static int sftppam_driver_close(sftp_kbdint_driver_t *driver) { + if (driver->driver_pool) { + destroy_pool(driver->driver_pool); + driver->driver_pool = NULL; + } + + if (sftppam_user) { + free(sftppam_user); + sftppam_user = NULL; + sftppam_userlen = 0; + } + + return 0; +} + +/* Auth handlers + */ + +MODRET sftppam_auth(cmd_rec *cmd) { + if (!sftppam_handle_auth) { + return PR_DECLINED(cmd); + } + + if (sftppam_auth_code != PR_AUTH_OK) { + if (sftppam_authoritative) + return PR_ERROR_INT(cmd, sftppam_auth_code); + + return PR_DECLINED(cmd); + } + + session.auth_mech = "mod_sftp_pam.c"; + pr_event_register(&sftp_pam_module, "core.exit", sftppam_exit_ev, NULL); + return PR_HANDLED(cmd); +} + +/* Configuration handlers + */ + +/* usage: SFTPPAMEngine on|off */ +MODRET set_sftppamengine(cmd_rec *cmd) { + int bool = -1; + config_rec *c; + + CHECK_ARGS(cmd, 1); + CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL); + + bool = get_boolean(cmd, 1); + if (bool == -1) { + CONF_ERROR(cmd, "expected Boolean parameter"); + } + + c = add_config_param(cmd->argv[0], 1, NULL); + c->argv[0] = pcalloc(c->pool, sizeof(int)); + *((int *) c->argv[0]) = bool; + + return PR_HANDLED(cmd); +} + +/* usage: SFTPPAMOptions opt1 ... */ +MODRET set_sftppamoptions(cmd_rec *cmd) { + config_rec *c = NULL; + register unsigned int i = 0; + unsigned long opts = 0UL; + + if (cmd->argc-1 == 0) + CONF_ERROR(cmd, "wrong number of parameters"); + + CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL); + + c = add_config_param(cmd->argv[0], 1, NULL); + + for (i = 1; i < cmd->argc; i++) { + if (strcmp(cmd->argv[i], "NoTTY") == 0) { + opts |= SFTP_PAM_OPT_NO_TTY; + + } else { + CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown SFTPPAMOption: '", + cmd->argv[i], "'", NULL)); + } + } + + c->argv[0] = pcalloc(c->pool, sizeof(unsigned long)); + *((unsigned long *) c->argv[0]) = opts; + + return PR_HANDLED(cmd); +} + +/* usage: SFTPPAMServiceName name */ +MODRET set_sftppamservicename(cmd_rec *cmd) { + CHECK_ARGS(cmd, 1); + CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL); + + add_config_param_str(cmd->argv[0], 1, cmd->argv[1]); + return PR_HANDLED(cmd); +} + +/* Event Handlers + */ + +static void sftppam_exit_ev(const void *event_data, void *user_data) { + int res; + + /* Close the PAM session */ + + if (sftppam_pamh == NULL) + return; + +#ifdef PAM_CRED_DELETE + res = pam_setcred(sftppam_pamh, PAM_CRED_DELETE); +#else + res = pam_setcred(sftppam_pamh, PAM_DELETE_CRED); +#endif + if (res != PAM_SUCCESS) { + pr_trace_msg(trace_channel, 9, "PAM error setting PAM_DELETE_CRED: %s", + pam_strerror(sftppam_pamh, res)); + } + + res = pam_close_session(sftppam_pamh, PAM_SILENT); + pam_end(sftppam_pamh, res); + sftppam_pamh = NULL; + + if (sftppam_user != NULL) { + free(sftppam_user); + sftppam_user = NULL; + sftppam_userlen = 0; + } +} + +#if defined(PR_SHARED_MODULE) +static void sftppam_mod_unload_ev(const void *event_data, void *user_data) { + if (strcmp("mod_sftp_pam.c", (const char *) event_data) == 0) { + if (sftppam_user) { + free(sftppam_user); + sftppam_user = NULL; + sftppam_userlen = 0; + } + + sftp_kbdint_unregister_driver("pam"); + pr_event_unregister(&sftp_pam_module, NULL, NULL); + } +} +#endif /* !PR_SHARED_MODULE */ + +/* Initialization functions + */ + +static int sftppam_init(void) { +#if defined(PR_SHARED_MODULE) + pr_event_register(&sftp_pam_module, "core.module-unload", + sftppam_mod_unload_ev, NULL); +#endif /* !PR_SHARED_MODULE */ + + /* Prepare our driver. */ + memset(&sftppam_driver, 0, sizeof(sftppam_driver)); + sftppam_driver.open = sftppam_driver_open; + sftppam_driver.authenticate = sftppam_driver_authenticate; + sftppam_driver.close = sftppam_driver_close; + + /* Register ourselves with mod_sftp. */ + if (sftp_kbdint_register_driver("pam", &sftppam_driver) < 0) { + pr_log_pri(PR_LOG_NOTICE, MOD_SFTP_PAM_VERSION + ": notice: error registering 'keyboard-interactive' driver: %s", + strerror(errno)); + return -1; + } + + return 0; +} + +static int sftppam_sess_init(void) { + config_rec *c; + + c = find_config(main_server->conf, CONF_PARAM, "SFTPPAMEngine", FALSE); + if (c) { + int engine; + + engine = *((int *) c->argv[0]); + if (!engine) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_PAM_VERSION, + "disabled by SFTPPAMEngine setting, unregistered 'pam' driver"); + sftp_kbdint_unregister_driver("pam"); + return 0; + } + } + + c = find_config(main_server->conf, CONF_PARAM, "SFTPPAMServiceName", FALSE); + if (c) { + sftppam_service = c->argv[0]; + } + + pr_trace_msg(trace_channel, 8, "using PAM service name '%s'", + sftppam_service); + + return 0; +} + +/* Module API tables + */ + +static conftable sftppam_conftab[] = { + { "SFTPPAMEngine", set_sftppamengine, NULL }, + { "SFTPPAMOptions", set_sftppamoptions, NULL }, + { "SFTPPAMServiceName", set_sftppamservicename, NULL }, + { NULL } +}; + +static authtable sftppam_authtab[] = { + { 0, "auth", sftppam_auth }, + { 0, NULL, NULL } +}; + +module sftp_pam_module = { + NULL, NULL, + + /* Module API version 2.0 */ + 0x20, + + /* Module name */ + "sftp_pam", + + /* Module configuration handler table */ + sftppam_conftab, + + /* Module command handler table */ + NULL, + + /* Module authentication handler table */ + sftppam_authtab, + + /* Module initialization function */ + sftppam_init, + + /* Session initialization function */ + sftppam_sess_init, + + /* Module version */ + MOD_SFTP_PAM_VERSION +}; diff --git a/contrib/mod_sftp_sql.c b/contrib/mod_sftp_sql.c new file mode 100644 index 0000000000..e91c0131e6 --- /dev/null +++ b/contrib/mod_sftp_sql.c @@ -0,0 +1,498 @@ +/* + * ProFTPD: mod_sftp_sql -- SQL backend module for retrieving authorized keys + * + * Copyright (c) 2008 TJ Saunders + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + * As a special exemption, TJ Saunders and other respective copyright holders + * give permission to link this program with OpenSSL, and distribute the + * resulting executable, without including the source code for OpenSSL in the + * source distribution. + * + * This is mod_sftp_sql, contrib software for proftpd 1.3.x and above. + * For more information contact TJ Saunders . + * + * $Id: mod_sftp_sql.c,v 1.1 2009-02-13 21:45:12 castaglia Exp $ + */ + +#include "conf.h" +#include "privs.h" +#include "mod_sftp.h" +#include "mod_sql.h" + +#define MOD_SFTP_SQL_VERSION "mod_sftp_sql/0.1" + +module sftp_sql_module; + +struct sqlstore_key { + const char *subject; + + /* Key data */ + char *key_data; + uint32_t key_datalen; +}; + +struct sqlstore_data { + const char *select_query; +}; + +static const char *trace_channel = "ssh2"; + +static cmd_rec *sqlstore_cmd_create(pool *parent_pool, int argc, ...) { + pool *cmd_pool = NULL; + cmd_rec *cmd = NULL; + register unsigned int i = 0; + va_list argp; + + cmd_pool = make_sub_pool(parent_pool); + cmd = (cmd_rec *) pcalloc(cmd_pool, sizeof(cmd_rec)); + cmd->pool = cmd_pool; + + cmd->argc = argc; + cmd->argv = (char **) pcalloc(cmd->pool, argc * sizeof(char *)); + + /* Hmmm... */ + cmd->tmp_pool = cmd->pool; + + va_start(argp, argc); + for (i = 0; i < argc; i++) + cmd->argv[i] = va_arg(argp, char *); + va_end(argp); + + return cmd; +} + +static struct sqlstore_key *sqlstore_get_key(pool *p, char *blob) { + char chunk[1024], *data = NULL; + BIO *bio = NULL, *b64 = NULL, *bmem = NULL; + int chunklen; + long datalen = 0; + size_t bloblen; + struct sqlstore_key *key = NULL; + + bloblen = strlen(blob); + + key = pcalloc(p, sizeof(struct sqlstore_key)); + bio = BIO_new(BIO_s_mem()); + + if (BIO_write(bio, blob, bloblen) < 0) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION, + "error buffering base64 data"); + } + + /* Add a base64 filter BIO, and read the data out, thus base64-decoding + * the key. Write the decoded data into another memory BIO. + */ + b64 = BIO_new(BIO_f_base64()); + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + bio = BIO_push(b64, bio); + + bmem = BIO_new(BIO_s_mem()); + + memset(chunk, '\0', sizeof(chunk)); + chunklen = BIO_read(bio, chunk, sizeof(chunk)); + + if (chunklen < 0 && + !BIO_should_retry(bio)) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION, + "unable to base64-decode data from database: %s", + sftp_crypto_get_errors()); + BIO_free_all(bio); + BIO_free_all(bmem); + + errno = EPERM; + return NULL; + } + + while (chunklen > 0) { + pr_signals_handle(); + + if (BIO_write(bmem, chunk, chunklen) < 0) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION, + "error writing to memory BIO: %s", sftp_crypto_get_errors()); + BIO_free_all(bio); + BIO_free_all(bmem); + + errno = EPERM; + return NULL; + } + + memset(chunk, '\0', sizeof(chunk)); + chunklen = BIO_read(bio, chunk, sizeof(chunk)); + } + + datalen = BIO_get_mem_data(bmem, &data); + + if (data != NULL && + datalen > 0) { + key->key_data = pcalloc(p, datalen + 1); + key->key_datalen = datalen; + memcpy(key->key_data, data, datalen); + + } else { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION, + "error base64-decoding key data from database"); + } + + BIO_free_all(bio); + bio = NULL; + + BIO_free_all(bmem); + return key; +} + +static char *sqlstore_get_str(pool *p, char *str) { + cmdtable *cmdtab; + cmd_rec *cmd; + modret_t *res; + + if (strlen(str) == 0) + return str; + + /* Find the cmdtable for the sql_escapestr command. */ + cmdtab = pr_stash_get_symbol(PR_SYM_HOOK, "sql_escapestr", NULL, NULL); + if (cmdtab == NULL) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION, + "unable to find SQL hook symbol 'sql_escapestr'"); + return str; + } + + cmd = sqlstore_cmd_create(p, 1, pr_str_strip(p, str)); + + /* Call the handler. */ + res = pr_module_call(cmdtab->m, cmdtab->handler, cmd); + + /* Check the results. */ + if (MODRET_ISERROR(res)) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION, + "error executing 'sql_escapestring'"); + return str; + } + + return res->data; +} + +static int sqlstore_verify_host_key(sftp_keystore_t *store, pool *p, + const char *user, const char *host_fqdn, const char *host_user, + char *key_data, uint32_t key_datalen) { + register unsigned int i; + struct sqlstore_key *key; + struct sqlstore_data *store_data; + pool *tmp_pool; + cmdtable *sql_cmdtab; + cmd_rec *sql_cmd; + modret_t *sql_res; + array_header *sql_data; + char **values; + int res; + + store_data = store->keystore_data; + + /* Find the cmdtable for the sql_lookup command. */ + sql_cmdtab = pr_stash_get_symbol(PR_SYM_HOOK, "sql_lookup", NULL, NULL); + if (sql_cmdtab == NULL) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION, + "unable to find SQL hook symbol 'sql_lookup'"); + errno = EPERM; + return -1; + } + + tmp_pool = make_sub_pool(store->keystore_pool); + + /* Prepare the SELECT query. */ + sql_cmd = sqlstore_cmd_create(tmp_pool, 3, "sql_lookup", + store_data->select_query, sqlstore_get_str(tmp_pool, (char *) host_fqdn)); + + /* Call the handler. */ + sql_res = pr_module_call(sql_cmdtab->m, sql_cmdtab->handler, sql_cmd); + if (sql_res == NULL || + MODRET_ISERROR(sql_res)) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION, + "error processing SQLNamedQuery '%s'", store_data->select_query); + destroy_pool(tmp_pool); + + errno = EPERM; + return -1; + } + + sql_data = (array_header *) sql_res->data; + + if (sql_data->nelts == 0) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION, + "SQLNamedQuery '%s' returned zero results", store_data->select_query); + destroy_pool(tmp_pool); + errno = ENOENT; + return -1; + + } else { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION, + "SQLNamedQuery '%s' returned %d %s", store_data->select_query, + sql_data->nelts, sql_data->nelts != 1 ? "rows" : "row"); + } + + values = (char **) sql_data->elts; + for (i = 0; i < sql_data->nelts; i++) { + pr_signals_handle(); + + key = sqlstore_get_key(p, values[i]); + if (key == NULL) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION, + "error obtaining SSH2 public key from SQL data (row %u)", i+1); + continue; + } + + res = sftp_keys_compare_keys(p, key_data, key_datalen, key->key_data, + key->key_datalen); + if (res != TRUE) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION, + "error comparing client key with SQL data (row %u) from SQLNamedQuery " + "'%s': %s", i+1, store_data->select_query, strerror(errno)); + continue; + } + + pr_trace_msg(trace_channel, 10, "found matching public key (row %u) for " + "host '%s' using SQLNamedQuery '%s'", i+1, host_fqdn, + store_data->select_query); + destroy_pool(tmp_pool); + return 0; + } + + destroy_pool(tmp_pool); + errno = ENOENT; + return -1; +} + +static int sqlstore_verify_user_key(sftp_keystore_t *store, pool *p, + const char *user, char *key_data, uint32_t key_datalen) { + register unsigned int i; + struct sqlstore_key *key; + struct sqlstore_data *store_data; + pool *tmp_pool; + cmdtable *sql_cmdtab; + cmd_rec *sql_cmd; + modret_t *sql_res; + array_header *sql_data; + char **values; + int res; + + store_data = store->keystore_data; + + /* Find the cmdtable for the sql_lookup command. */ + sql_cmdtab = pr_stash_get_symbol(PR_SYM_HOOK, "sql_lookup", NULL, NULL); + if (sql_cmdtab == NULL) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION, + "unable to find SQL hook symbol 'sql_lookup'"); + errno = EPERM; + return -1; + } + + tmp_pool = make_sub_pool(store->keystore_pool); + + /* Prepare the SELECT query. */ + sql_cmd = sqlstore_cmd_create(tmp_pool, 3, "sql_lookup", + store_data->select_query, sqlstore_get_str(tmp_pool, (char *) user)); + + /* Call the handler. */ + sql_res = pr_module_call(sql_cmdtab->m, sql_cmdtab->handler, sql_cmd); + if (sql_res == NULL || + MODRET_ISERROR(sql_res)) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION, + "error processing SQLNamedQuery '%s'", store_data->select_query); + destroy_pool(tmp_pool); + + errno = EPERM; + return -1; + } + + sql_data = (array_header *) sql_res->data; + + if (sql_data->nelts == 0) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION, + "SQLNamedQuery '%s' returned zero results", store_data->select_query); + destroy_pool(tmp_pool); + errno = ENOENT; + return -1; + + } else { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION, + "SQLNamedQuery '%s' returned %d %s", store_data->select_query, + sql_data->nelts, sql_data->nelts != 1 ? "rows" : "row"); + } + + values = (char **) sql_data->elts; + for (i = 0; i < sql_data->nelts; i++) { + pr_signals_handle(); + + key = sqlstore_get_key(p, values[i]); + if (key == NULL) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION, + "error obtaining SSH2 public key from SQL data (row %u)", i+1); + continue; + } + + res = sftp_keys_compare_keys(p, key_data, key_datalen, key->key_data, + key->key_datalen); + if (res != TRUE) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_SQL_VERSION, + "error comparing client key with SQL data (row %u) from SQLNamedQuery " + "'%s': %s", i+1, store_data->select_query, strerror(errno)); + continue; + } + + pr_trace_msg(trace_channel, 10, "found matching public key (row %u) for " + "user '%s' using SQLNamedQuery '%s'", i+1, user, + store_data->select_query); + destroy_pool(tmp_pool); + return 0; + } + + destroy_pool(tmp_pool); + errno = ENOENT; + return -1; +} + +static int sqlstore_close(sftp_keystore_t *store) { + /* Nothing really to do here. */ + return 0; +} + +static sftp_keystore_t *sqlstore_open(pool *parent_pool, + int requested_key_type, const char *store_info, const char *user) { + sftp_keystore_t *store; + pool *sqlstore_pool, *tmp_pool; + struct sqlstore_data *store_data; + char *named_query, *select_query, *ptr; + config_rec *c; + + tmp_pool = make_sub_pool(parent_pool); + + sqlstore_pool = make_sub_pool(parent_pool); + pr_pool_tag(sqlstore_pool, "SFTP SQL-based Keystore Pool"); + + store = pcalloc(sqlstore_pool, sizeof(sftp_keystore_t)); + store->keystore_pool = sqlstore_pool; + store->store_ktypes = requested_key_type; + + switch (requested_key_type) { + case SFTP_SSH2_HOST_KEY_STORE: + store->verify_host_key = sqlstore_verify_host_key; + break; + + case SFTP_SSH2_USER_KEY_STORE: + store->verify_user_key = sqlstore_verify_user_key; + break; + } + + store->store_close = sqlstore_close; + + /* Parse the SELECT query name out of the store_info string: + * + * "/conf, CONF_PARAM, named_query, FALSE); + if (c == NULL) { + (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, + "unable to resolve SQLNamedQuery '%s'", select_query); + destroy_pool(tmp_pool); + + errno = EINVAL; + return NULL; + } + + store_data = pcalloc(sqlstore_pool, sizeof(struct sqlstore_data)); + store->keystore_data = store_data; + store_data->select_query = pstrdup(sqlstore_pool, select_query); + + destroy_pool(tmp_pool); + return store; +} + +/* Event Handlers + */ + +#if defined(PR_SHARED_MODULE) +static void sftpsql_mod_unload_ev(const void *event_data, void *user_data) { + if (strcmp("mod_sftp_sql.c", (const char *) event_data) == 0) { + /* XXX any further cleanup here */ + + sftp_keystore_unregister_store("sql", + SFTP_SSH2_HOST_KEY_STORE|SFTP_SSH2_USER_KEY_STORE); + pr_event_unregister(&sftp_sql_module, NULL, NULL); + } +} +#endif /* !PR_SHARED_MODULE */ + +/* Initialization functions + */ + +static int sftpsql_init(void) { + + sftp_keystore_register_store("sql", sqlstore_open, + SFTP_SSH2_HOST_KEY_STORE|SFTP_SSH2_USER_KEY_STORE); + +#if defined(PR_SHARED_MODULE) + pr_event_register(&sftp_sql_module, "core.module-unload", + sftpsql_mod_unload_ev, NULL); +#endif /* !PR_SHARED_MODULE */ + + return 0; +} + +module sftp_sql_module = { + NULL, NULL, + + /* Module API version 2.0 */ + 0x20, + + /* Module name */ + "sftp_sql", + + /* Module configuration handler table */ + NULL, + + /* Module command handler table */ + NULL, + + /* Module authentication handler table */ + NULL, + + /* Module initialization function */ + sftpsql_init, + + /* Session initialization function */ + NULL, + + /* Module version */ + MOD_SFTP_SQL_VERSION +}; diff --git a/doc/contrib/mod_sftp.html b/doc/contrib/mod_sftp.html new file mode 100644 index 0000000000..b5dea062d3 --- /dev/null +++ b/doc/contrib/mod_sftp.html @@ -0,0 +1,1298 @@ + + + + + +ProFTPD module mod_sftp + + + + +
+
+

ProFTPD module mod_sftp

+
+

+ +

+SFTP versus FTPS
+There is a great deal of confusion and misunderstanding surrounding two very +different protocols: SFTP and FTPS. + +

+FTPS stands for "FTP over SSL/TLS". It is the existing FTP protocol, +made to run over an SSL/TLS connection; in this manner, it is very similar to +HTTPS (HTTP over SSL/TLS). In fact, that is where the name "FTPS" comes from. +The mod_tls module for ProFTPD implements FTPS. Since FTPS is still FTP, any +FTPS sessions require multiple TCP connections: one TCP connection for the +FTP control channel, and separate TCP connections for each FTP data channel. +The need for these multiple connections is undesirable for many network +administrators, especially those that wish to restrict all protocols to a +single TCP connection which can be passed through firewalls/NAT/router +equipment. The network equipment, now, often inspects the application-level +data in FTP packets in order to dynamically open the necessary firewall rules +for the FTP data channels. However, FTPS encrypts those packets, and thus +the network firewall must resort to using a statically configured range of +ports, or not allow FTPS. There is a third solution: the CCC FTP +command, which clears the FTP control channel of encryption, once the user has +authenticated. Unfortunately, support for the CCC command is not +widespread among FTPS clients and servers; the mod_tls module does +support the CCC command. + +

+By contrast, SFTP refers to "Secure File Transfer Protocol", and is not +related to FTP in any way. SFTP is based on the SSH2 protocol, which uses +binary encoding of messages over a secure channel. Unlike FTP, SSH2 only +uses a single TCP connection, and multiplexes multiple transfers or "channels" +over that single connection. For this reason, many sites prefer SFTP +to FTPS for secure transfer of data. + +

+SSH2 RFCs and SFTP Draft
+For those wishing to learn more about the SSH2 and SFTP protocols, see: +

+ +

+The mod_sftp module for ProFTPD
+The mod_sftp module implements the SSH2 protocol and its SFTP +subsystem, for secure file transfer over an SSH2 connection. The +mod_sftp module supports: +

    +
  • Public key authentication +
  • Password authentication (e.g. user authentication via + mod_sql, mod_ldap, mod_auth_file, + mod_auth_unix, mod_auth_pam) +
  • SCP support +
  • Quotas (via the mod_quotatab module) +
  • FIPS support (see Usage section) +
  • Throttled transfers (via TransferRate, and/or the + mod_shaper module) +
  • Blacklisted public keys +
  • Configurable traffic analysis protection +
  • Passphrase-protected host keys +
+ +

+The mod_sftp does not currently support: +

    +
  • <Anonymous> +
  • AllowOverwrite +
  • DirFakeUser/DirFakeGroup +
  • MaxRetrieveFileSize/MaxStoreFileSize +
  • UserOwner, GroupOwner +
  • UseLastlog +
+ +

+The mod_sftp module is contained in the mod_sftp/ +directory, is intended for ProFTPD 1.3.2rc2 and later, and is not compiled by +default. Installation instructions are discussed +here. + +

+The most current version of mod_sftp can be found at: +

+  http://www.castaglia.org/proftpd/
+
+ +

+This product includes software developed by the OpenSSL Project for use in the +OpenSSL Toolkit (http://www.openssl.org/). +This product includes cryptographic software written by Eric Young (eay@cryptsoft.com). + +

Author

+

+Please contact TJ Saunders <tj at castaglia.org> with any +questions, concerns, or suggestions regarding this module. + +

Directives

+ + +

+


+

SFTPAuthMethods

+Syntax: SFTPAuthMethods meth1 ...
+Default: publickey password
+Context: "server config", <VirtualHost>, <Global>
+Module: mod_sftp
+Compatibility: 1.3.2rc2 and later + +

+The SFTPAuthMethods directive configures the list of available +authentication methods presented to connecting clients. The list of +currently supported methods is: +

    +
  • hostbased +
  • keyboard-interactive +
  • publickey +
  • password +
+In general, there is no need to use this directive unless only one specific +authentication method must be used. Should it be needed, the list of +authentication methods to use is provided as a space-delimited list, +e.g.: +
+  # Offer publickey and keyboard-interactive authentication only
+  SFTPAuthMethods publickey keyboard-interactive
+
+ +

+


+

SFTPAuthorizedHostKeys

+Syntax: SFTPAuthorizedHostKeys store1 ...
+Default: None
+Context: "server config", <VirtualHost>, <Global>
+Module: mod_sftp
+Compatibility: 1.3.2rc2 and later + +

+The SFTPAuthorizedHostKeys directive configures the file that +mod_sftp will use during the handling of hostbased authentication +of users. The configured file may contain several public keys in RFC4716 +format (see the Usage section), in no particular order. + +

+Note that a SFTPAuthorizedHostKeys directive is +required for mod_sftp to handle sessions which want to use +hostbased authentication properly. + +

+


+

SFTPAuthorizedUserKeys

+Syntax: SFTPAuthorizedUserKeys store1 ...
+Default: None
+Context: "server config", <VirtualHost>, <Global>
+Module: mod_sftp
+Compatibility: 1.3.2rc2 and later + +

+The SFTPAuthorizedUserKeys directive configures the file that +mod_sftp will use during the handling of publickey authentication +of users. The configured file may contain several public keys in RFC4716 +format (see the Usage section), in no particular order. + +

+Note that a SFTPAuthorizedUserKeys directive is +required for mod_sftp to handle sessions properly. + +

+


+

SFTPCiphers

+Syntax: SFTPCiphers algo1 ...
+Default: None
+Context: "server config", <VirtualHost>, <Global>
+Module: mod_sftp
+Compatibility: 1.3.2rc2 and later + +

+The SFTPCiphers directive is used to specify the list of +cipher algorithms that mod_sftp should use. The current list +of supported cipher algorithms is, in the default order of preference: +

    +
  • aes256-ctr +
  • aes192-ctr +
  • aes128-ctr +
  • aes256-cbc +
  • aes196-cbc +
  • aes128-cbc +
  • blowfish-cbc +
  • cast128-cbc +
  • arcfour256 +
  • arcfour128 +
  • 3des-cbc +
+By default, all of the above cipher algorithms are presented to the client, +in the above order, during the key exchange. + +

+The "none" cipher (i.e. no encryption) will not be presented to +the client by default; any sites which wish to use "none" will have to +explicitly configure it via SFTPCiphers. + +

+Note that CTR mode ciphers (e.g. the aes128-ctr, +aes192-ctr, and aes256-ctr ciphers) are recommended. +Any CBC mode cipher allows for the possibility of an attack which causes +several bits of plaintext to be leaked; the attack is described +here. +This attack is on the SSH2 protocol design itself; any SSH2 implementation +which conforms to the RFCs will have this weakness. + +

+In general, there is no need to use this directive unless only one specific +cipher must be used. + +

+


+

SFTPClientMatch

+Syntax: SFTPClientMatch pattern key1 val1 ...
+Default: None
+Context: "server config", <VirtualHost>, <Global>
+Module: mod_sftp
+Compatibility: 1.3.2rc2 and later + +

+The SFTPClientMatch directive is used to tune some of the +SSH2/SFTP values based on the connecting client, for better interoperability +with those clients. The pattern parameter specifies a POSIX regular +expression which will be matched against the connecting client's SSH version +banner. If the client's banner version matchees pattern, then +the configured keys/values are used for that session. + +

+The currently supported SSH2/SFTP keys which can be tuned via +SFTPClientMatch are: +

    +
  • channelPacketSize
    + The value for this key can be a number, with an optional + "GB" (gigabytes), "MB" (megabytes), or "KB" (kilobytes) suffix. + +

    + The default mod_sftp channel packet size is 32KB. +

  • + +

    +

  • channelWindowSize
    + The value for this key can be a number, with an optional + "GB" (gigabytes), "MB" (megabytes), or "KB" (kilobytes) suffix. + +

    + The default mod_sftp channel window size is 4GB. Using + smaller window sizes may be necessary for some clients; larger window + sizes can help reduce latency for bulk data transfers. +

  • + +

    +

  • sftpProtocolVersion
    + The value for this key can be a specific version number, + e.g. "3", or it can be a range such as "1-3". The + mod_sftp module supports SFTP protocol versions 1-6; + using any other protocol versions will result in bad configuration. +
  • +
+ +

+For example, to make mod_sftp only allow SFTP protocol version 3 +to WinSCP clients, you would use: +

+  SFTPClientMatch ".*WinSCP.*" sftpProtocolVersion 3
+
+Or for older OpenSSH clients, you may need to use a smaller channel window +size, e.g.: +
+  SFTPClientMatch "^OpenSSH_3\\.*" channelWindowSize 8MB
+
+ +

+


+

SFTPCompression

+Syntax: SFTPCompression on|off|delayed
+Default: off
+Context: "server config", <VirtualHost>, <Global>
+Module: mod_sftp
+Compatibility: 1.3.2rc2 and later + +

+The SFTPCompression directive enables the use of zlib compression +of the payload of SSH2 packets. This can make for smaller packets, but require +more CPU time to compress/uncompress the data. + +

+The delayed parameter tells mod_sftp to support a custom +extension used by OpenSSH, where compression is not actually enabled until +after the client has successfully authenticated. + +

+


+

SFTPCryptoDevice

+Syntax: SFTPCryptoDevice driver|"all"|"none"
+Default: None
+Context: "server config", <VirtualHost>, <Global>
+Module: mod_sftp
+Compatibility: 1.3.2rc2 and later + +

+The SFTPCryptoDevice directive is used to configure +mod_sftp to use an OpenSSL "engine", a cryptographic module +that OpenSSL library can use for accelerated cryptographic support, or +HSM keys, etc. + +

+To use all of the engines compiled into OpenSSL, use: +

+  SFTPCryptoDevice all
+
+OpenSSL will find, from the list of supported engines, the first one +usable, if any. If no usable engines are found, OpenSSL will default to +its normal software implementation. If a specific engine is desired as +the default engine to use, specify the engine name, e.g.: +
+  SFTPCryptoDevice chil
+
+ +

+The OpenSSL command +

+  openssl engine
+
+may be used to list all of the engine drivers supported by OpenSSL. + +

+


+

SFTPDHParamFile

+Syntax: SFTPDHParamFile path
+Default: dhparams.pem
+Context: "server config", <VirtualHost>, <Global>
+Module: mod_sftp
+Compatibility: 1.3.2rc2 and later + +

+The SFTPDHParamFile directive is used to configure the path +to a file containing pre-computed Diffie-Hellman (DH) group parameters. +These parameters will be used by mod_sftp when handling the +Diffie-Hellman group exchange (see RFC4419) portion of the key exchange. + +

+The SFTPDHParamFile is produced using OpenSSL, which uses +PEM encodings of PKCS#3 DHparam structures. It is similar in +nature to OpenSSH's moduli(5) file. + +

+The mod_sftp source code comes with a dhparams.pem +file which should be sufficient. If for any reason you find that you +need to generate your own SFTPDHParamFile, use one of the +following OpenSSL commands to create this file: +

+  # openssl dhparam -outform PEM -2 nbits >> dhparams.pem
+  # openssl dhparam -outform PEM -5 nbits >> dhparams.pem
+
+Using either -2 or -5 as the generator is fine. +The nbits value used should vary between 1024 and 8192, inclusive. +Beware: this process is quite slow, and CPU/memory intensive! + +

+


+

SFTPDigests

+Syntax: SFTPDigests algo1 ...
+Default: None
+Context: "server config", <VirtualHost>, <Global>
+Module: mod_sftp
+Compatibility: 1.3.2rc2 and later + +

+The SFTPDigests directive is used to specify the list of +MAC digest algorithms that mod_sftp should use. The current list +of supported MAC algorithms is: +

    +
  • hmac-sha1 +
  • hmac-sha1-96 +
  • hmac-md5 +
  • hmac-md5-96 +
  • hmac-ripemd160 +
+By default, all of the above MAC algorithms are presented to the client, +in the above order, during the key exchange. + +

+The "none" MAC (i.e. no MAC) will not be presented to the client +by default; any sites which wish to use "none" will have to explicitly +configure it via SFTPDigests. + +

+In general, there is no need to use this directive unless only one specific +MAC algorithm must be used. + +

+


+

SFTPDisplayBanner

+Syntax: SFTPDisplayBanner path
+Default: None
+Context: "server config", <VirtualHost>, <Global>
+Module: mod_sftp
+Compatibility: 1.3.2rc2 and later + +

+The SFTPDisplayBanner directive is used to configure the path +of a file which will be sent to the client prior to authentication. Such +files are often used to sent terms of use or other such policy terms to +connecting clients. + +

+


+

SFTPEngine

+Syntax: SFTPEngine on|off
+Default: off
+Context: "server config", <VirtualHost>, <Global>
+Module: mod_sftp
+Compatibility: 1.3.2rc2 and later + +

+The SFTPEngine directive toggles the use of the SSH2/SFTP protocols +engine (e.g. mod_sftp). This is usually used inside a +<VirtualHost> section to enable SFTP sessions for a +particular virtual host. By default mod_sftp is disabled for both +the main server and all configured virtual hosts. + +

+


+

SFTPHostKey

+Syntax: SFTPHostKey file
+Default: None
+Context: "server config", <VirtualHost>, <Global>
+Module: mod_sftp
+Compatibility: 1.3.2rc2 and later + +

+The SFTPHostKey directive configures the path to a host key file. +The mod_sftp module uses the keys thus configured as part of +the SSH2 key exchange process. + +

+Note that the SFTPHostKey directive is required. In fact, +two SFTPHostKey directives are required, one pointing to +an RSA key, and one pointing to a DSA key. These should be the exact same +host key files as used by your SSH2 server, e.g.: +

+  SFTPHostKey /etc/ssh_host_dsa_key
+  SFTPHostKey /etc/ssh_host_rsa_key
+
+or the equivalents in the /usr/local/etc/ directory. Using +different host keys between your SSH2 server and mod_sftp will +only cause confusion and problems for any SFTP clients. Most SFTP clients +will assume that the host uses the same keys for SSH2 and SFTP; after all, +it is the same host (even the same port), just different services. +Thus having different host keys for an SSH2 server and mod_sftp +will cause clients to constantly switch the host keys they record for the +same host (and cause confusion for users). + +

+


+

SFTPKeyBlacklist

+Syntax: SFTPKeyBlacklist "none"|path
+Default: None
+Context: "server config", <VirtualHost>, <Global>
+Module: mod_sftp
+Compatibility: 1.3.2rc2 and later + +

+The SFTPKeyBlacklist directive configures the path to +a specially formatted dictionary of known weak public keys. This key +blacklist, and the code for using it, was primarily developed by +Dmitry V. Levin under sponsorship by +CivicActions. For the original +posting, and further information, see: +

+  http://www.openwall.com/lists/oss-security/2008/05/27/3
+
+ +

+These known weak public keys arose because a vulnerability in the OpenSSL +packages distributed by Debian: +

+  http://www.debian.org/security/2008/dsa-1571
+
+ +

+The mod_sftp source code comes with a blacklist.dat +file which should be sufficient. If for any reason you find that you +need to generate your own SFTPDKeyBlacklist, use the +blacklist-encode program mentioned in the above URLs. + +

+


+

SFTPKeyExchanges

+Syntax: SFTPKeyExchanges algo1 ...
+Default: None
+Context: "server config", <VirtualHost>, <Global>
+Module: mod_sftp
+Compatibility: 1.3.2rc2 and later + +

+The SFTPKeyExchanges directive is used to specify the list of +key exchange algorithms that mod_sftp should use. The current list +of supported key exchange algorithms is: +

    +
  • diffie-hellman-group-exchange-sha256 +
  • diffie-hellman-group-exchange-sha1 +
  • diffie-hellman-group14-sha1 +
  • diffie-hellman-group1-sha1 +
  • rsa1024-sha1 +
+By default, all of the above key exchange algorithms are presented to the +client, in the above order, during the key exchange. + +

+In general, there is no need to use this directive unless only one specific +key exchange algorithm must be used. + +

+


+

SFTPLog

+Syntax: SFTPLog file|"none"
+Default: None
+Context: "server config", <VirtualHost>, <Global>
+Module: mod_sftp
+Compatibility: 1.3.2rc2 and later + +

+The SFTPLog directive is used to specify a log file for +mod_sftp's reporting on a per-server basis. The file +parameter given must be the full path to the file to use for logging. + +

+Note that this path must not be to a world-writable directory and, +unless AllowLogSymlinks is explicitly set to on +(generally a bad idea), the path must not be a symbolic link. + +

+


+

SFTPMaxChannels

+Syntax: SFTPMaxChannels count
+Default: 10
+Context: server config, <VirtualHost>, <Global>
+Module: mod_sftp
+Compatibility: 1.3.2rc2 and later + +

+The SFTPMaxChannels directive is used to configure the maximum +number of simultaneously open channels permitted for a client. The +default value is 10. + +

+A malicious client might try to abuse the server's resources by opening +an large number of SSH2 channels and then not using them. Most well-behaved +SSH2 clients only every open one channel. + +

+


+

SFTPOptions

+Syntax: SFTPOptions opt1 ...
+Default: None
+Context: server config, <VirtualHost>, <Global>
+Module: mod_sftp
+Compatibility: 1.3.2rc2 and later + +

+The SFTPOptions directive is used to configure various optional +behavior of mod_sftp. + +

+The currently implemented options are: +

    +
  • IgnoreSFTPUploadPerms
    +

    + When an SFTP client uploads a file, the desired permissions on the file + are sent to the server as part of the upload. (This is different from + FTP, which does not include the file permissions in an upload.) + If you need more FTP-like functionality for any reason and wish to + have mod_sftp silently ignore any permissions sent by the + SFTP client, use this option. + +

    +

  • IgnoreSCPUploadPerms
    +

    + When an SCP client uploads a file, the desired permissions on the file + are sent to the server as part of the upload. (This is different from + FTP, which does not include the file permissions in an upload.) + If you need more FTP-like functionality for any reason and wish to + have mod_sftp silently ignore any permissions sent by the + SCP client, use this option. +

+ +

+


+

SFTPPassPhraseProvider

+Syntax: SFTPPassPhraseProvider path
+Default: None
+Context: server config
+Module: mod_sftp
+Compatibility: 1.3.2rc2 and later + +

+The SFTPPassPhraseProvider directive is used to specify an +external program which will be called, when mod_sftp starts up, +for each encrypted host key file. The program will be invoked with two +command-line arguments, passed on stdin to the program: +

+  servername:portnumber file
+
+where servername:portnumber indicates the +server using that encrypted certificate key, and file indicates the +host key file in question. The program then must print the corresponding +passphrase for the key to stdout. + +

+The intent is that this external program can perform any security checks +necessary, to make sure that the system is not compromised by an attacker, +and only when these checks pass successfully will the passphrase be provided. +These security checks, and the way the passphrase is determined, can be as +complex as you like. + +

+Example: +

+  SFTPPassPhraseProvider /etc/ftpd/sftp/get-passphrase
+
+ +

+


+

SFTPRekey

+Syntax: SFTPRekey "none"|"required" [[interval bytes] timeout]
+Default: None
+Context: "server config", <VirtualHost>, <Global>
+Module: mod_sftp
+Compatibility: 1.3.2rc2 and later + +

+The SFTPRekey directive configures the session rekey policy +used by the mod_sftp module. When an SSH2 client connects, +there is an initial exchange of hostkey, cipher, digest, and compression +algorithms, followed by sharing of information which leads to the calculation +of shared session keys. These keys are used in the various algorithms. +The exchange of algorithms and calculation of new session keys can happen +at any time during the SSH2 session; this is known as "rekeying". + +

+By default, mod_sftp will only require rekeying when +the packet sequence number (for sent or received SSH2 packets) reaches +a rather high limit. The digest algorithm used, for example, might begin +to leak cryptographically sensitive information if used for too many +packets (see RFC4344). Outside of these sequence number limits, however, +mod_sftp will not attempt to initiate rekeying. + +

+To prevent mod_sftp from attempting rekeying, use: +

+  SFTPRekey none
+
+Note that disabling rekeying altogether is a Very Bad Idea. While rekeying +can cause delays in the SSH2 sesssion, it is intended to guard against certain +attacks. You should therefore only disable rekeying for certain +SSH2 clients which cannot support rekeying. Please report those clients +(including the client version) to the mod_sftp maintainer, so that that +client can be added to the list of known clients which cannot handle rekeying. + +

+To require rekeying, simply use: +

+  SFTPRekey required
+
+With this parameter, mod_sftp will default to requiring a rekey +within 1 hour (3600 seconds) or after 2GB of data sent or received, +whichever happens first. There will be no timeout once a rekeying has been +requested. + +

+However, administrators wishing to use different rekey intervals or rekey +byte limits can use the optional interval (in seconds) and +bytes (in MB) parameters, e.g.: +

+  # Require rekeying after 30 minutes, or 4 GB
+  SFTPRekey required 1800 4096
+
+ +

+Finally, the paranoid administrator may wish to set a timeout, in seconds, +in which a client must complete a session rekeying, otherwise the +client will be disconnected. This would look like: +

+  # Require rekeying after 30 minutes, or 4 GB.  If the rekeying is not
+  # completed within 5 minutes of a rekey request, disconnect the client.
+  SFTPRekey required 1800 4096 300
+
+Note that normally such rekey timeouts are not necessary. + +

+


+

SFTPTrafficPolicy

+Syntax: SFTPTrafficPolicy policy
+Default: None
+Context: "server config", <VirtualHost>, <Global>
+Module: mod_sftp
+Compatibility: 1.3.2rc2 and later + +

+The SFTPTrafficPolicy directive is used to configure a measure +of protection against traffic analysis attacks. Such attacks use the number +of packets sent, the size of the packets, and the timing of the packets to try +to gain information about the nature of the data being transmitted. + +

+The SSH2 protocol includes a particular message, SSH_MSG_IGNORE, +which all implementations (both clients and servers) must handle by +dropping on the floor. Implementations, like mod_sftp, can +use these messages to add random data to the session in the hopes of making +such traffic analyses more difficult. + +

+The SFTPTrafficPolicy directive supports the following range +of policies; the chances columns list the probability that +mod_sftp will send an SSH_MSG_IGNORE message. + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Policy NameChancesChecks
"none"NeverNever
"low"1 in 1000Every 5 seconds
"medium"1 in 100Every 5 seconds
"high"1 in 10Every second
"paranoid"AlwaysEvery second
+ +

+The default SFTPTrafficPolicy setting is "none". + +

+


+

Installation

+To install mod_sftp, go to the third-party module area in +the proftpd source code and unpack the mod_sftp source tarball: +
+  cd proftpd-dir/contrib/
+  tar zxvf /path/to/mod_sftp-version.tar.gz
+
+after unpacking the latest proftpd-1.3.2 source code. For including +mod_sftp as a staticly linked module: +
+  ./configure --enable-openssl --with-modules=mod_sftp ...
+
+Alternatively, mod_sftp can be built as a DSO module: +
+  ./configure --enable-dso --enable-openssl --with-shared=mod_sftp ...
+
+Then follow the usual steps: +
+  make
+  make install
+
+ +

+


+

Usage

+

+ +

+Compiler Warnings
+When compiling mod_sftp, you may notice the following compiler +warning: +

+  contrib/mod_sftp/mod_sftp.a(fxp.o): In function `fxp_handle_create':
+  fxp.c:(.text+0x4dc8): warning: the use of `mktemp' is dangerous, better use `mkstemp'
+
+This warning can be ignored. + +

+The mktemp(3) function is generally considered unsafe because, +in the past, many applications would use this function to create the names of +temporary files. The actual problem was not the mktemp(3) +function itself, but rather that the applications did not check to see +if the name generated by mktemp(3) already existed in filesytem +first. This lead to race conditions where local users could create symlinks +of the generated names, and the application would overwrite the symlinked file. + +

+In the case of mod_sftp, however, the mktemp(3) +function is not used to create files. Instead, it is used to create +arbitrary unique strings that are used as "handles", as references, in the +protocol. These strings have no relation to any actual path on the filesystem, +and thus do not suffer from the above race condition. + +

+Access Controls for SFTP Requests
+Some of the requests supported by SFTP have FTP command equivalents. Other +requests in SFTP have no FTP command equivalent. This makes the use of +<Limit> sections for access controls in mod_sftp +a little interesting. + +

+First, the familiar territory. The following shows the +<Limit> sections for FTP commands (just as for normal FTP +sessions) which are honored by mod_sftp: +

    +
  • <Limit DELE>
    + Applies to the REMOVE SFTP request +
  • + +

    +

  • <Limit MKD XMKD>
    + Applies to the MKDIR SFTP request +
  • + +

    +

  • <Limit LIST NLST>
    + Applies to the READDIR SFTP request +
  • + +

    +

  • <Limit RETR>
    + Applies to the READ SFTP request +
  • + +

    +

  • <Limit RNFR RNTO>
    + Applies to the RENAME SFTP request +
  • + +

    +

  • <Limit RMD XRMD>
    + Applies to the RMDIR SFTP request +
  • + +

    +

  • <Limit STAT>
    + Applies to the FSTAT, LSTAT, and + STAT SFTP requests +
  • + +

    +

  • <Limit STOR>
    + Applies to the WRITE SFTP request +
  • + +

    +

  • <Limit SITE_CHMOD>
    + Applies to the SETSTAT and FSETSTAT SFTP + requests when they change file permissions +
  • +
+Thus you can see that mod_sftp will reuse much of your existing +<Limit>-based access controls for SFTP sessions. + +

+For those SFTP requests which have no direct FTP equivalents elsewhere in +proftpd, the mod_sftp module supports some +slightly different <Limit> sections. Note that +the following does not apply to FTP sessions, nor does it imply +that FTP supports these commands: +

    +
  • <Limit SETSTAT>
    + Applies to the FSETSTAT and SETSTAT SFTP requests +
  • + +

    +

  • <Limit SYMLINK>
    + Applies to the LINK and SYMLINK SFTP requests +
  • + +

    +

  • <Limit SYMLINK>
    + Applies to the LINK and SYMLINK SFTP requests +
  • + +

    +

  • <Limit LOCK>
    + Applies to the LOCK and UNLOCK SFTP requests +
  • + +

    +

  • <Limit OPEN>
    + Applies to the OPEN SFTP request +
  • +
+Note that not all versions of SFTP support all of these requests; the +LOCK and UNLOCK requests, for example, appear +only in later versions of SFTP. + +

+What about the READ and WRITE command groups? +

    +
  • <Limit READ>
    + Applies to the STAT, SYMLINK, and + READ SFTP requests +
  • + +

    +

  • <Limit WRITE>
    + Applies to the SETSTAT, SYMLINK, and + LOCK SFTP requests +
  • +
+ +

+Environment Variables
+The mod_sftp module will set the following environment +variables whenever an SSH2 client connects: +

    +
  • SFTP +
  • SFTP_LIBRARY_VERSION +
+In addition, the following environment variables will be set (with appropriate +values), once the client has successfully exchanged keys with +mod_sftp: +
    +
  • SFTP_CLIENT_CIPHER_ALGO +
  • SFTP_CLIENT_COMPRESSION_ALGO +
  • SFTP_CLIENT_MAC_ALGO +
  • SFTP_SERVER_CIPHER_ALGO +
  • SFTP_SERVER_COMPRESSION_ALGO +
  • SFTP_SERVER_MAC_ALGO +
+ +

+These environment variables are intended for informational purposes, +e.g. ExtendedLog or SQLLog directives. + +

+SSH2 Public Key Formats
+SSH2 uses public keys for authentication and message protection. The very +popular OpenSSH server uses a particular file format for storing public +keys in files. However, that format is not portable; it uses a format very +specific to OpenSSH. Thus the mod_sftp module uses a different +format for public keys, specifically, the format specified in +RFC 4716. The hope +is that this format is more interoperable with other SSH2/SFTP implementations. + +

+This means that if you wish to use your OpenSSH public keys with the +mod_sftp module, you will need to convert them from OpenSSH's +format to the RFC4716 format. Fortunately, this is supported by +OpenSSH's ssh-keygen utility, e.g.: +

+  # ssh-keygen -e -f ~/.ssh/id_rsa.pub
+
+The output from this command can be added to the +SFTPAuthorizedUserKeys used by mod_sftp. + +

+Note that the RFC4716 format is not to be used with the +SFTPHostKey directive. Even though OpenSSH's host keys have +".pub" public equivalents, host keys are different from the per-user keys +that are used for authentication. + +

+FIPS Compliance
+FIPS stands for "Federal Information Processing Standard", and refers to +a series of information processing standards issued and used by the US +government. When speaking of FIPS and cryptographic software, the pertinent +standard in particular is +FIPS-140. + +

+How then would you build OpenSSL and mod_sftp, and use them +such that mod_sftp would be FIPS-compliant? Using OpenSSL in +FIPS mode requires quite a few steps. First, you would configure +proftpd to use the mod_sftp module as normal, +assuming your OpenSSL installation has been compiled with FIPS support: +

+  ./configure --enable-openssl --with-modules=mod_sftp ...
+
+ +

+Compiling proftpd requires the following, for FIPS support +to work properly: +

+  make CC=/path/to/openssl/bin/fipsld FIPSLD_CC=gcc
+
+The FIPSLD_CC variable should point to your normal C compiler, +e.g. gcc. The use of this fipsld program +is mandatory. The FIPS standard requires that the linking process +happen a very specific way, involving verification of calculated and expected +checksums of compiled code, etc. The OpenSSL packages with FIPS +support supply this fipsld program which will link the compiled +code according to the FIPS specifications. If you do not use +fipsld, then attempts to use OpenSSL in FIPS mode will fail. +For example, you would see the following if starting a proftpd +daemon which has not been linked using fipsld while requesting +use of FIPS: +
+  - mod_sftp/0.0: unable to use FIPS mode: (unknown)
+  - Fatal: unable to load module 'mod_sftp.c': Operation not permitted
+
+ +

+Now, assuming you have compiled and installed your proftpd +properly, e.g.: +

+  make CC=/path/to/openssl/bin/fipsld FIPSLD_CC=gcc
+  make CC=/path/to/openssl/bin/fipsld FIPSLD_CC=gcc install
+
+you will now be ready to start proftpd. + +

+In order for FIPS mode to be effective, OpenSSL must be told to run in FIPS +mode from the very beginning. The mod_sftp module initializes the +OpenSSL library when the mod_sftp module is loaded, before the +proftpd.conf file is parsed. Thus the requesting of FIPS mode +cannot be done via a setting in proftpd.conf. (Annoying, +I know.) + +

+Instead, you must use the -D command-line parameter when starting +proftpd (see the docs for the <IfDefine> and +Define directives) to define a specific variable, which the +mod_sftp module will look for. Specifically, you will need to +start proftpd like thus: +

+  /path/to/proftpd -DSFTP_USE_FIPS ...
+
+This will define the SFTP_USE_FIPS variable; this tells +mod_sftp to initialize OpenSSL using FIPS mode. When this works, +you will see the following when proftpd starts up: +
+  - mod_sftp/0.0: FIPS mode enabled
+
+ +

+For additional reading on OpenSSL and FIPS, see: +

+  http://www.openssl.org/docs/fips/SecurityPolicy-1.1.1.pdf
+  http://www.openssl.org/docs/fips/UserGuide-1.1.1.pdf
+
+ +

+Example Configuration
+When using mod_sftp to handle SFTP connections, you will want +to create a separate <VirtualHost> section for the +mod_sftp configuration. SSH2 (and thus SFTP) requires a different +port than FTP; in order to listen on different ports, proftpd +requires different <VirtualHost> sections. You can have +multiple <VirtualHost> sections for the same address +(IP address or DNS name), just different Port directives. +

+  <IfModule mod_sftp.c>
+    <VirtualHost a.b.c.d>
+      SFTPEngine on
+      SFTPLog /etc/proftpd/sftp/sftp.log
+
+      # Configure the server to listen on the normal SSH2 port, port 22
+      Port 22
+
+      # Configure both the RSA and DSA host keys, using the same host key
+      # files that OpenSSH uses. 
+      SFTPHostKey /etc/ssh_host_rsa_key
+      SFTPHostKey /etc/ssh_host_dsa_key
+
+      # Configure the file used for comparing authorized public keys of users.
+      SFTPAuthorizedUserKeys file:~/.sftp/authorized_keys
+
+      # Enable compression
+      SFTPCompression delayed
+
+      # Allow the same number of authentication attempts as OpenSSH.
+      #
+      # It is recommended that you explicitly configure MaxLoginAttempts
+      # for your SSH2/SFTP instance to be higher than the normal
+      # MaxLoginAttempts value for FTP, as there are more ways to authenticate
+      # using SSH2.
+      MaxLoginAttempts 6
+
+    </VirtualHost>
+  </IfModule>
+
+ +

+Logging
+The mod_sftp module supports different forms of logging. The +main module logging is done via the SFTPLog directive. For +debugging purposes, the module also uses trace logging, via the module-specific "scp", "sftp", +and "ssh2" log channels. Thus for trace logging, to aid in debugging, you +would use the following in your proftpd.conf: +

+  TraceLog /path/to/sftp-trace.log
+  Trace scp:20 sftp:20 ssh2:20
+
+This trace logging can generate large files; it is intended for debugging +use only, and should be removed from any production configuration. + +

+Suggested Future Features
+The following lists the features I hope to add to mod_sftp, +according to need, demand, inclination, and time: +

    +
  • Look up authorized public keys from LDAP directories +
  • Honor the MaxTransferPerHost, + MaxTransfersPerUser, MaxRetrieveFileSize, + MaxStoreFileSize directives +
+ +

+Frequently Asked Questions
+ +

+Question: When I telnet to port 22 of other SSH2 servers, +I see the SSH2 version banner displayed, e.g.: +

+  # telnet example.com 22
+  Trying 1.2.3.4...
+  Connected to example.com.
+  Escape character is '^]'.
+  SSH-1.99-OpenSSH_5.1
+
+However, when I telnet to the port on which mod_sftp is listening, +I see the version banner plus some data which can't be rendered in the terminal. +What is that extra data?
+Answer: The short answer is that mod_sftp +is pre-emptively sending the first key exchange data; it is this data which is +not displayed well. + +

+This pre-emptive sending of the first key exchange message is an optimization. +The SSH2 protocol (and many SSH2 servers) start off doing: +

+ (1) client ------------ connect -------------> server
+ (2) client <-------- version banner ---------- server
+ (3) client --- client's first key exchange --> server
+ (4) client <-- server's first key exchange --- server
+
+So you can see that there's some back and forth. The "first key exchange" +messages sent by the client are always the same for that client, likewise the +server always sends the same "first key exchange" message (based on its +configuration). So the above has several TCP packets and ACKs that need to be +sent back and forth. + +

+The mod_sftp avoids one unnecessary TCP packet+ACK by sending its +version banner and its first key exchange message in the same TCP packet: +

+ (1) client ------------ connect -------------> server
+ (2) client <-- version banner/1st key exch---- server
+ (3) client --- client's first key exchange --> server
+
+This helps cut down on the latency during startup, since mod_sftp +saves on one TCP round-trip time. + +

+This optimization was added after reading this informative paper on SSH and +SSL/TLS by Peter Gutmann: +

+  Performance Characteristics of Application-level Security Protocols
+
+ +

+Question: My SSH2/SFTP client dies when trying to +connect to mod_sftp. The login succeeds, but then the client +is disconnected. The client logs show something like: +

+  ServerMaxPacket(32768) ServerWindow(-1) (from WS_FTP)
+
+or +
+  debug1: channel 0: open confirm rwindow -1 rmax 32768 (from OpenSSH_3.0.2p1)
+
+Answer: The issue, in short, involves that "-1" value +you see, and the particular client implementation in question. + +

+The SSH2 protocol specifications say that implementations must encode +the SSH2 channel "window size" as a 32-bit unsigned number. This means that +the maximum window size is effectively 4294967295 (i.e. 2 ^ 32 - 1). +This maximum window size is what mod_sftp uses by default. +However, some client implementations use a signed data type for +holding this value. This means that the value that mod_sftp +sends to the client becomes a negative signed number to the client (the +large value "wraps around" to the negative value), and the client chokes. + +

+There are two approaches for handling such cases. You can use the +SFTPClientMatch directive to +set a smaller channel window size for that particular client, e.g.: +

+  SFTPClientMatch ".*WS_FTP.*" channelWindowSize 32MB
+
+or you can use SFTPClientMatch to set a general channel window +size for all clients: +
+  SFTPClientMatch .* channelWindowSize 64MB
+
+ +

+The larger the channel window size, the better. I don't know why the SSH2 +protocol has this notion of a "window size". The SSH2 channel window size +has absolutely no relation to the TCP window size. And the TCP +layer handles window sizing and flow control automatically, so why require +it at the application protocol level? For whatever the reasons, it is part +of the SSH2 protocol. The smaller this SSH2 channel window size, the more +frequently the peer needs to send a "window size adjustment" SSH2 request +(which entails its own TCP packet and ACK) in order to keep the window +size open. The larger the channel window size, the fewer "window size +adjustment" requests the peer has to send, and thus the fewer TCP packets/ACKs +are needed. Since each TCP packet adds latency, the smaller channel window +sizes can increase the latency for data transfers. + +

+For this reason, my personal recommendation is to only set smaller channel +window sizes for those SSH2/SFTP clients which need it; no need to incur +the additional latency if the client supports the larger window sizes. + +

+



+ +Author: $Author: castaglia $
+Last Updated: $Date: 2009-02-13 21:45:12 $
+ +

+ + +© Copyright 2008-2009 TJ Saunders
+ All Rights Reserved
+
+ +

+ + + + diff --git a/doc/contrib/mod_sftp_pam.html b/doc/contrib/mod_sftp_pam.html new file mode 100644 index 0000000000..0ee0e43f2a --- /dev/null +++ b/doc/contrib/mod_sftp_pam.html @@ -0,0 +1,156 @@ + + + + + +ProFTPD module mod_sftp_pam + + + + +
+
+

ProFTPD module mod_sftp_pam

+
+

+ +

+The mod_sftp_pam module provides support for the "SSH Keyboard-Interactive Authentication" RFC (RFC4256). How is mod_sftp_pam different from ProFTPD's existing +PAM support, in the form of mod_auth_pam? The difference is +that the mod_auth_pam module does not echo the prompt, +provided by the underlying PAM library/modules, back to the FTP client; +this mod_sftp_pam module will echo any prompt back to the +connecting SSH2 client. This makes using onetime-password PAM modules, for +example, work very easily for authenticating SSH2 logins. + +

+This module is contained in the mod_sftp_pam.c file for +ProFTPD 1.3.x, and is not compiled by default. Installation +instructions are discussed here; a discussion +on usage is also available. + +

+The most current version of mod_sftp_pam can be found at: +

+  http://www.castaglia.org/proftpd/
+
+ +

Author

+

+Please contact TJ Saunders <tj at castaglia.org> with any +questions, concerns, or suggestions regarding this module. + +

Directives

+ + +
+

SFTPPAMEngine

+Syntax: SFTPPAMEngine on|off
+Default: Off
+Context: "server config", <VirtualHost>, <Global>
+Module: mod_sftp_pam
+Compatibility: 1.3.2rc2 and later + +

+The SFTPPAMEngine directive toggles the use of the PAM library +for supporting a keyboard-interactive authentication mechanism for SSH2 logins. +By default mod_sftp_pam is disabled for both the main server and +all configured virtual hosts. + +

+


+

SFTPPAMOptions

+Syntax: SFTPPAMOptions opt1 opt2 ... optN
+Default: None
+Context: "server config", <VirtualHost>, <Global>
+Module: mod_sftp_pam
+Compatibility: 1.3.2rc2 and later + +

+The SFTPPAMOptions directive is used to configure various +optional behaviors of mod_sftp_pam; it is directly analogous +to mod_auth_pam's AuthPAMOptions directive, and +supports the exact same range of options. See the mod_auth_pam +documentation for more information. + +

+


+

SFTPPAMServiceName

+Syntax: SFTPPAMServiceName service
+Default: SFTPPAMServiceName sshd
+Context: "server config", <VirtualHost>, <Global>
+Module: mod_sftp_pam
+Compatibility: 1.3.2rc2 and later + +

+The SFTPPAMConfig directive is used to specify the name of the +service used when performing the PAM check; PAM configurations can vary +depending on the service. By default, the "sshd" service is used. + +

+Here's an example of changing the service used: +

+  <IfModule mod_sftp_pam.c>
+    SFTPPAMEngine on
+    SFTPPAMServiceName ftpd
+  </IfModule>
+
+ +

+The SFTPPAMServiceName directive is directly analogous to +mod_auth_pam's AuthPAMConfig directive. + +

+


+

Installation

+To install mod_sftp_pam, copy the mod_sftp_pam.c file +into: +
+  proftpd-dir/contrib/
+
+after unpacking the latest proftpd-1.3.x source code. Then follow the +usual steps for using third-party modules in proftpd, making sure to include +the mod_sftp module, which mod_sftp_pam requires: +
+  ./configure --with-modules=mod_sftp:mod_sftp_pam ...
+  make
+  make install
+
+ +

+



+

Usage

+To use mod_sftp_pam, simply enable the module, and configure +it to use the correct PAM service name, e.g.: +
+  <IfModule mod_sftp_pam.c>
+    SFTPPAMEngine on
+    SFTPPAMServiceName sftp
+  </IfModule>
+
+There is no requirement that mod_sftp_pam use the same PAM +service name as the mod_auth_pam module; this allows you to have +different PAM configurations for FTP versus SSH2 logins. + +

+



+ +Author: $Author: castaglia $
+Last Updated: $Date: 2009-02-13 21:45:12 $
+ +

+ + +© Copyright 2008 TJ Saunders
+ All Rights Reserved
+
+ +

+ + + + diff --git a/doc/contrib/mod_sftp_sql.html b/doc/contrib/mod_sftp_sql.html new file mode 100644 index 0000000000..c57c14957b --- /dev/null +++ b/doc/contrib/mod_sftp_sql.html @@ -0,0 +1,168 @@ + + + + + +ProFTPD module mod_sftp_sql + + + + +
+
+

ProFTPD module mod_sftp_sql

+
+

+ +

+The mod_sftp module for ProFTPD can support different storage formats for +its user- and host-based authorized keys. By default, the mod_sftp +module supports storing authorized keys in flats. This +mod_sftp_sql module allows for authorized SSH keys to be stored +in SQL tables. + +

+This module is contained in the mod_sftp_sql.c file for +ProFTPD 1.3.x, and is not compiled by default. Installation +instructions are discussed here. Examples +of how to use the mod_sftp_sql module are available +here. + +

+The most current version of mod_sftp_sql can be found at: +

+  http://www.castaglia.org/proftpd/
+
+ +

Author

+

+Please contact TJ Saunders <tj at castaglia.org> with any +questions, concerns, or suggestions regarding this module. + +

+


+

Installation

+To install mod_sftp_sql, copy the mod_sftp_sql.c file +into: +
+  proftpd-dir/contrib/
+
+after unpacking the latest proftpd-1.3.x source code. Then follow the +usual steps for using third-party modules in proftpd, making sure to include +the mod_sftp and mod_sql modules, which +mod_sftp_sql requires. For example, if you use MySQL as your +SQL database, then you might use: +
+  ./configure --with-modules=mod_sql:mod_sql_mysql:mod_sftp:mod_sftp_sql ...
+  make
+  make install
+
+ +

+



+

Usage

+ +

+The mod_sftp_sql module works by using mod_sql's +SQLNamedQuery ability to define a SQL SELECT +statement which returns the requested key. Thus the mod_sftp_sql +module has no configuration directives of its own. + +

+To help demonstrate, see the example configuration below: +

+  <IfModule mod_sql.c>
+    # Other mod_sql configuration here
+
+    # Define a SELECT statement to retrieve users' authorized SSH keys
+    SQLNamedQuery get-user-authorized-keys SELECT "key FROM sftpuserkeys WHERE name='%U'"
+
+    # Define a SELECT statement to retrieve hosts' authorized SSH keys
+    SQLNamedQuery get-host-authorized-keys SELECT "key FROM sftphostkeys WHERE host='%{0}'"
+  </IfModule>
+
+  <IfModule mod_sftp.c>
+    SFTPEngine on
+    SFTPLog /path/to/sftp.log
+
+    # Host keys, for server host authentication
+    SFTPHostKey /etc/ssh_host_dsa_key
+    SFTPHostKey /etc/ssh_host_rsa_key
+
+    <IfModule mod_sftp_sql.c> 
+      # Instead of using a file-based key store, we tell mod_sftp to use
+      # the SQL-based key store provided by mod_sftp_sql
+      SFTPAuthorizedUserKeys sql:/get-user-authorized-keys
+      SFTPAuthorizedHostKeys sql:/get-host-authorized-keys
+    </IfModule>
+  </IfModule>
+
+ +

+What should the schema be, for the table which holds these authorized keys? +The required columns are one for the key (as a single base64-encoded +string) and one for the name of the entity owning that key, e.g. the +user name or FQDN (or IP address) of the host. These columns can be added to +existing tables you might have, or be part of a new table. + +

+For example, using SQLite, you could do: +

+  # sqlite3 sftp.db
+  sqlite> CREATE TABLE sftpuserkeys (
+  sqlite>  name TEXT NOT NULL,
+  sqlite>  key BLOB NOT NULL
+  sqlite> );
+
+  sqlite> CREATE TABLE sftphostkeys (
+  sqlite>  host TEXT NOT NULL,
+  sqlite>  key BLOB NOT NULL
+  sqlite> );
+
+and then configure mod_sql to use that sftp.db +database file. + +

+Which leads to the next question: how can I transfer existing authorized +SSH keys from their current flate files into the SQL tables? The +mod_sftp_sql source code is distributed with a +extract-rfc4716-key.pl Perl script which can be used to read +an existing authorized keys file and print out the string which should +be added to the database table. Note that the +extract-rfc47167-key.pl script will print out the key data for +each key contained in the file, one key per line, to stdout. + +

+  # extract-rfc4716-key.pl ~/.sftp/authorized_keys 
+AAAAB3NzaC1yc2EAAAABIwAAAIEA6d+bt06KSlp4Q6wMS57hFAEaiNIQfdguCN9uMP3C5dC4kCt/S85jYt3cdB9XECr8AIxCsivay8WTw77z4qBgR6XrOrzb2J57mX27oQxi4kjHNY3vcVU0Y9lRa6M8whdokpWQsgGaPDmoa2ScU56r0FFiHy0cDX+dDU3ycniQSwc=
+
+Since this is a user-specific key, and since we are using SQLite for this +example, you would then do: +
+  # sqlite sftp.db
+  sqlite> INSERT INTO sftpuserkeys (name, key) VALUES ('tj', 'AAAAB3NzaC1yc2EAAAABIwAAAIEA6d+bt06KSlp4Q6wMS57hFAEaiNIQfdguCN9uMP3C5dC4kCt/S85jYt3cdB9XECr8AIxCsivay8WTw77z4qBgR6XrOrzb2J57mX27oQxi4kjHNY3vcVU0Y9lRa6M8whdokpWQsgGaPDmoa2ScU56r0FFiHy0cDX+dDU3ycniQSwc=');
+
+ +

+Other databases (e.g. MySQL, Postgres, Oracle, etc) have +bulk data loading tools which can also be used to load a CSV file containing +keys into your SQL tables, for use via mod_sftp_sql. + +

+



+ +Author: $Author: castaglia $
+Last Updated: $Date: 2009-02-13 21:45:12 $
+ +

+ + +© Copyright 2008 TJ Saunders
+ All Rights Reserved
+
+ +

+ + + +