User authorization in ProFTPD on forum accounts

    Recently, there was a need to prohibit access to anyone on the FTP server, that is, block login under anonymous. But to maintain a separate database with FTP users is inconvenient, and moreover inefficient. Thought, thought, and decided to authorize users on the forum account, in my case SMF. Users are stored in the MySQL table smf_members, the username in memberName, and the password in passwd. ProFTPD has a well-documented ability to retrieve credentials with PostgreSQL / MySQL. But the trouble is, the forum passwords are stored in the SHA1 hash, and the username in lower case is added to the beginning of the password. And ProFTPD expects that it will return a password in the MySQL PASSWORD (), Crypt, or Plaintext formats from the SQL query to the database. Of course, there is no SHA1 there, not to mention the fact that the username is still added to the password. Google the day another

    Well, our business is not tricky. So what do we have? There is an SQLAuthTypes parameter that indicates which method the password is encrypted. I don’t have the option I need, which means we open the source. The last RC build was downloaded from the ProFTPD home site, at that moment proftpd-1.3.3rc3. A quick look at contrib / mod_sql.c and contrib / mod_sql.h showed that there is a mechanism for adding your authorization methods to a function:

    int sql_register_authtype(const char *name, modret_t *(*callback)(cmd_rec *, const char *, const char *));

    Interesting. After running around the source for a bit, finding enough examples of calling this function (which is strange, nothing of this is really documented, and on the Internet for “proftpd sql_register_authtype” 0 results), it became clear that it all comes down to creating a callback function that takes as parameters the password in its pure form and the hash obtained from mysql checks to see if the password matches the hash, and return returns the result of the verification. Well, it sounds like a snap. Created a makeshift module contrib / mod_sql_auth_smf.c with the following contents:

    #include "conf.h"
    #include "mod_sql.h"

    #define MOD_SQL_AUTH_SMF_VERSION "mod_sql_smf/0.1"

    #if defined(HAVE_OPENSSL) || defined(PR_USE_OPENSSL)

    #include

    static modret_t *auth_smf(cmd_rec *cmd, const char *c_clear, const char *c_hash) {
    const EVP_MD *md;
    EVP_MD_CTX md_ctxt;
    unsigned char mdval[EVP_MAX_MD_SIZE];
    char hash[EVP_MAX_MD_SIZE * 2];
    int mdlen, i;

    OpenSSL_add_all_digests();
    md = EVP_get_digestbyname("sha1");

    if (!md)
    return ERROR_INT(cmd, PR_AUTH_BADPWD);

    EVP_DigestInit(&md_ctxt, md);
    EVP_DigestUpdate(&md_ctxt, c_clear, strlen(c_clear));
    EVP_DigestFinal(&md_ctxt, mdval, &mdlen);

    for (i=0; i sprintf(hash+(i*2), "%02x", mdval[i]);
    }

    return strcmp(hash, c_hash) ? ERROR_INT(cmd, PR_AUTH_BADPWD) : HANDLED(cmd);
    }

    static int sql_auth_smf_init(void) {
    (void) sql_register_authtype("SMF", auth_smf);
    return 0;
    }

    #endif

    module sql_auth_smf_module = {

    /* Always NULL */
    NULL, NULL,

    /* Module API version */
    0x20,

    /* Module name */
    "sql_auth_smf",

    /* Module configuration directive table */
    NULL,

    /* Module command handler table */
    NULL,

    /* Module auth handler table */
    NULL,

    /* Module initialization */
    sql_auth_smf_init,

    /* Session initialization */
    NULL,

    /* Module Version */
    MOD_SQL_AUTH_SMF_VERSION

    };

    In this module, using OpenSSL, I implemented SHA1 hashing of a clear password, reconciling with the received hash, and returning the verification result to mod_sql.c. Compiled, not forgetting to add the option to configure --with-shared = mod_sql_auth_smf, changed the parameter in proftpd.conf SQLAuthTypes to SMF and added the line LoadModule mod_sql_mysql.c. Gathered, checked, works! But only if user User1 with the password PassWd22 indicates user1PassWd22 as the password. Well, that is easier. Finding the callback of the function in mod_sql.c, I changed it from this type:

    mr = sah->cb(cmd, plaintext, ciphertext);

    To this:

    if(strcmp(sah->name, "SMF")==0) {
    int i;
    char namepasswd[106];
    strncpy(namepasswd, cmd->argv[1], 25);
    for(i=0;namepasswd[i];i++) namepasswd[i]=tolower(namepasswd[i]);
    strncat(namepasswd, cmd->argv[2], 80);
    mr = sah->cb(cmd, namepasswd, ciphertext);
    } else
    mr = sah->cb(cmd, plaintext, ciphertext);

    That's all, the goal is achieved. If someone is interested in the details of setting up ProFTPD for working with MySQL, the subtleties of compilation, I will answer in the comments. The purpose of this article is not to give a complete step-by-step guide from scratch, but to briefly show how you can, with a little C knowledge, force ProFTPD to authorize users using the credentials of any forum, blog, and so on.

    Also popular now: