(root)/
glibc-2.38/
crypt/
md5-crypt.c
       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-2023 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 *
      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 (((uintptr_t) key) % __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  		- ((uintptr_t) tmp) % __alignof__ (md5_uint32),
     129  		key, key_len);
     130        assert (((uintptr_t) key) % __alignof__ (md5_uint32) == 0);
     131      }
     132  
     133    if (((uintptr_t) salt) % __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  		- ((uintptr_t) tmp) % __alignof__ (md5_uint32),
     139  		salt, salt_len);
     140        assert (((uintptr_t) salt) % __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  static char *buffer;
     303  
     304  char *
     305  __md5_crypt (const char *key, const char *salt)
     306  {
     307    /* We don't want to have an arbitrary limit in the size of the
     308       password.  We can compute the size of the result in advance and
     309       so we can prepare the buffer we pass to `md5_crypt_r'.  */
     310    static int buflen;
     311    int needed = 3 + strlen (salt) + 1 + 26 + 1;
     312  
     313    if (buflen < needed)
     314      {
     315        char *new_buffer = (char *) realloc (buffer, needed);
     316        if (new_buffer == NULL)
     317  	return NULL;
     318  
     319        buffer = new_buffer;
     320        buflen = needed;
     321      }
     322  
     323    return __md5_crypt_r (key, salt, buffer, buflen);
     324  }
     325  
     326  static void
     327  __attribute__ ((__destructor__))
     328  free_mem (void)
     329  {
     330    free (buffer);
     331  }