source: protocols/yahoo/crypt.c @ e4816ea

Last change on this file since e4816ea was c3c2e14, checked in by Wilmer van der Gaast <wilmer@…>, at 2005-11-30T12:12:25Z

Got rid of the config.h includes in IM-code. Now that HAVE_CONFIG_H is
defined, they started to cause problems.

  • Property mode set to 100644
File size: 6.3 KB
Line 
1/* One way encryption based on MD5 sum.
2   Copyright (C) 1996, 1997, 1999, 2000 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 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, write to the Free
18   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19   02111-1307 USA.  */
20
21/* warmenhoven took this file and made it work with the md5.[ch] we
22 * already had. isn't that lovely. people should just use linux or
23 * freebsd, crypt works properly on those systems. i hate solaris */
24
25#if HAVE_STRING_H
26#  include <string.h>
27#elif HAVE_STRINGS_H
28#  include <strings.h>
29#endif
30
31#include <stdlib.h>
32#include "yahoo_util.h"
33
34#include "md5.h"
35
36/* Define our magic string to mark salt for MD5 "encryption"
37   replacement.  This is meant to be the same as for other MD5 based
38   encryption implementations.  */
39static const char md5_salt_prefix[] = "$1$";
40
41/* Table with characters for base64 transformation.  */
42static const char b64t[64] =
43"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
44
45char *yahoo_crypt(char *key, char *salt)
46{
47        char *buffer = NULL;
48        int buflen = 0;
49        int needed = 3 + strlen (salt) + 1 + 26 + 1;
50
51        md5_byte_t alt_result[16];
52        md5_state_t ctx;
53        md5_state_t alt_ctx;
54        size_t salt_len;
55        size_t key_len;
56        size_t cnt;
57        char *cp;
58
59        if (buflen < needed) {
60                buflen = needed;
61                if ((buffer = realloc(buffer, buflen)) == NULL)
62                        return NULL;
63        }
64
65        /* Find beginning of salt string.  The prefix should normally always
66           be present.  Just in case it is not.  */
67        if (strncmp (md5_salt_prefix, salt, sizeof (md5_salt_prefix) - 1) == 0)
68                /* Skip salt prefix.  */
69                salt += sizeof (md5_salt_prefix) - 1;
70
71        salt_len = MIN (strcspn (salt, "$"), 8);
72        key_len = strlen (key);
73
74        /* Prepare for the real work.  */
75        md5_init(&ctx);
76
77        /* Add the key string.  */
78        md5_append(&ctx, (md5_byte_t *)key, key_len);
79
80        /* Because the SALT argument need not always have the salt prefix we
81           add it separately.  */
82        md5_append(&ctx, (md5_byte_t *)md5_salt_prefix, sizeof (md5_salt_prefix) - 1);
83
84        /* The last part is the salt string.  This must be at most 8
85           characters and it ends at the first `$' character (for
86           compatibility which existing solutions).  */
87        md5_append(&ctx, (md5_byte_t *)salt, salt_len);
88
89        /* Compute alternate MD5 sum with input KEY, SALT, and KEY.  The
90           final result will be added to the first context.  */
91        md5_init(&alt_ctx);
92
93        /* Add key.  */
94        md5_append(&alt_ctx, (md5_byte_t *)key, key_len);
95
96        /* Add salt.  */
97        md5_append(&alt_ctx, (md5_byte_t *)salt, salt_len);
98
99        /* Add key again.  */
100        md5_append(&alt_ctx, (md5_byte_t *)key, key_len);
101
102        /* Now get result of this (16 bytes) and add it to the other
103           context.  */
104        md5_finish(&alt_ctx, alt_result);
105
106        /* Add for any character in the key one byte of the alternate sum.  */
107        for (cnt = key_len; cnt > 16; cnt -= 16)
108                md5_append(&ctx, alt_result, 16);
109        md5_append(&ctx, alt_result, cnt);
110
111        /* For the following code we need a NUL byte.  */
112        alt_result[0] = '\0';
113
114        /* The original implementation now does something weird: for every 1
115           bit in the key the first 0 is added to the buffer, for every 0
116           bit the first character of the key.  This does not seem to be
117           what was intended but we have to follow this to be compatible.  */
118        for (cnt = key_len; cnt > 0; cnt >>= 1)
119                md5_append(&ctx, (cnt & 1) != 0 ? alt_result : (md5_byte_t *)key, 1);
120
121        /* Create intermediate result.  */
122        md5_finish(&ctx, alt_result);
123
124        /* Now comes another weirdness.  In fear of password crackers here
125           comes a quite long loop which just processes the output of the
126           previous round again.  We cannot ignore this here.  */
127        for (cnt = 0; cnt < 1000; ++cnt) {
128                /* New context.  */
129                md5_init(&ctx);
130
131                /* Add key or last result.  */
132                if ((cnt & 1) != 0)
133                        md5_append(&ctx, (md5_byte_t *)key, key_len);
134                else
135                        md5_append(&ctx, alt_result, 16);
136
137                /* Add salt for numbers not divisible by 3.  */
138                if (cnt % 3 != 0)
139                        md5_append(&ctx, (md5_byte_t *)salt, salt_len);
140
141                /* Add key for numbers not divisible by 7.  */
142                if (cnt % 7 != 0)
143                        md5_append(&ctx, (md5_byte_t *)key, key_len);
144
145                /* Add key or last result.  */
146                if ((cnt & 1) != 0)
147                        md5_append(&ctx, alt_result, 16);
148                else
149                        md5_append(&ctx, (md5_byte_t *)key, key_len);
150
151                /* Create intermediate result.  */
152                md5_finish(&ctx, alt_result);
153        }
154
155        /* Now we can construct the result string.  It consists of three
156           parts.  */
157
158        strncpy(buffer, md5_salt_prefix, MAX (0, buflen));
159        cp = buffer + strlen(buffer);
160        buflen -= sizeof (md5_salt_prefix);
161
162        strncpy(cp, salt, MIN ((size_t) buflen, salt_len));
163        cp = cp + strlen(cp);
164        buflen -= MIN ((size_t) buflen, salt_len);
165
166        if (buflen > 0) {
167                *cp++ = '$';
168                --buflen;
169        }
170
171#define b64_from_24bit(B2, B1, B0, N) \
172        do { \
173                unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0); \
174                int n = (N); \
175                while (n-- > 0 && buflen > 0) { \
176                        *cp++ = b64t[w & 0x3f]; \
177                        --buflen; \
178                        w >>= 6; \
179                }\
180        } while (0)
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                FREE(buffer);
190        } else
191                *cp = '\0';     /* Terminate the string.  */
192
193        /* Clear the buffer for the intermediate result so that people
194           attaching to processes or reading core dumps cannot get any
195           information.  We do it in this way to clear correct_words[]
196           inside the MD5 implementation as well.  */
197        md5_init(&ctx);
198        md5_finish(&ctx, alt_result);
199        memset (&ctx, '\0', sizeof (ctx));
200        memset (&alt_ctx, '\0', sizeof (alt_ctx));
201
202        return buffer;
203}
Note: See TracBrowser for help on using the repository browser.