source: src/router/proftpd/contrib/mod_sql_passwd.c @ 17876

Last change on this file since 17876 was 17876, checked in by BrainSlayer, 20 months ago

update proftp

File size: 25.6 KB
Line 
1/*
2 * ProFTPD: mod_sql_passwd -- Various SQL password handlers
3 * Copyright (c) 2009-2011 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 * $Id: mod_sql_passwd.c,v 1.16 2011/05/23 20:56:40 castaglia Exp $
25 */
26
27#include "conf.h"
28#include "privs.h"
29#include "mod_sql.h"
30
31#define MOD_SQL_PASSWD_VERSION          "mod_sql_passwd/0.4"
32
33/* Make sure the version of proftpd is as necessary. */
34#if PROFTPD_VERSION_NUMBER < 0x0001030302
35# error "ProFTPD 1.3.3rc2 or later required"
36#endif
37
38#if !defined(HAVE_OPENSSL) && !defined(PR_USE_OPENSSL)
39# error "OpenSSL support required (--enable-openssl)"
40#else
41# include <openssl/evp.h>
42#endif
43
44module sql_passwd_module;
45
46static int sql_passwd_engine = FALSE;
47
48#define SQL_PASSWD_USE_BASE64           1
49#define SQL_PASSWD_USE_HEX_LC           2
50#define SQL_PASSWD_USE_HEX_UC           3
51static unsigned int sql_passwd_encoding = SQL_PASSWD_USE_HEX_LC;
52
53static char *sql_passwd_salt = NULL;
54static size_t sql_passwd_salt_len = 0;
55
56#define SQL_PASSWD_SALT_FL_APPEND       0x0001
57#define SQL_PASSWD_SALT_FL_PREPEND      0x0002
58static unsigned long sql_passwd_salt_flags = SQL_PASSWD_SALT_FL_APPEND;
59
60#define SQL_PASSWD_OPT_HASH_SALT                0x0001
61#define SQL_PASSWD_OPT_ENCODE_SALT              0x0002
62#define SQL_PASSWD_OPT_HASH_PASSWORD            0x0004
63#define SQL_PASSWD_OPT_ENCODE_PASSWORD          0x0008
64
65static unsigned long sql_passwd_opts = 0UL;
66
67static unsigned int sql_passwd_nrounds = 1;
68
69static const char *trace_channel = "sql_passwd";
70
71static cmd_rec *sql_passwd_cmd_create(pool *parent_pool, int argc, ...) {
72  pool *cmd_pool = NULL;
73  cmd_rec *cmd = NULL;
74  register unsigned int i = 0;
75  va_list argp;
76 
77  cmd_pool = make_sub_pool(parent_pool);
78  cmd = (cmd_rec *) pcalloc(cmd_pool, sizeof(cmd_rec));
79  cmd->pool = cmd_pool;
80 
81  cmd->argc = argc;
82  cmd->argv = (char **) pcalloc(cmd->pool, argc * sizeof(char *));
83
84  /* Hmmm... */
85  cmd->tmp_pool = cmd->pool;
86
87  va_start(argp, argc);
88  for (i = 0; i < argc; i++)
89    cmd->argv[i] = va_arg(argp, char *);
90  va_end(argp);
91
92  return cmd;
93}
94
95static char *sql_passwd_get_str(pool *p, char *str) {
96  cmdtable *cmdtab;
97  cmd_rec *cmd;
98  modret_t *res;
99
100  if (strlen(str) == 0)
101    return str;
102
103  /* Find the cmdtable for the sql_escapestr command. */
104  cmdtab = pr_stash_get_symbol(PR_SYM_HOOK, "sql_escapestr", NULL, NULL);
105  if (cmdtab == NULL) {
106    pr_log_debug(DEBUG2, MOD_SQL_PASSWD_VERSION
107      ": unable to find SQL hook symbol 'sql_escapestr'");
108    return str;
109  }
110
111  cmd = sql_passwd_cmd_create(p, 1, pr_str_strip(p, str));
112
113  /* Call the handler. */
114  res = pr_module_call(cmdtab->m, cmdtab->handler, cmd);
115
116  /* Check the results. */
117  if (MODRET_ISDECLINED(res) ||
118      MODRET_ISERROR(res)) {
119    pr_log_debug(DEBUG0, MOD_SQL_PASSWD_VERSION
120      ": error executing 'sql_escapestring'");
121    return str;
122  }
123
124  return res->data;
125}
126
127static char *sql_passwd_encode(pool *p, unsigned char *data, size_t data_len) {
128  EVP_ENCODE_CTX base64_ctxt;
129  char *buf;
130
131  /* According to RATS, the output buffer for EVP_EncodeBlock() needs to be
132   * 4/3 the size of the input buffer (which is usually EVP_MAX_MD_SIZE).
133   * Let's make it easy, and use an output buffer that's twice the size of the
134   * input buffer.
135   */
136  buf = pcalloc(p, (2 * data_len) + 1);
137
138  switch (sql_passwd_encoding) {
139    case SQL_PASSWD_USE_BASE64:
140      EVP_EncodeInit(&base64_ctxt);
141      EVP_EncodeBlock((unsigned char *) buf, data, (int) data_len);
142      break;
143
144    case SQL_PASSWD_USE_HEX_LC: {
145      register unsigned int i;
146
147      for (i = 0; i < data_len; i++) {
148        sprintf((char *) &(buf[i*2]), "%02x", data[i]);
149      }
150
151      break;
152    }
153
154    case SQL_PASSWD_USE_HEX_UC: {
155      register unsigned int i;
156
157      for (i = 0; i < data_len; i++) {
158        sprintf((char *) &(buf[i*2]), "%02X", data[i]);
159      }
160
161      break;
162    }
163
164    default:
165      errno = EINVAL;
166      return NULL;
167  }
168
169  return buf;
170}
171
172/* This may look a little weird, with the data, prefix, and suffix arguments.
173 * But they are used to handle the case where we are hashing data with
174 * a salt (either as a prefix or as a suffix), and where we are hashing
175 * already hashed data.
176 */
177static unsigned char *sql_passwd_hash(pool *p, const EVP_MD *md,
178    unsigned char *data, size_t data_len,
179    unsigned char *prefix, size_t prefix_len,
180    unsigned char *suffix, size_t suffix_len,
181    unsigned int *hash_len) {
182
183  EVP_MD_CTX md_ctx;
184  unsigned char *hash;
185
186  hash = palloc(p, EVP_MAX_MD_SIZE);
187
188  EVP_DigestInit(&md_ctx, md);
189
190  if (prefix != NULL) {
191    EVP_DigestUpdate(&md_ctx, prefix, prefix_len);
192  }
193
194  EVP_DigestUpdate(&md_ctx, data, data_len);
195
196  if (suffix != NULL) {
197    EVP_DigestUpdate(&md_ctx, suffix, suffix_len);
198  }
199
200  EVP_DigestFinal(&md_ctx, hash, hash_len);
201  return hash;
202}
203
204static modret_t *sql_passwd_auth(cmd_rec *cmd, const char *plaintext,
205    const char *ciphertext, const char *digest) {
206  const EVP_MD *md;
207  unsigned char *hash = NULL, *data = NULL, *prefix = NULL, *suffix = NULL;
208  size_t data_len = 0, prefix_len = 0, suffix_len = 0;
209  unsigned int hash_len = 0;
210
211  /* Temporary copy of the ciphertext string */
212  char *copytext;
213  const char *encodedtext;
214
215  if (!sql_passwd_engine) {
216    return PR_ERROR_INT(cmd, PR_AUTH_ERROR);
217  }
218
219  /* We need a copy of the ciphertext. */
220  copytext = pstrdup(cmd->tmp_pool, ciphertext);
221
222  OpenSSL_add_all_digests();
223
224  md = EVP_get_digestbyname(digest);
225  if (md == NULL) {
226    sql_log(DEBUG_WARN, "no such digest '%s' supported", digest);
227    return PR_ERROR_INT(cmd, PR_AUTH_BADPWD);
228  }
229
230  /* If a salt is configured, do we prepend the salt as a prefix (i.e. throw
231   * it into the digest before the user-supplied password) or append it as a
232   * suffix?
233   */
234
235  if (sql_passwd_salt_len > 0 &&
236      (sql_passwd_salt_flags & SQL_PASSWD_SALT_FL_PREPEND)) {
237
238    /* If we have salt data, add it to the mix. */
239
240    if (!(sql_passwd_opts & SQL_PASSWD_OPT_HASH_SALT)) {
241      prefix = (unsigned char *) sql_passwd_salt;
242      prefix_len = sql_passwd_salt_len;
243
244      pr_trace_msg(trace_channel, 9,
245        "prepending %lu bytes of salt data", (unsigned long) prefix_len);
246
247    } else {
248      unsigned int salt_hashlen = 0;
249
250      prefix = sql_passwd_hash(cmd->tmp_pool, md,
251        (unsigned char *) sql_passwd_salt, sql_passwd_salt_len,
252        NULL, 0, NULL, 0, &salt_hashlen);
253      prefix_len = salt_hashlen;
254
255      if (sql_passwd_opts & SQL_PASSWD_OPT_ENCODE_SALT) {
256        prefix = (unsigned char *) sql_passwd_encode(cmd->tmp_pool,
257          (unsigned char *) prefix, prefix_len);
258        prefix_len = strlen((char *) prefix);
259      }
260
261      pr_trace_msg(trace_channel, 9,
262        "prepending %lu bytes of %s-hashed salt data (%s)",
263        (unsigned long) prefix_len, digest, prefix);
264    }
265  }
266
267  if (!(sql_passwd_opts & SQL_PASSWD_OPT_HASH_PASSWORD)) {
268    data = (unsigned char *) plaintext;
269    data_len = strlen(plaintext);
270
271  } else {
272    /* Note: We will only honor a HashEncodePassword option IFF there is
273     * also salt data present.  Otherwise, it is equivalent to another
274     * round of processing, which defeats the principle of least surprise.
275     */
276    if (sql_passwd_salt_len == 0 &&
277        (sql_passwd_opts & SQL_PASSWD_OPT_HASH_PASSWORD) &&
278        (sql_passwd_opts & SQL_PASSWD_OPT_ENCODE_PASSWORD)) {
279      pr_trace_msg(trace_channel, 4, "%s",
280        "no salt present, ignoring HashEncodePassword SQLPasswordOption");
281      data = (unsigned char *) plaintext;
282      data_len = strlen(plaintext);
283
284    } else {
285      unsigned int salt_hashlen = 0;
286
287      data = sql_passwd_hash(cmd->tmp_pool, md,
288        (unsigned char *) plaintext, strlen(plaintext),
289        NULL, 0, NULL, 0, &salt_hashlen);
290      data_len = salt_hashlen;
291
292      if (sql_passwd_opts & SQL_PASSWD_OPT_ENCODE_PASSWORD) {
293        data = (unsigned char *) sql_passwd_encode(cmd->tmp_pool,
294          (unsigned char *) data, data_len);
295        data_len = strlen((char *) data);
296      }
297    }
298  }
299
300  if (sql_passwd_salt_len > 0 &&
301      (sql_passwd_salt_flags & SQL_PASSWD_SALT_FL_APPEND)) {
302    /* If we have salt data, add it to the mix. */
303
304    if (!(sql_passwd_opts & SQL_PASSWD_OPT_HASH_SALT)) {
305      suffix = (unsigned char *) sql_passwd_salt;
306      suffix_len = sql_passwd_salt_len;
307
308      pr_trace_msg(trace_channel, 9,
309        "appending %lu bytes of salt data", (unsigned long) suffix_len);
310
311    } else {
312      unsigned int salt_hashlen = 0;
313
314      suffix = sql_passwd_hash(cmd->tmp_pool, md,
315        (unsigned char *) sql_passwd_salt, sql_passwd_salt_len,
316        NULL, 0, NULL, 0, &salt_hashlen);
317      suffix_len = salt_hashlen;
318
319      if (sql_passwd_opts & SQL_PASSWD_OPT_ENCODE_SALT) {
320        suffix = (unsigned char *) sql_passwd_encode(cmd->tmp_pool,
321          (unsigned char *) suffix, suffix_len);
322        suffix_len = strlen((char *) suffix);
323      }
324
325      pr_trace_msg(trace_channel, 9,
326        "appending %lu bytes of %s-hashed salt data",
327        (unsigned long) suffix_len, digest);
328    }
329  }
330
331  hash = sql_passwd_hash(cmd->tmp_pool, md, data, data_len, prefix, prefix_len,
332    suffix, suffix_len, &hash_len);
333
334  encodedtext = sql_passwd_encode(cmd->tmp_pool, hash, hash_len);
335  if (encodedtext == NULL) {
336    sql_log(DEBUG_WARN, "unsupported SQLPasswordEncoding configured");
337    return PR_ERROR_INT(cmd, PR_AUTH_ERROR);
338  }
339
340  /* The case of nrounds == 1 is a special case, as that is when the salt
341   * data is processed.  Any additional rounds are simply hashing and
342   * encoding the resulting data, over and over.
343   */
344  if (sql_passwd_nrounds > 1) {
345    register unsigned int i;
346    unsigned int nrounds = sql_passwd_nrounds - 1;
347
348    pr_trace_msg(trace_channel, 9,
349      "transforming the data for another %u %s", nrounds,
350      nrounds != 1 ? "rounds" : "round");
351
352    for (i = 0; i < nrounds; i++) {
353      pr_signals_handle();
354
355      hash = sql_passwd_hash(cmd->tmp_pool, md, (unsigned char *) encodedtext,
356        strlen(encodedtext), NULL, 0, NULL, 0, &hash_len);
357      encodedtext = sql_passwd_encode(cmd->tmp_pool, hash, hash_len);
358
359      pr_trace_msg(trace_channel, 15, "data after round %u: '%s'", i + 1,
360        encodedtext);
361    }
362  }
363
364  if (strcmp((char *) encodedtext, copytext) == 0) {
365    return PR_HANDLED(cmd);
366
367  } else {
368    pr_trace_msg(trace_channel, 9, "expected '%s', got '%s'", copytext,
369      encodedtext);
370
371    pr_log_debug(DEBUG9, MOD_SQL_PASSWD_VERSION ": expected '%s', got '%s'",
372      copytext, encodedtext);
373  }
374
375  return PR_ERROR_INT(cmd, PR_AUTH_BADPWD);
376}
377
378static modret_t *sql_passwd_md5(cmd_rec *cmd, const char *plaintext,
379    const char *ciphertext) {
380  return sql_passwd_auth(cmd, plaintext, ciphertext, "md5");
381}
382
383static modret_t *sql_passwd_sha1(cmd_rec *cmd, const char *plaintext,
384    const char *ciphertext) {
385  return sql_passwd_auth(cmd, plaintext, ciphertext, "sha1");
386}
387
388static modret_t *sql_passwd_sha256(cmd_rec *cmd, const char *plaintext,
389    const char *ciphertext) {
390  return sql_passwd_auth(cmd, plaintext, ciphertext, "sha256");
391}
392
393static modret_t *sql_passwd_sha512(cmd_rec *cmd, const char *plaintext,
394    const char *ciphertext) {
395  return sql_passwd_auth(cmd, plaintext, ciphertext, "sha512");
396}
397
398/* Event handlers
399 */
400
401#if defined(PR_SHARED_MODULE)
402static void sql_passwd_mod_unload_ev(const void *event_data, void *user_data) {
403  if (strcmp("mod_sql_passwd.c", (const char *) event_data) == 0) {
404    sql_unregister_authtype("md5");
405    sql_unregister_authtype("sha1");
406    sql_unregister_authtype("sha256");
407    sql_unregister_authtype("sha512");
408
409    pr_event_unregister(&sql_passwd_module, NULL, NULL);
410  }
411}
412#endif /* PR_SHARED_MODULE */
413
414/* Command handlers
415 */
416
417MODRET sql_passwd_pre_pass(cmd_rec *cmd) {
418  config_rec *c;
419
420  if (!sql_passwd_engine) {
421    return PR_DECLINED(cmd);
422  }
423
424  c = find_config(main_server->conf, CONF_PARAM, "SQLPasswordRounds", FALSE);
425  if (c) {
426    sql_passwd_nrounds = *((unsigned int *) c->argv[0]);
427  }
428
429  c = find_config(main_server->conf, CONF_PARAM, "SQLPasswordUserSalt", FALSE);
430  if (c) {
431    char *key;
432    unsigned long salt_flags;
433
434    key = c->argv[0];
435    salt_flags = *((unsigned long *) c->argv[1]);
436
437    if (strcasecmp(key, "name") == 0) {
438      char *user;
439
440      user = pr_table_get(session.notes, "mod_auth.orig-user", NULL);
441      sql_passwd_salt = user;
442      sql_passwd_salt_len = strlen(user);
443
444    } else if (strncasecmp(key, "sql:/", 5) == 0) {
445      char *named_query, *ptr, *user, **values;
446      cmdtable *sql_cmdtab;
447      cmd_rec *sql_cmd;
448      modret_t *sql_res;
449      array_header *sql_data;
450
451      ptr = key + 5;
452      named_query = pstrcat(cmd->tmp_pool, "SQLNamedQuery_", ptr, NULL);
453
454      c = find_config(main_server->conf, CONF_PARAM, named_query, FALSE);
455      if (c == NULL) {
456        pr_log_debug(DEBUG3, MOD_SQL_PASSWD_VERSION
457          ": unable to resolve SQLNamedQuery '%s'", ptr);
458        return PR_DECLINED(cmd);
459      }
460
461      sql_cmdtab = pr_stash_get_symbol(PR_SYM_HOOK, "sql_lookup", NULL, NULL);
462      if (sql_cmdtab == NULL) {
463        pr_log_debug(DEBUG3, MOD_SQL_PASSWD_VERSION
464          ": unable to find SQL hook symbol 'sql_lookup'");
465        return PR_DECLINED(cmd);
466      }
467
468      user = pr_table_get(session.notes, "mod_auth.orig-user", NULL);
469
470      sql_cmd = sql_passwd_cmd_create(cmd->tmp_pool, 3, "sql_lookup", ptr,
471        sql_passwd_get_str(cmd->tmp_pool, user));
472
473      /* Call the handler. */
474      sql_res = pr_module_call(sql_cmdtab->m, sql_cmdtab->handler, sql_cmd);
475      if (sql_res == NULL ||
476          MODRET_ISERROR(sql_res)) {
477        pr_log_debug(DEBUG0, MOD_SQL_PASSWD_VERSION
478          ": error processing SQLNamedQuery '%s'", ptr);
479        return PR_DECLINED(cmd);
480      }
481
482      sql_data = (array_header *) sql_res->data;
483
484      if (sql_data->nelts != 1) {
485        pr_log_debug(DEBUG0, MOD_SQL_PASSWD_VERSION
486          ": SQLNamedQuery '%s' returned wrong number of rows (%d)", ptr,
487          sql_data->nelts);
488        return PR_DECLINED(cmd);
489      }
490
491      values = sql_data->elts;
492      sql_passwd_salt = pstrdup(session.pool, values[0]);
493      sql_passwd_salt_len = strlen(values[0]);
494     
495    } else {
496      return PR_DECLINED(cmd);
497    }
498
499    sql_passwd_salt_flags = salt_flags;
500  }
501
502  return PR_DECLINED(cmd);
503}
504
505/* Configuration handlers
506 */
507
508/* usage: SQLPasswordEncoding "base64"|"hex"|"HEX" */
509MODRET set_sqlpasswdencoding(cmd_rec *cmd) {
510  unsigned int encoding;
511  config_rec *c = NULL;
512
513  CHECK_ARGS(cmd, 1);
514  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
515
516  if (strcmp(cmd->argv[1], "base64") == 0) {
517    encoding = SQL_PASSWD_USE_BASE64;
518
519  } else if (strcmp(cmd->argv[1], "hex") == 0) {
520    encoding = SQL_PASSWD_USE_HEX_LC;
521
522  } else if (strcmp(cmd->argv[1], "HEX") == 0) {
523    encoding = SQL_PASSWD_USE_HEX_UC;
524
525  } else {
526    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unsupported encoding '",
527      cmd->argv[1], "' configured", NULL));
528  }
529 
530  c = add_config_param(cmd->argv[0], 1, NULL);
531  c->argv[0] = pcalloc(c->pool, sizeof(unsigned int));
532  *((unsigned int *) c->argv[0]) = encoding;
533
534  return PR_HANDLED(cmd);
535}
536
537/* usage: SQLPasswordEngine on|off */
538MODRET set_sqlpasswdengine(cmd_rec *cmd) {
539  int bool = -1;
540  config_rec *c = NULL;
541
542  CHECK_ARGS(cmd, 1);
543  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
544
545  bool = get_boolean(cmd, 1);
546  if (bool == -1)
547    CONF_ERROR(cmd, "expected Boolean parameter");
548
549  c = add_config_param(cmd->argv[0], 1, NULL);
550  c->argv[0] = pcalloc(c->pool, sizeof(int));
551  *((int *) c->argv[0]) = bool;
552
553  return PR_HANDLED(cmd);
554}
555
556/* usage: SQLPasswordOptions opt1 ... optN */
557MODRET set_sqlpasswdoptions(cmd_rec *cmd) {
558  config_rec *c;
559  unsigned long opts = 0UL;
560  register unsigned int i;
561
562  if (cmd->argc < 2) {
563    CONF_ERROR(cmd, "wrong number of parameters");
564  }
565
566  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
567
568  for (i = 1; i < cmd->argc; i++) {
569    if (strcasecmp(cmd->argv[i], "HashPassword") == 0) {
570      opts |= SQL_PASSWD_OPT_HASH_PASSWORD;
571
572    } else if (strcasecmp(cmd->argv[i], "HashSalt") == 0) {
573      opts |= SQL_PASSWD_OPT_HASH_SALT;
574
575    } else if (strcasecmp(cmd->argv[i], "HashEncodePassword") == 0) {
576      opts |= SQL_PASSWD_OPT_HASH_PASSWORD;
577      opts |= SQL_PASSWD_OPT_ENCODE_PASSWORD;
578
579    } else if (strcasecmp(cmd->argv[i], "HashEncodeSalt") == 0) {
580      opts |= SQL_PASSWD_OPT_HASH_SALT;
581      opts |= SQL_PASSWD_OPT_ENCODE_SALT;
582
583    } else {
584      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown SQLPasswordOption '",
585        cmd->argv[i], "'", NULL));
586    }
587  }
588
589  c = add_config_param(cmd->argv[0], 1, NULL);
590  c->argv[0] = palloc(c->pool, sizeof(unsigned long));
591  *((unsigned long *) c->argv[0]) = opts;
592
593  return PR_HANDLED(cmd);
594}
595
596/* usage: SQLPasswordSaltFile path|"none" [flags] */
597MODRET set_sqlpasswdsaltfile(cmd_rec *cmd) {
598  config_rec *c;
599  register unsigned int i;
600  unsigned long flags = SQL_PASSWD_SALT_FL_APPEND;
601
602  if (cmd->argc < 2) {
603    CONF_ERROR(cmd, "wrong number of parameters");
604  }
605
606  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
607
608  for (i = 2; i < cmd->argc; i++) {
609    if (strcasecmp(cmd->argv[i], "Append") == 0) {
610      flags &= ~SQL_PASSWD_SALT_FL_PREPEND;
611      flags |= SQL_PASSWD_SALT_FL_APPEND;
612 
613    } else if (strcasecmp(cmd->argv[i], "Prepend") == 0) {
614      flags &= ~SQL_PASSWD_SALT_FL_APPEND;
615      flags |= SQL_PASSWD_SALT_FL_PREPEND;
616
617    } else {
618      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown salt flag '",
619        cmd->argv[i], "'", NULL));
620    }
621  }
622
623  c = add_config_param(cmd->argv[0], 2, NULL, NULL);
624  c->argv[0] = pstrdup(c->pool, cmd->argv[1]);
625  c->argv[1] = palloc(c->pool, sizeof(unsigned long));
626  *((unsigned long *) c->argv[1]) = flags;
627
628  return PR_HANDLED(cmd);
629}
630
631/* usage: SQLPasswordRounds count */
632MODRET set_sqlpasswdrounds(cmd_rec *cmd) {
633  config_rec *c;
634  int nrounds;
635
636  CHECK_ARGS(cmd, 1);
637  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
638
639  nrounds = atoi(cmd->argv[1]);
640  if (nrounds < 1) {
641    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "insufficient number of rounds (",
642      cmd->argv[1], ")", NULL));
643  }
644
645  c = add_config_param(cmd->argv[0], 1, NULL);
646  c->argv[0] = palloc(c->pool, sizeof(unsigned int));
647  *((unsigned int *) c->argv[0]) = nrounds;
648
649  return PR_HANDLED(cmd);
650}
651
652/* usage: SQLPasswordUserSalt "name"|"sql:/named-query" [flags]
653 */
654MODRET set_sqlpasswdusersalt(cmd_rec *cmd) {
655  config_rec *c;
656  register unsigned int i;
657  unsigned long flags = SQL_PASSWD_SALT_FL_APPEND;
658
659  if (cmd->argc < 2) {
660    CONF_ERROR(cmd, "wrong number of parameters");
661  }
662
663  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
664
665  if (strcasecmp(cmd->argv[1], "name") != 0 &&
666      strcasecmp(cmd->argv[1], "uid") != 0 &&
667      strncasecmp(cmd->argv[1], "sql:/", 5) != 0) {
668    CONF_ERROR(cmd, "badly formatted parameter");
669  }
670
671  for (i = 2; i < cmd->argc; i++) {
672    if (strcasecmp(cmd->argv[i], "Append") == 0) {
673      flags &= ~SQL_PASSWD_SALT_FL_PREPEND;
674      flags |= SQL_PASSWD_SALT_FL_APPEND;
675 
676    } else if (strcasecmp(cmd->argv[i], "Prepend") == 0) {
677      flags &= ~SQL_PASSWD_SALT_FL_APPEND;
678      flags |= SQL_PASSWD_SALT_FL_PREPEND;
679
680    } else {
681      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown salt flag '",
682        cmd->argv[i], "'", NULL));
683    }
684  }
685
686  c = add_config_param(cmd->argv[0], 2, NULL, NULL);
687  c->argv[0] = pstrdup(c->pool, cmd->argv[1]);
688  c->argv[1] = palloc(c->pool, sizeof(unsigned long));
689  *((unsigned long *) c->argv[1]) = flags;
690
691  return PR_HANDLED(cmd);
692}
693
694/* Initialization routines
695 */
696
697static int sql_passwd_init(void) {
698
699#if defined(PR_SHARED_MODULE)
700  pr_event_register(&sql_passwd_module, "core.module-unload",
701    sql_passwd_mod_unload_ev, NULL);
702#endif /* PR_SHARED_MODULE */
703
704  if (sql_register_authtype("md5", sql_passwd_md5) < 0) {
705    pr_log_pri(PR_LOG_WARNING, MOD_SQL_PASSWD_VERSION
706      ": unable to register 'md5' SQLAuthType handler: %s", strerror(errno));
707
708  } else {
709    pr_log_debug(DEBUG6, MOD_SQL_PASSWD_VERSION
710      ": registered 'md5' SQLAuthType handler");
711  }
712
713  if (sql_register_authtype("sha1", sql_passwd_sha1) < 0) {
714    pr_log_pri(PR_LOG_WARNING, MOD_SQL_PASSWD_VERSION
715      ": unable to register 'sha1' SQLAuthType handler: %s", strerror(errno));
716
717  } else {
718    pr_log_debug(DEBUG6, MOD_SQL_PASSWD_VERSION
719      ": registered 'sha1' SQLAuthType handler");
720  }
721
722  if (sql_register_authtype("sha256", sql_passwd_sha256) < 0) {
723    pr_log_pri(PR_LOG_WARNING, MOD_SQL_PASSWD_VERSION
724      ": unable to register 'sha256' SQLAuthType handler: %s", strerror(errno));
725
726  } else {
727    pr_log_debug(DEBUG6, MOD_SQL_PASSWD_VERSION
728      ": registered 'sha256' SQLAuthType handler");
729  }
730
731  if (sql_register_authtype("sha512", sql_passwd_sha512) < 0) {
732    pr_log_pri(PR_LOG_WARNING, MOD_SQL_PASSWD_VERSION
733      ": unable to register 'sha512' SQLAuthType handler: %s", strerror(errno));
734
735  } else {
736    pr_log_debug(DEBUG6, MOD_SQL_PASSWD_VERSION
737      ": registered 'sha512' SQLAuthType handler");
738  }
739
740  return 0;
741}
742
743static int sql_passwd_sess_init(void) {
744  config_rec *c;
745
746  c = find_config(main_server->conf, CONF_PARAM, "SQLPasswordEngine", FALSE);
747  if (c) {
748    sql_passwd_engine = *((int *) c->argv[0]);
749  }
750
751  c = find_config(main_server->conf, CONF_PARAM, "SQLPasswordEncoding", FALSE);
752  if (c) {
753    sql_passwd_encoding = *((unsigned int *) c->argv[0]);
754  }
755
756  c = find_config(main_server->conf, CONF_PARAM, "SQLPasswordOptions", FALSE);
757  if (c) {
758    sql_passwd_opts = *((unsigned long *) c->argv[0]);
759  }
760
761  c = find_config(main_server->conf, CONF_PARAM, "SQLPasswordSaltFile", FALSE);
762  if (c) {
763    char *path;
764    unsigned long salt_flags;
765
766    path = c->argv[0];
767    salt_flags = *((unsigned long *) c->argv[1]);
768
769    if (strcasecmp(path, "none") != 0) {
770      int fd, xerrno = 0;;
771
772      PRIVS_ROOT
773      fd = open(path, O_RDONLY|O_NONBLOCK);
774      if (fd < 0) {
775        xerrno = errno;
776      }
777      PRIVS_RELINQUISH
778
779      if (fd >= 0) {
780        int flags;
781        char buf[512];
782        ssize_t nread;
783 
784        /* Set this descriptor for blocking. */
785        flags = fcntl(fd, F_GETFL);
786        if (fcntl(fd, F_SETFL, flags & (U32BITS^O_NONBLOCK)) < 0) {
787          pr_log_debug(DEBUG3, MOD_SQL_PASSWD_VERSION
788            ": error setting blocking mode on SQLPasswordSaltFile '%s': %s",
789            path, strerror(errno));
790        }
791 
792        nread = read(fd, buf, sizeof(buf));
793        while (nread > 0) {
794          pr_signals_handle();
795
796          if (sql_passwd_salt == NULL) {
797
798            /* If the very last byte in the buffer is a newline, trim it. */
799            if (buf[nread-1] == '\n') {
800              buf[nread-1] = '\0';
801              nread--;
802            }
803
804            sql_passwd_salt_len = nread;
805            sql_passwd_salt = palloc(session.pool, sql_passwd_salt_len);
806            memcpy(sql_passwd_salt, buf, nread);
807
808          } else {
809            char *ptr, *tmp;
810
811            /* Allocate a larger buffer for the salt. */
812            ptr = tmp = palloc(session.pool, sql_passwd_salt_len + nread);
813            memcpy(tmp, sql_passwd_salt, sql_passwd_salt_len);
814            tmp += sql_passwd_salt_len;
815
816            memcpy(tmp, buf, nread);
817            sql_passwd_salt_len += nread;
818
819            /* XXX Yes, this is a minor memory leak; we are overwriting the
820             * previously allocated memory for the salt.  But it's per-session,
821             * so it's not a great concern at this point.
822             */
823            sql_passwd_salt = ptr;
824          }
825
826          nread = read(fd, buf, sizeof(buf));
827        }
828
829        if (nread < 0) {
830          pr_log_debug(DEBUG1, MOD_SQL_PASSWD_VERSION
831            ": error reading salt data from SQLPasswordSaltFile '%s': %s",
832            path, strerror(errno));
833          sql_passwd_salt = NULL;
834        }
835
836        (void) close(fd);
837
838        /* If the very last byte in the buffer is a newline, trim it.  This
839         * is to deal with cases where the SaltFile may have been written
840         * with an editor (e.g. vi) which automatically adds a trailing newline.
841         */
842        if (sql_passwd_salt[sql_passwd_salt_len-1] == '\n') {
843          sql_passwd_salt[sql_passwd_salt_len-1] = '\0';
844          sql_passwd_salt_len--;
845        }
846
847        sql_passwd_salt_flags = salt_flags;
848
849      } else {
850        pr_log_debug(DEBUG1, MOD_SQL_PASSWD_VERSION
851          ": unable to read SQLPasswordSaltFile '%s': %s", path,
852          strerror(xerrno));
853      }
854    }
855  }
856
857  return 0;
858}
859
860/* Module API tables
861 */
862
863static conftable sql_passwd_conftab[] = {
864  { "SQLPasswordEncoding",      set_sqlpasswdencoding,  NULL },
865  { "SQLPasswordEngine",        set_sqlpasswdengine,    NULL },
866  { "SQLPasswordOptions",       set_sqlpasswdoptions,   NULL },
867  { "SQLPasswordSaltFile",      set_sqlpasswdsaltfile,  NULL },
868  { "SQLPasswordRounds",        set_sqlpasswdrounds,    NULL },
869  { "SQLPasswordUserSalt",      set_sqlpasswdusersalt,  NULL },
870
871  { NULL, NULL, NULL }
872};
873
874static cmdtable sql_passwd_cmdtab[] = {
875  { PRE_CMD,    C_PASS, G_NONE, sql_passwd_pre_pass,    FALSE,  FALSE },
876
877  { 0, NULL }
878};
879
880module sql_passwd_module = {
881
882  /* Always NULL */
883  NULL, NULL,
884
885  /* Module API version */
886  0x20,
887
888  /* Module name */
889  "sql_passwd",
890
891  /* Module configuration directive table */
892  sql_passwd_conftab,
893
894  /* Module command handler table */
895  sql_passwd_cmdtab,
896
897  /* Module auth handler table */
898  NULL,
899
900  /* Module initialization */
901  sql_passwd_init,
902
903  /* Session initialization */
904  sql_passwd_sess_init,
905
906  /* Module version */
907  MOD_SQL_PASSWD_VERSION
908};
909
Note: See TracBrowser for help on using the repository browser.