]> granicus.if.org Git - shadow/blob - libmisc/salt.c
* src/newusers.c: Fix typo.
[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 static void seedRNG (void);
24 static /*@observer@*/const char *gensalt (size_t salt_size);
25 #ifdef USE_SHA_CRYPT
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 */
29
30 #ifndef HAVE_L64A
31 static /*@observer@*/char *l64a(long value)
32 {
33         static char buf[8];
34         char *s = buf;
35         int digit;
36         int i;
37
38         if (value < 0) {
39                 errno = EINVAL;
40                 return(NULL);
41         }
42
43         for (i = 0; value != 0 && i < 6; i++) {
44                 digit = value & 0x3f;
45
46                 if (digit < 2) {
47                         *s = digit + '.';
48                 } else if (digit < 12) {
49                         *s = digit + '0' - 2;
50                 } else if (digit < 38) {
51                         *s = digit + 'A' - 12;
52                 } else {
53                         *s = digit + 'a' - 38;
54                 }
55
56                 value >>= 6;
57                 s++;
58         }
59
60         *s = '\0';
61
62         return(buf);
63 }
64 #endif /* !HAVE_L64A */
65
66 static void seedRNG (void)
67 {
68         struct timeval tv;
69         static int seeded = 0;
70
71         if (0 == seeded) {
72                 (void) gettimeofday (&tv, NULL);
73                 srandom (tv.tv_sec ^ tv.tv_usec ^ getpid ());
74                 seeded = 1;
75         }
76 }
77
78 /*
79  * Add the salt prefix.
80  */
81 #define MAGNUM(array,ch)        (array)[0]=(array)[2]='$',(array)[1]=(ch),(array)[3]='\0'
82
83 #ifdef USE_SHA_CRYPT
84 /*
85  * Return the salt size.
86  * The size of the salt string is between 8 and 16 bytes for the SHA crypt
87  * methods.
88  */
89 static size_t SHA_salt_size (void)
90 {
91         double rand_size;
92         seedRNG ();
93         rand_size = (double) 9.0 * random () / RAND_MAX;
94         return (size_t) (8 + rand_size);
95 }
96
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
103
104 /*
105  * Return a salt prefix specifying the rounds number for the SHA crypt methods.
106  */
107 static /*@observer@*/const char *SHA_salt_rounds (/*@null@*/int *prefered_rounds)
108 {
109         static char rounds_prefix[18]; /* Max size: rounds=999999999$ */
110         long rounds;
111
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);
115                 double rand_rounds;
116
117                 if ((-1 == min_rounds) && (-1 == max_rounds)) {
118                         return "";
119                 }
120
121                 if (-1 == min_rounds) {
122                         min_rounds = max_rounds;
123                 }
124
125                 if (-1 == max_rounds) {
126                         max_rounds = min_rounds;
127                 }
128
129                 if (min_rounds > max_rounds) {
130                         max_rounds = min_rounds;
131                 }
132
133                 seedRNG ();
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) {
138                 return "";
139         } else {
140                 rounds = *prefered_rounds;
141         }
142
143         /* Sanity checks. The libc should also check this, but this
144          * protects against a rounds_prefix overflow. */
145         if (rounds < ROUNDS_MIN) {
146                 rounds = ROUNDS_MIN;
147         }
148
149         if (rounds > ROUNDS_MAX) {
150                 rounds = ROUNDS_MAX;
151         }
152
153         (void) snprintf (rounds_prefix, sizeof rounds_prefix,
154                          "rounds=%ld$", rounds);
155
156         return rounds_prefix;
157 }
158 #endif /* USE_SHA_CRYPT */
159
160 /*
161  *  Generate salt of size salt_size.
162  */
163 #define MAX_SALT_SIZE 16
164 #define MIN_SALT_SIZE 8
165
166 static /*@observer@*/const char *gensalt (size_t salt_size)
167 {
168         static char salt[32];
169
170         salt[0] = '\0';
171
172         assert (salt_size >= MIN_SALT_SIZE &&
173                 salt_size <= MAX_SALT_SIZE);
174         seedRNG ();
175         strcat (salt, l64a (random()));
176         do {
177                 strcat (salt, l64a (random()));
178         } while (strlen (salt) < salt_size);
179
180         salt[salt_size] = '\0';
181
182         return salt;
183 }
184
185 /*
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
191  *
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.
195  *
196  * If meth is specified, an additional parameter can be provided.
197  *  * For the SHA256 and SHA512 method, this specifies the number of rounds
198  *    (if not NULL).
199  */
200 /*@observer@*/const char *crypt_make_salt (/*@null@*//*@observer@*/const char *meth, /*@null@*/void *arg)
201 {
202         /* Max result size for the SHA methods:
203          *  +3          $5$
204          *  +17         rounds=999999999$
205          *  +16         salt
206          *  +1          \0
207          */
208         static char result[40];
209         size_t salt_len = 8;
210         const char *method;
211
212         result[0] = '\0';
213
214         if (NULL != meth)
215                 method = meth;
216         else {
217                 method = getdef_str ("ENCRYPT_METHOD");
218                 if (NULL == method) {
219                         method = getdef_bool ("MD5_CRYPT_ENAB") ? "MD5" : "DES";
220                 }
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