Index: include/unistd.h =================================================================== --- include/unistd.h +++ include/unistd.h @@ -496,6 +496,7 @@ int check_utility_compat(const char *); const char * crypt_get_format(void); +int crypt_makesalt(char *, const char *, size_t *); char *crypt_r(const char *, const char *, struct crypt_data *); int crypt_set_format(const char *); int dup3(int, int, int); Index: lib/libcrypt/Makefile =================================================================== --- lib/libcrypt/Makefile +++ lib/libcrypt/Makefile @@ -17,7 +17,9 @@ crypt-sha256.c sha256c.c \ crypt-sha512.c sha512c.c MAN= crypt.3 -MLINKS= crypt.3 crypt_get_format.3 crypt.3 crypt_r.3 \ +MLINKS= crypt.3 crypt_get_format.3 \ + crypt.3 crypt_makesalt.3 \ + crypt.3 crypt_r.3 \ crypt.3 crypt_set_format.3 CFLAGS+= -I${SRCTOP}/lib/libmd -I${SRCTOP}/lib/libutil \ -I${SRCTOP}/sys/crypto/sha2 Index: lib/libcrypt/crypt.3 =================================================================== --- lib/libcrypt/crypt.3 +++ lib/libcrypt/crypt.3 @@ -1,6 +1,7 @@ .\" FreeSec: libcrypt for NetBSD .\" .\" Copyright (c) 1994 David Burren +.\" Copyright (c) 2015 Derek Marcotte .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without @@ -15,10 +16,10 @@ .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" -.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS 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) @@ -29,222 +30,274 @@ .\" .\" $FreeBSD$ .\" -.Dd August 10, 2016 +.Dd August 16, 2018 .Dt CRYPT 3 .Os .Sh NAME -.Nm crypt -.Nd Trapdoor encryption +.Nm crypt , +.Nm crypt_makesalt , +.Nm crypt_get_format , +.Nm crypt_set_format +.Nd "library for password hashing" .Sh LIBRARY .Lb libcrypt .Sh SYNOPSIS .In unistd.h -.Ft char * +.Ft "char *" .Fn crypt "const char *key" "const char *salt" .Ft char * .Fn crypt_r "const char *key" "const char *salt" "struct crypt_data *data" -.Ft const char * +.Ft "int" +.Fn crypt_makesalt "char *output" "const char *format" "size_t *out_len" +.Ft "const char *" .Fn crypt_get_format "void" -.Ft int +.Ft "int" .Fn crypt_set_format "const char *string" .Sh DESCRIPTION The -.Fn crypt -function performs password hashing with additional code added to -deter key search attempts. -Different algorithms can be used to -in the hash. -.\" -.\" NOTICE: -.\" If you add more algorithms, make sure to update this list -.\" and the default used for the Traditional format, below. -.\" -Currently these include the -.Tn NBS -.Tn Data Encryption Standard (DES) , -.Tn MD5 -hash, -.Tn NT-Hash -.Pq compatible with Microsoft's NT scheme +.Nm libcrypt +library covers functions related to password hashing. +.Pp +Password hashing is different than a standard one way hash function. +Password hashing tries to make it difficult to recover a low-entropy key +.Pq e.g., a typed password +from the resultant output hash. +Offline recovery by exhaustive key search +.Pq brute-force and dictionary attacks +is stymied by the password hashing being one-way, and often computationally expensive. +Different algorithms can be selected to produce the password hash, with varying ability to effectively deter key search attempts. +.Pp +The hashing algorithm to be used for an invocation of crypt is chosen by supplying a salt parameter of a particular format. +The algorithms currently supported are: +.Sx bcrypt , +.Sx sha512-crypt , +.Sx sha256-crypt , +.Sx md5-crypt , +.Sx DES Extended Format , +.Sx Traditional DES Format , and -.Tn Blowfish . -The algorithm used will depend upon the format of the Salt -.Po -following -the Modular Crypt Format -.Pq MCF -.Pc , -if -.Tn DES -and/or -.Tn Blowfish -is installed or not, and whether -.Fn crypt_set_format -has been called to change the default. +.Sx NT-Hash . +.Pp +Current computational capabilities make all but the bcrypt and sha-crypt families known to be susceptible to exhaustive key searches. +The tune-able work factor of the bcrypt and sha-crypt families has allowed them to be set to be more computationally intensive over time. +The use of the other families is not recommended at this time, but are maintained for legacy compatibility. .Pp The first argument to -.Nm -is the data to hash +.Fn crypt +is the +.Fa key +to hash .Pq usually a password , -in a -.Dv NUL Ns -terminated -string. -The second is the salt, in one of three forms: +as a NUL-terminated string. +The second is the +.Fa salt . +A salt is a random value that assists in complicating offline recovery. +The length and format of the salt, and how it will be used is algorithm dependent. +For best results, specify at least sixteen characters of salt. .Pp -.Bl -tag -width Traditional -compact -offset indent -.It Extended +If the salt begins with the string +.Qq $digit , +then the +.Sx Modular Crypt Format +is used. If it begins with an underscore -.Pq Dq _ +.Qq _ , then the -.Tn DES -Extended Format -is used in interpreting both the key and the salt, as outlined below. -.It Modular -If it begins with the string -.Dq $digit$ -then the Modular Crypt Format is used, as outlined below. -.It Traditional -If neither of the above is true, it assumes the Traditional Format, -using the entire string as the salt -.Pq or the first portion . -.El +.Sx DES Extended Format +is used. +If the salt is 2 or 13 characters and includes only 0-9, a-z, A-Z a dot . or a slash /, then +.Sx Traditional DES Format +is used. +If none of these conditions are true, crypt will use the default hash function set by crypt_set_format, +.Pq or Traditional DES Format if no default has been set, for systems with DES support . .Pp -All routines are designed to be time-consuming. -.Ss DES Extended Format: The -.Ar key -is divided into groups of 8 characters -.Pq the last group is NUL-padded -and the low-order 7 bits of each character -.Pq 56 bits per group -are used to form the -.Tn DES -key as follows: -the first group of 56 bits becomes the initial -.Tn DES -key. -For each additional group, the XOR of the encryption of the current -.Tn DES -key with itself and the group bits becomes the next -.Tn DES -key. -.Pp -The salt is a 9-character array consisting of an underscore followed -by 4 bytes of iteration count and 4 bytes of salt. -These are encoded as printable characters, 6 bits per character, -least significant character first. -The values 0 to 63 are encoded as -.Dq ./0-9A-Za-z . -This allows 24 bits for both -.Fa count -and -.Fa salt . +.Fn crypt_makesalt +function populates an +.Fa output +buffer with a newly generated salt for the specified valid +.Fa format . +The size of the pre-allocated buffer is passed via +.Fa out_len . +If the output buffer is too small, the required size is set in +.Fa out_len . .Pp The -.Fa salt -introduces disorder in the -.Tn DES -algorithm in one of 16777216 or 4096 possible ways -.Po -i.e., with 24 or 12 bits: if bit -.Em i -of the -.Ar salt -is set, then bits -.Em i -and -.Em i+24 -are swapped in the -.Tn DES -E-box output -.Pc . +.Fn crypt_get_format +function returns a constant string that represents the name of the algorithm set as the default. +Its use is deprecated. .Pp The -.Tn DES -key is used to encrypt a 64-bit constant using -.Ar count -iterations of -.Tn DES . -The value returned is a -.Dv NUL Ns -terminated -string, 20 or 13 bytes -.Pq plus NUL -in length, consisting of the -.Ar salt -followed by the encoded 64-bit encryption. -.Ss Modular crypt: -If the salt begins with the string -.Fa $digit$ -then the Modular Crypt Format is used. -The -.Fa digit -represents which algorithm is used in encryption. -Following the token is -the actual salt to use in the encryption. -The maximum length of the salt used depends upon the module. -The salt must be terminated with the end of the string character -.Pq NUL -or a dollar sign. -Any characters after the dollar sign are ignored. -.Pp -Currently supported algorithms are: -.Pp -.Bl -enum -compact -offset indent -.It -MD5 -.It -Blowfish -.It -NT-Hash -.It -(unused) -.It -SHA-256 -.It -SHA-512 +.Fn crypt_set_format +function sets the default encoding +.Fa format +according to the supplied string. +Its use is deprecated. +.Pp +The salts, formats, and algorithms are outlined below. +.Sh Modular Crypt Format +Modular Crypt Format is the current standard way to describe which algorithm and parameters to use for a particular crypt invocation. +Salts that are prefixed with $digit are using the modular crypt format. +The prefix selects the algorithm to use. +Valid prefixes are: +.Bl -column -offset indent ".Sy Prefix" ".Sy Algorithm" +.It Sy Prefix Ta Sy Algorithm +.It Li $1$ Ta +.Sx md5-crypt +.It Li $2 Ta +.Sx bcrypt +.It Li $3$ Ta +.Sx NT-Hash +.It Li $5$ Ta +.Sx sha256-crypt +.It Li $6$ Ta +.Sx sha512-crypt .El .Pp -Other crypt formats may be easily added. -An example salt would be: -.Bl -tag -width 6n -offset indent -.It Cm "$4$thesalt$rest" +Everything following the prefix in the salt parameter is algorithm dependent, but includes things like work factor, and an actual salt +.Pq random value for that particular invocation . +.Ss md5-crypt +.Bl -column ".Sy Output Hash Example:" ".Sy 8 characters from the set [a-zA-Z0-9./]" +.It Li Format Strings : Ta $1$, md5 +.It Li Salt Format : Ta 8 characters from the set [a-zA-Z0-9./] +.It Li Full Salt Example : Ta $1$deadbeef$ +.It Li Output Hash Example : Ta $1$deadbeef$0Huu6KHrKLVWfqa4WljDE0 .El -.Ss Traditional crypt: -The algorithm used will depend upon whether -.Fn crypt_set_format -has been called and whether a global default format has been specified. -Unless a global default has been specified or -.Fn crypt_set_format -has set the format to something else, the built-in default format is -used. -This is currently -.\" -.\" NOTICE: Also make sure to update this -.\" -DES -if it is available, or MD5 if not. .Pp -How the salt is used will depend upon the algorithm for the hash. -For -best results, specify at least eight characters of salt. +md5-crypt was the default crypt format in +.Fx for many years, developed by Poul-Henning Kamp. .Pp -The -.Fn crypt_get_format -function returns a constant string that represents the name of the -algorithm currently used. -Valid values are -.\" -.\" NOTICE: Also make sure to update this, too, as well -.\" -.Ql des , -.Ql blf , -.Ql md5 , -.Ql sha256 , -.Ql sha512 -and -.Ql nth . +It has seen widespread use in many commercial products, and for a long time was good enough. +It has a fixed work factor, which means that as computers became faster, the ability to attack it has increased proportionally. .Pp -The +Its use is no longer recommended. +.Ss bcrypt +.Bl -column ".Sy Output Hash Example:" ".Sy $2b$08$CCCCCCCCCCCCCCCCCCCCC.HB0zhI4CA576EzokBu6usBUNIQZK6KS" +.It Li Format Strings : Ta $2$[rounds]$, $2a$[rounds]$, $2b$[rounds]$, blf +.It Li Salt Format : Ta 16 bytes then base-64 encoded from the set [a-zA-Z0-9./] +.It Li Full Salt Example : Ta $2b$08$CCCCCCCCCCCCCCCCCCCCC. +.It Li Output Hash Example : Ta $2b$08$CCCCCCCCCCCCCCCCCCCCC.HB0zhI4CA576EzokBu6usBUNIQZK6KS +.El +.Pp +bcrypt is the first algorithm to support a tuneable work factor. +It was initially introduced in +.Ox , by Niels Provos, based on the Usenix paper "A Future-Adaptable Password Scheme" by Niels Provos and David Mazi\(`eres. +.Pp +There are three variations supported, each with minor bug fixes. +2b is the recommended variation. +.Pp +The work factor is specified by the rounds parameter in the format string. +These rounds are logarithmic. +That is, +.Qq $2b$08$ +will take approximately twice as long as +.Qq $2b$07$ +for the same salt and key. +04 is the lowest supported work factor, and 31 is the highest. +.Pp +When the format string +.Qq blf +is specified, it is equivalent to specifying +.Qq $2b$04$ . +.Ss NT-Hash +.Bl -column ".Sy Output Hash Example:" ".Sy $3$sdlksjfdlksjdlfk" +.It Li Format Strings : Ta $3$, nth +.It Li Salt Format : Ta no salt +.It Li Full Salt Example : Ta $3$ +.It Li Output Hash Example : Ta $3$$cc0fb8a290eebbfe74e7207f2ace5927 +.El +.Pp +nt-hash is supported for compatibility reasons only. +It is strongly discouraged for any production uses. +.Ss sha256-crypt +.Bl -column ".Sy Output Hash Example:" ".Sy $5$saltstring$5B8vYYiY.CVt1RlTTf8KbXBH3hsxY/GNooZaBBGWEc5, $5$rounds=10000$saltstringsaltst$3xv.VbSHBb41AL9AvLeujZkZRBAwqFMz2.opqey6IcA " +.It Li Format Strings : Ta $5$, $5$rounds=[rounds]$, sha256 +.It Li Salt Format : Ta 16 characters from the set [a-zA-Z0-9./] +.It Li Full Salt Examples : Ta $5$saltstring$, $5$rounds=10000$saltstringsaltst$ +.It Li Output Hash Examples : Ta $5$saltstring$5B8vYYiY.CVt1RlTTf8KbXBH3hsxY/GNooZaBBGWEc5, $5$rounds=10000$saltstringsaltst$3xv.VbSHBb41AL9AvLeujZkZRBAwqFMz2.opqey6IcA +.El +.Pp +sha256 supports a tunable work factor. +It was developed by Ulrich Drepper of Red Hat, and is detailed in +.Qq Unix crypt using SHA-256 and SHA-512 . +.Pp +From that document: +.Pp +.Qo +Security departments in companies are trying to phase out all uses of MD5. +They demand a method which is officially sanctioned. +For US-based users this means tested by the NIST. +.Pp +This rules out the use of another already implemented method with limited spread: the use of the Blowfish encryption method. +The choice comes down to tested encryption +.Pq 3DES, AES +or hash sums +.Pq the SHA family . +.Qc +.Pp +The prepositions in the above statement are misleading. +Blowfish as a primitive, like 3DES or AES, has stood up to years of scrutinty by the cryptographic communinty. +bcrypt, the password hashing function, is ubiquitous. +It also currently provides greater resilience against pipe-lined or GPU-based attacks for the approximate same CPU workload as the sha-crypt family, based on its limited memory requirements. +This is not expected to remain true with future improvements to GPUs. +An additional subtle difference between bcrypt and the sha families is that bcrypt's salt is 2^128 bits, while the sha family is 2^96 bits. +.Pp +If you require a algorithm that includes NIST sanctioned primitives, choose one of the sha-crypt methods. +.Pp +The work factor is specified by the rounds parameter in the format string. +These rounds are linear. +That is, +.Qq $5$rounds=20000$ +will take approximately twice as long as +.Qq $5$rounds=10000$ +for the same salt and key. +1000 is the minimum number of rounds, 999999999 is the maximum. +.Pp +When the format string +.Qq sha256 , +or +.Qq $5$ +is specified, it is equivalent to specifying +.Qq $5$rounds=5000$ . +.Ss sha512-crypt +.Bl -column ".Sy Output Hash Example:" ".Sy $6$saltstring$svn8UoSVapNtMuq1ukKS4tPQd8iKwSMHWjl/O817G3uBnIFNjnQJuesI68u4OTLiBFdcbYEdFCoEOfaS35inz1, $6$rounds=10000$saltstringsaltst$OW1/O6BYHV6BcXZu8QVeXbDWra3Oeqh0sbHbbMCVNSnCM/UrjmM0Dp8vOuZeHBy/YTBmSK6H9qs/y3RnOaw5v." +.It Li Format Strings : Ta $6$, $6$rounds=[rounds]$, sha512 +.It Li Salt Format : Ta 16 characters from the set [a-zA-Z0-9./] +.It Li Full Salt Examples : Ta $6$saltstring$, $6$rounds=10000$saltstringsaltst$ +.It Li Output Hash Examples : Ta $6$saltstring$svn8UoSVapNtMuq1ukKS4tPQd8iKwSMHWjl/O817G3uBnIFNjnQJuesI68u4OTLiBFdcbYEdFCoEOfaS35inz1, $6$rounds=10000$saltstringsaltst$OW1/O6BYHV6BcXZu8QVeXbDWra3Oeqh0sbHbbMCVNSnCM/UrjmM0Dp8vOuZeHBy/YTBmSK6H9qs/y3RnOaw5v. +.El +sha512 is nearly equivalent to sha256, except that it uses SHA512 as a primitive. +See Unix crypt using SHA-256 and SHA-512 for more details. +The details provided in sha256 apply here as well. +When the format string +.Qq sha512 , +or +.Qq $6$ +is specified, it is equivalent to specifying +.Qq $6$rounds=5000$ . +.Sh DES Extended Format +The key is divided into groups of 8 characters +.Pq the last group is NUL-padded +and the low-order 7 bits of each character +.Pq 56 bits per group +are used to form the DES key as follows: the first group of 56 bits becomes the initial DES key. +For each additional group, the XOR of the encryption of the current DES key with itself and the group bits becomes the next DES key. +.Pp +The salt is a 9-character array consisting of an underscore followed by 4 bytes of iteration count and 4 bytes of salt. +These are encoded as printable characters, 6 bits per character, least significant character first. +The values 0 to 63 are encoded as +.Qq ./0-9A-Za-z . +This allows 24 bits for both count and salt. +.Pp +The salt introduces disorder in the DES algorithm in one of 16777216 or 4096 possible ways +.Pq i.e., with 24 or 12 bits: if bit i of the salt is set, then bits i and i+24 are swapped in the DES E-box output . +.Pp +The DES key is used to encrypt a 64-bit constant using count iterations of DES. +The value returned is a NUL-terminated string, 20 or 13 bytes +.Pq plus NUL +in length, consisting of the salt followed by the encoded 64-bit encryption. +.Sh Traditional DES Format +The algorithm used will depend upon whether .Fn crypt_set_format function sets the default encoding format according to the supplied .Fa string . @@ -263,32 +316,109 @@ .Fn crypt_r functions return a pointer to the encrypted value on success, and NULL on failure. -Note: this is not a standard behaviour, AT&T +Note: this is not a standard behavior, AT&T .Fn crypt will always return a pointer to a string. .Pp The +.Fn crypt_makesalt +function will return a 0 on success, or non-zero on failure. +It may fail in one of two ways. +If +.Fa out_len +has changed, the +.Fa output +buffer was not large enough to store the salt. +The required size will be stored in +.Fa out_len . +If +.Fa out_len +has not changed, then the +.Fa format +passed was invalid. +.Pp +The .Fn crypt_set_format function will return 1 if the supplied encoding format was valid. Otherwise, a value of 0 is returned. +.Sh EXAMPLES +.Bd -literal +#include +#include +#include + +int main(void) +{ + struct crypt_data buf1, buf2; + char *hash, *check; + char salt[256]; + size_t salt_sz; + + salt_sz = sizeof(salt); + + /* Generate a new salt for a crypt format specification. */ + if (crypt_makesalt(salt, "$2b$08$", &salt_sz)) { + if (salt_sz != sizeof(salt)) { + printf("Buffer too small for format salt.\\n"); + return (1); + } + + printf("Invalid format specified.\\n"); + return (2); + } + + printf("crypt_makesalt result: %s\\n", salt); + + /* + * Generate a crypt for storage, using salt as the algorithm selection + * and parameters. + */ + hash = crypt_r("Initial example password.", salt, &buf1); + if (hash == NULL) { + printf("crypt_r (hash) failed.\\n"); + return (3); + } + + printf("crypt_r (hash) result: %s\\n", hash); + + /* Generate a crypt of a known value using the salt of a stored hash. */ + check = crypt_r("Password provided at a later time.", hash, &buf2); + if (check == NULL) { + printf("crypt_r (check) failed.\\n"); + return (4); + } + + printf("crypt_r (check) result: %s\\n", check); + + /* + * Do not leak anything about the original hash when comparing. Timing + * safe comparison is prudent. + */ + if (timingsafe_bcmp(hash, check, strlen(hash))) { + printf("The two passwords do not match.\\n"); + return (5); + } + + printf("The two passwords match.\\n"); + + return (0); +} +.Ed .Sh SEE ALSO .Xr login 1 , .Xr passwd 1 , .Xr getpass 3 , +.Xr login_getcapstr 3 , .Xr passwd 5 .Sh HISTORY A rotor-based .Fn crypt -function appeared in -.At v6 . +function appeared in Version 6 AT&T UNIX. The current style .Fn crypt -first appeared in -.At v7 . +first appeared in Version 7 AT&T UNIX. .Pp -The -.Tn DES -section of the code (FreeSec 1.0) was developed outside the United +The DES section of the code (FreeSec 1.0) was developed outside the United States of America as an unencumbered replacement for the U.S.-only .Nx libcrypt encryption library. @@ -298,30 +428,42 @@ function was added in .Fx 12.0 . .Sh AUTHORS -.An -nosplit Originally written by -.An David Burren Aq Mt davidb@werj.com.au , -later additions and changes by +.An -nosplit +.An David Burren Aq Mt davidb@werj.com.au +, later additions and changes by .An Poul-Henning Kamp , .An Mark R V Murray , .An Michael Bretterklieber , .An Kris Kennaway , .An Brian Feldman , -.An Paul Herman -and -.An Niels Provos . +.An Paul Herman , +.An Niels Provos , and +.An Derek Marcotte . .Sh BUGS The .Fn crypt -function returns a pointer to static data, and subsequent calls to +function returns a pointer to static data, and subsequent +calls to .Fn crypt will modify the same data. Likewise, .Fn crypt_set_format modifies static data. +.Sh SECURITY CONSIDERATIONS +The following algorithms are considered insecure, and are not recommended +for new implementations: +.Sx md5-crypt , +.Sx DES Extended Format , +.Sx Traditional DES Format , +and +.Sx NT-Hash . .Pp -The NT-hash scheme does not use a salt, -and is not hard -for a competent attacker -to break. -Its use is not recommended. +.Sx bcrypt +is preferred over +.Sx sha512-crypt , +or +.Sx sha256-crypt , +because of +its resiliance to pipelined, and GPU based attacks - unless having a +NIST-approved algorithm is a requirement. Index: lib/libcrypt/crypt.c =================================================================== --- lib/libcrypt/crypt.c +++ lib/libcrypt/crypt.c @@ -3,6 +3,7 @@ * * Copyright (c) 1999 Mark Murray * Copyright (c) 2014 Dag-Erling Smørgrav + * Copyright (c) 2015 Derek Marcotte * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,48 +31,144 @@ #include __FBSDID("$FreeBSD$"); -#include +#include +#include #include +#include +#include +#include #include #include #include "crypt.h" +static const struct crypt_format *crypt_find_format(const char *); +static bool crypt_validate_format(const char *, const char *); +static bool crypt_format_is_modular(const char*); + /* * 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 *name; int (*func)(const char *, const char *, char *); const char *magic; + const char *const default_format; + const char *const format_regex; + + const uint8_t salt_bytes; + /* Do we tack on a $ at the end of the salt? */ + const bool salt_trailing_sign; } 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 } + /* Sentinel */ + { NULL, NULL, NULL, NULL, NULL, 0, NULL } }; -static const struct crypt_format *crypt_format = - &crypt_formats[(sizeof crypt_formats / sizeof *crypt_formats) - 2]; +/* + * Must be des if system has des. This was attempted to be changed on r261913, + * but had to be reverted on r264964, with the following comment: + * + * r261913 broke DES passwords, because the only way they could work, + * since they don't have an easily recognizable signature, was if they + * were the default. + * + */ +#ifdef HAS_DES +static char default_format[256] = "des"; +#else +static char default_format[256] = "sha512"; +#endif #define DES_SALT_ALPHABET \ - "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +/* + * Fill a buffer with a new salt conforming to a particular crypt format. + * + * We're breaking the API convention established by crypt_set_format (return 0 + * on success) because it might be nice to behave like the rest of C libraries, + * rather than the one deprecated function. + * + */ +int +crypt_makesalt(char *out, const char *format, size_t *outlen) +{ + const struct crypt_format *cf; + uint8_t rand_buf[3]; + char rand_b64[5]; + char *rand_b64_p; + const char *prefix; + size_t prefix_len; + size_t reqsz; + int diff; + unsigned int i; + + cf = crypt_find_format(format); + if (cf == NULL) { + return (EINVAL); + } + + /* 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++; + + if (reqsz > *outlen) { + *outlen = reqsz; + return (ENOMEM); + } + + strlcpy(out, prefix, *outlen); + for (i = 0; i < cf->salt_bytes; i += 4) { + arc4random_buf(rand_buf, 3); + + diff = MIN(cf->salt_bytes - i, 4); + rand_b64_p = (char *) rand_b64; + b64_from_24bit(rand_buf[2], rand_buf[1], rand_buf[0], diff, + &rand_b64_p); + rand_b64[diff] = '\0'; + + strlcat(out, rand_b64, *outlen); + } + + explicit_bzero(rand_buf, sizeof(rand_buf)); + explicit_bzero(rand_b64, sizeof(rand_b64)); + + if (cf->salt_trailing_sign) { + strlcat(out, "$", *outlen); + } + + return (0); +} /* * Returns the name of the currently selected format. @@ -80,7 +177,7 @@ crypt_get_format(void) { - return (crypt_format->name); + return (default_format); } /* @@ -88,16 +185,81 @@ */ int crypt_set_format(const char *format) +{ + + if (!crypt_find_format(format)) { + return (0); + } + strlcpy(default_format, format, sizeof(default_format)); + return (1); +} + +/* + * Is the crypt format a recognized as valid. + */ +static bool +crypt_format_validate(const char* regex, const char *format) +{ + regex_t regex_c; + int res; + + /* + * We could cache these, but they are simple, and this function won't be + * called often. + */ + if (regcomp(®ex_c, regex, REG_EXTENDED)) { + return (false); + } + res = regexec(®ex_c, format, 0, NULL, 0); + regfree(®ex_c); + return (!res); +} + +/* + * Is the crypt format a modular format. + */ +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] == '_'); +} + +/* + * Lookup our format in our internal table for a matching crypt_format + * structure. + */ +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")) { + 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(cf->format_regex, format)) { + return (cf); + } + } + } else { + /* Name lookup. */ + for (cf = crypt_formats; cf->name != NULL; ++cf) { + if (!strcasecmp(cf->name, format)) { + return (cf); + } } } - return (0); + + return (NULL); } /* @@ -114,22 +276,27 @@ int len; #endif - for (cf = crypt_formats; cf->name != NULL; ++cf) + /* 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) { func = cf->func; goto match; } + } #ifdef HAS_DES + /* Check if it's standard DES. */ len = strlen(salt); if ((len == 13 || len == 2) && strspn(salt, DES_SALT_ALPHABET) == len) { func = crypt_des; goto match; } #endif - func = crypt_format->func; + cf = crypt_find_format(default_format); + func = cf->func; match: - if (func(passwd, salt, data->__buf) != 0) + if (func(passwd, salt, data->__buf)) { return (NULL); + } return (data->__buf); } Index: lib/libpam/modules/pam_unix/pam_unix.c =================================================================== --- lib/libpam/modules/pam_unix/pam_unix.c +++ lib/libpam/modules/pam_unix/pam_unix.c @@ -69,17 +69,11 @@ #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" @@ -276,13 +270,15 @@ struct ypclnt *ypclnt; const void *yp_domain, *yp_server; #endif - char salt[SALTSIZE + 1]; + char salt[256]; + 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)) user = getlogin(); @@ -386,10 +382,16 @@ 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); + PAM_LOG("Unable to create salt for crypt(3) format: %s", + passwd_format); + return (PAM_SERVICE_ERR); + } + /* set password expiry date */ pwd->pw_change = 0; passwordtime = login_getcaptime(lc, "passwordtime", 0, 0); @@ -397,7 +399,6 @@ 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) { @@ -453,33 +454,4 @@ 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"); Index: lib/libutil/login.conf.5 =================================================================== --- lib/libutil/login.conf.5 +++ lib/libutil/login.conf.5 @@ -19,7 +19,7 @@ .\" .\" $FreeBSD$ .\" -.Dd July 8, 2011 +.Dd August 3, 2018 .Dt LOGIN.CONF 5 .Os .Sh NAME @@ -278,7 +278,14 @@ allowed before the login fails. .It "passwd_format string sha512 The encryption format that new or" changed passwords will use. -Valid values include "des", "md5", "blf", "sha256" and "sha512"; see +Valid values include Modular Crypt Format strings, +.Qq des , +.Qq md5 , +.Qq blf , +.Qq sha256 +and +.Qq sha512 ; +see .Xr crypt 3 for details. NIS clients using a Index: lib/libutil/login_cap.3 =================================================================== --- lib/libutil/login_cap.3 +++ lib/libutil/login_cap.3 @@ -19,7 +19,7 @@ .\" .\" $FreeBSD$ .\" -.Dd June 14, 2007 +.Dd August 2, 2018 .Dt LOGIN_CAP 3 .Os .Sh NAME @@ -362,7 +362,7 @@ of capability each deals with, be it a simple string, a list, a time value, a file or memory size value, a path (consisting of a colon-separated list of directories) or a boolean flag. -The manpage for +The man page for .Xr login.conf 5 deals in specific tags and their type. .Pp @@ -469,7 +469,7 @@ .Vt long long type, terabytes. The suffix used determines the units, and multiple values and -units may be used in combination (e.g.\& 1m500k = 1.5 megabytes). +units may be used in combination (e.g.,\& 1m500k = 1.5 megabytes). A value with no suffix is interpreted as bytes, .Ql B as 512-byte blocks, @@ -569,6 +569,13 @@ on the specifier fails, .Fa error is returned to indicate this. +.Fn login_setcryptfmt +and +.Xr crypt_set_format 3 +are both deprecated in favor of +.Fn login_getcapstr +and +.Xr crypt_makesalt 3 . .El .Sh SEE ALSO .Xr login 1 , Index: usr.sbin/pw/pw.h =================================================================== --- usr.sbin/pw/pw.h +++ usr.sbin/pw/pw.h @@ -107,7 +107,7 @@ 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[]; Index: usr.sbin/pw/pw_group.c =================================================================== --- usr.sbin/pw/pw_group.c +++ usr.sbin/pw/pw_group.c @@ -93,7 +93,7 @@ errx(EX_DATAERR, "wrong encrypted passwrd"); grp->gr_passwd = line; } else - grp->gr_passwd = pw_pwcrypt(line); + grp->gr_passwd = pw_pwcrypt(line, "default"); } int Index: usr.sbin/pw/pw_user.c =================================================================== --- usr.sbin/pw/pw_user.c +++ usr.sbin/pw/pw_user.c @@ -81,10 +81,12 @@ const char *user); static char *pw_shellpolicy(struct userconf * cnf); static char *pw_password(struct userconf * cnf, char const * user, - bool dryrun); + char const * format, bool dryrun); static char *shell_path(char const * path, char *shells[], char *sh); static void rmat(uid_t uid); static void rmopie(char const * name); +static void copy_passwd_format(char *passwd_format, login_cap_t *lc, + size_t max); static void mkdir_home_parents(int dfd, const char *dir) @@ -179,6 +181,7 @@ login_cap_t *lc; char line[_PASSWORD_LEN+1]; char *p; + char passwd_format[256]; if (fd == '-') { if (!pwd->pw_passwd || *pwd->pw_passwd != '*') { @@ -224,15 +227,36 @@ pwd->pw_passwd = strdup(line); } else { lc = login_getpwclass(pwd); - if (lc == NULL || - login_setcryptfmt(lc, "sha512", NULL) == NULL) - warn("setting crypt(3) format"); + copy_passwd_format(passwd_format, lc, sizeof(passwd_format)); login_close(lc); - pwd->pw_passwd = pw_pwcrypt(line); + pwd->pw_passwd = pw_pwcrypt(line, passwd_format); } return (1); } +void +copy_passwd_format(char *passwd_format, login_cap_t *lc, size_t max) +{ + const char *cap; + const char *def = "sha512"; + + if (lc == NULL) { + warn("setting crypt(3) format"); + strlcpy(passwd_format, def, max); + return; + } + + cap = login_getcapstr(lc, "passwd_format", def, NULL); + if (cap == NULL) { + warn("setting crypt(3) format"); + strlcpy(passwd_format, def, max); + return; + } + + strlcpy(passwd_format, cap, max); + return; +} + static void perform_chgpwent(const char *name, struct passwd *pwd, char *nispasswd) { @@ -482,25 +506,19 @@ return shell_path(cnf->shelldir, cnf->shells, 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[256]; + size_t salt_sz; char *cryptpw; static char buf[256]; size_t pwlen; - /* - * Calculate a salt value - */ - for (i = 0; i < SALTSIZE; i++) - salt[i] = chars[arc4random_uniform(sizeof(chars) - 1)]; - salt[SALTSIZE] = '\0'; + salt_sz = sizeof(salt); + if (crypt_makesalt(salt, format, &salt_sz)) { + errx(EX_CONFIG, "Unable to create salt for crypt(3) format: %s", format); + } cryptpw = crypt(password, salt); if (cryptpw == NULL) @@ -511,8 +529,9 @@ } static char * -pw_password(struct userconf * cnf, char const * user, bool dryrun) +pw_password(struct userconf * cnf, char const * user, const char * format, bool dryrun) { + static char const chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./!@#$%^&*()-+=<>?"; int i, l; char pwbuf[32]; @@ -543,7 +562,7 @@ default: return "*"; } - return pw_pwcrypt(pwbuf); + return pw_pwcrypt(pwbuf, format); } static int @@ -1198,6 +1217,7 @@ int rc, ch, fd = -1; size_t i; bool dryrun, nis, pretty, quiet, createhome, precrypted, genconf; + char passwd_format[256]; dryrun = nis = pretty = quiet = createhome = precrypted = false; genconf = false; @@ -1395,10 +1415,10 @@ pwd->pw_dir = pw_homepolicy(cmdcnf, homedir, pwd->pw_name); pwd->pw_shell = pw_shellpolicy(cmdcnf); lc = login_getpwclass(pwd); - if (lc == NULL || login_setcryptfmt(lc, "sha512", NULL) == NULL) - warn("setting crypt(3) format"); + copy_passwd_format(passwd_format, lc, sizeof(passwd_format)); login_close(lc); - pwd->pw_passwd = pw_password(cmdcnf, pwd->pw_name, dryrun); + pwd->pw_passwd = pw_password(cmdcnf, pwd->pw_name, passwd_format, + dryrun); if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0) warnx("WARNING: new account `%s' has a uid of 0 " "(superuser access!)", pwd->pw_name); @@ -1525,6 +1545,7 @@ bool precrypted; mode_t homemode = 0; time_t expire_time, password_time, now; + char passwd_format[256]; expire_time = password_time = -1; gecos = homedir = grname = name = newname = skel = shell =NULL; @@ -1740,12 +1761,12 @@ if (passwd && conf.fd == -1) { lc = login_getpwclass(pwd); - if (lc == NULL || login_setcryptfmt(lc, "sha512", NULL) == NULL) - warn("setting crypt(3) format"); + copy_passwd_format(passwd_format, lc, sizeof(passwd_format)); login_close(lc); cnf->default_password = passwd_val(passwd, cnf->default_password); - pwd->pw_passwd = pw_password(cnf, pwd->pw_name, dryrun); + pwd->pw_passwd = pw_password(cnf, pwd->pw_name, passwd_format, + dryrun); edited = true; }