Blame crypt/md5-crypt.c

Packit Service 82fcde
/* One way encryption based on MD5 sum.
Packit Service 82fcde
   Compatible with the behavior of MD5 crypt introduced in FreeBSD 2.0.
Packit Service 82fcde
   Copyright (C) 1996-2018 Free Software Foundation, Inc.
Packit Service 82fcde
   This file is part of the GNU C Library.
Packit Service 82fcde
   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is free software; you can redistribute it and/or
Packit Service 82fcde
   modify it under the terms of the GNU Lesser General Public
Packit Service 82fcde
   License as published by the Free Software Foundation; either
Packit Service 82fcde
   version 2.1 of the License, or (at your option) any later version.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service 82fcde
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 82fcde
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 82fcde
   Lesser General Public License for more details.
Packit Service 82fcde
Packit Service 82fcde
   You should have received a copy of the GNU Lesser General Public
Packit Service 82fcde
   License along with the GNU C Library; if not, see
Packit Service 82fcde
   <http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
#include <assert.h>
Packit Service 82fcde
#include <errno.h>
Packit Service 82fcde
#include <stdlib.h>
Packit Service 82fcde
#include <string.h>
Packit Service 82fcde
#include <sys/param.h>
Packit Service 82fcde
Packit Service 82fcde
#include "md5.h"
Packit Service 82fcde
#include "crypt-private.h"
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
#ifdef USE_NSS
Packit Service 82fcde
typedef int PRBool;
Packit Service 82fcde
# include <hasht.h>
Packit Service 82fcde
# include <nsslowhash.h>
Packit Service 82fcde
Packit Service 82fcde
# define md5_init_ctx(ctxp, nss_ctxp) \
Packit Service 82fcde
  do									      \
Packit Service 82fcde
    {									      \
Packit Service 82fcde
      if (((nss_ctxp = NSSLOWHASH_NewContext (nss_ictx, HASH_AlgMD5))	      \
Packit Service 82fcde
	   == NULL))							      \
Packit Service 82fcde
	{								      \
Packit Service 82fcde
	  if (nss_ctx != NULL)						      \
Packit Service 82fcde
	    NSSLOWHASH_Destroy (nss_ctx);				      \
Packit Service 82fcde
	  if (nss_alt_ctx != NULL)					      \
Packit Service 82fcde
	    NSSLOWHASH_Destroy (nss_alt_ctx);				      \
Packit Service 82fcde
	  return NULL;							      \
Packit Service 82fcde
	}								      \
Packit Service 82fcde
      NSSLOWHASH_Begin (nss_ctxp);					      \
Packit Service 82fcde
    }									      \
Packit Service 82fcde
  while (0)
Packit Service 82fcde
Packit Service 82fcde
# define md5_process_bytes(buf, len, ctxp, nss_ctxp) \
Packit Service 82fcde
  NSSLOWHASH_Update (nss_ctxp, (const unsigned char *) buf, len)
Packit Service 82fcde
Packit Service 82fcde
# define md5_finish_ctx(ctxp, nss_ctxp, result) \
Packit Service 82fcde
  do									      \
Packit Service 82fcde
    {									      \
Packit Service 82fcde
      unsigned int ret;							      \
Packit Service 82fcde
      NSSLOWHASH_End (nss_ctxp, result, &ret, sizeof (result));		      \
Packit Service 82fcde
      assert (ret == sizeof (result));					      \
Packit Service 82fcde
      NSSLOWHASH_Destroy (nss_ctxp);					      \
Packit Service 82fcde
      nss_ctxp = NULL;							      \
Packit Service 82fcde
    }									      \
Packit Service 82fcde
  while (0)
Packit Service 82fcde
#else
Packit Service 82fcde
# define md5_init_ctx(ctxp, nss_ctxp) \
Packit Service 82fcde
  __md5_init_ctx (ctxp)
Packit Service 82fcde
Packit Service 82fcde
# define md5_process_bytes(buf, len, ctxp, nss_ctxp) \
Packit Service 82fcde
  __md5_process_bytes(buf, len, ctxp)
Packit Service 82fcde
Packit Service 82fcde
# define md5_finish_ctx(ctxp, nss_ctxp, result) \
Packit Service 82fcde
  __md5_finish_ctx (ctxp, result)
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* Define our magic string to mark salt for MD5 "encryption"
Packit Service 82fcde
   replacement.  This is meant to be the same as for other MD5 based
Packit Service 82fcde
   encryption implementations.  */
