2 * salt.c - generate a random salt string for crypt()
4 * Written by Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>,
5 * it is in the public domain.
7 * l64a was Written by J.T. Conklin <jtc@netbsd.org>. Public domain.
18 #include "prototypes.h"
22 /* local function prototypes */
23 static void seedRNG (void);
24 static /*@observer@*/const char *gensalt (size_t salt_size);
26 static size_t SHA_salt_size (void);
27 static /*@observer@*/const char *SHA_salt_rounds (/*@null@*/int *prefered_rounds);
28 #endif /* USE_SHA_CRYPT */
31 static /*@observer@*/char *l64a(long value)
43 for (i = 0; value != 0 && i < 6; i++) {
48 } else if (digit < 12) {
50 } else if (digit < 38) {
51 *s = digit + 'A' - 12;
53 *s = digit + 'a' - 38;
64 #endif /* !HAVE_L64A */
66 static void seedRNG (void)
69 static int seeded = 0;
72 (void) gettimeofday (&tv, NULL);
73 srandom (tv.tv_sec ^ tv.tv_usec ^ getpid ());
79 * Add the salt prefix.
81 #define MAGNUM(array,ch) (array)[0]=(array)[2]='$',(array)[1]=(ch),(array)[3]='\0'
85 * Return the salt size.
86 * The size of the salt string is between 8 and 16 bytes for the SHA crypt
89 static size_t SHA_salt_size (void)
93 rand_size = (double) 9.0 * random () / RAND_MAX;
94 return (size_t) (8 + rand_size);
97 /* Default number of rounds if not explicitly specified. */
98 #define ROUNDS_DEFAULT 5000
99 /* Minimum number of rounds. */
100 #define ROUNDS_MIN 1000
101 /* Maximum number of rounds. */
102 #define ROUNDS_MAX 999999999
105 * Return a salt prefix specifying the rounds number for the SHA crypt methods.
107 static /*@observer@*/const char *SHA_salt_rounds (/*@null@*/int *prefered_rounds)
109 static char rounds_prefix[18]; /* Max size: rounds=999999999$ */
112 if (NULL == prefered_rounds) {
113 long min_rounds = getdef_long ("SHA_CRYPT_MIN_ROUNDS", -1);
114 long max_rounds = getdef_long ("SHA_CRYPT_MAX_ROUNDS", -1);
117 if ((-1 == min_rounds) && (-1 == max_rounds)) {
121 if (-1 == min_rounds) {
122 min_rounds = max_rounds;
125 if (-1 == max_rounds) {
126 max_rounds = min_rounds;
129 if (min_rounds > max_rounds) {
130 max_rounds = min_rounds;
134 rand_rounds = (double) (max_rounds-min_rounds+1.0) * random ();
135 rand_rounds /= RAND_MAX;
136 rounds = min_rounds + rand_rounds;
137 } else if (0 == *prefered_rounds) {
140 rounds = *prefered_rounds;
143 /* Sanity checks. The libc should also check this, but this
144 * protects against a rounds_prefix overflow. */
145 if (rounds < ROUNDS_MIN) {
149 if (rounds > ROUNDS_MAX) {
153 (void) snprintf (rounds_prefix, sizeof rounds_prefix,
154 "rounds=%ld$", rounds);
156 return rounds_prefix;
158 #endif /* USE_SHA_CRYPT */
161 * Generate salt of size salt_size.
163 #define MAX_SALT_SIZE 16
164 #define MIN_SALT_SIZE 8
166 static /*@observer@*/const char *gensalt (size_t salt_size)
168 static char salt[32];
172 assert (salt_size >= MIN_SALT_SIZE &&
173 salt_size <= MAX_SALT_SIZE);
175 strcat (salt, l64a (random()));
177 strcat (salt, l64a (random()));
178 } while (strlen (salt) < salt_size);
180 salt[salt_size] = '\0';
186 * Generate 8 base64 ASCII characters of random salt. If MD5_CRYPT_ENAB
187 * in /etc/login.defs is "yes", the salt string will be prefixed by "$1$"
188 * (magic) and pw_encrypt() will execute the MD5-based FreeBSD-compatible
189 * version of crypt() instead of the standard one.
190 * Other methods can be set with ENCRYPT_METHOD
192 * The method can be forced with the meth parameter.
193 * If NULL, the method will be defined according to the MD5_CRYPT_ENAB and
194 * ENCRYPT_METHOD login.defs variables.
196 * If meth is specified, an additional parameter can be provided.
197 * * For the SHA256 and SHA512 method, this specifies the number of rounds
200 /*@observer@*/const char *crypt_make_salt (/*@null@*//*@observer@*/const char *meth, /*@null@*/void *arg)
202 /* Max result size for the SHA methods:
204 * +17 rounds=999999999$
208 static char result[40];
217 method = getdef_str ("ENCRYPT_METHOD");
218 if (NULL == method) {
219 method = getdef_bool ("MD5_CRYPT_ENAB") ? "MD5" : "DES";
223 if (0 == strcmp (method, "MD5")) {
226 } else if (0 == strcmp (method, "SHA256")) {
228 strcat(result, SHA_salt_rounds((int *)arg));
229 salt_len = SHA_salt_size();
230 } else if (0 == strcmp (method, "SHA512")) {
232 strcat(result, SHA_salt_rounds((int *)arg));
233 salt_len = SHA_salt_size();
234 #endif /* USE_SHA_CRYPT */
235 } else if (0 != strcmp (method, "DES")) {
237 _("Invalid ENCRYPT_METHOD value: '%s'.\n"
238 "Defaulting to DES.\n"),
244 * Concatenate a pseudo random salt.
246 assert (sizeof (result) > strlen (result) + salt_len);
247 strncat (result, gensalt (salt_len),
248 sizeof (result) - strlen (result) - 1);