Coverage Report

Created: 2026-08-13 07:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}