Packit Service 82fcde
static const char md5_salt_prefix[] = "$1$";
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* Prototypes for local functions.  */
Packit Service 82fcde
extern char *__md5_crypt_r (const char *key, const char *salt,
Packit Service 82fcde
			    char *buffer, int buflen);
Packit Service 82fcde
extern char *__md5_crypt (const char *key, const char *salt);
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* This entry point is equivalent to the `crypt' function in Unix
Packit Service 82fcde
   libcs.  */
Packit Service 82fcde
char *
Packit Service 82fcde
__md5_crypt_r (const char *key, const char *salt, char *buffer, int buflen)
Packit Service 82fcde
{
Packit Service 82fcde
  unsigned char alt_result[16]
Packit Service 82fcde
    __attribute__ ((__aligned__ (__alignof__ (md5_uint32))));
Packit Service 82fcde
  size_t salt_len;
Packit Service 82fcde
  size_t key_len;
Packit Service 82fcde
  size_t cnt;
Packit Service 82fcde
  char *cp;
Packit Service 82fcde
  char *copied_key = NULL;
Packit Service 82fcde
  char *copied_salt = NULL;
Packit Service 82fcde
  char *free_key = NULL;
Packit Service 82fcde
  size_t alloca_used = 0;
Packit Service 82fcde
Packit Service 82fcde
  /* Find beginning of salt string.  The prefix should normally always
Packit Service 82fcde
     be present.  Just in case it is not.  */
Packit Service 82fcde
  if (strncmp (md5_salt_prefix, salt, sizeof (md5_salt_prefix) - 1) == 0)
Packit Service 82fcde
    /* Skip salt prefix.  */
Packit Service 82fcde
    salt += sizeof (md5_salt_prefix) - 1;
Packit Service 82fcde
Packit Service 82fcde
  salt_len = MIN (strcspn (salt, "$"), 8);
Packit Service 82fcde
  key_len = strlen (key);
Packit Service 82fcde
Packit Service 82fcde
  if ((key - (char *) 0) % __alignof__ (md5_uint32) != 0)
Packit Service 82fcde
    {
Packit Service 82fcde
      char *tmp;
Packit Service 82fcde
Packit Service 82fcde
      if (__libc_use_alloca (alloca_used + key_len + __alignof__ (md5_uint32)))
Packit Service 82fcde
	tmp = (char *) alloca (key_len + __alignof__ (md5_uint32));
Packit Service 82fcde
      else
Packit Service 82fcde
	{
Packit Service 82fcde
	  free_key = tmp = (char *) malloc (key_len + __alignof__ (md5_uint32));
Packit Service 82fcde
	  if (tmp == NULL)
Packit Service 82fcde
	    return NULL;
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      key = copied_key =
Packit Service 82fcde
	memcpy (tmp + __alignof__ (md5_uint32)
Packit Service 82fcde
		- (tmp - (char *) 0) % __alignof__ (md5_uint32),
Packit Service 82fcde
		key, key_len);
Packit Service 82fcde
      assert ((key - (char *) 0) % __alignof__ (md5_uint32) == 0);
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  if ((salt - (char *) 0) % __alignof__ (md5_uint32) != 0)
Packit Service 82fcde
    {
Packit Service 82fcde
      char *tmp = (char *) alloca (salt_len + __alignof__ (md5_uint32));
Packit Service 82fcde
      salt = copied_salt =
Packit Service 82fcde
	memcpy (tmp + __alignof__ (md5_uint32)
Packit Service 82fcde
		- (tmp - (char *) 0) % __alignof__ (md5_uint32),
Packit Service 82fcde
		salt, salt_len);
Packit Service 82fcde
      assert ((salt - (char *) 0) % __alignof__ (md5_uint32) == 0);
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
#ifdef USE_NSS
Packit Service 82fcde
  /* Initialize libfreebl3.  */
Packit Service 82fcde
  NSSLOWInitContext *nss_ictx = NSSLOW_Init ();
Packit Service 82fcde
  if (nss_ictx == NULL)
Packit Service 82fcde
    {
Packit Service 82fcde
      free (free_key);
Packit Service 82fcde
      return NULL;
Packit Service 82fcde
    }
Packit Service 82fcde
  NSSLOWHASHContext *nss_ctx = NULL;
Packit Service 82fcde
  NSSLOWHASHContext *nss_alt_ctx = NULL;
Packit Service 82fcde
#else
Packit Service 82fcde
  struct md5_ctx ctx;
Packit Service 82fcde
  struct md5_ctx alt_ctx;
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service 82fcde
  /* Prepare for the real work.  */
Packit Service 82fcde
  md5_init_ctx (&ctx, nss_ctx);
Packit Service 82fcde
Packit Service 82fcde
  /* Add the key string.  */
Packit Service 82fcde
  md5_process_bytes (key, key_len, &ctx, nss_ctx);
Packit Service 82fcde
Packit Service 82fcde
  /* Because the SALT argument need not always have the salt prefix we
Packit Service 82fcde
     add it separately.  */
Packit Service 82fcde
  md5_process_bytes (md5_salt_prefix, sizeof (md5_salt_prefix) - 1,
Packit Service 82fcde
		     &ctx, nss_ctx);
Packit Service 82fcde
Packit Service 82fcde
  /* The last part is the salt string.  This must be at most 8
Packit Service 82fcde
     characters and it ends at the first `$' character (for
