PostgreSQL Source Code  git master
scram-common.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  * scram-common.c
3  * Shared frontend/backend code for SCRAM authentication
4  *
5  * This contains the common low-level functions needed in both frontend and
6  * backend, for implement the Salted Challenge Response Authentication
7  * Mechanism (SCRAM), per IETF's RFC 5802.
8  *
9  * Portions Copyright (c) 2017-2024, PostgreSQL Global Development Group
10  *
11  * IDENTIFICATION
12  * src/common/scram-common.c
13  *
14  *-------------------------------------------------------------------------
15  */
16 #ifndef FRONTEND
17 #include "postgres.h"
18 #else
19 #include "postgres_fe.h"
20 #endif
21 
22 #include "common/base64.h"
23 #include "common/hmac.h"
24 #include "common/scram-common.h"
25 #ifndef FRONTEND
26 #include "miscadmin.h"
27 #endif
28 #include "port/pg_bswap.h"
29 
30 /*
31  * Calculate SaltedPassword.
32  *
33  * The password should already be normalized by SASLprep. Returns 0 on
34  * success, -1 on failure with *errstr pointing to a message about the
35  * error details.
36  */
37 int
39  pg_cryptohash_type hash_type, int key_length,
40  const char *salt, int saltlen, int iterations,
41  uint8 *result, const char **errstr)
42 {
43  int password_len = strlen(password);
44  uint32 one = pg_hton32(1);
45  int i,
46  j;
48  uint8 Ui_prev[SCRAM_MAX_KEY_LEN];
49  pg_hmac_ctx *hmac_ctx = pg_hmac_create(hash_type);
50 
51  if (hmac_ctx == NULL)
52  {
53  *errstr = pg_hmac_error(NULL); /* returns OOM */
54  return -1;
55  }
56 
57  /*
58  * Iterate hash calculation of HMAC entry using given salt. This is
59  * essentially PBKDF2 (see RFC2898) with HMAC() as the pseudorandom
60  * function.
61  */
62 
63  /* First iteration */
64  if (pg_hmac_init(hmac_ctx, (uint8 *) password, password_len) < 0 ||
65  pg_hmac_update(hmac_ctx, (uint8 *) salt, saltlen) < 0 ||
66  pg_hmac_update(hmac_ctx, (uint8 *) &one, sizeof(uint32)) < 0 ||
67  pg_hmac_final(hmac_ctx, Ui_prev, key_length) < 0)
68  {
69  *errstr = pg_hmac_error(hmac_ctx);
70  pg_hmac_free(hmac_ctx);
71  return -1;
72  }
73 
74  memcpy(result, Ui_prev, key_length);
75 
76  /* Subsequent iterations */
77  for (i = 2; i <= iterations; i++)
78  {
79 #ifndef FRONTEND
80  /*
81  * Make sure that this is interruptible as scram_iterations could be
82  * set to a large value.
83  */
85 #endif
86 
87  if (pg_hmac_init(hmac_ctx, (uint8 *) password, password_len) < 0 ||
88  pg_hmac_update(hmac_ctx, (uint8 *) Ui_prev, key_length) < 0 ||
89  pg_hmac_final(hmac_ctx, Ui, key_length) < 0)
90  {
91  *errstr = pg_hmac_error(hmac_ctx);
92  pg_hmac_free(hmac_ctx);
93  return -1;
94  }
95 
96  for (j = 0; j < key_length; j++)
97  result[j] ^= Ui[j];
98  memcpy(Ui_prev, Ui, key_length);
99  }
100 
101  pg_hmac_free(hmac_ctx);
102  return 0;
103 }
104 
105 
106 /*
107  * Calculate hash for a NULL-terminated string. (The NULL terminator is
108  * not included in the hash). Returns 0 on success, -1 on failure with *errstr
109  * pointing to a message about the error details.
110  */
111 int
112 scram_H(const uint8 *input, pg_cryptohash_type hash_type, int key_length,
113  uint8 *result, const char **errstr)
114 {
115  pg_cryptohash_ctx *ctx;
116 
117  ctx = pg_cryptohash_create(hash_type);
118  if (ctx == NULL)
119  {
120  *errstr = pg_cryptohash_error(NULL); /* returns OOM */
121  return -1;
122  }
123 
124  if (pg_cryptohash_init(ctx) < 0 ||
125  pg_cryptohash_update(ctx, input, key_length) < 0 ||
126  pg_cryptohash_final(ctx, result, key_length) < 0)
127  {
128  *errstr = pg_cryptohash_error(ctx);
129  pg_cryptohash_free(ctx);
130  return -1;
131  }
132 
133  pg_cryptohash_free(ctx);
134  return 0;
135 }
136 
137 /*
138  * Calculate ClientKey. Returns 0 on success, -1 on failure with *errstr
139  * pointing to a message about the error details.
140  */
141 int
142 scram_ClientKey(const uint8 *salted_password,
143  pg_cryptohash_type hash_type, int key_length,
144  uint8 *result, const char **errstr)
145 {
146  pg_hmac_ctx *ctx = pg_hmac_create(hash_type);
147 
148  if (ctx == NULL)
149  {
150  *errstr = pg_hmac_error(NULL); /* returns OOM */
151  return -1;
152  }
153 
154  if (pg_hmac_init(ctx, salted_password, key_length) < 0 ||
155  pg_hmac_update(ctx, (uint8 *) "Client Key", strlen("Client Key")) < 0 ||
156  pg_hmac_final(ctx, result, key_length) < 0)
157  {
158  *errstr = pg_hmac_error(ctx);
159  pg_hmac_free(ctx);
160  return -1;
161  }
162 
163  pg_hmac_free(ctx);
164  return 0;
165 }
166 
167 /*
168  * Calculate ServerKey. Returns 0 on success, -1 on failure with *errstr
169  * pointing to a message about the error details.
170  */
171 int
172 scram_ServerKey(const uint8 *salted_password,
173  pg_cryptohash_type hash_type, int key_length,
174  uint8 *result, const char **errstr)
175 {
176  pg_hmac_ctx *ctx = pg_hmac_create(hash_type);
177 
178  if (ctx == NULL)
179  {
180  *errstr = pg_hmac_error(NULL); /* returns OOM */
181  return -1;
182  }
183 
184  if (pg_hmac_init(ctx, salted_password, key_length) < 0 ||
185  pg_hmac_update(ctx, (uint8 *) "Server Key", strlen("Server Key")) < 0 ||
186  pg_hmac_final(ctx, result, key_length) < 0)
187  {
188  *errstr = pg_hmac_error(ctx);
189  pg_hmac_free(ctx);
190  return -1;
191  }
192 
193  pg_hmac_free(ctx);
194  return 0;
195 }
196 
197 
198 /*
199  * Construct a SCRAM secret, for storing in pg_authid.rolpassword.
200  *
201  * The password should already have been processed with SASLprep, if necessary!
202  *
203  * If iterations is 0, default number of iterations is used. The result is
204  * palloc'd or malloc'd, so caller is responsible for freeing it.
205  *
206  * On error, returns NULL and sets *errstr to point to a message about the
207  * error details.
208  */
209 char *
210 scram_build_secret(pg_cryptohash_type hash_type, int key_length,
211  const char *salt, int saltlen, int iterations,
212  const char *password, const char **errstr)
213 {
214  uint8 salted_password[SCRAM_MAX_KEY_LEN];
215  uint8 stored_key[SCRAM_MAX_KEY_LEN];
216  uint8 server_key[SCRAM_MAX_KEY_LEN];
217  char *result;
218  char *p;
219  int maxlen;
220  int encoded_salt_len;
221  int encoded_stored_len;
222  int encoded_server_len;
223  int encoded_result;
224 
225  /* Only this hash method is supported currently */
226  Assert(hash_type == PG_SHA256);
227 
228  Assert(iterations > 0);
229 
230  /* Calculate StoredKey and ServerKey */
231  if (scram_SaltedPassword(password, hash_type, key_length,
232  salt, saltlen, iterations,
233  salted_password, errstr) < 0 ||
234  scram_ClientKey(salted_password, hash_type, key_length,
235  stored_key, errstr) < 0 ||
236  scram_H(stored_key, hash_type, key_length,
237  stored_key, errstr) < 0 ||
238  scram_ServerKey(salted_password, hash_type, key_length,
239  server_key, errstr) < 0)
240  {
241  /* errstr is filled already here */
242 #ifdef FRONTEND
243  return NULL;
244 #else
245  elog(ERROR, "could not calculate stored key and server key: %s",
246  *errstr);
247 #endif
248  }
249 
250  /*----------
251  * The format is:
252  * SCRAM-SHA-256$<iteration count>:<salt>$<StoredKey>:<ServerKey>
253  *----------
254  */
255  encoded_salt_len = pg_b64_enc_len(saltlen);
256  encoded_stored_len = pg_b64_enc_len(key_length);
257  encoded_server_len = pg_b64_enc_len(key_length);
258 
259  maxlen = strlen("SCRAM-SHA-256") + 1
260  + 10 + 1 /* iteration count */
261  + encoded_salt_len + 1 /* Base64-encoded salt */
262  + encoded_stored_len + 1 /* Base64-encoded StoredKey */
263  + encoded_server_len + 1; /* Base64-encoded ServerKey */
264 
265 #ifdef FRONTEND
266  result = malloc(maxlen);
267  if (!result)
268  {
269  *errstr = _("out of memory");
270  return NULL;
271  }
272 #else
273  result = palloc(maxlen);
274 #endif
275 
276  p = result + sprintf(result, "SCRAM-SHA-256$%d:", iterations);
277 
278  /* salt */
279  encoded_result = pg_b64_encode(salt, saltlen, p, encoded_salt_len);
280  if (encoded_result < 0)
281  {
282  *errstr = _("could not encode salt");
283 #ifdef FRONTEND
284  free(result);
285  return NULL;
286 #else
287  elog(ERROR, "%s", *errstr);
288 #endif
289  }
290  p += encoded_result;
291  *(p++) = '$';
292 
293  /* stored key */
294  encoded_result = pg_b64_encode((char *) stored_key, key_length, p,
295  encoded_stored_len);
296  if (encoded_result < 0)
297  {
298  *errstr = _("could not encode stored key");
299 #ifdef FRONTEND
300  free(result);
301  return NULL;
302 #else
303  elog(ERROR, "%s", *errstr);
304 #endif
305  }
306 
307  p += encoded_result;
308  *(p++) = ':';
309 
310  /* server key */
311  encoded_result = pg_b64_encode((char *) server_key, key_length, p,
312  encoded_server_len);
313  if (encoded_result < 0)
314  {
315  *errstr = _("could not encode server key");
316 #ifdef FRONTEND
317  free(result);
318  return NULL;
319 #else
320  elog(ERROR, "%s", *errstr);
321 #endif
322  }
323 
324  p += encoded_result;
325  *(p++) = '\0';
326 
327  Assert(p - result <= maxlen);
328 
329  return result;
330 }
int pg_b64_enc_len(int srclen)
Definition: base64.c:224
int pg_b64_encode(const char *src, int len, char *dst, int dstlen)
Definition: base64.c:49
unsigned int uint32
Definition: c.h:506
#define Assert(condition)
Definition: c.h:858
unsigned char uint8
Definition: c.h:504
int pg_cryptohash_update(pg_cryptohash_ctx *ctx, const uint8 *data, size_t len)
Definition: cryptohash.c:136
int pg_cryptohash_init(pg_cryptohash_ctx *ctx)
Definition: cryptohash.c:100
void pg_cryptohash_free(pg_cryptohash_ctx *ctx)
Definition: cryptohash.c:238
pg_cryptohash_ctx * pg_cryptohash_create(pg_cryptohash_type type)
Definition: cryptohash.c:74
int pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest, size_t len)
Definition: cryptohash.c:172
const char * pg_cryptohash_error(pg_cryptohash_ctx *ctx)
Definition: cryptohash.c:254
pg_cryptohash_type
Definition: cryptohash.h:20
@ PG_SHA256
Definition: cryptohash.h:24
#define _(x)
Definition: elog.c:90
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
#define free(a)
Definition: header.h:65
#define malloc(a)
Definition: header.h:50
pg_hmac_ctx * pg_hmac_create(pg_cryptohash_type type)
Definition: hmac.c:77
const char * pg_hmac_error(pg_hmac_ctx *ctx)
Definition: hmac.c:306
void pg_hmac_free(pg_hmac_ctx *ctx)
Definition: hmac.c:289
int pg_hmac_update(pg_hmac_ctx *ctx, const uint8 *data, size_t len)
Definition: hmac.c:223
int pg_hmac_init(pg_hmac_ctx *ctx, const uint8 *key, size_t len)
Definition: hmac.c:138
int pg_hmac_final(pg_hmac_ctx *ctx, uint8 *dest, size_t len)
Definition: hmac.c:244
FILE * input
int j
Definition: isn.c:74
int i
Definition: isn.c:73
void * palloc(Size size)
Definition: mcxt.c:1316
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:122
#define pg_hton32(x)
Definition: pg_bswap.h:121
#define sprintf
Definition: port.h:240
int scram_ServerKey(const uint8 *salted_password, pg_cryptohash_type hash_type, int key_length, uint8 *result, const char **errstr)
Definition: scram-common.c:172
int scram_SaltedPassword(const char *password, pg_cryptohash_type hash_type, int key_length, const char *salt, int saltlen, int iterations, uint8 *result, const char **errstr)
Definition: scram-common.c:38
int scram_ClientKey(const uint8 *salted_password, pg_cryptohash_type hash_type, int key_length, uint8 *result, const char **errstr)
Definition: scram-common.c:142
char * scram_build_secret(pg_cryptohash_type hash_type, int key_length, const char *salt, int saltlen, int iterations, const char *password, const char **errstr)
Definition: scram-common.c:210
int scram_H(const uint8 *input, pg_cryptohash_type hash_type, int key_length, uint8 *result, const char **errstr)
Definition: scram-common.c:112
#define SCRAM_MAX_KEY_LEN
Definition: scram-common.h:30
static char * password
Definition: streamutil.c:54
int iterations
Definition: thread-thread.c:39