* Makeconfig (rpath-dirs): Add crypt.
[kopensolaris-gnu/glibc.git] / crypt / md5-crypt.c
1 /* One way encryption based on MD5 sum.
2    Copyright (C) 1996, 1997, 1999 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public License as
8    published by the Free Software Foundation; either version 2 of the
9    License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15
16    You should have received a copy of the GNU Library General Public
17    License along with the GNU C Library; see the file COPYING.LIB.  If not,
18    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19    Boston, MA 02111-1307, USA.  */
20
21 #include <errno.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/param.h>
25
26 #include "md5.h"
27
28
29 /* Define our magic string to mark salt for MD5 "encryption"
30    replacement.  This is meant to be the same as for other MD5 based
31    encryption implementations.  */
32 static const char md5_salt_prefix[] = "$1$";
33
34 /* Table with characters for base64 transformation.  */
35 static const char b64t[64] =
36 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
37
38
39 /* Prototypes for local functions.  */
40 extern char *__md5_crypt_r __P ((const char *key, const char *salt,
41                                  char *buffer, int buflen));
42 extern char *__md5_crypt __P ((const char *key, const char *salt));
43
44
45 /* This entry point is equivalent to the `crypt' function in Unix
46    libcs.  */
47 char *
48 __md5_crypt_r (key, salt, buffer, buflen)
49      const char *key;
50      const char *salt;
51      char *buffer;
52      int buflen;
53 {
54   unsigned char alt_result[16];
55   struct md5_ctx ctx;
56   struct md5_ctx alt_ctx;
57   size_t salt_len;
58   size_t key_len;
59   size_t cnt;
60   char *cp;
61
62   /* Find beginning of salt string.  The prefix should normally always
63      be present.  Just in case it is not.  */
64   if (strncmp (md5_salt_prefix, salt, sizeof (md5_salt_prefix) - 1) == 0)
65     /* Skip salt prefix.  */
66     salt += sizeof (md5_salt_prefix) - 1;
67
68   salt_len = MIN (strcspn (salt, "$"), 8);
69   key_len = strlen (key);
70
71   /* Prepare for the real work.  */
72   __md5_init_ctx (&ctx);
73
74   /* Add the key string.  */
75   __md5_process_bytes (key, key_len, &ctx);
76
77   /* Because the SALT argument need not always have the salt prefix we
78      add it separately.  */
79   __md5_process_bytes (md5_salt_prefix, sizeof (md5_salt_prefix) - 1, &ctx);
80
81   /* The last part is the salt string.  This must be at most 8
82      characters and it ends at the first `$' character (for
83      compatibility which existing solutions).  */
84   __md5_process_bytes (salt, salt_len, &ctx);
85
86
87   /* Compute alternate MD5 sum with input KEY, SALT, and KEY.  The
88      final result will be added to the first context.  */
89   __md5_init_ctx (&alt_ctx);
90
91   /* Add key.  */
92   __md5_process_bytes (key, key_len, &alt_ctx);
93
94   /* Add salt.  */
95   __md5_process_bytes (salt, salt_len, &alt_ctx);
96
97   /* Add key again.  */
98   __md5_process_bytes (key, key_len, &alt_ctx);
99
100   /* Now get result of this (16 bytes) and add it to the other
101      context.  */
102   __md5_finish_ctx (&alt_ctx, alt_result);
103
104   /* Add for any character in the key one byte of the alternate sum.  */
105   for (cnt = key_len; cnt > 16; cnt -= 16)
106     __md5_process_bytes (alt_result, 16, &ctx);
107   __md5_process_bytes (alt_result, cnt, &ctx);
108
109   /* For the following code we need a NUL byte.  */
110   *alt_result = '\0';
111
112   /* The original implementation now does something weird: for every 1
113      bit in the key the first 0 is added to the buffer, for every 0
114      bit the first character of the key.  This does not seem to be
115      what was intended but we have to follow this to be compatible.  */
116   for (cnt = key_len; cnt > 0; cnt >>= 1)
117     __md5_process_bytes ((cnt & 1) != 0 ? (const char *) alt_result : key, 1,
118                          &ctx);
119
120   /* Create intermediate result.  */
121   __md5_finish_ctx (&ctx, alt_result);
122
123   /* Now comes another weirdness.  In fear of password crackers here
124      comes a quite long loop which just processes the output of the
125      previous round again.  We cannot ignore this here.  */
126   for (cnt = 0; cnt < 1000; ++cnt)
127     {
128       /* New context.  */
129       __md5_init_ctx (&ctx);
130
131       /* Add key or last result.  */
132       if ((cnt & 1) != 0)
133         __md5_process_bytes (key, key_len, &ctx);
134       else
135         __md5_process_bytes (alt_result, 16, &ctx);
136
137       /* Add salt for numbers not divisible by 3.  */
138       if (cnt % 3 != 0)
139         __md5_process_bytes (salt, salt_len, &ctx);
140
141       /* Add key for numbers not divisible by 7.  */
142       if (cnt % 7 != 0)
143         __md5_process_bytes (key, key_len, &ctx);
144
145       /* Add key or last result.  */
146       if ((cnt & 1) != 0)
147         __md5_process_bytes (alt_result, 16, &ctx);
148       else
149         __md5_process_bytes (key, key_len, &ctx);
150
151       /* Create intermediate result.  */
152       __md5_finish_ctx (&ctx, alt_result);
153     }
154
155   /* Now we can construct the result string.  It consists of three
156      parts.  */
157   cp = __stpncpy (buffer, md5_salt_prefix, MAX (0, buflen));
158   buflen -= sizeof (md5_salt_prefix);
159
160   cp = __stpncpy (cp, salt, MIN ((size_t) buflen, salt_len));
161   buflen -= MIN ((size_t) buflen, salt_len);
162
163   if (buflen > 0)
164     {
165       *cp++ = '$';
166       --buflen;
167     }
168
169 #define b64_from_24bit(B2, B1, B0, N)                                         \
170   do {                                                                        \
171     unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0);                       \
172     int n = (N);                                                              \
173     while (n-- > 0 && buflen > 0)                                             \
174       {                                                                       \
175         *cp++ = b64t[w & 0x3f];                                               \
176         --buflen;                                                             \
177         w >>= 6;                                                              \
178       }                                                                       \
179   } while (0)
180
181
182   b64_from_24bit (alt_result[0], alt_result[6], alt_result[12], 4);
183   b64_from_24bit (alt_result[1], alt_result[7], alt_result[13], 4);
184   b64_from_24bit (alt_result[2], alt_result[8], alt_result[14], 4);
185   b64_from_24bit (alt_result[3], alt_result[9], alt_result[15], 4);
186   b64_from_24bit (alt_result[4], alt_result[10], alt_result[5], 4);
187   b64_from_24bit (0, 0, alt_result[11], 2);
188   if (buflen <= 0)
189     {
190       __set_errno (ERANGE);
191       buffer = NULL;
192     }
193   else
194     *cp = '\0';         /* Terminate the string.  */
195
196   /* Clear the buffer for the intermediate result so that people
197      attaching to processes or reading core dumps cannot get any
198      information.  */
199   memset (alt_result, '\0', sizeof (alt_result));
200
201   return buffer;
202 }
203
204
205 char *
206 __md5_crypt (const char *key, const char *salt)
207 {
208   /* We don't want to have an arbitrary limit in the size of the
209      password.  We can compute the size of the result in advance and
210      so we can prepare the buffer we pass to `md5_crypt_r'.  */
211   static char *buffer = NULL;
212   static int buflen = 0;
213   int needed = 3 + strlen (salt) + 1 + 26 + 1;
214
215   if (buflen < needed)
216     {
217       buflen = needed;
218       if ((buffer = realloc (buffer, buflen)) == NULL)
219         return NULL;
220     }
221
222   return __md5_crypt_r (key, salt, buffer, buflen);
223 }