Changeset 17876 for src/router/proftpd/contrib/mod_sql_passwd.c
- Timestamp:
- 11/11/11 13:17:43 (19 months ago)
- File:
-
- 1 edited
-
src/router/proftpd/contrib/mod_sql_passwd.c (modified) (17 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/router/proftpd/contrib/mod_sql_passwd.c
r14677 r17876 1 1 /* 2 2 * ProFTPD: mod_sql_passwd -- Various SQL password handlers 3 * Copyright (c) 2009-201 0TJ Saunders3 * Copyright (c) 2009-2011 TJ Saunders 4 4 * 5 5 * This program is free software; you can redistribute it and/or modify … … 15 15 * You should have received a copy of the GNU General Public License 16 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 5 9 Temple Place, Suite 330, Boston, MA 02111-1307, USA.17 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. 18 18 * 19 19 * As a special exemption, TJ Saunders and other respective copyright holders … … 22 22 * the source distribution. 23 23 * 24 * $Id: mod_sql_passwd.c,v 1.1 0 2010/02/01 19:20:05castaglia Exp $24 * $Id: mod_sql_passwd.c,v 1.16 2011/05/23 20:56:40 castaglia Exp $ 25 25 */ 26 26 … … 29 29 #include "mod_sql.h" 30 30 31 #define MOD_SQL_PASSWD_VERSION "mod_sql_passwd/0. 2"31 #define MOD_SQL_PASSWD_VERSION "mod_sql_passwd/0.4" 32 32 33 33 /* Make sure the version of proftpd is as necessary. */ … … 53 53 static char *sql_passwd_salt = NULL; 54 54 static size_t sql_passwd_salt_len = 0; 55 static unsigned int sql_passwd_salt_append = TRUE; 55 56 #define SQL_PASSWD_SALT_FL_APPEND 0x0001 57 #define SQL_PASSWD_SALT_FL_PREPEND 0x0002 58 static 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 65 static unsigned long sql_passwd_opts = 0UL; 66 67 static unsigned int sql_passwd_nrounds = 1; 68 69 static const char *trace_channel = "sql_passwd"; 56 70 57 71 static cmd_rec *sql_passwd_cmd_create(pool *parent_pool, int argc, ...) { … … 101 115 102 116 /* Check the results. */ 103 if (MODRET_ISERROR(res)) { 117 if (MODRET_ISDECLINED(res) || 118 MODRET_ISERROR(res)) { 104 119 pr_log_debug(DEBUG0, MOD_SQL_PASSWD_VERSION 105 120 ": error executing 'sql_escapestring'"); … … 110 125 } 111 126 127 static 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 */ 177 static 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 112 204 static modret_t *sql_passwd_auth(cmd_rec *cmd, const char *plaintext, 113 205 const char *ciphertext, const char *digest) { 114 EVP_MD_CTX md_ctxt;115 EVP_ENCODE_CTX base64_ctxt;116 206 const EVP_MD *md; 117 118 /* According to RATS, the output buffer (buf) for EVP_EncodeBlock() needs to 119 * be 4/3 the size of the input buffer (mdval). Let's make it easy, and 120 * use an output buffer that's twice the size of the input buffer. 121 */ 122 unsigned char buf[EVP_MAX_MD_SIZE*2+1], mdval[EVP_MAX_MD_SIZE]; 123 unsigned int mdlen; 124 125 char *copytext; /* temporary copy of the ciphertext string */ 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; 126 214 127 215 if (!sql_passwd_engine) { … … 140 228 } 141 229 142 EVP_DigestInit(&md_ctxt, md);143 144 230 /* If a salt is configured, do we prepend the salt as a prefix (i.e. throw 145 231 * it into the digest before the user-supplied password) or append it as a … … 148 234 149 235 if (sql_passwd_salt_len > 0 && 150 sql_passwd_salt_append == FALSE) { 236 (sql_passwd_salt_flags & SQL_PASSWD_SALT_FL_PREPEND)) { 237 151 238 /* If we have salt data, add it to the mix. */ 152 pr_log_debug(DEBUG9, MOD_SQL_PASSWD_VERSION 153 ": adding %lu bytes of salt data", (unsigned long) sql_passwd_salt_len); 154 EVP_DigestUpdate(&md_ctxt, (unsigned char *) sql_passwd_salt, 155 sql_passwd_salt_len); 156 } 157 158 EVP_DigestUpdate(&md_ctxt, plaintext, strlen(plaintext)); 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 } 159 299 160 300 if (sql_passwd_salt_len > 0 && 161 sql_passwd_salt_append == TRUE) {301 (sql_passwd_salt_flags & SQL_PASSWD_SALT_FL_APPEND)) { 162 302 /* If we have salt data, add it to the mix. */ 163 pr_log_debug(DEBUG9, MOD_SQL_PASSWD_VERSION 164 ": adding %lu bytes of salt data", (unsigned long) sql_passwd_salt_len); 165 EVP_DigestUpdate(&md_ctxt, (unsigned char *) sql_passwd_salt, 166 sql_passwd_salt_len); 167 } 168 169 EVP_DigestFinal(&md_ctxt, mdval, &mdlen); 170 171 memset(buf, '\0', sizeof(buf)); 172 173 switch (sql_passwd_encoding) { 174 case SQL_PASSWD_USE_BASE64: 175 EVP_EncodeInit(&base64_ctxt); 176 EVP_EncodeBlock(buf, mdval, (int) mdlen); 177 break; 178 179 case SQL_PASSWD_USE_HEX_LC: { 180 register unsigned int i; 181 182 for (i = 0; i < mdlen; i++) { 183 sprintf((char *) &(buf[i*2]), "%02x", mdval[i]); 184 } 185 186 break; 187 } 188 189 case SQL_PASSWD_USE_HEX_UC: { 190 register unsigned int i; 191 192 for (i = 0; i < mdlen; i++) { 193 sprintf((char *) &(buf[i*2]), "%02X", mdval[i]); 194 } 195 196 break; 197 } 198 199 default: 200 sql_log(DEBUG_WARN, "unsupported SQLPasswordEncoding configured"); 201 return PR_ERROR_INT(cmd, PR_AUTH_ERROR); 202 } 203 204 if (strcmp((char *) buf, copytext) == 0) { 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) { 205 365 return PR_HANDLED(cmd); 206 366 207 367 } else { 368 pr_trace_msg(trace_channel, 9, "expected '%s', got '%s'", copytext, 369 encodedtext); 370 208 371 pr_log_debug(DEBUG9, MOD_SQL_PASSWD_VERSION ": expected '%s', got '%s'", 209 buf, copytext);372 copytext, encodedtext); 210 373 } 211 374 … … 259 422 } 260 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 261 429 c = find_config(main_server->conf, CONF_PARAM, "SQLPasswordUserSalt", FALSE); 262 430 if (c) { 263 431 char *key; 264 char *append;432 unsigned long salt_flags; 265 433 266 434 key = c->argv[0]; 267 append = c->argv[1];435 salt_flags = *((unsigned long *) c->argv[1]); 268 436 269 437 if (strcasecmp(key, "name") == 0) { … … 329 497 } 330 498 331 if (strcasecmp(append, "prepend") == 0) { 332 sql_passwd_salt_append = FALSE; 333 334 } else { 335 sql_passwd_salt_append = TRUE; 336 } 499 sql_passwd_salt_flags = salt_flags; 337 500 } 338 501 … … 391 554 } 392 555 393 /* usage: SQLPasswordSaltFile path|"none" ["prepend"|"append"] */ 556 /* usage: SQLPasswordOptions opt1 ... optN */ 557 MODRET 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] */ 394 597 MODRET set_sqlpasswdsaltfile(cmd_rec *cmd) { 395 if (cmd->argc < 2 || 396 cmd->argc > 3) { 598 config_rec *c; 599 register unsigned int i; 600 unsigned long flags = SQL_PASSWD_SALT_FL_APPEND; 601 602 if (cmd->argc < 2) { 397 603 CONF_ERROR(cmd, "wrong number of parameters"); 398 604 } … … 400 606 CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL); 401 607 402 (void) add_config_param_str(cmd->argv[0], 2, cmd->argv[1], 403 cmd->argc == 3 ? cmd->argv[2] : "append"); 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 404 628 return PR_HANDLED(cmd); 405 629 } 406 630 407 /* usage: SQLPasswordUserSalt "name"|"sql:/named-query" ["prepend"|"append"] */ 631 /* usage: SQLPasswordRounds count */ 632 MODRET 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 */ 408 654 MODRET set_sqlpasswdusersalt(cmd_rec *cmd) { 409 if (cmd->argc < 2 || 410 cmd->argc > 3) { 655 config_rec *c; 656 register unsigned int i; 657 unsigned long flags = SQL_PASSWD_SALT_FL_APPEND; 658 659 if (cmd->argc < 2) { 411 660 CONF_ERROR(cmd, "wrong number of parameters"); 412 661 } … … 420 669 } 421 670 422 (void) add_config_param_str(cmd->argv[0], 2, cmd->argv[1], 423 cmd->argc == 3 ? cmd->argv[2] : "append"); 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 424 691 return PR_HANDLED(cmd); 425 692 } … … 487 754 } 488 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 489 761 c = find_config(main_server->conf, CONF_PARAM, "SQLPasswordSaltFile", FALSE); 490 762 if (c) { 491 763 char *path; 492 char *append;764 unsigned long salt_flags; 493 765 494 766 path = c->argv[0]; 495 append = c->argv[1];767 salt_flags = *((unsigned long *) c->argv[1]); 496 768 497 769 if (strcasecmp(path, "none") != 0) { … … 573 845 } 574 846 575 /* Determine whether to use the obtained salt as a prefix or suffix. */ 576 if (strcasecmp(append, "prepend") == 0) { 577 sql_passwd_salt_append = FALSE; 578 579 } else { 580 /* The default, for better/worse, is to append the salt as 581 * a suffix. 582 */ 583 sql_passwd_salt_append = TRUE; 584 } 847 sql_passwd_salt_flags = salt_flags; 585 848 586 849 } else { … … 601 864 { "SQLPasswordEncoding", set_sqlpasswdencoding, NULL }, 602 865 { "SQLPasswordEngine", set_sqlpasswdengine, NULL }, 866 { "SQLPasswordOptions", set_sqlpasswdoptions, NULL }, 603 867 { "SQLPasswordSaltFile", set_sqlpasswdsaltfile, NULL }, 868 { "SQLPasswordRounds", set_sqlpasswdrounds, NULL }, 604 869 { "SQLPasswordUserSalt", set_sqlpasswdusersalt, NULL }, 605 870
Note: See TracChangeset
for help on using the changeset viewer.
