1 /*
2  * ProFTPD: mod_sql_passwd -- Various SQL password handlers
3  * Copyright (c) 2009-2020 TJ Saunders
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18  *
19  * As a special exemption, TJ Saunders and other respective copyright holders
20  * give permission to link this program with OpenSSL, and distribute the
21  * resulting executable, without including the source code for OpenSSL in
22  * the source distribution.
23  *
24  * -----DO NOT EDIT BELOW THIS LINE-----
25  * $Libraries: -lcrypto$
26  */
27 
28 #include "conf.h"
29 #include "privs.h"
30 #include "mod_sql.h"
31 
32 #define MOD_SQL_PASSWD_VERSION		"mod_sql_passwd/1.2"
33 
34 #ifdef PR_USE_SODIUM
35 # include <sodium.h>
36 /* Use/support Argon2, if libsodium is new enough. */
37 # if SODIUM_LIBRARY_VERSION_MAJOR > 9 || \
38      (SODIUM_LIBRARY_VERSION_MAJOR == 9 && \
39       SODIUM_LIBRARY_VERSION_MINOR >= 2)
40 #  define USE_SODIUM_ARGON2
41 # endif
42 #endif /* PR_USE_SODIUM */
43 
44 /* Make sure the version of proftpd is as necessary. */
45 #if PROFTPD_VERSION_NUMBER < 0x0001030703
46 # error "ProFTPD 1.3.7rc3 or later required"
47 #endif
48 
49 #if !defined(HAVE_OPENSSL) && !defined(PR_USE_OPENSSL)
50 # error "OpenSSL support required (--enable-openssl)"
51 #else
52 # include <openssl/evp.h>
53 # include <openssl/err.h>
54 # include <openssl/objects.h>
55 #endif
56 
57 module sql_passwd_module;
58 
59 static int sql_passwd_engine = FALSE;
60 
61 #define SQL_PASSWD_COST_INTERACTIVE		1
62 #define SQL_PASSWD_COST_SENSITIVE		2
63 static unsigned int sql_passwd_cost = SQL_PASSWD_COST_INTERACTIVE;
64 
65 #define SQL_PASSWD_ENC_USE_BASE64		1
66 #define SQL_PASSWD_ENC_USE_HEX_LC		2
67 #define SQL_PASSWD_ENC_USE_HEX_UC		3
68 #define SQL_PASSWD_ENC_USE_NONE			4
69 static unsigned int sql_passwd_encoding = SQL_PASSWD_ENC_USE_HEX_LC;
70 static unsigned int sql_passwd_salt_encoding = SQL_PASSWD_ENC_USE_NONE;
71 
72 static unsigned char *sql_passwd_file_salt = NULL;
73 static size_t sql_passwd_file_salt_len = 0;
74 static unsigned char *sql_passwd_user_salt = NULL;
75 static size_t sql_passwd_user_salt_len = 0;
76 
77 #define SQL_PASSWD_SALT_FL_APPEND	0x0001
78 #define SQL_PASSWD_SALT_FL_PREPEND	0x0002
79 static unsigned long sql_passwd_file_salt_flags = SQL_PASSWD_SALT_FL_APPEND;
80 static unsigned long sql_passwd_user_salt_flags = SQL_PASSWD_SALT_FL_APPEND;
81 
82 #define SQL_PASSWD_OPT_HASH_SALT		0x0001
83 #define SQL_PASSWD_OPT_ENCODE_SALT		0x0002
84 #define SQL_PASSWD_OPT_HASH_PASSWORD		0x0004
85 #define SQL_PASSWD_OPT_ENCODE_PASSWORD		0x0008
86 
87 static unsigned long sql_passwd_opts = 0UL;
88 
89 static unsigned long sql_passwd_nrounds = 1;
90 
91 /* For PBKDF2 */
92 static const EVP_MD *sql_passwd_pbkdf2_digest = NULL;
93 static int sql_passwd_pbkdf2_iter = -1;
94 static int sql_passwd_pbkdf2_len = -1;
95 #define SQL_PASSWD_ERR_PBKDF2_UNKNOWN_DIGEST		-1
96 #define SQL_PASSWD_ERR_PBKDF2_UNSUPPORTED_DIGEST	-2
97 #define SQL_PASSWD_ERR_PBKDF2_BAD_ROUNDS		-3
98 #define SQL_PASSWD_ERR_PBKDF2_BAD_LENGTH		-4
99 
100 #ifdef PR_USE_SODIUM
101 /* For Scrypt */
102 # define SQL_PASSWD_SCRYPT_DEFAULT_HASH_SIZE	32U
103 # define SQL_PASSWD_SCRYPT_DEFAULT_SALT_SIZE	32U
104 static unsigned int sql_passwd_scrypt_hash_len = SQL_PASSWD_SCRYPT_DEFAULT_HASH_SIZE;
105 
106 /* For Argon2 */
107 # ifdef USE_SODIUM_ARGON2
108 #  define SQL_PASSWD_ARGON2_DEFAULT_HASH_SIZE	32U
109 #  define SQL_PASSWD_ARGON2_DEFAULT_SALT_SIZE	16U
110 static unsigned int sql_passwd_argon2_hash_len = SQL_PASSWD_ARGON2_DEFAULT_HASH_SIZE;
111 # endif /* USE_SODIUM_ARGON2 */
112 #endif /* PR_USE_SODIUM */
113 
114 static const char *trace_channel = "sql.passwd";
115 
116 /* Necessary prototypes */
117 static int sql_passwd_sess_init(void);
118 
sql_passwd_cmd_create(pool * parent_pool,unsigned int argc,...)119 static cmd_rec *sql_passwd_cmd_create(pool *parent_pool,
120     unsigned int argc, ...) {
121   register unsigned int i = 0;
122   pool *cmd_pool = NULL;
123   cmd_rec *cmd = NULL;
124   va_list argp;
125 
126   cmd_pool = make_sub_pool(parent_pool);
127   cmd = (cmd_rec *) pcalloc(cmd_pool, sizeof(cmd_rec));
128   cmd->pool = cmd_pool;
129 
130   cmd->argc = argc;
131   cmd->argv = pcalloc(cmd->pool, argc * sizeof(void *));
132 
133   /* Hmmm... */
134   cmd->tmp_pool = cmd->pool;
135 
136   va_start(argp, argc);
137   for (i = 0; i < argc; i++) {
138     cmd->argv[i] = va_arg(argp, char *);
139   }
140   va_end(argp);
141 
142   return cmd;
143 }
144 
sql_passwd_get_str(pool * p,const char * str)145 static const char *sql_passwd_get_str(pool *p, const char *str) {
146   cmdtable *cmdtab;
147   cmd_rec *cmd;
148   modret_t *res;
149 
150   if (strlen(str) == 0) {
151     return str;
152   }
153 
154   /* Find the cmdtable for the sql_escapestr command. */
155   cmdtab = pr_stash_get_symbol2(PR_SYM_HOOK, "sql_escapestr", NULL, NULL, NULL);
156   if (cmdtab == NULL) {
157     pr_log_debug(DEBUG2, MOD_SQL_PASSWD_VERSION
158       ": unable to find SQL hook symbol 'sql_escapestr'");
159     return str;
160   }
161 
162   cmd = sql_passwd_cmd_create(p, 1, pr_str_strip(p, str));
163 
164   /* Call the handler. */
165   res = pr_module_call(cmdtab->m, cmdtab->handler, cmd);
166 
167   /* Check the results. */
168   if (MODRET_ISDECLINED(res) ||
169       MODRET_ISERROR(res)) {
170     pr_log_debug(DEBUG0, MOD_SQL_PASSWD_VERSION
171       ": error executing 'sql_escapestring'");
172     return str;
173   }
174 
175   return res->data;
176 }
177 
get_crypto_errors(void)178 static const char *get_crypto_errors(void) {
179   unsigned int count = 0;
180   unsigned long error_code;
181   BIO *bio = NULL;
182   char *data = NULL;
183   long datalen;
184   const char *error_data = NULL, *str = "(unknown)";
185   int error_flags = 0;
186 
187   /* Use ERR_print_errors() and a memory BIO to build up a string with
188    * all of the error messages from the error queue.
189    */
190 
191   error_code = ERR_get_error_line_data(NULL, NULL, &error_data, &error_flags);
192   if (error_code) {
193     bio = BIO_new(BIO_s_mem());
194   }
195 
196   while (error_code) {
197     pr_signals_handle();
198 
199     if (error_flags & ERR_TXT_STRING) {
200       BIO_printf(bio, "\n  (%u) %s [%s]", ++count,
201         ERR_error_string(error_code, NULL), error_data);
202 
203     } else {
204       BIO_printf(bio, "\n  (%u) %s", ++count,
205         ERR_error_string(error_code, NULL));
206     }
207 
208     error_data = NULL;
209     error_flags = 0;
210     error_code = ERR_get_error_line_data(NULL, NULL, &error_data, &error_flags);
211   }
212 
213   datalen = BIO_get_mem_data(bio, &data);
214   if (data) {
215     data[datalen] = '\0';
216     str = pstrdup(session.pool, data);
217   }
218 
219   if (bio) {
220     BIO_free(bio);
221   }
222 
223   return str;
224 }
225 
get_pbkdf2_config(char * algo,const EVP_MD ** md,char * iter_str,int * iter,char * len_str,int * len)226 static int get_pbkdf2_config(char *algo, const EVP_MD **md,
227     char *iter_str, int *iter, char *len_str, int *len) {
228 
229   *md = EVP_get_digestbyname(algo);
230   if (*md == NULL) {
231     return SQL_PASSWD_ERR_PBKDF2_UNKNOWN_DIGEST;
232   }
233 
234 #if OPENSSL_VERSION_NUMBER < 0x1000003f
235   /* The necessary OpenSSL support for non-SHA1 digests for PBKDF2 appeared in
236    * 1.0.0c.
237    */
238   if (EVP_MD_type(*md) != EVP_MD_type(EVP_sha1())) {
239     return SQL_PASSWD_ERR_PBKDF2_UNSUPPORTED_DIGEST;
240   }
241 #endif /* OpenSSL-1.0.0b and earlier */
242 
243   *iter = atoi(iter_str);
244   if (*iter < 1) {
245     return SQL_PASSWD_ERR_PBKDF2_BAD_ROUNDS;
246   }
247 
248   *len = atoi(len_str);
249   if (*len < 1) {
250     return SQL_PASSWD_ERR_PBKDF2_BAD_LENGTH;
251   }
252 
253   return 0;
254 }
255 
sql_passwd_decode(pool * p,unsigned int encoding,char * text,size_t text_len,size_t * data_len)256 static unsigned char *sql_passwd_decode(pool *p, unsigned int encoding,
257     char *text, size_t text_len, size_t *data_len) {
258   unsigned char *data = NULL;
259 
260   switch (encoding) {
261     case SQL_PASSWD_ENC_USE_NONE:
262       *data_len = text_len;
263       data = (unsigned char *) pstrndup(p, text, text_len);
264       break;
265 
266     case SQL_PASSWD_ENC_USE_BASE64: {
267       int have_padding = FALSE, res;
268 
269       /* Due to Base64's padding, we need to detect if the last block was
270        * padded with zeros; we do this by looking for '=' characters at the
271        * end of the text being decoded.  If we see these characters, then we
272        * will "trim" off any trailing zero values in the decoded data, on the
273        * ASSUMPTION that they are the auto-added padding bytes.
274        */
275       if (text[text_len-1] == '=') {
276         have_padding = TRUE;
277       }
278 
279       data = pcalloc(p, text_len);
280       res = EVP_DecodeBlock((unsigned char *) data, (unsigned char *) text,
281         (int) text_len);
282       if (res <= 0) {
283         /* Base64-decoding error. */
284         errno = EINVAL;
285         return NULL;
286       }
287 
288       if (have_padding) {
289         /* Assume that only one or two zero bytes of padding were added. */
290         if (data[res-1] == '\0') {
291           res -= 1;
292 
293           if (data[res-1] == '\0') {
294             res -= 1;
295           }
296         }
297       }
298 
299       *data_len = (size_t) res;
300       break;
301     }
302 
303     case SQL_PASSWD_ENC_USE_HEX_LC: {
304       register unsigned int i, j;
305       unsigned int len = 0;
306 
307       data = pcalloc(p, text_len);
308       for (i = 0, j = 0; i < text_len; i += 2) {
309         int res;
310 
311         res = sscanf(text + i, "%02hhx", &(data[j++]));
312         if (res == 0) {
313           /* hex decoding error. */
314           errno = EINVAL;
315           return NULL;
316         }
317 
318         len += res;
319       }
320 
321       *data_len = len;
322       break;
323     }
324 
325     case SQL_PASSWD_ENC_USE_HEX_UC: {
326       register unsigned int i, j;
327       unsigned int len = 0;
328 
329       data = pcalloc(p, text_len);
330       for (i = 0, j = 0; i < text_len; i += 2) {
331         int res;
332 
333         res = sscanf(text + i, "%02hhX", &(data[j++]));
334         if (res == 0) {
335           /* hex decoding error. */
336           errno = EINVAL;
337           return NULL;
338         }
339 
340         len += res;
341       }
342 
343       *data_len = len;
344       break;
345     }
346 
347     default:
348       errno = EPERM;
349       return NULL;
350   }
351 
352   return data;
353 }
354 
sql_passwd_encode(pool * p,unsigned int encoding,unsigned char * data,size_t data_len)355 static char *sql_passwd_encode(pool *p, unsigned int encoding,
356     unsigned char *data, size_t data_len) {
357   char *buf = NULL;
358 
359   switch (encoding) {
360     case SQL_PASSWD_ENC_USE_BASE64: {
361       /* According to RATS, the output buffer for EVP_EncodeBlock() needs to be
362        * 4/3 the size of the input buffer (which is usually EVP_MAX_MD_SIZE).
363        * Let's make it easy, and use an output buffer that's twice the size of
364        * the input buffer.
365        */
366       buf = pcalloc(p, (2 * data_len) + 1);
367       EVP_EncodeBlock((unsigned char *) buf, data, (int) data_len);
368       break;
369     }
370 
371     case SQL_PASSWD_ENC_USE_HEX_LC: {
372       buf = pr_str_bin2hex(p, data, data_len, PR_STR_FL_HEX_USE_LC);
373       break;
374     }
375 
376     case SQL_PASSWD_ENC_USE_HEX_UC: {
377       buf = pr_str_bin2hex(p, data, data_len, PR_STR_FL_HEX_USE_UC);
378       break;
379     }
380 
381     default:
382       errno = EPERM;
383       return NULL;
384   }
385 
386   return buf;
387 }
388 
389 /* This may look a little weird, with the data, prefix, and suffix arguments.
390  * But they are used to handle the case where we are hashing data with
391  * a salt (either as a prefix or as a suffix), and where we are hashing
392  * already hashed data.
393  */
sql_passwd_hash(pool * p,const EVP_MD * md,unsigned char * data,size_t data_len,unsigned char * prefix,size_t prefix_len,unsigned char * suffix,size_t suffix_len,unsigned int * hash_len)394 static unsigned char *sql_passwd_hash(pool *p, const EVP_MD *md,
395     unsigned char *data, size_t data_len,
396     unsigned char *prefix, size_t prefix_len,
397     unsigned char *suffix, size_t suffix_len,
398     unsigned int *hash_len) {
399 
400   EVP_MD_CTX *md_ctx;
401   unsigned char *hash;
402 
403   hash = palloc(p, EVP_MAX_MD_SIZE);
404 
405   /* In OpenSSL 0.9.6, many of the EVP_Digest* functions returned void, not
406    * int.  Without these ugly OpenSSL version preprocessor checks, the
407    * compiler will error out with "void value not ignored as it ought to be".
408    */
409 
410   md_ctx = EVP_MD_CTX_create();
411 #if OPENSSL_VERSION_NUMBER >= 0x000907000L
412   if (EVP_DigestInit(md_ctx, md) != 1) {
413     sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
414       ": error initializing '%s' digest: %s", OBJ_nid2ln(EVP_MD_type(md)),
415       get_crypto_errors());
416     EVP_MD_CTX_destroy(md_ctx);
417     errno = EPERM;
418     return NULL;
419   }
420 #else
421   EVP_DigestInit(md_ctx, md);
422 #endif
423 
424   if (prefix != NULL) {
425 #if OPENSSL_VERSION_NUMBER >= 0x000907000L
426     if (EVP_DigestUpdate(md_ctx, prefix, prefix_len) != 1) {
427       sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
428         ": error updating '%s' digest: %s", OBJ_nid2ln(EVP_MD_type(md)),
429         get_crypto_errors());
430       EVP_MD_CTX_destroy(md_ctx);
431       errno = EPERM;
432       return NULL;
433     }
434 #else
435     EVP_DigestUpdate(md_ctx, prefix, prefix_len);
436 #endif
437   }
438 
439 #if OPENSSL_VERSION_NUMBER >= 0x000907000L
440   if (EVP_DigestUpdate(md_ctx, data, data_len) != 1) {
441     sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
442       ": error updating '%s' digest: %s", OBJ_nid2ln(EVP_MD_type(md)),
443       get_crypto_errors());
444     EVP_MD_CTX_destroy(md_ctx);
445     errno = EPERM;
446     return NULL;
447   }
448 #else
449   EVP_DigestUpdate(md_ctx, data, data_len);
450 #endif
451 
452   if (suffix != NULL) {
453 #if OPENSSL_VERSION_NUMBER >= 0x000907000L
454     if (EVP_DigestUpdate(md_ctx, suffix, suffix_len) != 1) {
455       sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
456         ": error updating '%s' digest: %s", OBJ_nid2ln(EVP_MD_type(md)),
457         get_crypto_errors());
458       EVP_MD_CTX_destroy(md_ctx);
459       errno = EPERM;
460       return NULL;
461     }
462 #else
463     EVP_DigestUpdate(md_ctx, suffix, suffix_len);
464 #endif
465   }
466 
467 #if OPENSSL_VERSION_NUMBER >= 0x000907000L
468   if (EVP_DigestFinal(md_ctx, hash, hash_len) != 1) {
469     sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
470       ": error finishing '%s' digest: %s", OBJ_nid2ln(EVP_MD_type(md)),
471       get_crypto_errors());
472     EVP_MD_CTX_destroy(md_ctx);
473     errno = EPERM;
474     return NULL;
475   }
476 #else
477   EVP_DigestFinal(md_ctx, hash, hash_len);
478 #endif
479   EVP_MD_CTX_destroy(md_ctx);
480 
481   return hash;
482 }
483 
484 #if !defined(HAVE_TIMINGSAFE_BCMP)
timingsafe_bcmp(const void * b1,const void * b2,size_t n)485 static int timingsafe_bcmp(const void *b1, const void *b2, size_t n) {
486   const unsigned char *p1 = b1, *p2 = b2;
487   int ret = 0;
488 
489   for (; n > 0; n--) {
490     ret |= *p1++ ^ *p2++;
491   }
492 
493   return (ret != 0);
494 }
495 #endif /* HAVE_TIMINGSAFE_BCMP */
496 
sql_passwd_auth(cmd_rec * cmd,const char * plaintext,const char * ciphertext,const char * digest)497 static modret_t *sql_passwd_auth(cmd_rec *cmd, const char *plaintext,
498     const char *ciphertext, const char *digest) {
499   const EVP_MD *md;
500   unsigned char *hash = NULL, *data = NULL, *prefix = NULL, *suffix = NULL;
501   size_t data_len = 0, prefix_len = 0, suffix_len = 0;
502   unsigned int hash_len = 0;
503 
504   /* Temporary copy of the ciphertext string */
505   char *copytext;
506   const char *encodedtext;
507 
508   if (sql_passwd_engine == FALSE) {
509     return PR_ERROR_INT(cmd, PR_AUTH_ERROR);
510   }
511 
512   md = EVP_get_digestbyname(digest);
513   if (md == NULL) {
514     sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
515       ": no such digest '%s' supported", digest);
516     return PR_ERROR_INT(cmd, PR_AUTH_BADPWD);
517   }
518 
519   /* We need a copy of the ciphertext. */
520   copytext = pstrdup(cmd->tmp_pool, ciphertext);
521 
522   /* If a salt is configured, do we prepend the salt as a prefix (i.e. throw
523    * it into the digest before the user-supplied password) or append it as a
524    * suffix?
525    */
526 
527   if (sql_passwd_file_salt_len > 0 &&
528       (sql_passwd_file_salt_flags & SQL_PASSWD_SALT_FL_PREPEND)) {
529 
530     /* If we have salt data, add it to the mix. */
531 
532     if (!(sql_passwd_opts & SQL_PASSWD_OPT_HASH_SALT)) {
533       prefix = (unsigned char *) sql_passwd_file_salt;
534       prefix_len = sql_passwd_file_salt_len;
535 
536       pr_trace_msg(trace_channel, 9,
537         "prepending %lu bytes of file salt data", (unsigned long) prefix_len);
538 
539     } else {
540       unsigned int salt_hashlen = 0;
541 
542       prefix = sql_passwd_hash(cmd->tmp_pool, md,
543         (unsigned char *) sql_passwd_file_salt, sql_passwd_file_salt_len,
544         NULL, 0, NULL, 0, &salt_hashlen);
545       prefix_len = salt_hashlen;
546 
547       if (sql_passwd_opts & SQL_PASSWD_OPT_ENCODE_SALT) {
548         prefix = (unsigned char *) sql_passwd_encode(cmd->tmp_pool,
549           sql_passwd_encoding, (unsigned char *) prefix, prefix_len);
550         prefix_len = strlen((char *) prefix);
551       }
552 
553       pr_trace_msg(trace_channel, 9,
554         "prepending %lu bytes of %s-hashed file salt data (%s)",
555         (unsigned long) prefix_len, digest, prefix);
556     }
557   }
558 
559   if (sql_passwd_user_salt_len > 0 &&
560       (sql_passwd_user_salt_flags & SQL_PASSWD_SALT_FL_PREPEND)) {
561 
562     /* If we have user salt data, add it to the mix. */
563 
564     if (!(sql_passwd_opts & SQL_PASSWD_OPT_HASH_SALT)) {
565       prefix = (unsigned char *) sql_passwd_user_salt;
566       prefix_len = sql_passwd_user_salt_len;
567 
568       pr_trace_msg(trace_channel, 9,
569         "prepending %lu bytes of user salt data", (unsigned long) prefix_len);
570 
571     } else {
572       unsigned int salt_hashlen = 0;
573 
574       prefix = sql_passwd_hash(cmd->tmp_pool, md,
575         (unsigned char *) sql_passwd_user_salt, sql_passwd_user_salt_len,
576         NULL, 0, NULL, 0, &salt_hashlen);
577       prefix_len = salt_hashlen;
578 
579       if (sql_passwd_opts & SQL_PASSWD_OPT_ENCODE_SALT) {
580         prefix = (unsigned char *) sql_passwd_encode(cmd->tmp_pool,
581           sql_passwd_encoding, (unsigned char *) prefix, prefix_len);
582         prefix_len = strlen((char *) prefix);
583       }
584 
585       pr_trace_msg(trace_channel, 9,
586         "prepending %lu bytes of %s-hashed user salt data (%s)",
587         (unsigned long) prefix_len, digest, prefix);
588     }
589   }
590 
591   if (!(sql_passwd_opts & SQL_PASSWD_OPT_HASH_PASSWORD)) {
592     data = (unsigned char *) plaintext;
593     data_len = strlen(plaintext);
594 
595   } else {
596     /* Note: We will only honor a HashEncodePassword option IFF there is
597      * also salt data present.  Otherwise, it is equivalent to another
598      * round of processing, which defeats the principle of least surprise.
599      */
600     if ((sql_passwd_file_salt_len == 0 &&
601          sql_passwd_user_salt_len == 0) &&
602         (sql_passwd_opts & SQL_PASSWD_OPT_HASH_PASSWORD) &&
603         (sql_passwd_opts & SQL_PASSWD_OPT_ENCODE_PASSWORD)) {
604       pr_trace_msg(trace_channel, 4, "%s",
605         "no salt present, ignoring HashEncodePassword SQLPasswordOption");
606       data = (unsigned char *) plaintext;
607       data_len = strlen(plaintext);
608 
609     } else {
610       unsigned int salt_hashlen = 0;
611 
612       data = sql_passwd_hash(cmd->tmp_pool, md,
613         (unsigned char *) plaintext, strlen(plaintext),
614         NULL, 0, NULL, 0, &salt_hashlen);
615       data_len = salt_hashlen;
616 
617       if (sql_passwd_opts & SQL_PASSWD_OPT_ENCODE_PASSWORD) {
618         data = (unsigned char *) sql_passwd_encode(cmd->tmp_pool,
619           sql_passwd_encoding, (unsigned char *) data, data_len);
620         data_len = strlen((char *) data);
621       }
622     }
623   }
624 
625   if (sql_passwd_file_salt_len > 0 &&
626       (sql_passwd_file_salt_flags & SQL_PASSWD_SALT_FL_APPEND)) {
627     /* If we have file salt data, add it to the mix. */
628 
629     if (!(sql_passwd_opts & SQL_PASSWD_OPT_HASH_SALT)) {
630       suffix = (unsigned char *) sql_passwd_file_salt;
631       suffix_len = sql_passwd_file_salt_len;
632 
633       pr_trace_msg(trace_channel, 9,
634         "appending %lu bytes of file salt data", (unsigned long) suffix_len);
635 
636     } else {
637       unsigned int salt_hashlen = 0;
638 
639       suffix = sql_passwd_hash(cmd->tmp_pool, md,
640         (unsigned char *) sql_passwd_file_salt, sql_passwd_file_salt_len,
641         NULL, 0, NULL, 0, &salt_hashlen);
642       suffix_len = salt_hashlen;
643 
644       if (sql_passwd_opts & SQL_PASSWD_OPT_ENCODE_SALT) {
645         suffix = (unsigned char *) sql_passwd_encode(cmd->tmp_pool,
646           sql_passwd_encoding, (unsigned char *) suffix, suffix_len);
647         suffix_len = strlen((char *) suffix);
648       }
649 
650       pr_trace_msg(trace_channel, 9,
651         "appending %lu bytes of %s-hashed file salt data",
652         (unsigned long) suffix_len, digest);
653     }
654   }
655 
656   if (sql_passwd_user_salt_len > 0 &&
657       (sql_passwd_user_salt_flags & SQL_PASSWD_SALT_FL_APPEND)) {
658     /* If we have user salt data, add it to the mix. */
659 
660     if (!(sql_passwd_opts & SQL_PASSWD_OPT_HASH_SALT)) {
661       suffix = (unsigned char *) sql_passwd_user_salt;
662       suffix_len = sql_passwd_user_salt_len;
663 
664       pr_trace_msg(trace_channel, 9,
665         "appending %lu bytes of user salt data", (unsigned long) suffix_len);
666 
667     } else {
668       unsigned int salt_hashlen = 0;
669 
670       suffix = sql_passwd_hash(cmd->tmp_pool, md,
671         (unsigned char *) sql_passwd_user_salt, sql_passwd_user_salt_len,
672         NULL, 0, NULL, 0, &salt_hashlen);
673       suffix_len = salt_hashlen;
674 
675       if (sql_passwd_opts & SQL_PASSWD_OPT_ENCODE_SALT) {
676         suffix = (unsigned char *) sql_passwd_encode(cmd->tmp_pool,
677           sql_passwd_encoding, (unsigned char *) suffix, suffix_len);
678         suffix_len = strlen((char *) suffix);
679       }
680 
681       pr_trace_msg(trace_channel, 9,
682         "appending %lu bytes of %s-hashed user salt data",
683         (unsigned long) suffix_len, digest);
684     }
685   }
686 
687   hash = sql_passwd_hash(cmd->tmp_pool, md, data, data_len, prefix, prefix_len,
688     suffix, suffix_len, &hash_len);
689   if (hash == NULL) {
690     sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
691       ": unable to obtain password hash: %s", strerror(errno));
692     return PR_ERROR_INT(cmd, PR_AUTH_BADPWD);
693   }
694 
695   encodedtext = sql_passwd_encode(cmd->tmp_pool, sql_passwd_encoding, hash,
696     hash_len);
697   if (encodedtext == NULL) {
698     sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
699       ": unsupported SQLPasswordEncoding configured");
700     return PR_ERROR_INT(cmd, PR_AUTH_ERROR);
701   }
702 
703   /* The case of nrounds == 1 is a special case, as that is when the salt
704    * data is processed.  Any additional rounds are simply hashing and
705    * encoding the resulting data, over and over.
706    */
707   if (sql_passwd_nrounds > 1) {
708     register unsigned int i;
709     unsigned long nrounds = sql_passwd_nrounds - 1;
710 
711     pr_trace_msg(trace_channel, 9,
712       "transforming the data for another %lu %s", nrounds,
713       nrounds != 1 ? "rounds" : "round");
714 
715     for (i = 0; i < nrounds; i++) {
716       pr_signals_handle();
717 
718       hash = sql_passwd_hash(cmd->tmp_pool, md, (unsigned char *) encodedtext,
719         strlen(encodedtext), NULL, 0, NULL, 0, &hash_len);
720       encodedtext = sql_passwd_encode(cmd->tmp_pool, sql_passwd_encoding,
721         hash, hash_len);
722 
723       pr_trace_msg(trace_channel, 15, "data after round %u: '%s'", i + 1,
724         encodedtext);
725     }
726   }
727 
728   if (timingsafe_bcmp(encodedtext, copytext, strlen(copytext)) == 0) {
729     return PR_HANDLED(cmd);
730   }
731 
732   pr_trace_msg(trace_channel, 9, "expected '%s', got '%s'", copytext,
733     encodedtext);
734   pr_log_debug(DEBUG9, MOD_SQL_PASSWD_VERSION ": expected '%s', got '%s'",
735     copytext, encodedtext);
736 
737   return PR_ERROR_INT(cmd, PR_AUTH_BADPWD);
738 }
739 
sql_passwd_bcrypt(cmd_rec * cmd,const char * plaintext,const char * ciphertext)740 static modret_t *sql_passwd_bcrypt(cmd_rec *cmd, const char *plaintext,
741     const char *ciphertext) {
742   char *hashed;
743   size_t hashed_len = 0;
744 
745   if (sql_passwd_engine == FALSE) {
746     return PR_ERROR_INT(cmd, PR_AUTH_ERROR);
747   }
748 
749   /* OpenSSL does not implement the bcrypt algorithm, so we handle it
750    * ourselves.
751    */
752 
753   hashed = pr_auth_bcrypt(cmd->tmp_pool, plaintext, ciphertext, &hashed_len);
754   if (hashed == NULL) {
755     pr_trace_msg(trace_channel, 3, "error using 'bcrypt': %s", strerror(errno));
756     return PR_ERROR_INT(cmd, PR_AUTH_BADPWD);
757   }
758 
759   if (timingsafe_bcmp(hashed, ciphertext, strlen(ciphertext)) == 0) {
760     return PR_HANDLED(cmd);
761   }
762 
763   pr_trace_msg(trace_channel, 9, "expected '%s', got '%s'", ciphertext, hashed);
764   pr_log_debug(DEBUG9, MOD_SQL_PASSWD_VERSION ": expected '%s', got '%s'",
765     ciphertext, hashed);
766 
767   return PR_ERROR_INT(cmd, PR_AUTH_BADPWD);
768 }
769 
sql_passwd_md5(cmd_rec * cmd,const char * plaintext,const char * ciphertext)770 static modret_t *sql_passwd_md5(cmd_rec *cmd, const char *plaintext,
771     const char *ciphertext) {
772   return sql_passwd_auth(cmd, plaintext, ciphertext, "md5");
773 }
774 
sql_passwd_sha1(cmd_rec * cmd,const char * plaintext,const char * ciphertext)775 static modret_t *sql_passwd_sha1(cmd_rec *cmd, const char *plaintext,
776     const char *ciphertext) {
777   return sql_passwd_auth(cmd, plaintext, ciphertext, "sha1");
778 }
779 
sql_passwd_sha256(cmd_rec * cmd,const char * plaintext,const char * ciphertext)780 static modret_t *sql_passwd_sha256(cmd_rec *cmd, const char *plaintext,
781     const char *ciphertext) {
782   return sql_passwd_auth(cmd, plaintext, ciphertext, "sha256");
783 }
784 
sql_passwd_sha512(cmd_rec * cmd,const char * plaintext,const char * ciphertext)785 static modret_t *sql_passwd_sha512(cmd_rec *cmd, const char *plaintext,
786     const char *ciphertext) {
787   return sql_passwd_auth(cmd, plaintext, ciphertext, "sha512");
788 }
789 
sql_passwd_pbkdf2(cmd_rec * cmd,const char * plaintext,const char * ciphertext)790 static modret_t *sql_passwd_pbkdf2(cmd_rec *cmd, const char *plaintext,
791     const char *ciphertext) {
792   unsigned char *derived_key;
793   const char *encodedtext;
794   char *pbkdf2_salt = NULL;
795   size_t pbkdf2_salt_len = 0;
796   int res;
797 
798   if (sql_passwd_engine == FALSE) {
799     sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
800       ": SQLPasswordEngine disabled; unable to handle PBKDF2 SQLAuthType");
801     return PR_ERROR_INT(cmd, PR_AUTH_ERROR);
802   }
803 
804   if (sql_passwd_pbkdf2_digest == NULL) {
805     sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
806       ": PBKDF2 not configured (see SQLPasswordPBKDF2 directive)");
807     return PR_ERROR_INT(cmd, PR_AUTH_ERROR);
808   }
809 
810   /* PBKDF2 requires a salt; if no salt is configured, it is an error. */
811   if (sql_passwd_file_salt == NULL &&
812       sql_passwd_user_salt == NULL) {
813     sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
814       ": no salt configured (PBKDF2 requires salt)");
815     return PR_ERROR_INT(cmd, PR_AUTH_ERROR);
816   }
817 
818   derived_key = palloc(cmd->tmp_pool, sql_passwd_pbkdf2_len);
819 
820   /* Prefer user salts over global salts. */
821   if (sql_passwd_user_salt_len > 0) {
822     pbkdf2_salt = (char *) sql_passwd_user_salt;
823     pbkdf2_salt_len = sql_passwd_user_salt_len;
824 
825   } else {
826     pbkdf2_salt = (char *) sql_passwd_file_salt;
827     pbkdf2_salt_len = sql_passwd_file_salt_len;
828   }
829 
830 #if OPENSSL_VERSION_NUMBER >= 0x1000003f
831   /* For digests other than SHA1, the necessary OpenSSL support
832    * (via PKCS5_PBKDF2_HMAC) appeared in 1.0.0c.
833    */
834   res = PKCS5_PBKDF2_HMAC(plaintext, -1,
835     (const unsigned char *) pbkdf2_salt, pbkdf2_salt_len,
836     sql_passwd_pbkdf2_iter, sql_passwd_pbkdf2_digest, sql_passwd_pbkdf2_len,
837     derived_key);
838 #else
839   res = PKCS5_PBKDF2_HMAC_SHA1(plaintext, -1,
840     (const unsigned char *) pbkdf2_salt, pbkdf2_salt_len,
841     sql_passwd_pbkdf2_iter, sql_passwd_pbkdf2_len, derived_key);
842 #endif /* OpenSSL-1.0.0b and earlier */
843 
844   if (res != 1) {
845     sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
846       ": error deriving PBKDF2 key: %s", get_crypto_errors());
847     return PR_ERROR_INT(cmd, PR_AUTH_ERROR);
848   }
849 
850   encodedtext = sql_passwd_encode(cmd->tmp_pool, sql_passwd_encoding,
851     derived_key,
852     sql_passwd_pbkdf2_len);
853   if (encodedtext == NULL) {
854     sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
855       ": unsupported SQLPasswordEncoding configured");
856     return PR_ERROR_INT(cmd, PR_AUTH_ERROR);
857   }
858 
859   if (timingsafe_bcmp(encodedtext, ciphertext, strlen(ciphertext)) == 0) {
860     return PR_HANDLED(cmd);
861   }
862 
863   pr_trace_msg(trace_channel, 9, "expected '%s', got '%s'", ciphertext,
864     encodedtext);
865   pr_log_debug(DEBUG9, MOD_SQL_PASSWD_VERSION ": expected '%s', got '%s'",
866     ciphertext, encodedtext);
867 
868   return PR_ERROR_INT(cmd, PR_AUTH_BADPWD);
869 }
870 
871 #ifdef PR_USE_SODIUM
sql_passwd_scrypt(cmd_rec * cmd,const char * plaintext,const char * ciphertext)872 static modret_t *sql_passwd_scrypt(cmd_rec *cmd, const char *plaintext,
873     const char *ciphertext) {
874   int res;
875   unsigned char *hash = NULL;
876   unsigned int hash_len = 0;
877   const char *encodedtext;
878   const unsigned char *scrypt_salt;
879   size_t ops_limit, mem_limit, plaintext_len, scrypt_salt_len;
880 
881   if (sql_passwd_engine == FALSE) {
882     return PR_ERROR_INT(cmd, PR_AUTH_ERROR);
883   }
884 
885   /* scrypt requires a salt; if no salt is configured, it is an error. */
886   if (sql_passwd_file_salt == NULL &&
887       sql_passwd_user_salt == NULL) {
888     sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
889       ": no salt configured (scrypt requires salt)");
890     return PR_ERROR_INT(cmd, PR_AUTH_ERROR);
891   }
892 
893   /* Prefer user salts over global salts. */
894   if (sql_passwd_user_salt_len > 0) {
895     scrypt_salt = sql_passwd_user_salt;
896     scrypt_salt_len = sql_passwd_user_salt_len;
897 
898   } else {
899     scrypt_salt = sql_passwd_file_salt;
900     scrypt_salt_len = sql_passwd_file_salt_len;
901   }
902 
903   /* scrypt requires 32 bytes of salt */
904   if (scrypt_salt_len != SQL_PASSWD_SCRYPT_DEFAULT_SALT_SIZE) {
905     sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
906       ": scrypt requires %u bytes of salt (%lu bytes of salt configured)",
907       SQL_PASSWD_SCRYPT_DEFAULT_SALT_SIZE, (unsigned long) scrypt_salt_len);
908     return PR_ERROR_INT(cmd, PR_AUTH_ERROR);
909   }
910 
911   switch (sql_passwd_cost) {
912     case SQL_PASSWD_COST_INTERACTIVE:
913       ops_limit = crypto_pwhash_scryptsalsa208sha256_opslimit_interactive();
914       mem_limit = crypto_pwhash_scryptsalsa208sha256_memlimit_interactive();
915       break;
916 
917     case SQL_PASSWD_COST_SENSITIVE:
918       ops_limit = crypto_pwhash_scryptsalsa208sha256_opslimit_sensitive();
919       mem_limit = crypto_pwhash_scryptsalsa208sha256_memlimit_sensitive();
920       break;
921 
922     default:
923       sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
924         ": unknown SQLPasswordCost value");
925       return PR_ERROR_INT(cmd, PR_AUTH_ERROR);
926   }
927 
928   hash_len = sql_passwd_scrypt_hash_len;
929   hash = palloc(cmd->tmp_pool, hash_len);
930 
931   plaintext_len = strlen(plaintext);
932   res = crypto_pwhash_scryptsalsa208sha256(hash, hash_len, plaintext,
933     plaintext_len, scrypt_salt, ops_limit, mem_limit);
934   if (res < 0) {
935     sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION ": scrypt error: %s",
936       strerror(errno));
937     return PR_ERROR_INT(cmd, PR_AUTH_ERROR);
938   }
939 
940   encodedtext = sql_passwd_encode(cmd->tmp_pool, sql_passwd_encoding, hash,
941     hash_len);
942   if (encodedtext == NULL) {
943     sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
944       ": unsupported SQLPasswordEncoding configured");
945     return PR_ERROR_INT(cmd, PR_AUTH_ERROR);
946   }
947 
948   if (timingsafe_bcmp(encodedtext, ciphertext, strlen(ciphertext)) == 0) {
949     return PR_HANDLED(cmd);
950   }
951 
952   pr_trace_msg(trace_channel, 9, "expected '%s', got '%s'", ciphertext,
953     encodedtext);
954   pr_log_debug(DEBUG9, MOD_SQL_PASSWD_VERSION ": expected '%s', got '%s'",
955     ciphertext, encodedtext);
956 
957   return PR_ERROR_INT(cmd, PR_AUTH_BADPWD);
958 }
959 
sql_passwd_argon2(cmd_rec * cmd,const char * plaintext,const char * ciphertext)960 static modret_t *sql_passwd_argon2(cmd_rec *cmd, const char *plaintext,
961     const char *ciphertext) {
962 # if defined(USE_SODIUM_ARGON2)
963   int argon2_algo, res;
964   unsigned char *hash = NULL;
965   unsigned int hash_len = 0;
966   const char *encodedtext;
967   const unsigned char *argon2_salt;
968   size_t ops_limit, mem_limit, plaintext_len, argon2_salt_len;
969 
970   if (sql_passwd_engine == FALSE) {
971     return PR_ERROR_INT(cmd, PR_AUTH_ERROR);
972   }
973 
974   /* argon2 requires a salt; if no salt is configured, it is an error. */
975   if (sql_passwd_file_salt == NULL &&
976       sql_passwd_user_salt == NULL) {
977     sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
978       ": no salt configured (argon2 requires salt)");
979     return PR_ERROR_INT(cmd, PR_AUTH_ERROR);
980   }
981 
982   /* Prefer user salts over global salts. */
983   if (sql_passwd_user_salt_len > 0) {
984     argon2_salt = sql_passwd_user_salt;
985     argon2_salt_len = sql_passwd_user_salt_len;
986 
987   } else {
988     argon2_salt = sql_passwd_file_salt;
989     argon2_salt_len = sql_passwd_file_salt_len;
990   }
991 
992   /* argon2 requires 16 bytes of salt */
993   if (argon2_salt_len != SQL_PASSWD_ARGON2_DEFAULT_SALT_SIZE) {
994     sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
995       ": argon2 requires %u bytes of salt (%lu bytes of salt configured)",
996       SQL_PASSWD_ARGON2_DEFAULT_SALT_SIZE, (unsigned long) argon2_salt_len);
997     return PR_ERROR_INT(cmd, PR_AUTH_ERROR);
998   }
999 
1000   argon2_algo = crypto_pwhash_argon2i_alg_argon2i13();
1001 
1002   switch (sql_passwd_cost) {
1003     case SQL_PASSWD_COST_INTERACTIVE:
1004       ops_limit = crypto_pwhash_argon2i_opslimit_interactive();
1005       mem_limit = crypto_pwhash_argon2i_memlimit_interactive();
1006       break;
1007 
1008     case SQL_PASSWD_COST_SENSITIVE:
1009       ops_limit = crypto_pwhash_argon2i_opslimit_sensitive();
1010       mem_limit = crypto_pwhash_argon2i_memlimit_sensitive();
1011       break;
1012 
1013     default:
1014       sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
1015         ": unknown SQLPasswordCost value");
1016       return PR_ERROR_INT(cmd, PR_AUTH_ERROR);
1017   }
1018 
1019   hash_len = sql_passwd_argon2_hash_len;
1020   hash = palloc(cmd->tmp_pool, hash_len);
1021 
1022   plaintext_len = strlen(plaintext);
1023   res = crypto_pwhash_argon2i(hash, hash_len, plaintext, plaintext_len,
1024     argon2_salt, ops_limit, mem_limit, argon2_algo);
1025   if (res < 0) {
1026     sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION ": argon2 error: %s",
1027       strerror(errno));
1028     return PR_ERROR_INT(cmd, PR_AUTH_ERROR);
1029   }
1030 
1031   encodedtext = sql_passwd_encode(cmd->tmp_pool, sql_passwd_encoding, hash,
1032     hash_len);
1033   if (encodedtext == NULL) {
1034     sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
1035       ": unsupported SQLPasswordEncoding configured");
1036     return PR_ERROR_INT(cmd, PR_AUTH_ERROR);
1037   }
1038 
1039   if (timingsafe_bcmp(encodedtext, ciphertext, strlen(ciphertext)) == 0) {
1040     return PR_HANDLED(cmd);
1041   }
1042 
1043   pr_trace_msg(trace_channel, 9, "expected '%s', got '%s'", ciphertext,
1044     encodedtext);
1045   pr_log_debug(DEBUG9, MOD_SQL_PASSWD_VERSION ": expected '%s', got '%s'",
1046     ciphertext, encodedtext);
1047 
1048   return PR_ERROR_INT(cmd, PR_AUTH_BADPWD);
1049 # else
1050   sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
1051     ": argon2 not supported on this system (requires libsodium-1.0.9 or "
1052     "later)");
1053   return PR_ERROR_INT(cmd, PR_AUTH_ERROR);
1054 # endif /* USE_SODIUM_ARGON2 */
1055 }
1056 #endif /* PR_USE_SODIUM */
1057 
1058 /* Event handlers
1059  */
1060 
1061 #if defined(PR_SHARED_MODULE)
sql_passwd_mod_unload_ev(const void * event_data,void * user_data)1062 static void sql_passwd_mod_unload_ev(const void *event_data, void *user_data) {
1063   if (strcmp("mod_sql_passwd.c", (const char *) event_data) == 0) {
1064     sql_unregister_authtype("bcrypt");
1065     sql_unregister_authtype("md5");
1066     sql_unregister_authtype("sha1");
1067     sql_unregister_authtype("sha256");
1068     sql_unregister_authtype("sha512");
1069     sql_unregister_authtype("pbkdf2");
1070 # ifdef PR_USE_SODIUM
1071     sql_unregister_authtype("argon2");
1072     sql_unregister_authtype("scrypt");
1073 # endif /* PR_USE_SODIUM */
1074 
1075     pr_event_unregister(&sql_passwd_module, NULL, NULL);
1076   }
1077 }
1078 #endif /* PR_SHARED_MODULE */
1079 
1080 /* Command handlers
1081  */
1082 
sql_passwd_pre_pass(cmd_rec * cmd)1083 MODRET sql_passwd_pre_pass(cmd_rec *cmd) {
1084   config_rec *c;
1085 
1086   if (sql_passwd_engine == FALSE) {
1087     return PR_DECLINED(cmd);
1088   }
1089 
1090   c = find_config(main_server->conf, CONF_PARAM, "SQLPasswordRounds", FALSE);
1091   if (c != NULL) {
1092     sql_passwd_nrounds = *((unsigned long *) c->argv[0]);
1093   }
1094 
1095   c = find_config(main_server->conf, CONF_PARAM, "SQLPasswordPBKDF2", FALSE);
1096   if (c != NULL) {
1097     if (c->argc == 3) {
1098       sql_passwd_pbkdf2_digest = c->argv[0];
1099       sql_passwd_pbkdf2_iter = *((int *) c->argv[1]);
1100       sql_passwd_pbkdf2_len = *((int *) c->argv[2]);
1101 
1102     } else {
1103       const char *user;
1104       char *key, *named_query, *ptr;
1105       cmdtable *sql_cmdtab;
1106       cmd_rec *sql_cmd;
1107       modret_t *sql_res;
1108       array_header *sql_data;
1109 
1110       key = c->argv[0];
1111 
1112       ptr = key + 5;
1113       named_query = pstrcat(cmd->tmp_pool, "SQLNamedQuery_", ptr, NULL);
1114 
1115       c = find_config(main_server->conf, CONF_PARAM, named_query, FALSE);
1116       if (c == NULL) {
1117         sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
1118           ": unable to resolve SQLNamedQuery '%s'", ptr);
1119         return PR_DECLINED(cmd);
1120       }
1121 
1122       sql_cmdtab = pr_stash_get_symbol2(PR_SYM_HOOK, "sql_lookup", NULL, NULL,
1123         NULL);
1124       if (sql_cmdtab == NULL) {
1125         sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
1126           ": unable to find SQL hook symbol 'sql_lookup'");
1127         return PR_DECLINED(cmd);
1128       }
1129 
1130       user = pr_table_get(session.notes, "mod_auth.orig-user", NULL);
1131 
1132       sql_cmd = sql_passwd_cmd_create(cmd->tmp_pool, 3, "sql_lookup", ptr,
1133         sql_passwd_get_str(cmd->tmp_pool, user));
1134 
1135       /* Call the handler. */
1136       sql_res = pr_module_call(sql_cmdtab->m, sql_cmdtab->handler, sql_cmd);
1137       if (sql_res == NULL ||
1138           MODRET_ISERROR(sql_res)) {
1139         sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
1140           ": error processing SQLNamedQuery '%s'", ptr);
1141         return PR_DECLINED(cmd);
1142       }
1143 
1144       sql_data = (array_header *) sql_res->data;
1145 
1146       if (sql_data->nelts != 3) {
1147         sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
1148           ": SQLNamedQuery '%s' returned wrong number of columns (%d)", ptr,
1149           sql_data->nelts);
1150 
1151       } else {
1152         char **values;
1153         int iter, len, res;
1154         const EVP_MD *md;
1155 
1156         values = sql_data->elts;
1157 
1158         res = get_pbkdf2_config(values[0], &md, values[1], &iter,
1159           values[2], &len);
1160         switch (res) {
1161           case SQL_PASSWD_ERR_PBKDF2_UNKNOWN_DIGEST:
1162             sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
1163               ": SQLNamedQuery '%s' returned unknown PKBDF2 digest: %s",
1164               ptr, values[0]);
1165             break;
1166 
1167           case SQL_PASSWD_ERR_PBKDF2_UNSUPPORTED_DIGEST:
1168             sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
1169               ": SQLNamedQuery '%s' returned unsupported PKBDF2 digest: %s",
1170               ptr, values[0]);
1171             break;
1172 
1173           case SQL_PASSWD_ERR_PBKDF2_BAD_ROUNDS:
1174             sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
1175               ": SQLNamedQuery '%s' returned insufficient number of rounds: %s",
1176               ptr, values[1]);
1177             break;
1178 
1179           case SQL_PASSWD_ERR_PBKDF2_BAD_LENGTH:
1180             sql_log(DEBUG_WARN, MOD_SQL_PASSWD_VERSION
1181               ": SQLNamedQuery '%s' returned insufficient length: %s", ptr,
1182               values[2]);
1183             break;
1184 
1185           case 0:
1186             sql_passwd_pbkdf2_digest = md;
1187             sql_passwd_pbkdf2_iter = iter;
1188             sql_passwd_pbkdf2_len = len;
1189             break;
1190         }
1191       }
1192     }
1193   }
1194 
1195   c = find_config(main_server->conf, CONF_PARAM, "SQLPasswordUserSalt", FALSE);
1196   if (c != NULL) {
1197     char *key;
1198     unsigned long salt_flags;
1199 
1200     key = c->argv[0];
1201     salt_flags = *((unsigned long *) c->argv[1]);
1202 
1203     if (strcasecmp(key, "name") == 0) {
1204       const char *user;
1205 
1206       user = pr_table_get(session.notes, "mod_auth.orig-user", NULL);
1207       if (user == NULL) {
1208         pr_log_debug(DEBUG3, MOD_SQL_PASSWD_VERSION
1209           ": unable to determine original USER name");
1210         return PR_DECLINED(cmd);
1211       }
1212 
1213       sql_passwd_user_salt = (unsigned char *) user;
1214       sql_passwd_user_salt_len = strlen(user);
1215 
1216     } else if (strncasecmp(key, "sql:/", 5) == 0) {
1217       const char *user;
1218       char *named_query, *ptr, **values;
1219       cmdtable *sql_cmdtab;
1220       cmd_rec *sql_cmd;
1221       modret_t *sql_res;
1222       array_header *sql_data;
1223       size_t value_len;
1224 
1225       ptr = key + 5;
1226       named_query = pstrcat(cmd->tmp_pool, "SQLNamedQuery_", ptr, NULL);
1227 
1228       c = find_config(main_server->conf, CONF_PARAM, named_query, FALSE);
1229       if (c == NULL) {
1230         pr_log_debug(DEBUG3, MOD_SQL_PASSWD_VERSION
1231           ": unable to resolve SQLNamedQuery '%s'", ptr);
1232         return PR_DECLINED(cmd);
1233       }
1234 
1235       sql_cmdtab = pr_stash_get_symbol2(PR_SYM_HOOK, "sql_lookup", NULL, NULL,
1236         NULL);
1237       if (sql_cmdtab == NULL) {
1238         pr_log_debug(DEBUG3, MOD_SQL_PASSWD_VERSION
1239           ": unable to find SQL hook symbol 'sql_lookup'");
1240         return PR_DECLINED(cmd);
1241       }
1242 
1243       user = pr_table_get(session.notes, "mod_auth.orig-user", NULL);
1244       if (user == NULL) {
1245         pr_log_debug(DEBUG3, MOD_SQL_PASSWD_VERSION
1246           ": unable to determine original USER name");
1247         return PR_DECLINED(cmd);
1248       }
1249 
1250       sql_cmd = sql_passwd_cmd_create(cmd->tmp_pool, 3, "sql_lookup", ptr,
1251         sql_passwd_get_str(cmd->tmp_pool, user));
1252 
1253       /* Call the handler. */
1254       sql_res = pr_module_call(sql_cmdtab->m, sql_cmdtab->handler, sql_cmd);
1255       if (sql_res == NULL ||
1256           MODRET_ISERROR(sql_res)) {
1257         pr_log_debug(DEBUG0, MOD_SQL_PASSWD_VERSION
1258           ": error processing SQLNamedQuery '%s'", ptr);
1259         return PR_DECLINED(cmd);
1260       }
1261 
1262       sql_data = (array_header *) sql_res->data;
1263 
1264       if (sql_data->nelts != 1) {
1265         pr_log_debug(DEBUG0, MOD_SQL_PASSWD_VERSION
1266           ": SQLNamedQuery '%s' returned wrong number of rows (%d)", ptr,
1267           sql_data->nelts);
1268         return PR_DECLINED(cmd);
1269       }
1270 
1271       values = sql_data->elts;
1272 
1273       /* Note: this ASSUMES that the value coming from the database is a
1274        * string.
1275        */
1276       value_len = strlen(values[0]);
1277 
1278       sql_passwd_user_salt = sql_passwd_decode(session.pool,
1279         sql_passwd_salt_encoding, values[0], value_len,
1280         &sql_passwd_user_salt_len);
1281       if (sql_passwd_user_salt == NULL) {
1282         pr_log_debug(DEBUG0, MOD_SQL_PASSWD_VERSION
1283           ": error decoding salt from SQLNamedQuery '%s': %s", ptr,
1284           strerror(errno));
1285         return PR_DECLINED(cmd);
1286       }
1287 
1288     } else {
1289       return PR_DECLINED(cmd);
1290     }
1291 
1292     sql_passwd_user_salt_flags = salt_flags;
1293   }
1294 
1295   return PR_DECLINED(cmd);
1296 }
1297 
1298 /* Configuration handlers
1299  */
1300 
1301 /* usage: SQLPasswordArgon2 len */
set_sqlpasswdargon2(cmd_rec * cmd)1302 MODRET set_sqlpasswdargon2(cmd_rec *cmd) {
1303 #ifdef USE_SODIUM_ARGON2
1304   config_rec *c;
1305   int len;
1306 
1307   CHECK_ARGS(cmd, 1);
1308   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
1309 
1310   len = atoi(cmd->argv[1]);
1311   if (len <= 0) {
1312     CONF_ERROR(cmd, "length must be greater than 0");
1313   }
1314 
1315   c = add_config_param(cmd->argv[0], 1, NULL);
1316   c->argv[0] = palloc(c->pool, sizeof(unsigned int));
1317   *((unsigned int *) c->argv[0]) = len;
1318 
1319   return PR_HANDLED(cmd);
1320 #else
1321   CONF_ERROR(cmd, "requires libsodium Argon2 support");
1322 #endif /* No libsodium Argon2 support */
1323 }
1324 
1325 /* usage: SQLPasswordCost "interactive"|"sensitive" */
set_sqlpasswdcost(cmd_rec * cmd)1326 MODRET set_sqlpasswdcost(cmd_rec *cmd) {
1327   unsigned int cost;
1328   config_rec *c = NULL;
1329 
1330   CHECK_ARGS(cmd, 1);
1331   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
1332 
1333   if (strcasecmp(cmd->argv[1], "interactive") == 0) {
1334     cost = SQL_PASSWD_COST_INTERACTIVE;
1335 
1336   } else if (strcasecmp(cmd->argv[1], "sensitive") == 0) {
1337     cost = SQL_PASSWD_COST_SENSITIVE;
1338 
1339   } else {
1340     CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown/unsupported cost: '",
1341       cmd->argv[1], "'", NULL));
1342   }
1343 
1344   c = add_config_param(cmd->argv[0], 1, NULL);
1345   c->argv[0] = palloc(c->pool, sizeof(unsigned int));
1346   *((unsigned int *) c->argv[0]) = cost;
1347 
1348   return PR_HANDLED(cmd);
1349 }
1350 
1351 /* usage: SQLPasswordEncoding "base64"|"hex"|"HEX" */
set_sqlpasswdencoding(cmd_rec * cmd)1352 MODRET set_sqlpasswdencoding(cmd_rec *cmd) {
1353   unsigned int encoding;
1354   config_rec *c = NULL;
1355 
1356   CHECK_ARGS(cmd, 1);
1357   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
1358 
1359   if (strcasecmp(cmd->argv[1], "none") == 0) {
1360     encoding = SQL_PASSWD_ENC_USE_NONE;
1361 
1362   } else if (strcasecmp(cmd->argv[1], "base64") == 0) {
1363     encoding = SQL_PASSWD_ENC_USE_BASE64;
1364 
1365   } else if (strcmp(cmd->argv[1], "hex") == 0) {
1366     encoding = SQL_PASSWD_ENC_USE_HEX_LC;
1367 
1368   } else if (strcmp(cmd->argv[1], "HEX") == 0) {
1369     encoding = SQL_PASSWD_ENC_USE_HEX_UC;
1370 
1371   } else {
1372     CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unsupported encoding '",
1373       cmd->argv[1], "' configured", NULL));
1374   }
1375 
1376   c = add_config_param(cmd->argv[0], 1, NULL);
1377   c->argv[0] = pcalloc(c->pool, sizeof(unsigned int));
1378   *((unsigned int *) c->argv[0]) = encoding;
1379 
1380   return PR_HANDLED(cmd);
1381 }
1382 
1383 /* usage: SQLPasswordEngine on|off */
set_sqlpasswdengine(cmd_rec * cmd)1384 MODRET set_sqlpasswdengine(cmd_rec *cmd) {
1385   int bool = -1;
1386   config_rec *c = NULL;
1387 
1388   CHECK_ARGS(cmd, 1);
1389   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
1390 
1391   bool = get_boolean(cmd, 1);
1392   if (bool == -1)
1393     CONF_ERROR(cmd, "expected Boolean parameter");
1394 
1395   c = add_config_param(cmd->argv[0], 1, NULL);
1396   c->argv[0] = pcalloc(c->pool, sizeof(int));
1397   *((int *) c->argv[0]) = bool;
1398 
1399   return PR_HANDLED(cmd);
1400 }
1401 
1402 /* usage: SQLPasswordOptions opt1 ... optN */
set_sqlpasswdoptions(cmd_rec * cmd)1403 MODRET set_sqlpasswdoptions(cmd_rec *cmd) {
1404   config_rec *c;
1405   unsigned long opts = 0UL;
1406   register unsigned int i;
1407 
1408   if (cmd->argc < 2) {
1409     CONF_ERROR(cmd, "wrong number of parameters");
1410   }
1411 
1412   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
1413 
1414   for (i = 1; i < cmd->argc; i++) {
1415     if (strcasecmp(cmd->argv[i], "HashPassword") == 0) {
1416       opts |= SQL_PASSWD_OPT_HASH_PASSWORD;
1417 
1418     } else if (strcasecmp(cmd->argv[i], "HashSalt") == 0) {
1419       opts |= SQL_PASSWD_OPT_HASH_SALT;
1420 
1421     } else if (strcasecmp(cmd->argv[i], "HashEncodePassword") == 0) {
1422       opts |= SQL_PASSWD_OPT_HASH_PASSWORD;
1423       opts |= SQL_PASSWD_OPT_ENCODE_PASSWORD;
1424 
1425     } else if (strcasecmp(cmd->argv[i], "HashEncodeSalt") == 0) {
1426       opts |= SQL_PASSWD_OPT_HASH_SALT;
1427       opts |= SQL_PASSWD_OPT_ENCODE_SALT;
1428 
1429     } else {
1430       CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown SQLPasswordOption '",
1431         cmd->argv[i], "'", NULL));
1432     }
1433   }
1434 
1435   c = add_config_param(cmd->argv[0], 1, NULL);
1436   c->argv[0] = palloc(c->pool, sizeof(unsigned long));
1437   *((unsigned long *) c->argv[0]) = opts;
1438 
1439   return PR_HANDLED(cmd);
1440 }
1441 
1442 /* usage: SQLPasswordPBKDF2 "sql:/"named-query|algo iter len */
set_sqlpasswdpbkdf2(cmd_rec * cmd)1443 MODRET set_sqlpasswdpbkdf2(cmd_rec *cmd) {
1444   config_rec *c;
1445 
1446   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
1447 
1448   if (cmd->argc == 4) {
1449     int iter, len, res;
1450     const EVP_MD *md;
1451 
1452     res = get_pbkdf2_config(cmd->argv[1], &md, cmd->argv[2], &iter,
1453       cmd->argv[3], &len);
1454     switch (res) {
1455       case SQL_PASSWD_ERR_PBKDF2_UNKNOWN_DIGEST:
1456         CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unsupported digest algorithm '",
1457           cmd->argv[1], "' configured", NULL));
1458         break;
1459 
1460       case SQL_PASSWD_ERR_PBKDF2_UNSUPPORTED_DIGEST:
1461         CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
1462           "Use of non-SHA1 digests for PBKDF2, such as ", cmd->argv[1],
1463           ", requires OpenSSL-1.0.0c or later (currently using ",
1464           OPENSSL_VERSION_TEXT, ")", NULL));
1465         break;
1466 
1467       case SQL_PASSWD_ERR_PBKDF2_BAD_ROUNDS:
1468         CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
1469           "insufficient number of rounds (", cmd->argv[2], ")", NULL));
1470         break;
1471 
1472       case SQL_PASSWD_ERR_PBKDF2_BAD_LENGTH:
1473         CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "insufficient length (",
1474           cmd->argv[3], ")", NULL));
1475         break;
1476 
1477       case 0:
1478         break;
1479     }
1480 
1481     c = add_config_param(cmd->argv[0], 3, NULL, NULL, NULL);
1482     c->argv[0] = (void *) md;
1483     c->argv[1] = palloc(c->pool, sizeof(int));
1484     *((int *) c->argv[1]) = iter;
1485     c->argv[2] = palloc(c->pool, sizeof(int));
1486     *((int *) c->argv[2]) = len;
1487 
1488   } else if (cmd->argc == 2) {
1489     if (strncasecmp(cmd->argv[1], "sql:/", 5) != 0) {
1490       CONF_ERROR(cmd, "badly formatted parameter");
1491     }
1492 
1493     c = add_config_param(cmd->argv[0], 1, NULL);
1494     c->argv[0] = pstrdup(c->pool, cmd->argv[1]);
1495 
1496   } else {
1497     CONF_ERROR(cmd, "wrong number of parameters");
1498   }
1499 
1500   return PR_HANDLED(cmd);
1501 }
1502 
1503 /* usage: SQLPasswordRounds count */
set_sqlpasswdrounds(cmd_rec * cmd)1504 MODRET set_sqlpasswdrounds(cmd_rec *cmd) {
1505   config_rec *c;
1506   long nrounds;
1507 
1508   CHECK_ARGS(cmd, 1);
1509   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
1510 
1511   nrounds = atol(cmd->argv[1]);
1512   if (nrounds < 1) {
1513     CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "insufficient number of rounds (",
1514       cmd->argv[1], ")", NULL));
1515   }
1516 
1517   c = add_config_param(cmd->argv[0], 1, NULL);
1518   c->argv[0] = palloc(c->pool, sizeof(unsigned int));
1519   *((unsigned long *) c->argv[0]) = nrounds;
1520 
1521   return PR_HANDLED(cmd);
1522 }
1523 
1524 /* usage: SQLPasswordSaltEncoding "base64"|"hex"|"HEX"|"none" */
set_sqlpasswdsaltencoding(cmd_rec * cmd)1525 MODRET set_sqlpasswdsaltencoding(cmd_rec *cmd) {
1526   /* Reuse the parsing code for the SQLPasswordEncoding directive. */
1527   return set_sqlpasswdencoding(cmd);
1528 }
1529 
1530 /* usage: SQLPasswordSaltFile path|"none" [flags] */
set_sqlpasswdsaltfile(cmd_rec * cmd)1531 MODRET set_sqlpasswdsaltfile(cmd_rec *cmd) {
1532   config_rec *c;
1533   register unsigned int i;
1534   unsigned long flags = SQL_PASSWD_SALT_FL_APPEND;
1535 
1536   if (cmd->argc < 2) {
1537     CONF_ERROR(cmd, "wrong number of parameters");
1538   }
1539 
1540   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
1541 
1542   for (i = 2; i < cmd->argc; i++) {
1543     if (strcasecmp(cmd->argv[i], "Append") == 0) {
1544       flags &= ~SQL_PASSWD_SALT_FL_PREPEND;
1545       flags |= SQL_PASSWD_SALT_FL_APPEND;
1546 
1547     } else if (strcasecmp(cmd->argv[i], "Prepend") == 0) {
1548       flags &= ~SQL_PASSWD_SALT_FL_APPEND;
1549       flags |= SQL_PASSWD_SALT_FL_PREPEND;
1550 
1551     } else {
1552       CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown salt flag '",
1553         cmd->argv[i], "'", NULL));
1554     }
1555   }
1556 
1557   c = add_config_param(cmd->argv[0], 2, NULL, NULL);
1558   c->argv[0] = pstrdup(c->pool, cmd->argv[1]);
1559   c->argv[1] = palloc(c->pool, sizeof(unsigned long));
1560   *((unsigned long *) c->argv[1]) = flags;
1561 
1562   return PR_HANDLED(cmd);
1563 }
1564 
1565 /* usage: SQLPasswordScrypt len */
set_sqlpasswdscrypt(cmd_rec * cmd)1566 MODRET set_sqlpasswdscrypt(cmd_rec *cmd) {
1567 #ifdef PR_USE_SODIUM
1568   config_rec *c;
1569   int len;
1570 
1571   CHECK_ARGS(cmd, 1);
1572   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
1573 
1574   len = atoi(cmd->argv[1]);
1575   if (len <= 0) {
1576     CONF_ERROR(cmd, "length must be greater than 0");
1577   }
1578 
1579   c = add_config_param(cmd->argv[0], 1, NULL);
1580   c->argv[0] = palloc(c->pool, sizeof(unsigned int));
1581   *((unsigned int *) c->argv[0]) = len;
1582 
1583   return PR_HANDLED(cmd);
1584 #else
1585   CONF_ERROR(cmd, "requires libsodium support");
1586 #endif /* No libsodium support */
1587 }
1588 
1589 /* usage: SQLPasswordUserSalt "name"|"sql:/named-query" [flags] */
set_sqlpasswdusersalt(cmd_rec * cmd)1590 MODRET set_sqlpasswdusersalt(cmd_rec *cmd) {
1591   config_rec *c;
1592   register unsigned int i;
1593   unsigned long flags = SQL_PASSWD_SALT_FL_APPEND;
1594 
1595   if (cmd->argc < 2) {
1596     CONF_ERROR(cmd, "wrong number of parameters");
1597   }
1598 
1599   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
1600 
1601   if (strcasecmp(cmd->argv[1], "name") != 0 &&
1602       strcasecmp(cmd->argv[1], "uid") != 0 &&
1603       strncasecmp(cmd->argv[1], "sql:/", 5) != 0) {
1604     CONF_ERROR(cmd, "badly formatted parameter");
1605   }
1606 
1607   for (i = 2; i < cmd->argc; i++) {
1608     if (strcasecmp(cmd->argv[i], "Append") == 0) {
1609       flags &= ~SQL_PASSWD_SALT_FL_PREPEND;
1610       flags |= SQL_PASSWD_SALT_FL_APPEND;
1611 
1612     } else if (strcasecmp(cmd->argv[i], "Prepend") == 0) {
1613       flags &= ~SQL_PASSWD_SALT_FL_APPEND;
1614       flags |= SQL_PASSWD_SALT_FL_PREPEND;
1615 
1616     } else {
1617       CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown salt flag '",
1618         cmd->argv[i], "'", NULL));
1619     }
1620   }
1621 
1622   c = add_config_param(cmd->argv[0], 2, NULL, NULL);
1623   c->argv[0] = pstrdup(c->pool, cmd->argv[1]);
1624   c->argv[1] = palloc(c->pool, sizeof(unsigned long));
1625   *((unsigned long *) c->argv[1]) = flags;
1626 
1627   return PR_HANDLED(cmd);
1628 }
1629 
1630 /* Event listeners
1631  */
1632 
sql_passwd_sess_reinit_ev(const void * event_data,void * user_data)1633 static void sql_passwd_sess_reinit_ev(const void *event_data, void *user_data) {
1634   int res;
1635 
1636   /* A HOST command changed the main_server pointer; reinitialize ourselves. */
1637 
1638   pr_event_unregister(&sql_passwd_module, "core.session-reinit",
1639     sql_passwd_sess_reinit_ev);
1640 
1641   sql_passwd_engine = FALSE;
1642   sql_passwd_encoding = SQL_PASSWD_ENC_USE_HEX_LC;
1643   sql_passwd_salt_encoding = SQL_PASSWD_ENC_USE_NONE;
1644   sql_passwd_file_salt = NULL;
1645   sql_passwd_file_salt_len = 0;
1646   sql_passwd_user_salt = NULL;
1647   sql_passwd_user_salt_len = 0;
1648   sql_passwd_file_salt_flags = SQL_PASSWD_SALT_FL_APPEND;
1649   sql_passwd_user_salt_flags = SQL_PASSWD_SALT_FL_APPEND;
1650   sql_passwd_opts = 0UL;
1651   sql_passwd_nrounds = 1;
1652 
1653 #ifdef PR_USE_SODIUM
1654   sql_passwd_scrypt_hash_len = SQL_PASSWD_SCRYPT_DEFAULT_HASH_SIZE;
1655 # ifdef USE_SODIUM_ARGON2
1656   sql_passwd_argon2_hash_len = SQL_PASSWD_ARGON2_DEFAULT_HASH_SIZE;
1657 # endif /* USE_SODIUM_ARGON2 */
1658 #endif /* PR_USE_SODIUM */
1659 
1660   res = sql_passwd_sess_init();
1661   if (res < 0) {
1662     pr_session_disconnect(&sql_passwd_module,
1663       PR_SESS_DISCONNECT_SESSION_INIT_FAILED, NULL);
1664   }
1665 }
1666 
1667 /* Initialization routines
1668  */
1669 
sql_passwd_init(void)1670 static int sql_passwd_init(void) {
1671   OpenSSL_add_all_digests();
1672 
1673 #if defined(PR_SHARED_MODULE)
1674   pr_event_register(&sql_passwd_module, "core.module-unload",
1675     sql_passwd_mod_unload_ev, NULL);
1676 #endif /* PR_SHARED_MODULE */
1677 
1678 #ifdef PR_USE_SODIUM
1679   if (sodium_init() < 0) {
1680     pr_log_pri(PR_LOG_NOTICE, MOD_SQL_PASSWD_VERSION
1681       ": error initializing libsodium");
1682 
1683   } else {
1684     const char *sodium_version;
1685 
1686     sodium_version = sodium_version_string();
1687     pr_log_debug(DEBUG2, MOD_SQL_PASSWD_VERSION ": using libsodium-%s",
1688       sodium_version);
1689   }
1690 #endif /* PR_USE_SODIUM */
1691 
1692   if (sql_register_authtype("bcrypt", sql_passwd_bcrypt) < 0) {
1693     pr_log_pri(PR_LOG_WARNING, MOD_SQL_PASSWD_VERSION
1694       ": unable to register 'bcrypt' SQLAuthType handler: %s", strerror(errno));
1695 
1696   } else {
1697     pr_log_debug(DEBUG6, MOD_SQL_PASSWD_VERSION
1698       ": registered 'bcrypt' SQLAuthType handler");
1699   }
1700 
1701   if (sql_register_authtype("md5", sql_passwd_md5) < 0) {
1702     pr_log_pri(PR_LOG_WARNING, MOD_SQL_PASSWD_VERSION
1703       ": unable to register 'md5' SQLAuthType handler: %s", strerror(errno));
1704 
1705   } else {
1706     pr_log_debug(DEBUG6, MOD_SQL_PASSWD_VERSION
1707       ": registered 'md5' SQLAuthType handler");
1708   }
1709 
1710   if (sql_register_authtype("sha1", sql_passwd_sha1) < 0) {
1711     pr_log_pri(PR_LOG_WARNING, MOD_SQL_PASSWD_VERSION
1712       ": unable to register 'sha1' SQLAuthType handler: %s", strerror(errno));
1713 
1714   } else {
1715     pr_log_debug(DEBUG6, MOD_SQL_PASSWD_VERSION
1716       ": registered 'sha1' SQLAuthType handler");
1717   }
1718 
1719   if (sql_register_authtype("sha256", sql_passwd_sha256) < 0) {
1720     pr_log_pri(PR_LOG_WARNING, MOD_SQL_PASSWD_VERSION
1721       ": unable to register 'sha256' SQLAuthType handler: %s", strerror(errno));
1722 
1723   } else {
1724     pr_log_debug(DEBUG6, MOD_SQL_PASSWD_VERSION
1725       ": registered 'sha256' SQLAuthType handler");
1726   }
1727 
1728   if (sql_register_authtype("sha512", sql_passwd_sha512) < 0) {
1729     pr_log_pri(PR_LOG_WARNING, MOD_SQL_PASSWD_VERSION
1730       ": unable to register 'sha512' SQLAuthType handler: %s", strerror(errno));
1731 
1732   } else {
1733     pr_log_debug(DEBUG6, MOD_SQL_PASSWD_VERSION
1734       ": registered 'sha512' SQLAuthType handler");
1735   }
1736 
1737   if (sql_register_authtype("pbkdf2", sql_passwd_pbkdf2) < 0) {
1738     pr_log_pri(PR_LOG_WARNING, MOD_SQL_PASSWD_VERSION
1739       ": unable to register 'pbkdf2' SQLAuthType handler: %s", strerror(errno));
1740 
1741   } else {
1742     pr_log_debug(DEBUG6, MOD_SQL_PASSWD_VERSION
1743       ": registered 'pbkdf2' SQLAuthType handler");
1744   }
1745 
1746 #ifdef PR_USE_SODIUM
1747   if (sql_register_authtype("scrypt", sql_passwd_scrypt) < 0) {
1748     pr_log_pri(PR_LOG_WARNING, MOD_SQL_PASSWD_VERSION
1749       ": unable to register 'scrypt' SQLAuthType handler: %s", strerror(errno));
1750 
1751   } else {
1752     pr_log_debug(DEBUG6, MOD_SQL_PASSWD_VERSION
1753       ": registered 'scrypt' SQLAuthType handler");
1754   }
1755 
1756   if (sql_register_authtype("argon2", sql_passwd_argon2) < 0) {
1757     pr_log_pri(PR_LOG_WARNING, MOD_SQL_PASSWD_VERSION
1758       ": unable to register 'argon2' SQLAuthType handler: %s", strerror(errno));
1759 
1760   } else {
1761     pr_log_debug(DEBUG6, MOD_SQL_PASSWD_VERSION
1762       ": registered 'argon2' SQLAuthType handler");
1763   }
1764 #endif /* PR_USE_SODIUM */
1765 
1766   return 0;
1767 }
1768 
sql_passwd_sess_init(void)1769 static int sql_passwd_sess_init(void) {
1770   config_rec *c;
1771 
1772   pr_event_register(&sql_passwd_module, "core.session-reinit",
1773     sql_passwd_sess_reinit_ev, NULL);
1774 
1775   c = find_config(main_server->conf, CONF_PARAM, "SQLPasswordEngine", FALSE);
1776   if (c != NULL) {
1777     sql_passwd_engine = *((int *) c->argv[0]);
1778   }
1779 
1780   if (sql_passwd_engine == FALSE) {
1781     return 0;
1782   }
1783 
1784   c = find_config(main_server->conf, CONF_PARAM, "SQLPasswordCost", FALSE);
1785   if (c != NULL) {
1786     sql_passwd_cost = *((unsigned int *) c->argv[0]);
1787   }
1788 
1789   c = find_config(main_server->conf, CONF_PARAM, "SQLPasswordEncoding", FALSE);
1790   if (c != NULL) {
1791     sql_passwd_encoding = *((unsigned int *) c->argv[0]);
1792   }
1793 
1794   c = find_config(main_server->conf, CONF_PARAM, "SQLPasswordOptions", FALSE);
1795   while (c != NULL) {
1796     unsigned long opts;
1797 
1798     pr_signals_handle();
1799 
1800     opts = *((unsigned long *) c->argv[0]);
1801     sql_passwd_opts |= opts;
1802 
1803     c = find_config_next(c, c->next, CONF_PARAM, "SQLPasswordOptions", FALSE);
1804   }
1805 
1806   c = find_config(main_server->conf, CONF_PARAM, "SQLPasswordSaltEncoding",
1807     FALSE);
1808   if (c != NULL) {
1809     sql_passwd_salt_encoding = *((unsigned int *) c->argv[0]);
1810   }
1811 
1812   c = find_config(main_server->conf, CONF_PARAM, "SQLPasswordSaltFile", FALSE);
1813   if (c != NULL) {
1814     char *path;
1815     unsigned long salt_flags;
1816 
1817     path = c->argv[0];
1818     salt_flags = *((unsigned long *) c->argv[1]);
1819 
1820     if (strcasecmp(path, "none") != 0) {
1821       int fd, xerrno = 0;
1822 
1823       PRIVS_ROOT
1824       fd = open(path, O_RDONLY|O_NONBLOCK);
1825       if (fd < 0) {
1826         xerrno = errno;
1827       }
1828       PRIVS_RELINQUISH
1829 
1830       if (fd >= 0) {
1831         char *file_salt = NULL;
1832         size_t file_salt_len = 0;
1833         int flags;
1834 
1835         /* XXX Rather than using a fixed size, this should be a dynamically
1836          * allocated buffer, of st.st_blksize bytes, for optimal disk IO.
1837          */
1838         char buf[512];
1839         ssize_t nread;
1840 
1841         /* Set this descriptor for blocking. */
1842         flags = fcntl(fd, F_GETFL);
1843         if (fcntl(fd, F_SETFL, flags & (U32BITS^O_NONBLOCK)) < 0) {
1844           pr_log_debug(DEBUG3, MOD_SQL_PASSWD_VERSION
1845             ": error setting blocking mode on SQLPasswordSaltFile '%s': %s",
1846             path, strerror(errno));
1847         }
1848 
1849         nread = read(fd, buf, sizeof(buf));
1850         while (nread > 0) {
1851           pr_signals_handle();
1852 
1853           if (file_salt == NULL) {
1854             /* If the very last byte in the buffer is a newline, trim it. */
1855             if (buf[nread-1] == '\n') {
1856               buf[nread-1] = '\0';
1857               nread--;
1858             }
1859 
1860             file_salt_len = nread;
1861             file_salt = palloc(session.pool, file_salt_len);
1862             memcpy(file_salt, buf, nread);
1863 
1864           } else {
1865             char *ptr, *tmp;
1866 
1867             /* Allocate a larger buffer for the salt. */
1868             ptr = tmp = palloc(session.pool, file_salt_len + nread);
1869             memcpy(tmp, file_salt, file_salt_len);
1870             tmp += file_salt_len;
1871 
1872             memcpy(tmp, buf, nread);
1873             file_salt_len += nread;
1874             file_salt = ptr;
1875           }
1876 
1877           nread = read(fd, buf, sizeof(buf));
1878         }
1879 
1880         if (nread < 0) {
1881           pr_log_debug(DEBUG1, MOD_SQL_PASSWD_VERSION
1882             ": error reading salt data from SQLPasswordSaltFile '%s': %s",
1883             path, strerror(errno));
1884           file_salt = NULL;
1885         }
1886 
1887         (void) close(fd);
1888 
1889         if (file_salt != NULL) {
1890           /* If the very last byte in the buffer is a newline, trim it.  This
1891            * is to deal with cases where the SaltFile may have been written
1892            * with an editor (e.g. vi) which automatically adds a trailing
1893            * newline.
1894            */
1895           if (file_salt[file_salt_len-1] == '\n') {
1896             file_salt[file_salt_len-1] = '\0';
1897             file_salt_len--;
1898           }
1899 
1900           sql_passwd_file_salt = sql_passwd_decode(session.pool,
1901             sql_passwd_salt_encoding, file_salt, file_salt_len,
1902             &sql_passwd_file_salt_len);
1903           if (sql_passwd_file_salt == NULL) {
1904             pr_log_debug(DEBUG0, MOD_SQL_PASSWD_VERSION
1905               ": error decoding salt from SQLPasswordSaltFile '%s': %s", path,
1906               strerror(errno));
1907 
1908           } else {
1909             sql_passwd_file_salt_flags = salt_flags;
1910           }
1911         }
1912 
1913       } else {
1914         pr_log_debug(DEBUG1, MOD_SQL_PASSWD_VERSION
1915           ": unable to read SQLPasswordSaltFile '%s': %s", path,
1916           strerror(xerrno));
1917       }
1918     }
1919   }
1920 
1921 #ifdef PR_USE_SODIUM
1922   c = find_config(main_server->conf, CONF_PARAM, "SQLPasswordScrypt", FALSE);
1923   if (c != NULL) {
1924     sql_passwd_scrypt_hash_len = *((unsigned int *) c->argv[0]);
1925   }
1926 
1927 # ifdef USE_SODIUM_ARGON2
1928   c = find_config(main_server->conf, CONF_PARAM, "SQLPasswordArgon2", FALSE);
1929   if (c != NULL) {
1930     sql_passwd_argon2_hash_len = *((unsigned int *) c->argv[0]);
1931   }
1932 # endif /* USE_SODIUM_ARGON2 */
1933 #endif /* PR_USE_SODIUM */
1934 
1935   return 0;
1936 }
1937 
1938 /* Module API tables
1939  */
1940 
1941 static conftable sql_passwd_conftab[] = {
1942   { "SQLPasswordArgon2",	set_sqlpasswdargon2,		NULL },
1943   { "SQLPasswordCost",		set_sqlpasswdcost,		NULL },
1944   { "SQLPasswordEncoding",	set_sqlpasswdencoding,		NULL },
1945   { "SQLPasswordEngine",	set_sqlpasswdengine,		NULL },
1946   { "SQLPasswordOptions",	set_sqlpasswdoptions,		NULL },
1947   { "SQLPasswordPBKDF2",	set_sqlpasswdpbkdf2,		NULL },
1948   { "SQLPasswordRounds",	set_sqlpasswdrounds,		NULL },
1949   { "SQLPasswordSaltEncoding",	set_sqlpasswdsaltencoding,	NULL },
1950   { "SQLPasswordSaltFile",	set_sqlpasswdsaltfile,		NULL },
1951   { "SQLPasswordScrypt",	set_sqlpasswdscrypt,		NULL },
1952   { "SQLPasswordUserSalt",	set_sqlpasswdusersalt,		NULL },
1953 
1954   { NULL, NULL, NULL }
1955 };
1956 
1957 static cmdtable sql_passwd_cmdtab[] = {
1958   { PRE_CMD,	C_PASS, G_NONE,	sql_passwd_pre_pass,	FALSE,	FALSE },
1959 
1960   { 0, NULL }
1961 };
1962 
1963 module sql_passwd_module = {
1964 
1965   /* Always NULL */
1966   NULL, NULL,
1967 
1968   /* Module API version */
1969   0x20,
1970 
1971   /* Module name */
1972   "sql_passwd",
1973 
1974   /* Module configuration directive table */
1975   sql_passwd_conftab,
1976 
1977   /* Module command handler table */
1978   sql_passwd_cmdtab,
1979 
1980   /* Module auth handler table */
1981   NULL,
1982 
1983   /* Module initialization */
1984   sql_passwd_init,
1985 
1986   /* Session initialization */
1987   sql_passwd_sess_init,
1988 
1989   /* Module version */
1990   MOD_SQL_PASSWD_VERSION
1991 };
1992