Line data Source code
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-2024, 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 142 : bytesToHex(uint8 b[16], char *s)
29 : {
30 : static const char *hex = "0123456789abcdef";
31 : int q,
32 : w;
33 :
34 2414 : for (q = 0, w = 0; q < 16; q++)
35 : {
36 2272 : s[w++] = hex[(b[q] >> 4) & 0x0F];
37 2272 : s[w++] = hex[b[q] & 0x0F];
38 : }
39 142 : s[w] = '\0';
40 142 : }
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 142 : pg_md5_hash(const void *buff, size_t len, char *hexsum, const char **errstr)
75 : {
76 : uint8 sum[MD5_DIGEST_LENGTH];
77 : pg_cryptohash_ctx *ctx;
78 :
79 142 : *errstr = NULL;
80 142 : ctx = pg_cryptohash_create(PG_MD5);
81 142 : if (ctx == NULL)
82 : {
83 0 : *errstr = pg_cryptohash_error(NULL); /* returns OOM */
84 0 : return false;
85 : }
86 :
87 284 : if (pg_cryptohash_init(ctx) < 0 ||
88 284 : pg_cryptohash_update(ctx, buff, len) < 0 ||
89 142 : pg_cryptohash_final(ctx, sum, sizeof(sum)) < 0)
90 : {
91 0 : *errstr = pg_cryptohash_error(ctx);
92 0 : pg_cryptohash_free(ctx);
93 0 : return false;
94 : }
95 :
96 142 : bytesToHex(sum, hexsum);
97 142 : pg_cryptohash_free(ctx);
98 142 : return true;
99 : }
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 0 : pg_md5_binary(const void *buff, size_t len, void *outbuf, const char **errstr)
109 : {
110 : pg_cryptohash_ctx *ctx;
111 :
112 0 : *errstr = NULL;
113 0 : ctx = pg_cryptohash_create(PG_MD5);
114 0 : if (ctx == NULL)
115 : {
116 0 : *errstr = pg_cryptohash_error(NULL); /* returns OOM */
117 0 : return false;
118 : }
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 : {
124 0 : *errstr = pg_cryptohash_error(ctx);
125 0 : pg_cryptohash_free(ctx);
126 0 : return false;
127 : }
128 :
129 0 : pg_cryptohash_free(ctx);
130 0 : return true;
131 : }
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 54 : pg_md5_encrypt(const char *passwd, const char *salt, size_t salt_len,
146 : char *buf, const char **errstr)
147 : {
148 54 : size_t passwd_len = strlen(passwd);
149 :
150 : /* +1 here is just to avoid risk of unportable malloc(0) */
151 54 : char *crypt_buf = malloc(passwd_len + salt_len + 1);
152 : bool ret;
153 :
154 54 : if (!crypt_buf)
155 : {
156 0 : *errstr = _("out of memory");
157 0 : return false;
158 : }
159 :
160 : /*
161 : * Place salt at the end because it may be known by users trying to crack
162 : * the MD5 output.
163 : */
164 54 : memcpy(crypt_buf, passwd, passwd_len);
165 54 : memcpy(crypt_buf + passwd_len, salt, salt_len);
166 :
167 54 : strcpy(buf, "md5");
168 54 : ret = pg_md5_hash(crypt_buf, passwd_len + salt_len, buf + 3, errstr);
169 :
170 54 : free(crypt_buf);
171 :
172 54 : return ret;
173 : }
|