Packit Service 82fcde
     compatibility with existing implementations).  */
Packit Service 82fcde
  md5_process_bytes (salt, salt_len, &ctx, nss_ctx);
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
  /* Compute alternate MD5 sum with input KEY, SALT, and KEY.  The
Packit Service 82fcde
     final result will be added to the first context.  */
Packit Service 82fcde
  md5_init_ctx (&alt_ctx, nss_alt_ctx);
Packit Service 82fcde
Packit Service 82fcde
  /* Add key.  */
Packit Service 82fcde
  md5_process_bytes (key, key_len, &alt_ctx, nss_alt_ctx);
Packit Service 82fcde
Packit Service 82fcde
  /* Add salt.  */
Packit Service 82fcde
  md5_process_bytes (salt, salt_len, &alt_ctx, nss_alt_ctx);
Packit Service 82fcde
Packit Service 82fcde
  /* Add key again.  */
Packit Service 82fcde
  md5_process_bytes (key, key_len, &alt_ctx, nss_alt_ctx);
Packit Service 82fcde
Packit Service 82fcde
  /* Now get result of this (16 bytes) and add it to the other
Packit Service 82fcde
     context.  */
Packit Service 82fcde
  md5_finish_ctx (&alt_ctx, nss_alt_ctx, alt_result);
Packit Service 82fcde
Packit Service 82fcde
  /* Add for any character in the key one byte of the alternate sum.  */
Packit Service 82fcde
  for (cnt = key_len; cnt > 16; cnt -= 16)
Packit Service 82fcde
    md5_process_bytes (alt_result, 16, &ctx, nss_ctx);
Packit Service 82fcde
  md5_process_bytes (alt_result, cnt, &ctx, nss_ctx);
Packit Service 82fcde
Packit Service 82fcde
  /* For the following code we need a NUL byte.  */
Packit Service 82fcde
  *alt_result = '\0';
