Age Owner TLA 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-2023, 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
850 michael 28 CBC 395997 : bytesToHex(uint8 b[16], char *s)
29 : {
30 : static const char *hex = "0123456789abcdef";
31 : int q,
32 : w;
33 :
34 6731949 : for (q = 0, w = 0; q < 16; q++)
35 : {
36 6335952 : s[w++] = hex[(b[q] >> 4) & 0x0F];
37 6335952 : s[w++] = hex[b[q] & 0x0F];
38 : }
39 395997 : s[w] = '\0';
40 395997 : }
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 : * int pg_md5_hash(const void *buff, size_t len, char *hexsum)
49 : *
50 : * INPUT buff the buffer containing the bytes that you want
51 : * the MD5 sum of.
52 : * len number of bytes in the buffer.
53 : *
54 : * OUTPUT hexsum the MD5 sum as a '\0'-terminated string of
55 : * hexadecimal digits. an MD5 sum is 16 bytes long.
56 : * each byte is represented by two hexadecimal
57 : * characters. you thus need to provide an array
58 : * of 33 characters, including the trailing '\0'.
59 : *
60 : * errstr filled with a constant-string error message
61 : * on failure return; NULL on success.
62 : *
63 : * RETURNS false on failure (out of memory for internal buffers
64 : * or MD5 computation failure) or true on success.
65 : *
66 : * STANDARDS MD5 is described in RFC 1321.
67 : *
68 : * AUTHOR Sverre H. Huseby <sverrehu@online.no>
69 : *
70 : */
71 :
72 : bool
453 73 395997 : pg_md5_hash(const void *buff, size_t len, char *hexsum, const char **errstr)
74 : {
75 : uint8 sum[MD5_DIGEST_LENGTH];
76 : pg_cryptohash_ctx *ctx;
77 :
356 tgl 78 395997 : *errstr = NULL;
850 michael 79 395997 : ctx = pg_cryptohash_create(PG_MD5);
80 395997 : if (ctx == NULL)
81 : {
356 tgl 82 UBC 0 : *errstr = pg_cryptohash_error(NULL); /* returns OOM */
850 michael 83 0 : return false;
84 : }
85 :
850 michael 86 CBC 791994 : if (pg_cryptohash_init(ctx) < 0 ||
87 791994 : pg_cryptohash_update(ctx, buff, len) < 0 ||
783 88 395997 : pg_cryptohash_final(ctx, sum, sizeof(sum)) < 0)
89 : {
453 michael 90 UBC 0 : *errstr = pg_cryptohash_error(ctx);
850 91 0 : pg_cryptohash_free(ctx);
92 0 : return false;
93 : }
94 :
850 michael 95 CBC 395997 : bytesToHex(sum, hexsum);
96 395997 : pg_cryptohash_free(ctx);
97 395997 : return true;
98 : }
99 :
100 : /*
101 : * pg_md5_binary
102 : *
103 : * As above, except that the MD5 digest is returned as a binary string
104 : * (of size MD5_DIGEST_LENGTH) rather than being converted to ASCII hex.
105 : */
106 : bool
453 michael 107 UBC 0 : pg_md5_binary(const void *buff, size_t len, void *outbuf, const char **errstr)
108 : {
109 : pg_cryptohash_ctx *ctx;
110 :
111 0 : *errstr = NULL;
850 112 0 : ctx = pg_cryptohash_create(PG_MD5);
113 0 : if (ctx == NULL)
114 : {
453 115 0 : *errstr = pg_cryptohash_error(NULL); /* returns OOM */
850 116 0 : return false;
117 : }
118 :
119 0 : if (pg_cryptohash_init(ctx) < 0 ||
120 0 : pg_cryptohash_update(ctx, buff, len) < 0 ||
783 121 0 : pg_cryptohash_final(ctx, outbuf, MD5_DIGEST_LENGTH) < 0)
122 : {
453 123 0 : *errstr = pg_cryptohash_error(ctx);
850 124 0 : pg_cryptohash_free(ctx);
125 0 : return false;
126 : }
127 :
128 0 : pg_cryptohash_free(ctx);
129 0 : return true;
130 : }
131 :
132 :
133 : /*
134 : * Computes MD5 checksum of "passwd" (a null-terminated string) followed
135 : * by "salt" (which need not be null-terminated).
136 : *
137 : * Output format is "md5" followed by a 32-hex-digit MD5 checksum.
138 : * Hence, the output buffer "buf" must be at least 36 bytes long.
139 : *
140 : * Returns true if okay, false on error with *errstr providing some
141 : * error context.
142 : */
143 : bool
850 michael 144 CBC 47 : pg_md5_encrypt(const char *passwd, const char *salt, size_t salt_len,
145 : char *buf, const char **errstr)
146 : {
147 47 : size_t passwd_len = strlen(passwd);
148 :
149 : /* +1 here is just to avoid risk of unportable malloc(0) */
150 47 : char *crypt_buf = malloc(passwd_len + salt_len + 1);
151 : bool ret;
152 :
153 47 : if (!crypt_buf)
154 : {
453 michael 155 UBC 0 : *errstr = _("out of memory");
850 156 0 : return false;
157 : }
158 :
159 : /*
160 : * Place salt at the end because it may be known by users trying to crack
161 : * the MD5 output.
162 : */
850 michael 163 CBC 47 : memcpy(crypt_buf, passwd, passwd_len);
164 47 : memcpy(crypt_buf + passwd_len, salt, salt_len);
165 :
166 47 : strcpy(buf, "md5");
453 167 47 : ret = pg_md5_hash(crypt_buf, passwd_len + salt_len, buf + 3, errstr);
168 :
850 169 47 : free(crypt_buf);
170 :
171 47 : return ret;
172 : }
|