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