Initial commit for Tizen
[profile/extras/shadow-utils.git] / 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: salt.c 2788 2009-04-24 22:49:20Z nekral-guest $"
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];
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         snprintf (rounds_prefix, 18, "rounds=%ld$", rounds);
154
155         /* Sanity checks. That should not be necessary. */
156         rounds_prefix[17] = '\0';
157         if ('$' != rounds_prefix[16]) {
158                 rounds_prefix[17] = '$';
159         }
160
161         return rounds_prefix;
162 }
163 #endif /* USE_SHA_CRYPT */
164
165 /*
166  *  Generate salt of size salt_size.
167  */
168 #define MAX_SALT_SIZE 16
169 #define MIN_SALT_SIZE 8
170
171 static /*@observer@*/const char *gensalt (size_t salt_size)
172 {
173         static char salt[32];
174
175         salt[0] = '\0';
176
177         assert (salt_size >= MIN_SALT_SIZE &&
178                 salt_size <= MAX_SALT_SIZE);
179         seedRNG ();
180         strcat (salt, l64a (random()));
181         do {
182                 strcat (salt, l64a (random()));
183         } while (strlen (salt) < salt_size);
184
185         salt[salt_size] = '\0';
186
187         return salt;
188 }
189
190 /*
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
196  *
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.
200  *
201  * If meth is specified, an additional parameter can be provided.
202  *  * For the SHA256 and SHA512 method, this specifies the number of rounds
203  *    (if not NULL).
204  */
205 /*@observer@*/const char *crypt_make_salt (/*@null@*/const char *meth, /*@null@*/void *arg)
206 {
207         /* Max result size for the SHA methods:
208          *  +3          $5$
209          *  +17         rounds=999999999$
210          *  +16         salt
211          *  +1          \0
212          */
213         static char result[40];
214         size_t salt_len = 8;
215         const char *method;
216
217         result[0] = '\0';
218
219         if (NULL != meth)
220                 method = meth;
221         else {
222                 method = getdef_str ("ENCRYPT_METHOD");
223                 if (NULL == method) {
224                         method = getdef_bool ("MD5_CRYPT_ENAB") ? "MD5" : "DES";
225                 }
226         }
227
228         if (0 == strcmp (method, "MD5")) {
229                 MAGNUM(result, '1');
230 #ifdef USE_SHA_CRYPT
231         } else if (0 == strcmp (method, "SHA256")) {
232                 MAGNUM(result, '5');
233                 strcat(result, SHA_salt_rounds((int *)arg));
234                 salt_len = SHA_salt_size();
235         } else if (0 == strcmp (method, "SHA512")) {
236                 MAGNUM(result, '6');
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")) {
241                 fprintf (stderr,
242                          _("Invalid ENCRYPT_METHOD value: '%s'.\n"
243                            "Defaulting to DES.\n"),
244                          method);
245                 result[0] = '\0';
246         }
247
248         /*
249          * Concatenate a pseudo random salt.
250          */
251         assert (sizeof (result) > strlen (result) + salt_len);
252         strncat (result, gensalt (salt_len),
253                  sizeof (result) - strlen (result) - 1);
254
255         return result;
256 }
257