Packit Service 82fcde
Packit Service 82fcde
  /* The original implementation now does something weird: for every 1
Packit Service 82fcde
     bit in the key the first 0 is added to the buffer, for every 0
Packit Service 82fcde
     bit the first character of the key.  This does not seem to be
Packit Service 82fcde
     what was intended but we have to follow this to be compatible.  */
Packit Service 82fcde
  for (cnt = key_len; cnt > 0; cnt >>= 1)
Packit Service 82fcde
    md5_process_bytes ((cnt & 1) != 0
Packit Service 82fcde
		       ? (const void *) alt_result : (const void *) key, 1,
Packit Service 82fcde
		       &ctx, nss_ctx);
Packit Service 82fcde
Packit Service 82fcde
  /* Create intermediate result.  */
Packit Service 82fcde
  md5_finish_ctx (&ctx, nss_ctx, alt_result);
Packit Service 82fcde
Packit Service 82fcde
  /* Now comes another weirdness.  In fear of password crackers here
Packit Service 82fcde
     comes a quite long loop which just processes the output of the
Packit Service 82fcde
     previous round again.  We cannot ignore this here.  */
Packit Service 82fcde
  for (cnt = 0; cnt < 1000; ++cnt)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* New context.  */
Packit Service 82fcde
      md5_init_ctx (&ctx, nss_ctx);
Packit Service 82fcde
Packit Service 82fcde
      /* Add key or last result.  */
Packit Service 82fcde
      if ((cnt & 1) != 0)
Packit Service 82fcde
	md5_process_bytes (key, key_len, &ctx, nss_ctx);
Packit Service 82fcde
      else
Packit Service 82fcde
	md5_process_bytes (alt_result, 16, &ctx, nss_ctx);
Packit Service 82fcde
Packit Service 82fcde
      /* Add salt for numbers not divisible by 3.  */
Packit Service 82fcde
      if (cnt % 3 != 0)
Packit Service 82fcde
	md5_process_bytes (salt, salt_len, &ctx, nss_ctx);
Packit Service 82fcde
Packit Service 82fcde
      /* Add key for numbers not divisible by 7.  */
Packit Service 82fcde
      if (cnt % 7 != 0)
Packit Service 82fcde
	md5_process_bytes (key, key_len, &ctx, nss_ctx);
Packit Service 82fcde
Packit Service 82fcde
      /* Add key or last result.  */
Packit Service 82fcde
      if ((cnt & 1) != 0)
Packit Service 82fcde
	md5_process_bytes (alt_result, 16, &ctx, nss_ctx);
Packit Service 82fcde
      else
Packit Service 82fcde
	md5_process_bytes (key, key_len, &ctx, nss_ctx);
Packit Service 82fcde
Packit Service 82fcde
      /* Create intermediate result.  */
Packit Service 82fcde
      md5_finish_ctx (&ctx, nss_ctx, alt_result);
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
#ifdef USE_NSS
Packit Service 82fcde
  /* Free libfreebl3 resources. */
Packit Service 82fcde
  NSSLOW_Shutdown (nss_ictx);
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service 82fcde
  /* Now we can construct the result string.  It consists of three
Packit Service 82fcde
     parts.  */
Packit Service 82fcde
  cp = __stpncpy (buffer, md5_salt_prefix, MAX (0, buflen));
Packit Service 82fcde
  buflen -= sizeof (md5_salt_prefix) - 1;
Packit Service 82fcde
Packit Service 82fcde
  cp = __stpncpy (cp, salt, MIN ((size_t) MAX (0, buflen), salt_len));
Packit Service 82fcde
  buflen -= MIN ((size_t) MAX (0, buflen), salt_len);
Packit Service 82fcde
Packit Service 82fcde
  if (buflen > 0)
