1 /* One way encryption based on MD5 sum.
2    Compatible with the behavior of MD5 crypt introduced in FreeBSD 2.0.
3    Copyright (C) 1996-2022 Free Software Foundation, Inc.
4    This file is part of the GNU C Library.
5 
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the 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    Lesser General Public License for more details.
15 
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, see
18    <https://www.gnu.org/licenses/>.  */
19 
20 #include <assert.h>
21 #include <errno.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/param.h>
25 
26 #include "md5.h"
27 #include "crypt-private.h"
28 
29 
30 #ifdef USE_NSS
31 typedef int PRBool;
32 # include <hasht.h>
33 # include <nsslowhash.h>
34 
35 # define md5_init_ctx(ctxp, nss_ctxp) \
36   do									      \
37     {									      \
38       if (((nss_ctxp = NSSLOWHASH_NewContext (nss_ictx, HASH_AlgMD5))	      \
39 	   == NULL))							      \
40 	{								      \
41 	  if (nss_ctx != NULL)						      \
42 	    NSSLOWHASH_Destroy (nss_ctx);				      \
43 	  if (nss_alt_ctx != NULL)					      \
44 	    NSSLOWHASH_Destroy (nss_alt_ctx);				      \
45 	  return NULL;							      \
46 	}								      \
47       NSSLOWHASH_Begin (nss_ctxp);					      \
48     }									      \
49   while (0)
50 
51 # define md5_process_bytes(buf, len, ctxp, nss_ctxp) \
52   NSSLOWHASH_Update (nss_ctxp, (const unsigned char *) buf, len)
53 
54 # define md5_finish_ctx(ctxp, nss_ctxp, result) \
55   do									      \
56     {									      \
57       unsigned int ret;							      \
58       NSSLOWHASH_End (nss_ctxp, result, &ret, sizeof (result));		      \
59       assert (ret == sizeof (result));					      \
60       NSSLOWHASH_Destroy (nss_ctxp);					      \
61       nss_ctxp = NULL;							      \
62     }									      \
63   while (0)
64 #else
65 # define md5_init_ctx(ctxp, nss_ctxp) \
66   __md5_init_ctx (ctxp)
67 
68 # define md5_process_bytes(buf, len, ctxp, nss_ctxp) \
69   __md5_process_bytes(buf, len, ctxp)
70 
71 # define md5_finish_ctx(ctxp, nss_ctxp, result) \
72   __md5_finish_ctx (ctxp, result)
73 #endif
74 
75 
76 /* Define our magic string to mark salt for MD5 "encryption"
77    replacement.  This is meant to be the same as for other MD5 based
78    encryption implementations.  */
79 static const char md5_salt_prefix[] = "$1$";
80 
81 
82 /* Prototypes for local functions.  */
83 extern char *__md5_crypt_r (const char *key, const char *salt,
84 			    char *buffer, int buflen);
85 extern char *__md5_crypt (const char *key, const char *salt);
86 
87 
88 /* This entry point is equivalent to the `crypt' function in Unix
89    libcs.  */
90 char *
__md5_crypt_r(const char * key,const char * salt,char * buffer,int buflen)91 __md5_crypt_r (const char *key, const char *salt, char *buffer, int buflen)
92 {
93   unsigned char alt_result[16]
94     __attribute__ ((__aligned__ (__alignof__ (md5_uint32))));
95   size_t salt_len;
96   size_t key_len;
97   size_t cnt;
98   char *cp;
99   char *copied_key = NULL;
100   char *copied_salt = NULL;
101   char *free_key = NULL;
102   size_t alloca_used = 0;
103 
104   /* Find beginning of salt string.  The prefix should normally always
105      be present.  Just in case it is not.  */
106   if (strncmp (md5_salt_prefix, salt, sizeof (md5_salt_prefix) - 1) == 0)
107     /* Skip salt prefix.  */
108     salt += sizeof (md5_salt_prefix) - 1;
109 
110   salt_len = MIN (strcspn (salt, "$"), 8);
111   key_len = strlen (key);
112 
113   if ((key - (char *) 0) % __alignof__ (md5_uint32) != 0)
114     {
115       char *tmp;
116 
117       if (__libc_use_alloca (alloca_used + key_len + __alignof__ (md5_uint32)))
118 	tmp = (char *) alloca (key_len + __alignof__ (md5_uint32));
119       else
120 	{
121 	  free_key = tmp = (char *) malloc (key_len + __alignof__ (md5_uint32));
122 	  if (tmp == NULL)
123 	    return NULL;
124 	}
125 
126       key = copied_key =
127 	memcpy (tmp + __alignof__ (md5_uint32)
128 		- (tmp - (char *) 0) % __alignof__ (md5_uint32),
129 		key, key_len);
130       assert ((key - (char *) 0) % __alignof__ (md5_uint32) == 0);
131     }
132 
133   if ((salt - (char *) 0) % __alignof__ (md5_uint32) != 0)
134     {
135       char *tmp = (char *) alloca (salt_len + __alignof__ (md5_uint32));
136       salt = copied_salt =
137 	memcpy (tmp + __alignof__ (md5_uint32)
138 		- (tmp - (char *) 0) % __alignof__ (md5_uint32),
139 		salt, salt_len);
140       assert ((salt - (char *) 0) % __alignof__ (md5_uint32) == 0);
141     }
142 
143 #ifdef USE_NSS
144   /* Initialize libfreebl3.  */
145   NSSLOWInitContext *nss_ictx = NSSLOW_Init ();
146   if (nss_ictx == NULL)
147     {
148       free (free_key);
149       return NULL;
150     }
151   NSSLOWHASHContext *nss_ctx = NULL;
152   NSSLOWHASHContext *nss_alt_ctx = NULL;
153 #else
154   struct md5_ctx ctx;
155   struct md5_ctx alt_ctx;
156 #endif
157 
158   /* Prepare for the real work.  */
159   md5_init_ctx (&ctx, nss_ctx);
160 
161   /* Add the key string.  */
162   md5_process_bytes (key, key_len, &ctx, nss_ctx);
163 
164   /* Because the SALT argument need not always have the salt prefix we
165      add it separately.  */
166   md5_process_bytes (md5_salt_prefix, sizeof (md5_salt_prefix) - 1,
167 		     &ctx, nss_ctx);
168 
169   /* The last part is the salt string.  This must be at most 8
170      characters and it ends at the first `$' character (for
171      compatibility with existing implementations).  */
172   md5_process_bytes (salt, salt_len, &ctx, nss_ctx);
173 
174 
175   /* Compute alternate MD5 sum with input KEY, SALT, and KEY.  The
176      final result will be added to the first context.  */
177   md5_init_ctx (&alt_ctx, nss_alt_ctx);
178 
179   /* Add key.  */
180   md5_process_bytes (key, key_len, &alt_ctx, nss_alt_ctx);
181 
182   /* Add salt.  */
183   md5_process_bytes (salt, salt_len, &alt_ctx, nss_alt_ctx);
184 
185   /* Add key again.  */
186   md5_process_bytes (key, key_len, &alt_ctx, nss_alt_ctx);
187 
188   /* Now get result of this (16 bytes) and add it to the other
189      context.  */
190   md5_finish_ctx (&alt_ctx, nss_alt_ctx, alt_result);
191 
192   /* Add for any character in the key one byte of the alternate sum.  */
193   for (cnt = key_len; cnt > 16; cnt -= 16)
194     md5_process_bytes (alt_result, 16, &ctx, nss_ctx);
195   md5_process_bytes (alt_result, cnt, &ctx, nss_ctx);
196 
197   /* For the following code we need a NUL byte.  */
198   *alt_result = '\0';
199 
200   /* The original implementation now does something weird: for every 1
201      bit in the key the first 0 is added to the buffer, for every 0
202      bit the first character of the key.  This does not seem to be
203      what was intended but we have to follow this to be compatible.  */
204   for (cnt = key_len; cnt > 0; cnt >>= 1)
205     md5_process_bytes ((cnt & 1) != 0
206 		       ? (const void *) alt_result : (const void *) key, 1,
207 		       &ctx, nss_ctx);
208 
209   /* Create intermediate result.  */
210   md5_finish_ctx (&ctx, nss_ctx, alt_result);
211 
212   /* Now comes another weirdness.  In fear of password crackers here
213      comes a quite long loop which just processes the output of the
214      previous round again.  We cannot ignore this here.  */
215   for (cnt = 0; cnt < 1000; ++cnt)
216     {
217       /* New context.  */
218       md5_init_ctx (&ctx, nss_ctx);
219 
220       /* Add key or last result.  */
221       if ((cnt & 1) != 0)
222 	md5_process_bytes (key, key_len, &ctx, nss_ctx);
223       else
224 	md5_process_bytes (alt_result, 16, &ctx, nss_ctx);
225 
226       /* Add salt for numbers not divisible by 3.  */
227       if (cnt % 3 != 0)
228 	md5_process_bytes (salt, salt_len, &ctx, nss_ctx);
229 
230       /* Add key for numbers not divisible by 7.  */
231       if (cnt % 7 != 0)
232 	md5_process_bytes (key, key_len, &ctx, nss_ctx);
233 
234       /* Add key or last result.  */
235       if ((cnt & 1) != 0)
236 	md5_process_bytes (alt_result, 16, &ctx, nss_ctx);
237       else
238 	md5_process_bytes (key, key_len, &ctx, nss_ctx);
239 
240       /* Create intermediate result.  */
241       md5_finish_ctx (&ctx, nss_ctx, alt_result);
242     }
243 
244 #ifdef USE_NSS
245   /* Free libfreebl3 resources. */
246   NSSLOW_Shutdown (nss_ictx);
247 #endif
248 
249   /* Now we can construct the result string.  It consists of three
250      parts.  */
251   cp = __stpncpy (buffer, md5_salt_prefix, MAX (0, buflen));
252   buflen -= sizeof (md5_salt_prefix) - 1;
253 
254   cp = __stpncpy (cp, salt, MIN ((size_t) MAX (0, buflen), salt_len));
255   buflen -= MIN ((size_t) MAX (0, buflen), salt_len);
256 
257   if (buflen > 0)
258     {
259       *cp++ = '$';
260       --buflen;
261     }
262 
263   __b64_from_24bit (&cp, &buflen,
264 		    alt_result[0], alt_result[6], alt_result[12], 4);
265   __b64_from_24bit (&cp, &buflen,
266 		    alt_result[1], alt_result[7], alt_result[13], 4);
267   __b64_from_24bit (&cp, &buflen,
268 		    alt_result[2], alt_result[8], alt_result[14], 4);
269   __b64_from_24bit (&cp, &buflen,
270 		    alt_result[3], alt_result[9], alt_result[15], 4);
271   __b64_from_24bit (&cp, &buflen,
272 		    alt_result[4], alt_result[10], alt_result[5], 4);
273   __b64_from_24bit (&cp, &buflen,
274 		    0, 0, alt_result[11], 2);
275   if (buflen <= 0)
276     {
277       __set_errno (ERANGE);
278       buffer = NULL;
279     }
280   else
281     *cp = '\0';		/* Terminate the string.  */
282 
283   /* Clear the buffer for the intermediate result so that people
284      attaching to processes or reading core dumps cannot get any
285      information.  We do it in this way to clear correct_words[]
286      inside the MD5 implementation as well.  */
287 #ifndef USE_NSS
288   __md5_init_ctx (&ctx);
289   __md5_finish_ctx (&ctx, alt_result);
290   explicit_bzero (&ctx, sizeof (ctx));
291   explicit_bzero (&alt_ctx, sizeof (alt_ctx));
292 #endif
293   if (copied_key != NULL)
294     explicit_bzero (copied_key, key_len);
295   if (copied_salt != NULL)
296     explicit_bzero (copied_salt, salt_len);
297 
298   free (free_key);
299   return buffer;
300 }
301 
302 #ifndef _LIBC
303 # define libc_freeres_ptr(decl) decl
304 #endif
305 libc_freeres_ptr (static char *buffer);
306 
307 char *
__md5_crypt(const char * key,const char * salt)308 __md5_crypt (const char *key, const char *salt)
309 {
310   /* We don't want to have an arbitrary limit in the size of the
311      password.  We can compute the size of the result in advance and
312      so we can prepare the buffer we pass to `md5_crypt_r'.  */
313   static int buflen;
314   int needed = 3 + strlen (salt) + 1 + 26 + 1;
315 
316   if (buflen < needed)
317     {
318       char *new_buffer = (char *) realloc (buffer, needed);
319       if (new_buffer == NULL)
320 	return NULL;
321 
322       buffer = new_buffer;
323       buflen = needed;
324     }
325 
326   return __md5_crypt_r (key, salt, buffer, buflen);
327 }
328 
329 #ifndef _LIBC
330 static void
331 __attribute__ ((__destructor__))
free_mem(void)332 free_mem (void)
333 {
334   free (buffer);
335 }
336 #endif
337