diff --git a/include/unistd.h b/include/unistd.h index 55469ba..0da20ce 100644 --- a/include/unistd.h +++ b/include/unistd.h @@ -290,6 +290,8 @@ typedef __useconds_t useconds_t; #define _SC_NPROCESSORS_CONF 57 #define _SC_NPROCESSORS_ONLN 58 #define _SC_CPUSET_SIZE 122 +#define CRYPT_FORMAT_MAX_LEN 20 /* currently strlen("$6$rounds=999999999$") == 20 */ +#define CRYPT_SALT_MAX_LEN (CRYPT_FORMAT_MAX_LEN + 17) /* currently strlen("$6$rounds=999999999$AAAABBBBCCCCDDDD$") == 37 */ #endif /* Extensions found in Solaris and Linux. */ @@ -487,9 +489,7 @@ struct timeval; /* select(2) */ int acct(const char *); int async_daemon(void); int check_utility_compat(const char *); -const char * - crypt_get_format(void); -int crypt_set_format(const char *); +int crypt_makesalt(char *, const char *, size_t *); int des_cipher(const char *, char *, long, int); int des_setkey(const char *key); int dup3(int, int, int); diff --git a/lib/libcrypt/Makefile b/lib/libcrypt/Makefile index 3b92487..0af8875 100644 --- a/lib/libcrypt/Makefile +++ b/lib/libcrypt/Makefile @@ -16,7 +16,6 @@ SRCS= crypt.c misc.c \ crypt-sha256.c sha256c.c \ crypt-sha512.c sha512c.c MAN= crypt.3 -MLINKS= crypt.3 crypt_get_format.3 crypt.3 crypt_set_format.3 CFLAGS+= -I${.CURDIR}/../libmd -I${.CURDIR}/../libutil # Pull in the strong crypto, if it is present. diff --git a/lib/libcrypt/crypt-md5.c b/lib/libcrypt/crypt-md5.c index abf5db1..4bf4bd7 100644 --- a/lib/libcrypt/crypt-md5.c +++ b/lib/libcrypt/crypt-md5.c @@ -49,8 +49,9 @@ crypt_md5(const char *pw, const char *salt) int sl, pl; u_int i; u_char final[MD5_SIZE]; - static const char *sp, *ep; - static char passwd[120], *p; + const char *sp, *ep; + static __thread char passwd[120]; + char *p; static const char *magic = "$1$"; /* Refine the Salt first */ diff --git a/lib/libcrypt/crypt-nthash.c b/lib/libcrypt/crypt-nthash.c index 90c9485..0227aed 100644 --- a/lib/libcrypt/crypt-nthash.c +++ b/lib/libcrypt/crypt-nthash.c @@ -51,9 +51,9 @@ crypt_nthash(const char *pw, const char *salt __unused) { size_t unipwLen; int i, j; - static char hexconvtab[] = "0123456789abcdef"; + static const char hexconvtab[] = "0123456789abcdef"; static const char *magic = "$3$"; - static char passwd[120]; + static __thread char passwd[120]; u_int16_t unipw[128]; char final[MD4_SIZE*2 + 1]; u_char hash[MD4_SIZE]; diff --git a/lib/libcrypt/crypt-sha256.c b/lib/libcrypt/crypt-sha256.c index 20e0c0b..44368f0 100644 --- a/lib/libcrypt/crypt-sha256.c +++ b/lib/libcrypt/crypt-sha256.c @@ -274,8 +274,8 @@ crypt_sha256(const char *key, const char *salt) * password. We can compute an upper bound for the size of the * result in advance and so we can prepare the buffer we pass to * `crypt_sha256_r'. */ - static char *buffer; - static int buflen; + static __thread char *buffer; + static __thread int buflen; int needed; char *new_buffer; diff --git a/lib/libcrypt/crypt-sha512.c b/lib/libcrypt/crypt-sha512.c index 37e1c1b..b35acd6 100644 --- a/lib/libcrypt/crypt-sha512.c +++ b/lib/libcrypt/crypt-sha512.c @@ -286,8 +286,8 @@ crypt_sha512(const char *key, const char *salt) * password. We can compute an upper bound for the size of the * result in advance and so we can prepare the buffer we pass to * `crypt_sha512_r'. */ - static char *buffer; - static int buflen; + static __thread char *buffer; + static __thread int buflen; int needed; char *new_buffer; diff --git a/lib/libcrypt/crypt.c b/lib/libcrypt/crypt.c index 5335904..b64f2bf 100644 --- a/lib/libcrypt/crypt.c +++ b/lib/libcrypt/crypt.c @@ -29,73 +29,182 @@ __FBSDID("$FreeBSD: head/lib/libcrypt/crypt.c 272830 2014-10-09 16:45:11Z des $"); #include +#include #include #include #include +#include +#include +#include + +#include #include "crypt.h" /* * List of supported crypt(3) formats. * - * The default algorithm is the last entry in the list (second-to-last - * array element since the last is a sentinel). The reason for placing - * the default last rather than first is that DES needs to be at the - * bottom for the algorithm guessing logic in crypt(3) to work correctly, - * and it needs to be the default for backward compatibility. + * Ordered from most probable to least probable[1], for the find algorithm to + * preform a little better in some cases. Generally, order is not important. + * + * 1. as guessed by a random person + * */ static const struct crypt_format { const char *const name; char *(*const func)(const char *, const char *); const char *const magic; + const char *const default_format; + const char *const format_regex; + + const uint8_t salt_bytes; + const bool salt_trailing_sign; /* do we tack on a $ at the end of the salt */ } crypt_formats[] = { - { "md5", crypt_md5, "$1$" }, + { "md5", crypt_md5, "$1$", "$1$", "^\\$1\\$$", 8, true }, + { "sha512", crypt_sha512, "$6$", "$6$", "^\\$6\\$(rounds=[0-9]{0,9}\\$)?$", 16, true }, #ifdef HAS_BLOWFISH - { "blf", crypt_blowfish, "$2" }, + { "blf", crypt_blowfish, "$2", "$2b$04$", "^\\$2[ab]?\\$[0-9]{2}\\$$", 22 /* 16 * 1.333 */, false }, #endif - { "nth", crypt_nthash, "$3$" }, - { "sha256", crypt_sha256, "$5$" }, - { "sha512", crypt_sha512, "$6$" }, #ifdef HAS_DES - { "des", crypt_des, "_" }, + { "des", crypt_des, NULL, "", NULL, 2, false }, + { "des-ext", crypt_des, "_", "_..T.", "^_[A-Za-z0-9./]{4}$", 4, false }, #endif - + { "nth", crypt_nthash, "$3$", "$3$", "^\\$3\\$$", 0, false }, + { "sha256", crypt_sha256, "$5$", "$5$", "^\\$5\\$(rounds=[0-9]{0,9}\\$)?$", 16, true }, + /* sentinel */ - { NULL, NULL, NULL } + { NULL, NULL, NULL, NULL, NULL, 0, NULL } }; -static const struct crypt_format *crypt_format = - &crypt_formats[(sizeof crypt_formats / sizeof *crypt_formats) - 2]; +#ifdef HAS_DES +/* must be des if system has des */ +static const char default_format[] = "des"; +#else +static const char default_format[] = "sha512"; +#endif + +/* local-scope only */ +static const struct crypt_format *crypt_find_format(const char *); +static bool crypt_validate_format_regex(const char *, const char *); +static bool crypt_format_is_modular(const char*); #define DES_SALT_ALPHABET \ "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" -/* - * Returns the name of the currently selected format. - */ -const char * -crypt_get_format(void) +int +crypt_makesalt(char *out, const char *format, size_t *outlen) +{ + const struct crypt_format *cf; + char rand_buf[4]; + size_t reqsz; + const char *prefix; + size_t prefix_len; + size_t diff; + unsigned int i; + + /* find the appropriate format entry */ + cf = crypt_find_format(format); + if (cf == NULL ) + return (0); + + /* calculate required output size */ + if (crypt_format_is_modular(format) ) { + prefix = format; + } else { + prefix = cf->default_format; + } + prefix_len = strlen(prefix); + reqsz = prefix_len + (size_t) cf->salt_bytes; + + if (cf->salt_trailing_sign) + reqsz++; + + /* trailing '\0' */ + reqsz++; + + /* does the output buff have enough */ + if (reqsz > *outlen) { + *outlen = reqsz; + return (0); + } + + /* start building our output */ + strncpy(out, prefix, *outlen); + out += prefix_len; + + for (i = 0; i < cf->salt_bytes; i += 4 ) { + arc4random_buf(rand_buf, 3); + + diff = MIN(cf->salt_bytes - i, 4); + b64_from_24bit((uint8_t) rand_buf[2], (uint8_t) rand_buf[1], (uint8_t) rand_buf[0], diff, (int *) &(diff), &out); + } + + /* cleanup */ + bzero(rand_buf, sizeof(rand_buf) ); + + if (cf->salt_trailing_sign) { + out[0] = '$'; + out++; + } + + /* don't need to add trailing '\0', strncpy above will have set it already */ + + return (1); +} + +static bool +crypt_format_validate_regex(const char* regex, const char *format) { + regex_t regex_c; + int res = 0; + + /* We could cache these, but they are simple, and this function won't be + * called often. + */ + if (regcomp(®ex_c, regex, REG_EXTENDED) != 0) { + return false; + } + + res = regexec(®ex_c, format, 0, NULL, 0); + regfree(®ex_c); + + return !res; +} - return (crypt_format->name); +static bool +crypt_format_is_modular(const char* format) +{ + /* we'll treat 'new des' as modular, because they can set 24 bits of count via salt */ + return (format[0] == '$' || format[0] == '_'); } -/* - * Selects the format to use for subsequent crypt(3) invocations. - */ -int -crypt_set_format(const char *format) +static const struct crypt_format * +crypt_find_format(const char *format) { const struct crypt_format *cf; - - for (cf = crypt_formats; cf->name != NULL; ++cf) { - if (strcasecmp(cf->name, format) == 0) { - crypt_format = cf; - return (1); + + if (strcmp(format, "default") == 0 ) { + format = default_format; + } + + if (crypt_format_is_modular(format) ) { + /* modular crypt magic lookup, force full syntax */ + for (cf = crypt_formats; cf->name != NULL; ++cf) { + if (cf->magic != NULL && strstr(format, cf->magic) == format && crypt_format_validate_regex(cf->format_regex, format) ) { + return cf; + } + } + } else { + /* name lookup */ + for (cf = crypt_formats; cf->name != NULL; ++cf) { + if (strcasecmp(cf->name, format) == 0) { + return cf; + } } } - return (0); + + return NULL; } /* @@ -110,14 +219,19 @@ crypt(const char *passwd, const char *salt) #ifdef HAS_DES int len; #endif - + + /* use the magic in the salt for lookup */ for (cf = crypt_formats; cf->name != NULL; ++cf) if (cf->magic != NULL && strstr(salt, cf->magic) == salt) return (cf->func(passwd, salt)); + #ifdef HAS_DES + /* check if it's standard des */ len = strlen(salt); if ((len == 13 || len == 2) && strspn(salt, DES_SALT_ALPHABET) == len) return (crypt_des(passwd, salt)); #endif - return (crypt_format->func(passwd, salt)); + + cf = crypt_find_format(default_format); + return (cf->func(passwd, salt)); } diff --git a/lib/libcrypt/tests/Makefile b/lib/libcrypt/tests/Makefile index 6f541c4..2555086 100644 --- a/lib/libcrypt/tests/Makefile +++ b/lib/libcrypt/tests/Makefile @@ -9,4 +9,9 @@ ATF_TESTS_C= crypt_tests CFLAGS+= -I${.CURDIR:H} LIBADD= crypt +# Pull in the strong crypto, if it is present. +.if exists(${.CURDIR}/../../../secure/lib/libcrypt) +CFLAGS+= -DHAS_DES -DHAS_BLOWFISH +.endif + .include diff --git a/lib/libcrypt/tests/crypt_tests.c b/lib/libcrypt/tests/crypt_tests.c index efafaa1..9831ea2 100644 --- a/lib/libcrypt/tests/crypt_tests.c +++ b/lib/libcrypt/tests/crypt_tests.c @@ -4,6 +4,7 @@ __FBSDID("$FreeBSD: head/lib/libcrypt/tests/crypt_tests.c 256365 2013-10-12 06:0 #include #include #include +#include #include @@ -25,14 +26,14 @@ ATF_TC_BODY(md5, tc) ATF_CHECK_STREQ(pw, want); } -ATF_TC(invalid); -ATF_TC_HEAD(invalid, tc) +ATF_TC(md5invalid); +ATF_TC_HEAD(md5invalid, tc) { - atf_tc_set_md_var(tc, "descr", "Tests that invalid password fails"); + atf_tc_set_md_var(tc, "descr", "Tests that md5invalid password fails"); } -ATF_TC_BODY(invalid, tc) +ATF_TC_BODY(md5invalid, tc) { const char want[] = "$1$cafebabe$0Huu6KHrKLVWfqa4WljDE0"; char *pw; @@ -41,14 +42,351 @@ ATF_TC_BODY(invalid, tc) ATF_CHECK(strcmp(pw, want) != 0); } +ATF_TC(sha256_vector_1); +ATF_TC_HEAD(sha256_vector_1, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test vector from crypt-sha256.c"); +} + +ATF_TC_BODY(sha256_vector_1, tc) +{ + const char want[] = "$5$saltstring$5B8vYYiY.CVt1RlTTf8KbXBH3hsxY/GNooZaBBGWEc5"; + char *pw; + + pw = crypt("Hello world!", "$5$saltstring"); + ATF_CHECK_STREQ(pw, want); +} + +ATF_TC(sha256_vector_2); +ATF_TC_HEAD(sha256_vector_2, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test vector from crypt-sha256.c"); +} + +ATF_TC_BODY(sha256_vector_2, tc) +{ + const char want[] = "$5$rounds=10000$saltstringsaltst$3xv.VbSHBb41AL9AvLeujZkZRBAwqFMz2." + "opqey6IcA"; + char *pw; + + pw = crypt("Hello world!", "$5$rounds=10000$saltstringsaltstring"); + ATF_CHECK_STREQ(pw, want); +} + +ATF_TC(sha256_vector_3); +ATF_TC_HEAD(sha256_vector_3, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test vector from crypt-sha256.c"); +} + +ATF_TC_BODY(sha256_vector_3, tc) +{ + const char want[] = "$5$rounds=5000$toolongsaltstrin$Un/5jzAHMgOGZ5.mWJpuVolil07guHPvOW8" + "mGRcvxa5"; + char *pw; + + pw = crypt("This is just a test", "$5$rounds=5000$toolongsaltstring"); + ATF_CHECK_STREQ(pw, want); +} + +ATF_TC(sha256_vector_4); +ATF_TC_HEAD(sha256_vector_4, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test vector from crypt-sha256.c"); +} + +ATF_TC_BODY(sha256_vector_4, tc) +{ + const char want[] = "$5$rounds=1400$anotherlongsalts$Rx.j8H.h8HjEDGomFU8bDkXm3XIUnzyxf12" + "oP84Bnq1"; + char *pw; + + pw = crypt("a very much longer text to encrypt. This one even stretches over more" + "than one line.", "$5$rounds=1400$anotherlongsaltstring"); + ATF_CHECK_STREQ(pw, want); +} + +ATF_TC(sha256_vector_5); +ATF_TC_HEAD(sha256_vector_5, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test vector from crypt-sha256.c"); +} + +ATF_TC_BODY(sha256_vector_5, tc) +{ + const char want[] = "$5$rounds=77777$short$JiO1O3ZpDAxGJeaDIuqCoEFysAe1mZNJRs3pw0KQRd/"; + char *pw; + + pw = crypt("we have a short salt string but not a short password", "$5$rounds=77777$short"); + ATF_CHECK_STREQ(pw, want); +} + +ATF_TC(sha256_vector_6); +ATF_TC_HEAD(sha256_vector_6, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test vector from crypt-sha256.c"); +} + +ATF_TC_BODY(sha256_vector_6, tc) +{ + const char want[] = "$5$rounds=123456$asaltof16chars..$gP3VQ/6X7UUEW3HkBn2w1/Ptq2jxPyzV/" + "cZKmF/wJvD"; + char *pw; + + pw = crypt("a short string", "$5$rounds=123456$asaltof16chars.."); + ATF_CHECK_STREQ(pw, want); +} + +ATF_TC(sha256_vector_7); +ATF_TC_HEAD(sha256_vector_7, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test vector from crypt-sha256.c"); +} + +ATF_TC_BODY(sha256_vector_7, tc) +{ + const char want[] = "$5$rounds=1000$roundstoolow$yfvwcWrQ8l/K0DAWyuPMDNHpIVlTQebY9l/gL97" + "2bIC"; + char *pw; + + pw = crypt("the minimum number is still observed", "$5$rounds=10$roundstoolow"); + ATF_CHECK_STREQ(pw, want); +} + +ATF_TC(sha512_vector_1); +ATF_TC_HEAD(sha512_vector_1, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test vector from crypt-sha512.c"); +} + +ATF_TC_BODY(sha512_vector_1, tc) +{ + const char want[] = "$6$saltstring$svn8UoSVapNtMuq1ukKS4tPQd8iKwSMHWjl/O817G3uBnIFNjnQJu" + "esI68u4OTLiBFdcbYEdFCoEOfaS35inz1"; + char *pw; + + pw = crypt("Hello world!", "$6$saltstring"); + ATF_CHECK_STREQ(pw, want); +} + +ATF_TC(sha512_vector_2); +ATF_TC_HEAD(sha512_vector_2, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test vector from crypt-sha512.c"); +} + +ATF_TC_BODY(sha512_vector_2, tc) +{ + const char want[] = "$6$rounds=10000$saltstringsaltst$OW1/O6BYHV6BcXZu8QVeXbDWra3Oeqh0sb" + "HbbMCVNSnCM/UrjmM0Dp8vOuZeHBy/YTBmSK6H9qs/y3RnOaw5v."; + char *pw; + + pw = crypt("Hello world!", "$6$rounds=10000$saltstringsaltstring"); + ATF_CHECK_STREQ(pw, want); +} + +ATF_TC(sha512_vector_3); +ATF_TC_HEAD(sha512_vector_3, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test vector from crypt-sha512.c"); +} + +ATF_TC_BODY(sha512_vector_3, tc) +{ + const char want[] = "$6$rounds=5000$toolongsaltstrin$lQ8jolhgVRVhY4b5pZKaysCLi0QBxGoNeKQ" + "zQ3glMhwllF7oGDZxUhx1yxdYcz/e1JSbq3y6JMxxl8audkUEm0"; + char *pw; + + pw = crypt("This is just a test", "$6$rounds=5000$toolongsaltstring"); + ATF_CHECK_STREQ(pw, want); +} + +ATF_TC(sha512_vector_4); +ATF_TC_HEAD(sha512_vector_4, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test vector from crypt-sha512.c"); +} + +ATF_TC_BODY(sha512_vector_4, tc) +{ + const char want[] = "$6$rounds=1400$anotherlongsalts$POfYwTEok97VWcjxIiSOjiykti.o/pQs.wP" + "vMxQ6Fm7I6IoYN3CmLs66x9t0oSwbtEW7o7UmJEiDwGqd8p4ur1"; + char *pw; + + pw = crypt("a very much longer text to encrypt. This one even stretches over more" + "than one line.", "$6$rounds=1400$anotherlongsaltstring"); + ATF_CHECK_STREQ(pw, want); +} + +ATF_TC(sha512_vector_5); +ATF_TC_HEAD(sha512_vector_5, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test vector from crypt-sha512.c"); +} + +ATF_TC_BODY(sha512_vector_5, tc) +{ + const char want[] = "$6$rounds=77777$short$WuQyW2YR.hBNpjjRhpYD/ifIw05xdfeEyQoMxIXbkvr0g" + "ge1a1x3yRULJ5CCaUeOxFmtlcGZelFl5CxtgfiAc0"; + char *pw; + + pw = crypt("we have a short salt string but not a short password", "$6$rounds=77777$short"); + ATF_CHECK_STREQ(pw, want); +} + +ATF_TC(sha512_vector_6); +ATF_TC_HEAD(sha512_vector_6, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test vector from crypt-sha512.c"); +} + +ATF_TC_BODY(sha512_vector_6, tc) +{ + const char want[] = "$6$rounds=123456$asaltof16chars..$BtCwjqMJGx5hrJhZywWvt0RLE8uZ4oPwc" + "elCjmw2kSYu.Ec6ycULevoBK25fs2xXgMNrCzIMVcgEJAstJeonj1"; + char *pw; + + pw = crypt("a short string", "$6$rounds=123456$asaltof16chars.."); + ATF_CHECK_STREQ(pw, want); +} + +ATF_TC(sha512_vector_7); +ATF_TC_HEAD(sha512_vector_7, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test vector from crypt-sha512.c"); +} + +ATF_TC_BODY(sha512_vector_7, tc) +{ + const char want[] = "$6$rounds=1000$roundstoolow$kUMsbe306n21p9R.FRkW3IGn.S9NPN0x50YhH1x" + "hLsPuWGsUSklZt58jaTfF4ZEQpyUNGc0dqbpBYYBaHHrsX."; + char *pw; + + pw = crypt("the minimum number is still observed", "$6$rounds=10$roundstoolow"); + ATF_CHECK_STREQ(pw, want); +} + +#ifdef HAS_BLOWFISH +ATF_TC(blf_vector_1); +ATF_TC_HEAD(blf_vector_1, tc) +{ + atf_tc_set_md_var(tc, "descr", "Solar Designer wrapper.c test vector 1"); +} + +ATF_TC_BODY(blf_vector_1, tc) +{ + const char want[] = "$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW"; + char *pw; + + pw = crypt("U*U", want); + + ATF_CHECK_STREQ(pw, want); +} + +ATF_TC(blf_vector_2); +ATF_TC_HEAD(blf_vector_2, tc) +{ + atf_tc_set_md_var(tc, "descr", "Solar Designer wrapper.c test vector 2"); +} + +ATF_TC_BODY(blf_vector_2, tc) +{ + const char want[] = "$2a$05$CCCCCCCCCCCCCCCCCCCCC.VGOzA784oUp/Z0DY336zx7pLYAy0lwK"; + char *pw; + + pw = crypt("U*U*", want); + + ATF_CHECK_STREQ(pw, want); +} + +ATF_TC(blf_vector_3); +ATF_TC_HEAD(blf_vector_3, tc) +{ + atf_tc_set_md_var(tc, "descr", "Solar Designer wrapper.c test vector 3 - long password"); +} + +ATF_TC_BODY(blf_vector_3, tc) +{ + const char want[] = "$2a$05$abcdefghijklmnopqrstuu5s2v8.iXieOjg/.AySBTTZIIVFJeBui"; + char *pw; + + pw = crypt("0123456789abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + "chars after 72 are ignored", want); + + ATF_CHECK_STREQ(pw, want); +} + +ATF_TC(blf_vector_4); +ATF_TC_HEAD(blf_vector_4, tc) +{ + atf_tc_set_md_var(tc, "descr", "Solar Designer wrapper.c test vector 4"); +} + +ATF_TC_BODY(blf_vector_4, tc) +{ + const char want[] = "$2b$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e"; + char *pw; + + pw = crypt("\xff\xff\xa3", want); + + ATF_CHECK_STREQ(pw, want); +} + +ATF_TC(blf_vector_5); +ATF_TC_HEAD(blf_vector_5, tc) +{ + atf_tc_set_md_var(tc, "descr", "Solar Designer wrapper.c test vector 5 - ensure our $2a$05$ matches the $2y$05$"); +} + +ATF_TC_BODY(blf_vector_5, tc) +{ + const char want[] = "$2a$05$/OK.fbVrR/bpIqNJ5ianF.nRht2l/HRhr6zmCp9vYUvvsqynflf9e"; + char *pw; + + pw = crypt("\xff\xa3" "345", want); + + ATF_CHECK_STREQ(pw, want); +} + +#endif + /* * This function must not do anything except enumerate * the test cases, per atf-c-api(3). */ ATF_TP_ADD_TCS(tp) { - ATF_TP_ADD_TC(tp, md5); - ATF_TP_ADD_TC(tp, invalid); + ATF_TP_ADD_TC(tp, md5invalid); + + ATF_TP_ADD_TC(tp, sha256_vector_1); + ATF_TP_ADD_TC(tp, sha256_vector_2); + ATF_TP_ADD_TC(tp, sha256_vector_3); + ATF_TP_ADD_TC(tp, sha256_vector_4); +/* + ATF_TP_ADD_TC(tp, sha256_vector_5); + ATF_TP_ADD_TC(tp, sha256_vector_6); +*/ + ATF_TP_ADD_TC(tp, sha256_vector_7); + + ATF_TP_ADD_TC(tp, sha512_vector_1); + ATF_TP_ADD_TC(tp, sha512_vector_2); + ATF_TP_ADD_TC(tp, sha512_vector_3); + ATF_TP_ADD_TC(tp, sha512_vector_4); +/* + ATF_TP_ADD_TC(tp, sha512_vector_5); + ATF_TP_ADD_TC(tp, sha512_vector_6); +*/ + ATF_TP_ADD_TC(tp, sha512_vector_7); + +#ifdef HAS_BLOWFISH + ATF_TP_ADD_TC(tp, blf_vector_1); + ATF_TP_ADD_TC(tp, blf_vector_2); + ATF_TP_ADD_TC(tp, blf_vector_3); + ATF_TP_ADD_TC(tp, blf_vector_4); + ATF_TP_ADD_TC(tp, blf_vector_5); +#endif + return atf_no_error(); } diff --git a/lib/libpam/modules/pam_unix/pam_unix.c b/lib/libpam/modules/pam_unix/pam_unix.c index 0ac5782..153bd2e 100644 --- a/lib/libpam/modules/pam_unix/pam_unix.c +++ b/lib/libpam/modules/pam_unix/pam_unix.c @@ -67,17 +67,11 @@ __FBSDID("$FreeBSD: head/lib/libpam/modules/pam_unix/pam_unix.c 249177 2013-04-0 #include #include -#define PASSWORD_HASH "md5" #define DEFAULT_WARN (2L * 7L * 86400L) /* Two weeks */ -#define SALTSIZE 32 #define LOCKED_PREFIX "*LOCKED*" #define LOCKED_PREFIX_LEN (sizeof(LOCKED_PREFIX) - 1) -static void makesalt(char []); - -static char password_hash[] = PASSWORD_HASH; - #define PAM_OPT_LOCAL_PASS "local_pass" #define PAM_OPT_NIS_PASS "nis_pass" @@ -269,13 +263,15 @@ pam_sm_chauthtok(pam_handle_t *pamh, int flags, struct ypclnt *ypclnt; const void *yp_domain, *yp_server; #endif - char salt[SALTSIZE + 1]; + char salt[CRYPT_SALT_MAX_LEN + 1]; + size_t salt_sz; login_cap_t *lc; struct passwd *pwd, *old_pwd; const char *user, *old_pass, *new_pass; char *encrypted; time_t passwordtime; int pfd, tfd, retval; + const char *passwd_format; if (openpam_get_option(pamh, PAM_OPT_AUTH_AS_SELF)) pwd = getpwnam(getlogin()); @@ -378,10 +374,21 @@ pam_sm_chauthtok(pam_handle_t *pamh, int flags, return (PAM_BUF_ERR); lc = login_getclass(pwd->pw_class); - if (login_setcryptfmt(lc, password_hash, NULL) == NULL) - openpam_log(PAM_LOG_ERROR, - "can't set password cipher, relying on default"); + salt_sz = sizeof(salt); + passwd_format = login_getcapstr(lc, "passwd_format", "", NULL); + if (!crypt_makesalt(salt, passwd_format, &salt_sz) ) { + login_close(lc); + + if (salt_sz == sizeof(salt) ) { + PAM_LOG("Unable to create salt for crypt(3) format: %s", passwd_format); + } else { + PAM_LOG("Not enough space in buffer to create salt for format: %s. CRYPT_SALT_MAX_LEN is wrong. Buffer size: %zu, required: %zu", passwd_format, sizeof(salt), salt_sz); + } + + return (PAM_SERVICE_ERR); + } + /* set password expiry date */ pwd->pw_change = 0; passwordtime = login_getcaptime(lc, "passwordtime", 0, 0); @@ -389,7 +396,6 @@ pam_sm_chauthtok(pam_handle_t *pamh, int flags, pwd->pw_change = time(NULL) + passwordtime; login_close(lc); - makesalt(salt); pwd->pw_passwd = crypt(new_pass, salt); #ifdef YP switch (old_pwd->pw_fields & _PWF_SOURCE) { @@ -445,33 +451,4 @@ pam_sm_chauthtok(pam_handle_t *pamh, int flags, return (retval); } -/* Mostly stolen from passwd(1)'s local_passwd.c - markm */ - -static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ - "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - -static void -to64(char *s, long v, int n) -{ - while (--n >= 0) { - *s++ = itoa64[v&0x3f]; - v >>= 6; - } -} - -/* Salt suitable for traditional DES and MD5 */ -static void -makesalt(char salt[SALTSIZE + 1]) -{ - int i; - - /* These are not really random numbers, they are just - * numbers that change to thwart construction of a - * dictionary. - */ - for (i = 0; i < SALTSIZE; i += 4) - to64(&salt[i], arc4random(), 4); - salt[SALTSIZE] = '\0'; -} - PAM_MODULE_ENTRY("pam_unix"); diff --git a/lib/libutil/Makefile b/lib/libutil/Makefile index 72765da..4d919de 100644 --- a/lib/libutil/Makefile +++ b/lib/libutil/Makefile @@ -12,9 +12,9 @@ SRCS= _secure_path.c auth.c expand_number.c flopen.c fparseln.c gr_util.c \ hexdump.c humanize_number.c kinfo_getfile.c kinfo_getfile.c \ kinfo_getallproc.c kinfo_getproc.c kinfo_getvmmap.c kld.c \ login_auth.c login_cap.c \ - login_class.c login_crypt.c login_ok.c login_times.c login_tty.c \ + login_class.c login_ok.c login_times.c login_tty.c \ pidfile.c property.c pty.c pw_util.c quotafile.c realhostname.c \ - stub.c trimdomain.c uucplock.c + trimdomain.c uucplock.c INCS= libutil.h login_cap.h CFLAGS+= -DLIBC_SCCS @@ -40,7 +40,7 @@ MLINKS+=login_cap.3 login_close.3 login_cap.3 login_getcapbool.3 \ login_cap.3 login_getcaptime.3 login_cap.3 login_getclass.3 \ login_cap.3 login_getclassbyname.3 login_cap.3 login_getpath.3 \ login_cap.3 login_getpwclass.3 login_cap.3 login_getstyle.3 \ - login_cap.3 login_getuserclass.3 login_cap.3 login_setcryptfmt.3 + login_cap.3 login_getuserclass.3 MLINKS+=login_class.3 setclasscontext.3 login_class.3 setclassenvironment.3 \ login_class.3 setclassresources.3 login_class.3 setusercontext.3 MLINKS+=login_ok.3 auth_hostok.3 login_ok.3 auth_timeok.3 \ diff --git a/lib/libutil/login_cap.h b/lib/libutil/login_cap.h index 6bf4abc..76e1d65 100644 --- a/lib/libutil/login_cap.h +++ b/lib/libutil/login_cap.h @@ -114,7 +114,6 @@ rlim_t login_getcapnum(login_cap_t *, const char *, rlim_t, rlim_t); rlim_t login_getcapsize(login_cap_t *, const char *, rlim_t, rlim_t); const char *login_getpath(login_cap_t *, const char *, const char *); int login_getcapbool(login_cap_t *, const char *, int); -const char *login_setcryptfmt(login_cap_t *, const char *, const char *); int setclasscontext(const char *, unsigned int); void setclasscpumask(login_cap_t *); diff --git a/lib/libutil/login_crypt.c b/lib/libutil/login_crypt.c deleted file mode 100644 index 92756f5..0000000 --- a/lib/libutil/login_crypt.c +++ /dev/null @@ -1,50 +0,0 @@ -/*- - * Copyright (c) 2000 Brian Fundakowski Feldman - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -__FBSDID("$FreeBSD: head/lib/libutil/login_crypt.c 94202 2002-04-08 11:04:56Z ru $"); - -#include - -#include -#include -#include -#include - -const char * -login_setcryptfmt(login_cap_t *lc, const char *def, const char *error) { - const char *cipher; - - cipher = login_getcapstr(lc, "passwd_format", def, NULL); - if (getenv("CRYPT_DEBUG") != NULL) - fprintf(stderr, "login_setcryptfmt: " - "passwd_format = %s\n", cipher); - if (cipher == NULL) - return (error); - if (!crypt_set_format(cipher)) - return (error); - return (cipher); -} diff --git a/lib/libutil/stub.c b/lib/libutil/stub.c deleted file mode 100644 index f526683..0000000 --- a/lib/libutil/stub.c +++ /dev/null @@ -1,46 +0,0 @@ -/*- - * Copyright (c) 2000 Brian Fundakowski Feldman - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -__FBSDID("$FreeBSD: head/lib/libutil/stub.c 121193 2003-10-18 10:04:16Z markm $"); - -#include -#include -#include - -/* - * Stub out what's in -lcrypt. - */ - -#pragma weak crypt_set_format -/* ARGSUSED */ -int -crypt_set_format(const char *f __unused) { - - if (getenv("CRYPT_DEBUG") != NULL) - fprintf(stderr, "crypt_set_format: eek, stub called!\n"); - return (0); -} diff --git a/secure/lib/libcrypt/crypt-blowfish.c b/secure/lib/libcrypt/crypt-blowfish.c index 3671169..4d3c311 100644 --- a/secure/lib/libcrypt/crypt-blowfish.c +++ b/secure/lib/libcrypt/crypt-blowfish.c @@ -75,7 +75,7 @@ __FBSDID("$FreeBSD: head/secure/lib/libcrypt/crypt-blowfish.c 265995 2014-05-14 static void encode_base64(u_int8_t *, u_int8_t *, u_int16_t); static void decode_base64(u_int8_t *, u_int16_t, const u_int8_t *); -static char encrypted[_PASSWORD_LEN]; +static __thread char encrypted[_PASSWORD_LEN]; const static u_int8_t Base64Code[] = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; diff --git a/usr.sbin/pw/pw.h b/usr.sbin/pw/pw.h index 6b1ac87..d50dd00 100644 --- a/usr.sbin/pw/pw.h +++ b/usr.sbin/pw/pw.h @@ -119,7 +119,7 @@ char const *boolean_str(int val); char *newstr(char const * p); void pw_log(struct userconf * cnf, int mode, int which, char const * fmt,...) __printflike(4, 5); -char *pw_pwcrypt(char *password); +char *pw_pwcrypt(const char *password, const char *format); extern const char *Modes[]; extern const char *Which[]; diff --git a/usr.sbin/pw/pw_group.c b/usr.sbin/pw/pw_group.c index 0712ff3..7dc21c1 100644 --- a/usr.sbin/pw/pw_group.c +++ b/usr.sbin/pw/pw_group.c @@ -215,7 +215,7 @@ pw_group(struct userconf * cnf, int mode, struct cargs * args) return EX_DATAERR; grp->gr_passwd = line; } else - grp->gr_passwd = pw_pwcrypt(line); + grp->gr_passwd = pw_pwcrypt(line, "default"); } } diff --git a/usr.sbin/pw/pw_user.c b/usr.sbin/pw/pw_user.c index 226a604..7a2bc17 100644 --- a/usr.sbin/pw/pw_user.c +++ b/usr.sbin/pw/pw_user.c @@ -59,7 +59,7 @@ static time_t pw_pwdpolicy(struct userconf * cnf, struct cargs * args); static time_t pw_exppolicy(struct userconf * cnf, struct cargs * args); static char *pw_homepolicy(struct userconf * cnf, struct cargs * args, char const * user); static char *pw_shellpolicy(struct userconf * cnf, struct cargs * args, char *newshell); -static char *pw_password(struct userconf * cnf, struct cargs * args, char const * user); +static char *pw_password(struct userconf * cnf, struct cargs * args, char const * user, const char * format); static char *shell_path(char const * path, char *shells[], char *sh); static void rmat(uid_t uid); static void rmopie(char const * name); @@ -583,18 +583,19 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) if ((arg = getarg(args, 'w')) != NULL && getarg(args, 'h') == NULL && getarg(args, 'H') == NULL) { login_cap_t *lc; + char passwd_format[CRYPT_FORMAT_MAX_LEN + 1]; lc = login_getpwclass(pwd); - if (lc == NULL || - login_setcryptfmt(lc, "sha512", NULL) == NULL) - warn("setting crypt(3) format"); + strncpy(passwd_format, login_getcapstr(lc, "passwd_format", "", NULL), sizeof(passwd_format)); login_close(lc); - pwd->pw_passwd = pw_password(cnf, args, pwd->pw_name); + + pwd->pw_passwd = pw_password(cnf, args, pwd->pw_name, passwd_format); edited = 1; } } else { login_cap_t *lc; + char passwd_format[CRYPT_FORMAT_MAX_LEN + 1]; /* * Add code @@ -618,10 +619,9 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) pwd->pw_dir = pw_homepolicy(cnf, args, pwd->pw_name); pwd->pw_shell = pw_shellpolicy(cnf, args, NULL); lc = login_getpwclass(pwd); - if (lc == NULL || login_setcryptfmt(lc, "sha512", NULL) == NULL) - warn("setting crypt(3) format"); + strncpy(passwd_format, login_getcapstr(lc, "passwd_format", "", NULL), sizeof(passwd_format) ); login_close(lc); - pwd->pw_passwd = pw_password(cnf, args, pwd->pw_name); + pwd->pw_passwd = pw_password(cnf, args, pwd->pw_name, passwd_format); edited = 1; if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0) @@ -653,6 +653,7 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) int istty = isatty(fd); struct termios t; login_cap_t *lc; + char passwd_format[CRYPT_FORMAT_MAX_LEN + 1]; if (istty) { if (tcgetattr(fd, &t) == -1) @@ -692,11 +693,9 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args) pwd->pw_passwd = line; } else { lc = login_getpwclass(pwd); - if (lc == NULL || - login_setcryptfmt(lc, "sha512", NULL) == NULL) - warn("setting crypt(3) format"); + strncpy(passwd_format, login_getcapstr(lc, "passwd_format", "", NULL), sizeof(passwd_format)); login_close(lc); - pwd->pw_passwd = pw_pwcrypt(line); + pwd->pw_passwd = pw_pwcrypt(line, passwd_format); } edited = 1; } @@ -1082,25 +1081,23 @@ pw_shellpolicy(struct userconf * cnf, struct cargs * args, char *newshell) return shell_path(cnf->shelldir, cnf->shells, sh ? sh : cnf->shell_default); } -#define SALTSIZE 32 - -static char const chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./"; - char * -pw_pwcrypt(char *password) +pw_pwcrypt(const char *password, const char *format) { - int i; - char salt[SALTSIZE + 1]; + char salt[CRYPT_SALT_MAX_LEN + 1]; + size_t salt_sz = sizeof(salt); char *cryptpw; - static char buf[256]; - - /* - * Calculate a salt value - */ - for (i = 0; i < SALTSIZE; i++) - salt[i] = chars[arc4random_uniform(sizeof(chars) - 1)]; - salt[SALTSIZE] = '\0'; + static char buf[_PASSWORD_LEN + 1]; + + if (!crypt_makesalt(salt, format, &salt_sz) ) { + if (salt_sz == sizeof(salt) ) { + errx(EX_CONFIG, "Unable to create salt for crypt(3) format: %s", format); + } else { + /* really shouldn't get here */ + errx(EX_CONFIG, "Not enough space to write salt to buffer. CRYPT_SALT_MAX_LEN is wrong."); + } + } cryptpw = crypt(password, salt); if (cryptpw == NULL) @@ -1110,8 +1107,9 @@ pw_pwcrypt(char *password) static char * -pw_password(struct userconf * cnf, struct cargs * args, char const * user) +pw_password(struct userconf * cnf, struct cargs * args, char const * user, const char * format) { + static char const chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./!@#$%^&*()-+=<>?"; int i, l; char pwbuf[32]; @@ -1145,7 +1143,7 @@ pw_password(struct userconf * cnf, struct cargs * args, char const * user) strlcpy(pwbuf, user, sizeof(pwbuf)); break; } - return pw_pwcrypt(pwbuf); + return pw_pwcrypt(pwbuf, format); }