Packit Service 82fcde
    {
Packit Service 82fcde
      *cp++ = '$';
Packit Service 82fcde
      --buflen;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  __b64_from_24bit (&cp, &buflen,
Packit Service 82fcde
		    alt_result[0], alt_result[6], alt_result[12], 4);
Packit Service 82fcde
  __b64_from_24bit (&cp, &buflen,
Packit Service 82fcde
		    alt_result[1], alt_result[7], alt_result[13], 4);
Packit Service 82fcde
  __b64_from_24bit (&cp, &buflen,
Packit Service 82fcde
		    alt_result[2], alt_result[8], alt_result[14], 4);
Packit Service 82fcde
  __b64_from_24bit (&cp, &buflen,
Packit Service 82fcde
		    alt_result[3], alt_result[9], alt_result[15], 4);
Packit Service 82fcde
  __b64_from_24bit (&cp, &buflen,
Packit Service 82fcde
		    alt_result[4], alt_result[10], alt_result[5], 4);
Packit Service 82fcde
  __b64_from_24bit (&cp, &buflen,
Packit Service 82fcde
		    0, 0, alt_result[11], 2);
Packit Service 82fcde
  if (buflen <= 0)
Packit Service 82fcde
    {
Packit Service 82fcde
      __set_errno (ERANGE);
Packit Service 82fcde
      buffer = NULL;
Packit Service 82fcde
    }
Packit Service 82fcde
  else
Packit Service 82fcde
    *cp = '\0';		/* Terminate the string.  */
Packit Service 82fcde
Packit Service 82fcde
  /* Clear the buffer for the intermediate result so that people
Packit Service 82fcde
     attaching to processes or reading core dumps cannot get any
Packit Service 82fcde
     information.  We do it in this way to clear correct_words[]
Packit Service 82fcde
     inside the MD5 implementation as well.  */
Packit Service 82fcde
#ifndef USE_NSS
Packit Service 82fcde
  __md5_init_ctx (&ctx;;
Packit Service 82fcde
  __md5_finish_ctx (&ctx, alt_result);
Packit Service 82fcde
  explicit_bzero (&ctx, sizeof (ctx));
Packit Service 82fcde
  explicit_bzero (&alt_ctx, sizeof (alt_ctx));
Packit Service 82fcde
#endif
Packit Service 82fcde
  if (copied_key != NULL)
Packit Service 82fcde
    explicit_bzero (copied_key, key_len);
Packit Service 82fcde
  if (copied_salt != NULL)
Packit Service 82fcde
    explicit_bzero (copied_salt, salt_len);
Packit Service 82fcde
Packit Service 82fcde
  free (free_key);
Packit Service 82fcde
  return buffer;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
#ifndef _LIBC
Packit Service 82fcde
# define libc_freeres_ptr(decl) decl
Packit Service 82fcde
#endif
Packit Service 82fcde
libc_freeres_ptr (static char *buffer);
Packit Service 82fcde
Packit Service 82fcde
char *
Packit Service 82fcde
__md5_crypt (const char *key, const char *salt)
Packit Service 82fcde
{
Packit Service 82fcde
  /* We don't want to have an arbitrary limit in the size of the
Packit Service 82fcde
     password.  We can compute the size of the result in advance and
Packit Service 82fcde
     so we can prepare the buffer we pass to `md5_crypt_r'.  */
Packit Service 82fcde
  static int buflen;
Packit Service 82fcde
  int needed = 3 + strlen (salt) + 1 + 26 + 1;
Packit Service 82fcde
Packit Service 82fcde
  if (buflen < needed)
Packit Service 82fcde
    {
Packit Service 82fcde
      char *new_buffer = (char *) realloc (buffer, needed);
Packit Service 82fcde
      if (new_buffer == NULL)
Packit Service 82fcde
	return NULL;
Packit Service 82fcde
Packit Service 82fcde
      buffer = new_buffer;
Packit Service 82fcde
      buflen = needed;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  return __md5_crypt_r (key, salt, buffer, buflen);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
#ifndef _LIBC
Packit Service 82fcde
static void
Packit Service 82fcde
__attribute__ ((__destructor__))
Packit Service 82fcde
free_mem (void)
Packit Service 82fcde
{
Packit Service 82fcde
  free (buffer);
Packit Service 82fcde
}
Packit Service 82fcde
#endif