/src/postgres/src/common/md5_common.c
Line | Count | Source |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * md5_common.c |
4 | | * Routines shared between all MD5 implementations used for encrypted |
5 | | * passwords. |
6 | | * |
7 | | * Sverre H. Huseby <sverrehu@online.no> |
8 | | * |
9 | | * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group |
10 | | * Portions Copyright (c) 1994, Regents of the University of California |
11 | | * |
12 | | * IDENTIFICATION |
13 | | * src/common/md5_common.c |
14 | | * |
15 | | *------------------------------------------------------------------------- |
16 | | */ |
17 | | |
18 | | #ifndef FRONTEND |
19 | | #include "postgres.h" |
20 | | #else |
21 | | #include "postgres_fe.h" |
22 | | #endif |
23 | | |
24 | | #include "common/cryptohash.h" |
25 | | #include "common/md5.h" |
26 | | |
27 | | static void |
28 | | bytesToHex(uint8 b[16], char *s) |
29 | 0 | { |
30 | 0 | static const char *hex = "0123456789abcdef"; |
31 | 0 | int q, |
32 | 0 | w; |
33 | |
|
34 | 0 | for (q = 0, w = 0; q < 16; q++) |
35 | 0 | { |
36 | 0 | s[w++] = hex[(b[q] >> 4) & 0x0F]; |
37 | 0 | s[w++] = hex[b[q] & 0x0F]; |
38 | 0 | } |
39 | 0 | s[w] = '\0'; |
40 | 0 | } |
41 | | |
42 | | /* |
43 | | * pg_md5_hash |
44 | | * |
45 | | * Calculates the MD5 sum of the bytes in a buffer. |
46 | | * |
47 | | * SYNOPSIS #include "md5.h" |
48 | | * bool pg_md5_hash(const void *buff, size_t len, char *hexsum, |
49 | | * const char **errstr) |
50 | | * |
51 | | * INPUT buff the buffer containing the bytes that you want |
52 | | * the MD5 sum of. |
53 | | * len number of bytes in the buffer. |
54 | | * |
55 | | * OUTPUT hexsum the MD5 sum as a '\0'-terminated string of |
56 | | * hexadecimal digits. an MD5 sum is 16 bytes long. |
57 | | * each byte is represented by two hexadecimal |
58 | | * characters. you thus need to provide an array |
59 | | * of 33 characters, including the trailing '\0'. |
60 | | * |
61 | | * errstr filled with a constant-string error message |
62 | | * on failure return; NULL on success. |
63 | | * |
64 | | * RETURNS false on failure (out of memory for internal buffers |
65 | | * or MD5 computation failure) or true on success. |
66 | | * |
67 | | * STANDARDS MD5 is described in RFC 1321. |
68 | | * |
69 | | * AUTHOR Sverre H. Huseby <sverrehu@online.no> |
70 | | * |
71 | | */ |
72 | | |
73 | | bool |
74 | | pg_md5_hash(const void *buff, size_t len, char *hexsum, const char **errstr) |
75 | 0 | { |
76 | 0 | uint8 sum[MD5_DIGEST_LENGTH]; |
77 | 0 | pg_cryptohash_ctx *ctx; |
78 | |
|
79 | 0 | *errstr = NULL; |
80 | 0 | ctx = pg_cryptohash_create(PG_MD5); |
81 | 0 | if (ctx == NULL) |
82 | 0 | { |
83 | 0 | *errstr = pg_cryptohash_error(NULL); /* returns OOM */ |
84 | 0 | return false; |
85 | 0 | } |
86 | | |
87 | 0 | if (pg_cryptohash_init(ctx) < 0 || |
88 | 0 | pg_cryptohash_update(ctx, buff, len) < 0 || |
89 | 0 | pg_cryptohash_final(ctx, sum, sizeof(sum)) < 0) |
90 | 0 | { |
91 | 0 | *errstr = pg_cryptohash_error(ctx); |
92 | 0 | pg_cryptohash_free(ctx); |
93 | 0 | return false; |
94 | 0 | } |
95 | | |
96 | 0 | bytesToHex(sum, hexsum); |
97 | 0 | pg_cryptohash_free(ctx); |
98 | 0 | return true; |
99 | 0 | } |
100 | | |
101 | | /* |
102 | | * pg_md5_binary |
103 | | * |
104 | | * As above, except that the MD5 digest is returned as a binary string |
105 | | * (of size MD5_DIGEST_LENGTH) rather than being converted to ASCII hex. |
106 | | */ |
107 | | bool |
108 | | pg_md5_binary(const void *buff, size_t len, uint8 *outbuf, const char **errstr) |
109 | 0 | { |
110 | 0 | pg_cryptohash_ctx *ctx; |
111 | |
|
112 | 0 | *errstr = NULL; |
113 | 0 | ctx = pg_cryptohash_create(PG_MD5); |
114 | 0 | if (ctx == NULL) |
115 | 0 | { |
116 | 0 | *errstr = pg_cryptohash_error(NULL); /* returns OOM */ |
117 | 0 | return false; |
118 | 0 | } |
119 | | |
120 | 0 | if (pg_cryptohash_init(ctx) < 0 || |
121 | 0 | pg_cryptohash_update(ctx, buff, len) < 0 || |
122 | 0 | pg_cryptohash_final(ctx, outbuf, MD5_DIGEST_LENGTH) < 0) |
123 | 0 | { |
124 | 0 | *errstr = pg_cryptohash_error(ctx); |
125 | 0 | pg_cryptohash_free(ctx); |
126 | 0 | return false; |
127 | 0 | } |
128 | | |
129 | 0 | pg_cryptohash_free(ctx); |
130 | 0 | return true; |
131 | 0 | } |
132 | | |
133 | | |
134 | | /* |
135 | | * Computes MD5 checksum of "passwd" (a null-terminated string) followed |
136 | | * by "salt" (which need not be null-terminated). |
137 | | * |
138 | | * Output format is "md5" followed by a 32-hex-digit MD5 checksum. |
139 | | * Hence, the output buffer "buf" must be at least 36 bytes long. |
140 | | * |
141 | | * Returns true if okay, false on error with *errstr providing some |
142 | | * error context. |
143 | | */ |
144 | | bool |
145 | | pg_md5_encrypt(const char *passwd, const uint8 *salt, size_t salt_len, |
146 | | char *buf, const char **errstr) |
147 | 0 | { |
148 | 0 | size_t passwd_len = strlen(passwd); |
149 | | |
150 | | /* +1 here is just to avoid risk of unportable malloc(0) */ |
151 | 0 | char *crypt_buf = malloc(passwd_len + salt_len + 1); |
152 | 0 | bool ret; |
153 | |
|
154 | 0 | if (!crypt_buf) |
155 | 0 | { |
156 | 0 | *errstr = _("out of memory"); |
157 | 0 | return false; |
158 | 0 | } |
159 | | |
160 | | /* |
161 | | * Place salt at the end because it may be known by users trying to crack |
162 | | * the MD5 output. |
163 | | */ |
164 | 0 | memcpy(crypt_buf, passwd, passwd_len); |
165 | 0 | memcpy(crypt_buf + passwd_len, salt, salt_len); |
166 | |
|
167 | 0 | strcpy(buf, "md5"); |
168 | 0 | ret = pg_md5_hash(crypt_buf, passwd_len + salt_len, buf + 3, errstr); |
169 | |
|
170 | 0 | free(crypt_buf); |
171 | |
|
172 | 0 | return ret; |
173 | 0 | } |