5

I've recently begun the task of setting up an PureFTP server. At work we use Postgresql 8.4. The schema essentially boils down to,

username        text
password        character(40)
password_salt   text

The password is stored as hash of sha1( password + salt ). Using Postgresql's pgcrypto I can supply a username, and password and find out if the user has auth:

SELECT
 encode( digest( $password ||password_salt, 'sha1' ), 'hex' ) = password
   AS password_correct
 , username
 , password
 , password_salt
FROM contact.person;

Now the issue that I'm having is that such a function would require me getting the password into the query. This doesn't seem possible with Pureftp's current implementation of auth-postgresql. It only supports providing:

\L is replaced by the login of a user trying to authenticate.
\I is replaced by the IP address the client connected to.
\P is replaced by the port number the client connected to.
\R is replaced by the remote IP address the client connected from.
\D is replaced by the remote IPv4 address, as a long decimal number.

Is there another way I can do this? I either need to get the password into the query, or get the salt and password out and find another way to write the code in Pureftp.

Obviously, I have another option of writing a custom authentication module, but I would think this basic salting would be supported by the pg module.

References

Evan Carroll
  • 78,363
  • 46
  • 261
  • 468
  • I think you have to do it the hard way: *The user's password, in plaintext, crypt()ed format or MD5. Pure-FTPd also accepts the "any" value for the PGSQLCrypt field. With "any", all hashing functions (not plaintext) are tried.* – mu is too short Aug 03 '11 at 06:59
  • Yea, but they're tried without the DB salts. =( Well damn, that's no good. – Evan Carroll Aug 03 '11 at 14:48
  • You might have to dig in the source code to find out, but maybe it accepts the salt prepended or postpended to the md5'd or crypted password? I believe that's the way /etc/shadow stores them. If you end up writing your own auth and can do whatever you want, you should use the postgres crypt with the blowfish algorithm instead of sha1. – Tim Jun 14 '12 at 05:09

1 Answers1

2

I had the very same problem. However, writing my own custom authentication module would've been an overkill since the available pgsql auth does nearly everything I want.. Here's what changes I made for it to suit my needs:

In log_pgsql_p.h add static char *salting; and static char *sqlreq_getsalt; and extend the static ConfigKeywords pgsql_config_keywords[] with { "PGSQLSalting", &salting }, and { "PGSQLGetSalt", &sqlreq_getsalt },.

In log_pgsql.h I added #define SALT_SQL_APPEND "append", #define SALT_SQL_PREPEND "prepend" and #define SALT_SQL_NONE "none".

In log_pgsql.c I then made the following changes in the pw_psql_check function:

I declared const char *salt = NULL; and char * salted_password = NULL; at the top. Directly before spwd gets assigned the result of the query to sqlreq_getpw I added

if (strcasecmp(salting, SALT_SQL_NONE) != 0) {
    salt = pw_pgsql_getquery(id_sql_server, sqlreq_getsalt,
                             escaped_account, escaped_ip,
                             escaped_port, escaped_peer_ip,
                             escaped_decimal_ip);
}

Then, before the encryption takes place:

if (salt != NULL) {
    int salted_pw_size = strlen(salt) + strlen(password) + 1;
    salted_password = (char *) malloc(salted_pw_size);
    if (strcasecmp(salting, SALT_SQL_APPEND) == 0) {
        strcpy(salted_password, password);
        strcat(salted_password, salt);            
    } else if (strcasecmp(salting, SALT_SQL_PREPEND) == 0) {
        strcpy(salted_password, salt);
        strcat(salted_password, password);
    }
} else {
    salted_password = (char *) malloc(strlen(password));
    strcpy(salted_password, password);
}

And then I replaced the password argument in subsequent calls to the crypt-methods (crypt, crypto_hash_md5, crypto_hash_sha1) and the strcasecmp for 'cleartext' with (const char*)salted_password.

Now all that's left to do is tidying up the memory we allocated. Especially the plaintext-password with appended/prepended salt shouldn't remain in memory - call it paranoia if you want. So after the bye: label add

free((void *) salt;
if(strcasecmp(salting, SALT_SQL_NONE) != 0) {
    volatile char *salted_password_ = (volatile char *) salted_password;
    while(*salted_password_ != 0) {
        *salted_password_++ = 0;
    }
    free((void *) salted_password);
}

With these changes you now have two additional parameters in your config file available:

  • PGSQLSalting: Accepts 'append' (appends the salt to the pw), 'prepend' and 'none' (without the apostrophe)
  • PGSQLGetSalt: Here you specify the field in your db to fetch the salt from, much like with the crypted password you need to retrieve via PGSQLGetPw.

Edit: Oh, and don't forget to free the allocated memory at the end of the function!

I also can provide a diff file that works for the release 1.0.36.. here you go! Beware though, i added the if around the freeing of salted_password later (because i only later realized how this might lead to an error if salted_password points to password), so this is not in the diff and I'm too lazy to change the diff file :/

kaikuchn
  • 795
  • 7
  • 15