]> granicus.if.org Git - shadow/blob - libmisc/salt.c
(failcheck): The failed argument is a bool.
[shadow] / libmisc / salt.c
1 /*
2  * salt.c - generate a random salt string for crypt()
3  *
4  * Written by Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>,
5  * it is in the public domain.
6  *
7  * l64a was Written by J.T. Conklin <jtc@netbsd.org>. Public domain.
8  */
9
10 #include <config.h>
11
12 #ident "$Id$"
13
14 #include <sys/time.h>
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <assert.h>
18 #include "prototypes.h"
19 #include "defines.h"
20 #include "getdef.h"
21
22 /* local function prototypes */
23 #ifndef HAVE_L64A
24 char *l64a(long value);
25 #endif /* !HAVE_L64A */
26 static void seedRNG (void);
27 static char *gensalt (unsigned int salt_size);
28 #ifdef USE_SHA_CRYPT
29 static unsigned int SHA_salt_size (void);
30 static const char *SHA_salt_rounds (int *prefered_rounds);
31 #endif /* USE_SHA_CRYPT */
32
33 #ifndef HAVE_L64A
34 static char *l64a(long value)
35 {
36         static char buf[8];
37         char *s = buf;
38         int digit;
39         int i;
40
41         if (value < 0) {
42                 errno = EINVAL;
43                 return(NULL);
44         }
45
46         for (i = 0; value != 0 && i < 6; i++) {
47                 digit = value & 0x3f;
48
49                 if (digit < 2)
50                         *s = digit + '.';
51                 else if (digit < 12)
52                         *s = digit + '0' - 2;
53                 else if (digit < 38)
54                         *s = digit + 'A' - 12;
55                 else
56                         *s = digit + 'a' - 38;
57
58                 value >>= 6;
59                 s++;
60         }
61
62         *s = '\0';
63
64         return(buf);
65 }
66 #endif /* !HAVE_L64A */
67
68 static void seedRNG (void)
69 {
70         struct timeval tv;
71         static int seeded = 0;
72
73         if (0 == seeded) {
74                 gettimeofday(&tv, NULL);
75                 srandom (tv.tv_sec + tv.tv_usec);
76                 seeded = 1;
77         }
78 }
79
80 /*
81  * Add the salt prefix.
82  */
83 #define MAGNUM(array,ch)        (array)[0]=(array)[2]='$',(array)[1]=(ch),(array)[3]='\0'
84
85 #ifdef USE_SHA_CRYPT
86 /*
87  * Return the salt size.
88  * The size of the salt string is between 8 and 16 bytes for the SHA crypt
89  * methods.
90  */
91 static unsigned int SHA_salt_size (void)
92 {
93         double rand_size;
94         seedRNG ();
95         rand_size = (double) 9.0 * random () / RAND_MAX;
96         return 8 + rand_size;
97 }
98
99 /* ! Arguments evaluated twice ! */
100 #define MAX(x,y) ((x) > (y) ? (x) : (y))
101 #define MIN(x,y) ((x) < (y) ? (x) : (y))
102
103 /* Default number of rounds if not explicitly specified.  */
104 #define ROUNDS_DEFAULT 5000
105 /* Minimum number of rounds.  */
106 #define ROUNDS_MIN 1000
107 /* Maximum number of rounds.  */
108 #define ROUNDS_MAX 999999999
109
110 /*
111  * Return a salt prefix specifying the rounds number for the SHA crypt methods.
112  */
113 static const char *SHA_salt_rounds (int *prefered_rounds)
114 {
115         static char rounds_prefix[18];
116         long rounds;
117
118         if (NULL == prefered_rounds) {
119                 long min_rounds = getdef_long ("SHA_CRYPT_MIN_ROUNDS", -1);
120                 long max_rounds = getdef_long ("SHA_CRYPT_MAX_ROUNDS", -1);
121                 double rand_rounds;
122
123                 if (-1 == min_rounds && -1 == max_rounds)
124                         return "";
125
126                 if (-1 == min_rounds)
127                         min_rounds = max_rounds;
128
129                 if (-1 == max_rounds)
130                         max_rounds = min_rounds;
131
132                 if (min_rounds > max_rounds)
133                         max_rounds = min_rounds;
134
135                 seedRNG ();
136                 rand_rounds = (double) (max_rounds-min_rounds+1.0) * random ();
137                 rand_rounds /= RAND_MAX;
138                 rounds = min_rounds + rand_rounds;
139         } else if (0 == *prefered_rounds)
140                 return "";
141         else
142                 rounds = *prefered_rounds;
143
144         /* Sanity checks. The libc should also check this, but this
145          * protects against a rounds_prefix overflow. */
146         if (rounds < ROUNDS_MIN)
147                 rounds = ROUNDS_MIN;
148
149         if (rounds > ROUNDS_MAX)
150                 rounds = ROUNDS_MAX;
151
152         snprintf (rounds_prefix, 18, "rounds=%ld$", rounds);
153
154         /* Sanity checks. That should not be necessary. */
155         rounds_prefix[17] = '\0';
156         if ('$' != rounds_prefix[16])
157                 rounds_prefix[17] = '$';
158
159         return rounds_prefix;
160 }
161 #endif /* USE_SHA_CRYPT */
162
163 /*
164  *  Generate salt of size salt_size.
165  */
166 #define MAX_SALT_SIZE 16
167 #define MIN_SALT_SIZE 8
168
169 static char *gensalt (unsigned int salt_size)
170 {
171         static char salt[32];
172
173         salt[0] = '\0';
174
175         assert (salt_size >= MIN_SALT_SIZE &&
176                 salt_size <= MAX_SALT_SIZE);
177         seedRNG ();
178         strcat (salt, l64a (random()));
179         do {
180                 strcat (salt, l64a (random()));
181         } while (strlen (salt) < salt_size);
182         salt[salt_size] = '\0';
183
184         return salt;
185 }
186
187 /*
188  * Generate 8 base64 ASCII characters of random salt.  If MD5_CRYPT_ENAB
189  * in /etc/login.defs is "yes", the salt string will be prefixed by "$1$"
190  * (magic) and pw_encrypt() will execute the MD5-based FreeBSD-compatible
191  * version of crypt() instead of the standard one.
192  * Other methods can be set with ENCRYPT_METHOD
193  *
194  * The method can be forced with the meth parameter.
195  * If NULL, the method will be defined according to the MD5_CRYPT_ENAB and
196  * ENCRYPT_METHOD login.defs variables.
197  *
198  * If meth is specified, an additional parameter can be provided.
199  *  * For the SHA256 and SHA512 method, this specifies the number of rounds
200  *    (if not NULL).
201  */
202 char *crypt_make_salt (const char *meth, void *arg)
203 {
204         /* Max result size for the SHA methods:
205          *  +3          $5$
206          *  +17         rounds=999999999$
207          *  +16         salt
208          *  +1          \0
209          */
210         static char result[40];
211         size_t salt_len = 8;
212         const char *method;
213
214         result[0] = '\0';
215
216         if (NULL != meth)
217                 method = meth;
218         else {
219         if ((method = getdef_str ("ENCRYPT_METHOD")) == NULL)
220                 method = getdef_bool ("MD5_CRYPT_ENAB") ? "MD5" : "DES";
221         }
222
223         if (0 == strcmp (method, "MD5")) {
224                 MAGNUM(result, '1');
225 #ifdef USE_SHA_CRYPT
226         } else if (0 == strcmp (method, "SHA256")) {
227                 MAGNUM(result, '5');
228                 strcat(result, SHA_salt_rounds((int *)arg));
229                 salt_len = SHA_salt_size();
230         } else if (0 == strcmp (method, "SHA512")) {
231                 MAGNUM(result, '6');
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")) {
236                 fprintf (stderr,
237                          _("Invalid ENCRYPT_METHOD value: '%s'.\n"
238                            "Defaulting to DES.\n"),
239                          method);
240                 result[0] = '\0';
241         }
242
243         /*
244          * Concatenate a pseudo random salt.
245          */
246         assert (sizeof (result) > strlen (result) + salt_len);
247         strncat (result, gensalt (salt_len),
248                  sizeof (result) - strlen (result) - 1);
249
250         return result;
251 }
252