lib/misc/md5-crypt.c
changeset 0 6f7a81934006
equal deleted inserted replaced
-1:000000000000 0:6f7a81934006
       
     1 /* One way encryption based on MD5 sum.
       
     2    Copyright (C) 1996, 1997 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 #ifndef MAX
       
    46 #define MAX(X,Y) (((X)>(Y))?(X):(Y))
       
    47 #endif
       
    48 
       
    49 #ifndef MIN
       
    50 #define MIN(X,Y) (((X)>(Y))?(Y):(X))
       
    51 #endif
       
    52 
       
    53 /* This entry point is equivalent to the `crypt' function in Unix
       
    54    libcs.  */
       
    55 char *
       
    56 md5_crypt_r (key, salt, buffer, buflen)
       
    57      const char *key;
       
    58      const char *salt;
       
    59      char *buffer;
       
    60      int buflen;
       
    61 {
       
    62   unsigned char alt_result[16];
       
    63   struct md5_ctx ctx;
       
    64   struct md5_ctx alt_ctx;
       
    65   size_t salt_len;
       
    66   size_t key_len;
       
    67   size_t cnt;
       
    68   char *cp;
       
    69 
       
    70   /* Find beginning of salt string.  The prefix should normally always
       
    71      be present.  Just in case it is not.  */
       
    72   if (strncmp (md5_salt_prefix, salt, sizeof (md5_salt_prefix) - 1) == 0)
       
    73     /* Skip salt prefix.  */
       
    74     salt += sizeof (md5_salt_prefix) - 1;
       
    75 
       
    76   salt_len = MIN (strcspn (salt, "$"), 8);
       
    77   key_len = strlen (key);
       
    78 
       
    79   /* Prepare for the real work.  */
       
    80   md5_init_ctx (&ctx);
       
    81 
       
    82   /* Add the key string.  */
       
    83   md5_process_bytes (key, key_len, &ctx);
       
    84 
       
    85   /* Because the SALT argument need not always have the salt prefix we
       
    86      add it separately.  */
       
    87   md5_process_bytes (md5_salt_prefix, sizeof (md5_salt_prefix) - 1, &ctx);
       
    88 
       
    89   /* The last part is the salt string.  This must be at most 8
       
    90      characters and it ends at the first `$' character (for
       
    91      compatibility which existing solutions).  */
       
    92   md5_process_bytes (salt, salt_len, &ctx);
       
    93 
       
    94 
       
    95   /* Compute alternate MD5 sum with input KEY, SALT, and KEY.  The
       
    96      final result will be added to the first context.  */
       
    97   md5_init_ctx (&alt_ctx);
       
    98 
       
    99   /* Add key.  */
       
   100   md5_process_bytes (key, key_len, &alt_ctx);
       
   101 
       
   102   /* Add salt.  */
       
   103   md5_process_bytes (salt, salt_len, &alt_ctx);
       
   104 
       
   105   /* Add key again.  */
       
   106   md5_process_bytes (key, key_len, &alt_ctx);
       
   107 
       
   108   /* Now get result of this (16 bytes) and add it to the other
       
   109      context.  */
       
   110   md5_finish_ctx (&alt_ctx, alt_result);
       
   111 
       
   112   /* Add for any character in the key one byte of the alternate sum.  */
       
   113   for (cnt = key_len; cnt > 16; cnt -= 16)
       
   114     md5_process_bytes (alt_result, 16, &ctx);
       
   115   md5_process_bytes (alt_result, cnt, &ctx);
       
   116 
       
   117   /* For the following code we need a NUL byte.  */
       
   118   *alt_result = '\0';
       
   119 
       
   120   /* The original implementation now does something weird: for every 1
       
   121      bit in the key the first 0 is added to the buffer, for every 0
       
   122      bit the first character of the key.  This does not seem to be
       
   123      what was intended but we have to follow this to be compatible.  */
       
   124   for (cnt = key_len; cnt > 0; cnt >>= 1)
       
   125     md5_process_bytes ((cnt & 1) != 0 ? (const char *) alt_result : key, 1,
       
   126 		       &ctx);
       
   127 
       
   128   /* Create intermediate result.  */
       
   129   md5_finish_ctx (&ctx, alt_result);
       
   130 
       
   131   /* Now comes another weirdness.  In fear of password crackers here
       
   132      comes a quite long loop which just processes the output of the
       
   133      previous round again.  We cannot ignore this here.  */
       
   134   for (cnt = 0; cnt < 1000; ++cnt)
       
   135     {
       
   136       /* New context.  */
       
   137       md5_init_ctx (&ctx);
       
   138 
       
   139       /* Add key or last result.  */
       
   140       if ((cnt & 1) != 0)
       
   141 	md5_process_bytes (key, key_len, &ctx);
       
   142       else
       
   143 	md5_process_bytes (alt_result, 16, &ctx);
       
   144 
       
   145       /* Add salt for numbers not divisible by 3.  */
       
   146       if (cnt % 3 != 0)
       
   147 	md5_process_bytes (salt, salt_len, &ctx);
       
   148 
       
   149       /* Add key for numbers not divisible by 7.  */
       
   150       if (cnt % 7 != 0)
       
   151 	md5_process_bytes (key, key_len, &ctx);
       
   152 
       
   153       /* Add key or last result.  */
       
   154       if ((cnt & 1) != 0)
       
   155 	md5_process_bytes (alt_result, 16, &ctx);
       
   156       else
       
   157 	md5_process_bytes (key, key_len, &ctx);
       
   158 
       
   159       /* Create intermediate result.  */
       
   160       md5_finish_ctx (&ctx, alt_result);
       
   161     }
       
   162 
       
   163   /* Now we can construct the result string.  It consists of three
       
   164      parts.  */
       
   165   strncpy (buffer, md5_salt_prefix, MAX (0, buflen));
       
   166   buflen -= strlen (md5_salt_prefix);
       
   167   cp = buffer + strlen (md5_salt_prefix);
       
   168 
       
   169   strncpy (cp, salt, MIN ((size_t) buflen, salt_len));
       
   170   buflen -= MIN ((size_t) buflen, salt_len);
       
   171   cp += MIN ((size_t) buflen, salt_len);
       
   172 
       
   173   if (buflen > 0)
       
   174     {
       
   175       *cp++ = '$';
       
   176       --buflen;
       
   177     }
       
   178 
       
   179 #define b64_from_24bit(B2, B1, B0, N)					      \
       
   180   do {									      \
       
   181     unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0);			      \
       
   182     int n = (N);							      \
       
   183     while (n-- > 0 && buflen > 0)					      \
       
   184       {									      \
       
   185 	*cp++ = b64t[w & 0x3f];						      \
       
   186 	--buflen;							      \
       
   187 	w >>= 6;							      \
       
   188       }									      \
       
   189   } while (0)
       
   190 
       
   191 
       
   192   b64_from_24bit (alt_result[0], alt_result[6], alt_result[12], 4);
       
   193   b64_from_24bit (alt_result[1], alt_result[7], alt_result[13], 4);
       
   194   b64_from_24bit (alt_result[2], alt_result[8], alt_result[14], 4);
       
   195   b64_from_24bit (alt_result[3], alt_result[9], alt_result[15], 4);
       
   196   b64_from_24bit (alt_result[4], alt_result[10], alt_result[5], 4);
       
   197   b64_from_24bit (0, 0, alt_result[11], 2);
       
   198   if (buflen <= 0)
       
   199     {
       
   200       errno = ERANGE;
       
   201       buffer = NULL;
       
   202     }
       
   203   else
       
   204     *cp = '\0';		/* Terminate the string.  */
       
   205 
       
   206   /* Clear the buffer for the intermediate result so that people
       
   207      attaching to processes or reading core dumps cannot get any
       
   208      information.  */
       
   209   memset (alt_result, '\0', sizeof (alt_result));
       
   210 
       
   211   return buffer;
       
   212 }
       
   213 
       
   214 
       
   215 char *
       
   216 md5_crypt (key, salt)
       
   217      const char *key;
       
   218      const char *salt;
       
   219 {
       
   220   /* We don't want to have an arbitrary limit in the size of the
       
   221      password.  We can compute the size of the result in advance and
       
   222      so we can prepare the buffer we pass to `md5_crypt_r'.  */
       
   223   static char *buffer = NULL;
       
   224   static int buflen = 0;
       
   225   int needed = 3 + strlen (salt) + 1 + 26 + 1;
       
   226 
       
   227   if (buflen < needed)
       
   228     {
       
   229       buflen = needed;
       
   230       if ((buffer = realloc (buffer, buflen)) == NULL)
       
   231 	return NULL;
       
   232     }
       
   233 
       
   234   return md5_crypt_r (key, salt, buffer, buflen);
       
   235 }