Line data Source code
1 : /*
2 : * File imported from FreeBSD, original by Poul-Henning Kamp.
3 : *
4 : * $FreeBSD: src/lib/libcrypt/crypt-md5.c,v 1.5 1999/12/17 20:21:45 peter Exp $
5 : *
6 : * contrib/pgcrypto/crypt-md5.c
7 : */
8 :
9 : #include "postgres.h"
10 :
11 : #include "px-crypt.h"
12 : #include "px.h"
13 :
14 : #define MD5_SIZE 16
15 :
16 : static const char _crypt_a64[] =
17 : "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
18 :
19 : static void
20 48 : _crypt_to64(char *s, unsigned long v, int n)
21 : {
22 224 : while (--n >= 0)
23 : {
24 176 : *s++ = _crypt_a64[v & 0x3f];
25 176 : v >>= 6;
26 : }
27 48 : }
28 :
29 : /*
30 : * UNIX password
31 : */
32 :
33 : char *
34 8 : px_crypt_md5(const char *pw, const char *salt, char *passwd, unsigned dstlen)
35 : {
36 : static char *magic = "$1$"; /* This string is magic for this algorithm.
37 : * Having it this way, we can get better later
38 : * on */
39 : static char *p;
40 : static const char *sp,
41 : *ep;
42 : unsigned char final[MD5_SIZE];
43 : int sl,
44 : pl,
45 : i;
46 : PX_MD *ctx,
47 : *ctx1;
48 : int err;
49 : unsigned long l;
50 :
51 8 : if (!passwd || dstlen < 120)
52 0 : return NULL;
53 :
54 : /* Refine the Salt first */
55 8 : sp = salt;
56 :
57 : /* If it starts with the magic string, then skip that */
58 8 : if (strncmp(sp, magic, strlen(magic)) == 0)
59 8 : sp += strlen(magic);
60 :
61 : /* It stops at the first '$', max 8 chars */
62 72 : for (ep = sp; *ep && *ep != '$' && ep < (sp + 8); ep++)
63 64 : continue;
64 :
65 : /* get the length of the true salt */
66 8 : sl = ep - sp;
67 :
68 : /* we need two PX_MD objects */
69 8 : err = px_find_digest("md5", &ctx);
70 8 : if (err)
71 0 : return NULL;
72 8 : err = px_find_digest("md5", &ctx1);
73 8 : if (err)
74 : {
75 : /* this path is possible under low-memory circumstances */
76 0 : px_md_free(ctx);
77 0 : return NULL;
78 : }
79 :
80 : /* The password first, since that is what is most unknown */
81 8 : px_md_update(ctx, (const uint8 *) pw, strlen(pw));
82 :
83 : /* Then our magic string */
84 8 : px_md_update(ctx, (uint8 *) magic, strlen(magic));
85 :
86 : /* Then the raw salt */
87 8 : px_md_update(ctx, (const uint8 *) sp, sl);
88 :
89 : /* Then just as many characters of the MD5(pw,salt,pw) */
90 8 : px_md_update(ctx1, (const uint8 *) pw, strlen(pw));
91 8 : px_md_update(ctx1, (const uint8 *) sp, sl);
92 8 : px_md_update(ctx1, (const uint8 *) pw, strlen(pw));
93 8 : px_md_finish(ctx1, final);
94 14 : for (pl = strlen(pw); pl > 0; pl -= MD5_SIZE)
95 6 : px_md_update(ctx, final, pl > MD5_SIZE ? MD5_SIZE : pl);
96 :
97 : /* Don't leave anything around in vm they could use. */
98 8 : px_memset(final, 0, sizeof final);
99 :
100 : /* Then something really weird... */
101 30 : for (i = strlen(pw); i; i >>= 1)
102 22 : if (i & 1)
103 6 : px_md_update(ctx, final, 1);
104 : else
105 16 : px_md_update(ctx, (const uint8 *) pw, 1);
106 :
107 : /* Now make the output string */
108 8 : strcpy(passwd, magic);
109 8 : strncat(passwd, sp, sl);
110 8 : strcat(passwd, "$");
111 :
112 8 : px_md_finish(ctx, final);
113 :
114 : /*
115 : * and now, just to make sure things don't run too fast On a 60 Mhz
116 : * Pentium this takes 34 msec, so you would need 30 seconds to build a
117 : * 1000 entry dictionary...
118 : */
119 8008 : for (i = 0; i < 1000; i++)
120 : {
121 8000 : px_md_reset(ctx1);
122 8000 : if (i & 1)
123 4000 : px_md_update(ctx1, (const uint8 *) pw, strlen(pw));
124 : else
125 4000 : px_md_update(ctx1, final, MD5_SIZE);
126 :
127 8000 : if (i % 3)
128 5328 : px_md_update(ctx1, (const uint8 *) sp, sl);
129 :
130 8000 : if (i % 7)
131 6856 : px_md_update(ctx1, (const uint8 *) pw, strlen(pw));
132 :
133 8000 : if (i & 1)
134 4000 : px_md_update(ctx1, final, MD5_SIZE);
135 : else
136 4000 : px_md_update(ctx1, (const uint8 *) pw, strlen(pw));
137 8000 : px_md_finish(ctx1, final);
138 : }
139 :
140 8 : p = passwd + strlen(passwd);
141 :
142 8 : l = (final[0] << 16) | (final[6] << 8) | final[12];
143 8 : _crypt_to64(p, l, 4);
144 8 : p += 4;
145 8 : l = (final[1] << 16) | (final[7] << 8) | final[13];
146 8 : _crypt_to64(p, l, 4);
147 8 : p += 4;
148 8 : l = (final[2] << 16) | (final[8] << 8) | final[14];
149 8 : _crypt_to64(p, l, 4);
150 8 : p += 4;
151 8 : l = (final[3] << 16) | (final[9] << 8) | final[15];
152 8 : _crypt_to64(p, l, 4);
153 8 : p += 4;
154 8 : l = (final[4] << 16) | (final[10] << 8) | final[5];
155 8 : _crypt_to64(p, l, 4);
156 8 : p += 4;
157 8 : l = final[11];
158 8 : _crypt_to64(p, l, 2);
159 8 : p += 2;
160 8 : *p = '\0';
161 :
162 : /* Don't leave anything around in vm they could use. */
163 8 : px_memset(final, 0, sizeof final);
164 :
165 8 : px_md_free(ctx1);
166 8 : px_md_free(ctx);
167 :
168 8 : return passwd;
169 : }
|