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.
12 #ident "$Id: salt.c 2788 2009-04-24 22:49:20Z nekral-guest $"
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];
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 snprintf (rounds_prefix, 18, "rounds=%ld$", rounds);
155 /* Sanity checks. That should not be necessary. */
156 rounds_prefix[17] = '\0';
157 if ('$' != rounds_prefix[16]) {
158 rounds_prefix[17] = '$';
161 return rounds_prefix;
163 #endif /* USE_SHA_CRYPT */
166 * Generate salt of size salt_size.
168 #define MAX_SALT_SIZE 16
169 #define MIN_SALT_SIZE 8
171 static /*@observer@*/const char *gensalt (size_t salt_size)
173 static char salt[32];
177 assert (salt_size >= MIN_SALT_SIZE &&
178 salt_size <= MAX_SALT_SIZE);
180 strcat (salt, l64a (random()));
182 strcat (salt, l64a (random()));
183 } while (strlen (salt) < salt_size);
185 salt[salt_size] = '\0';
191 * Generate 8 base64 ASCII characters of random salt. If MD5_CRYPT_ENAB
192 * in /etc/login.defs is "yes", the salt string will be prefixed by "$1$"
193 * (magic) and pw_encrypt() will execute the MD5-based FreeBSD-compatible
194 * version of crypt() instead of the standard one.
195 * Other methods can be set with ENCRYPT_METHOD
197 * The method can be forced with the meth parameter.
198 * If NULL, the method will be defined according to the MD5_CRYPT_ENAB and
199 * ENCRYPT_METHOD login.defs variables.
201 * If meth is specified, an additional parameter can be provided.
202 * * For the SHA256 and SHA512 method, this specifies the number of rounds
205 /*@observer@*/const char *crypt_make_salt (/*@null@*/const char *meth, /*@null@*/void *arg)
207 /* Max result size for the SHA methods:
209 * +17 rounds=999999999$
213 static char result[40];
222 method = getdef_str ("ENCRYPT_METHOD");
223 if (NULL == method) {
224 method = getdef_bool ("MD5_CRYPT_ENAB") ? "MD5" : "DES";
228 if (0 == strcmp (method, "MD5")) {
231 } else if (0 == strcmp (method, "SHA256")) {
233 strcat(result, SHA_salt_rounds((int *)arg));
234 salt_len = SHA_salt_size();
235 } else if (0 == strcmp (method, "SHA512")) {
237 strcat(result, SHA_salt_rounds((int *)arg));
238 salt_len = SHA_salt_size();
239 #endif /* USE_SHA_CRYPT */
240 } else if (0 != strcmp (method, "DES")) {
242 _("Invalid ENCRYPT_METHOD value: '%s'.\n"
243 "Defaulting to DES.\n"),
249 * Concatenate a pseudo random salt.
251 assert (sizeof (result) > strlen (result) + salt_len);
252 strncat (result, gensalt (salt_len),
253 sizeof (result) - strlen (result) - 1);