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 0 : _crypt_to64(char *s, unsigned long v, int n)
21 : {
22 0 : while (--n >= 0)
23 : {
24 0 : *s++ = _crypt_a64[v & 0x3f];
25 0 : v >>= 6;
26 : }
27 0 : }
28 :
29 : /*
30 : * UNIX password
31 : */
32 :
33 : char *
34 0 : px_crypt_md5(const char *pw, const char *salt, char *passwd, unsigned dstlen)
35 : {
36 : static const char *magic = "$1$"; /* This string is magic for this
37 : * algorithm. Having it this way, we
38 : * can get better later on */
39 0 : char *p;
40 0 : const char *sp,
41 : *ep;
42 0 : unsigned char final[MD5_SIZE];
43 0 : int sl,
44 : pl,
45 : i;
46 0 : PX_MD *ctx,
47 : *ctx1;
48 0 : int err;
49 0 : unsigned long l;
50 :
51 0 : if (!passwd || dstlen < 120)
52 0 : return NULL;
53 :
54 : /* Refine the Salt first */
55 0 : sp = salt;
56 :
57 : /* If it starts with the magic string, then skip that */
58 0 : if (strncmp(sp, magic, strlen(magic)) == 0)
59 0 : sp += strlen(magic);
60 :
61 : /* It stops at the first '$', max 8 chars */
62 0 : for (ep = sp; *ep && *ep != '$' && ep < (sp + 8); ep++)
63 0 : continue;
64 :
65 : /* get the length of the true salt */
66 0 : sl = ep - sp;
67 :
68 : /* we need two PX_MD objects */
69 0 : err = px_find_digest("md5", &ctx);
70 0 : if (err)
71 0 : return NULL;
72 0 : err = px_find_digest("md5", &ctx1);
73 0 : 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 0 : px_md_update(ctx, (const uint8 *) pw, strlen(pw));
82 :
83 : /* Then our magic string */
84 0 : px_md_update(ctx, (const uint8 *) magic, strlen(magic));
85 :
86 : /* Then the raw salt */
87 0 : px_md_update(ctx, (const uint8 *) sp, sl);
88 :
89 : /* Then just as many characters of the MD5(pw,salt,pw) */
90 0 : px_md_update(ctx1, (const uint8 *) pw, strlen(pw));
91 0 : px_md_update(ctx1, (const uint8 *) sp, sl);
92 0 : px_md_update(ctx1, (const uint8 *) pw, strlen(pw));
93 0 : px_md_finish(ctx1, final);
94 0 : for (pl = strlen(pw); pl > 0; pl -= MD5_SIZE)
95 0 : px_md_update(ctx, final, pl > MD5_SIZE ? MD5_SIZE : pl);
96 :
97 : /* Don't leave anything around in vm they could use. */
98 0 : px_memset(final, 0, sizeof final);
99 :
100 : /* Then something really weird... */
101 0 : for (i = strlen(pw); i; i >>= 1)
102 0 : if (i & 1)
103 0 : px_md_update(ctx, final, 1);
104 : else
105 0 : px_md_update(ctx, (const uint8 *) pw, 1);
106 :
107 : /* Now make the output string */
108 0 : strcpy(passwd, magic);
109 0 : strncat(passwd, sp, sl);
110 0 : strcat(passwd, "$");
111 :
112 0 : 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 0 : for (i = 0; i < 1000; i++)
120 : {
121 0 : px_md_reset(ctx1);
122 0 : if (i & 1)
123 0 : px_md_update(ctx1, (const uint8 *) pw, strlen(pw));
124 : else
125 0 : px_md_update(ctx1, final, MD5_SIZE);
126 :
127 0 : if (i % 3)
128 0 : px_md_update(ctx1, (const uint8 *) sp, sl);
129 :
130 0 : if (i % 7)
131 0 : px_md_update(ctx1, (const uint8 *) pw, strlen(pw));
132 :
133 0 : if (i & 1)
134 0 : px_md_update(ctx1, final, MD5_SIZE);
135 : else
136 0 : px_md_update(ctx1, (const uint8 *) pw, strlen(pw));
137 0 : px_md_finish(ctx1, final);
138 0 : }
139 :
140 0 : p = passwd + strlen(passwd);
141 :
142 0 : l = (final[0] << 16) | (final[6] << 8) | final[12];
143 0 : _crypt_to64(p, l, 4);
144 0 : p += 4;
145 0 : l = (final[1] << 16) | (final[7] << 8) | final[13];
146 0 : _crypt_to64(p, l, 4);
147 0 : p += 4;
148 0 : l = (final[2] << 16) | (final[8] << 8) | final[14];
149 0 : _crypt_to64(p, l, 4);
150 0 : p += 4;
151 0 : l = (final[3] << 16) | (final[9] << 8) | final[15];
152 0 : _crypt_to64(p, l, 4);
153 0 : p += 4;
154 0 : l = (final[4] << 16) | (final[10] << 8) | final[5];
155 0 : _crypt_to64(p, l, 4);
156 0 : p += 4;
157 0 : l = final[11];
158 0 : _crypt_to64(p, l, 2);
159 0 : p += 2;
160 0 : *p = '\0';
161 :
162 : /* Don't leave anything around in vm they could use. */
163 0 : px_memset(final, 0, sizeof final);
164 :
165 0 : px_md_free(ctx1);
166 0 : px_md_free(ctx);
167 :
168 0 : return passwd;
169 0 : }
|