Line data Source code
1 : /*
2 : * pgcrypto.c
3 : * Various cryptographic stuff for PostgreSQL.
4 : *
5 : * Copyright (c) 2001 Marko Kreen
6 : * All rights reserved.
7 : *
8 : * Redistribution and use in source and binary forms, with or without
9 : * modification, are permitted provided that the following conditions
10 : * are met:
11 : * 1. Redistributions of source code must retain the above copyright
12 : * notice, this list of conditions and the following disclaimer.
13 : * 2. Redistributions in binary form must reproduce the above copyright
14 : * notice, this list of conditions and the following disclaimer in the
15 : * documentation and/or other materials provided with the distribution.
16 : *
17 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 : * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 : * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 : * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 : * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 : * SUCH DAMAGE.
28 : *
29 : * contrib/pgcrypto/pgcrypto.c
30 : */
31 :
32 : #include "postgres.h"
33 :
34 : #include <ctype.h>
35 :
36 : #include "parser/scansup.h"
37 : #include "pgcrypto.h"
38 : #include "px-crypt.h"
39 : #include "px.h"
40 : #include "utils/builtins.h"
41 : #include "utils/guc.h"
42 : #include "varatt.h"
43 :
44 48 : PG_MODULE_MAGIC_EXT(
45 : .name = "pgcrypto",
46 : .version = PG_VERSION
47 : );
48 :
49 : /* private stuff */
50 :
51 : static const struct config_enum_entry builtin_crypto_options[] = {
52 : {"on", BC_ON, false},
53 : {"off", BC_OFF, false},
54 : {"fips", BC_FIPS, false},
55 : {NULL, 0, false}
56 : };
57 :
58 : typedef int (*PFN) (const char *name, void **res);
59 : static void *find_provider(text *name, PFN provider_lookup, const char *desc,
60 : int silent);
61 :
62 : int builtin_crypto_enabled = BC_ON;
63 :
64 : /*
65 : * Entrypoint of this module.
66 : */
67 : void
68 48 : _PG_init(void)
69 : {
70 48 : DefineCustomEnumVariable("pgcrypto.builtin_crypto_enabled",
71 : "Sets if builtin crypto functions are enabled.",
72 : "\"on\" enables builtin crypto, \"off\" unconditionally disables and \"fips\" "
73 : "will disable builtin crypto if OpenSSL is in FIPS mode",
74 : &builtin_crypto_enabled,
75 : BC_ON,
76 : builtin_crypto_options,
77 : PGC_SUSET,
78 : 0,
79 : NULL,
80 : NULL,
81 : NULL);
82 48 : MarkGUCPrefixReserved("pgcrypto");
83 48 : }
84 :
85 : /* SQL function: hash(bytea, text) returns bytea */
86 16 : PG_FUNCTION_INFO_V1(pg_digest);
87 :
88 : Datum
89 88 : pg_digest(PG_FUNCTION_ARGS)
90 : {
91 : bytea *arg;
92 : text *name;
93 : unsigned len,
94 : hlen;
95 : PX_MD *md;
96 : bytea *res;
97 :
98 88 : name = PG_GETARG_TEXT_PP(1);
99 :
100 : /* will give error if fails */
101 88 : md = find_provider(name, (PFN) px_find_digest, "Digest", 0);
102 :
103 86 : hlen = px_md_result_size(md);
104 :
105 86 : res = (text *) palloc(hlen + VARHDRSZ);
106 86 : SET_VARSIZE(res, hlen + VARHDRSZ);
107 :
108 86 : arg = PG_GETARG_BYTEA_PP(0);
109 86 : len = VARSIZE_ANY_EXHDR(arg);
110 :
111 86 : px_md_update(md, (uint8 *) VARDATA_ANY(arg), len);
112 86 : px_md_finish(md, (uint8 *) VARDATA(res));
113 86 : px_md_free(md);
114 :
115 86 : PG_FREE_IF_COPY(arg, 0);
116 86 : PG_FREE_IF_COPY(name, 1);
117 :
118 86 : PG_RETURN_BYTEA_P(res);
119 : }
120 :
121 : /* SQL function: hmac(data:bytea, key:bytea, type:text) returns bytea */
122 14 : PG_FUNCTION_INFO_V1(pg_hmac);
123 :
124 : Datum
125 30 : pg_hmac(PG_FUNCTION_ARGS)
126 : {
127 : bytea *arg;
128 : bytea *key;
129 : text *name;
130 : unsigned len,
131 : hlen,
132 : klen;
133 : PX_HMAC *h;
134 : bytea *res;
135 :
136 30 : name = PG_GETARG_TEXT_PP(2);
137 :
138 : /* will give error if fails */
139 30 : h = find_provider(name, (PFN) px_find_hmac, "HMAC", 0);
140 :
141 28 : hlen = px_hmac_result_size(h);
142 :
143 28 : res = (text *) palloc(hlen + VARHDRSZ);
144 28 : SET_VARSIZE(res, hlen + VARHDRSZ);
145 :
146 28 : arg = PG_GETARG_BYTEA_PP(0);
147 28 : key = PG_GETARG_BYTEA_PP(1);
148 28 : len = VARSIZE_ANY_EXHDR(arg);
149 28 : klen = VARSIZE_ANY_EXHDR(key);
150 :
151 28 : px_hmac_init(h, (uint8 *) VARDATA_ANY(key), klen);
152 28 : px_hmac_update(h, (uint8 *) VARDATA_ANY(arg), len);
153 28 : px_hmac_finish(h, (uint8 *) VARDATA(res));
154 28 : px_hmac_free(h);
155 :
156 28 : PG_FREE_IF_COPY(arg, 0);
157 28 : PG_FREE_IF_COPY(key, 1);
158 28 : PG_FREE_IF_COPY(name, 2);
159 :
160 28 : PG_RETURN_BYTEA_P(res);
161 : }
162 :
163 :
164 : /* SQL function: pg_gen_salt(text) returns text */
165 10 : PG_FUNCTION_INFO_V1(pg_gen_salt);
166 :
167 : Datum
168 12 : pg_gen_salt(PG_FUNCTION_ARGS)
169 : {
170 12 : text *arg0 = PG_GETARG_TEXT_PP(0);
171 : int len;
172 : char buf[PX_MAX_SALT_LEN + 1];
173 :
174 12 : text_to_cstring_buffer(arg0, buf, sizeof(buf));
175 12 : len = px_gen_salt(buf, buf, 0);
176 10 : if (len < 0)
177 2 : ereport(ERROR,
178 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
179 : errmsg("gen_salt: %s", px_strerror(len))));
180 :
181 8 : PG_FREE_IF_COPY(arg0, 0);
182 :
183 8 : PG_RETURN_TEXT_P(cstring_to_text_with_len(buf, len));
184 : }
185 :
186 : /* SQL function: pg_gen_salt(text, int4) returns text */
187 8 : PG_FUNCTION_INFO_V1(pg_gen_salt_rounds);
188 :
189 : Datum
190 16 : pg_gen_salt_rounds(PG_FUNCTION_ARGS)
191 : {
192 16 : text *arg0 = PG_GETARG_TEXT_PP(0);
193 16 : int rounds = PG_GETARG_INT32(1);
194 : int len;
195 : char buf[PX_MAX_SALT_LEN + 1];
196 :
197 16 : text_to_cstring_buffer(arg0, buf, sizeof(buf));
198 16 : len = px_gen_salt(buf, buf, rounds);
199 16 : if (len < 0)
200 8 : ereport(ERROR,
201 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
202 : errmsg("gen_salt: %s", px_strerror(len))));
203 :
204 8 : PG_FREE_IF_COPY(arg0, 0);
205 :
206 8 : PG_RETURN_TEXT_P(cstring_to_text_with_len(buf, len));
207 : }
208 :
209 : /* SQL function: pg_crypt(psw:text, salt:text) returns text */
210 12 : PG_FUNCTION_INFO_V1(pg_crypt);
211 :
212 : Datum
213 104 : pg_crypt(PG_FUNCTION_ARGS)
214 : {
215 104 : text *arg0 = PG_GETARG_TEXT_PP(0);
216 104 : text *arg1 = PG_GETARG_TEXT_PP(1);
217 : char *buf0,
218 : *buf1,
219 : *cres,
220 : *resbuf;
221 : text *res;
222 :
223 104 : buf0 = text_to_cstring(arg0);
224 104 : buf1 = text_to_cstring(arg1);
225 :
226 104 : resbuf = palloc0(PX_MAX_CRYPT);
227 :
228 104 : cres = px_crypt(buf0, buf1, resbuf, PX_MAX_CRYPT);
229 :
230 92 : pfree(buf0);
231 92 : pfree(buf1);
232 :
233 92 : if (cres == NULL)
234 4 : ereport(ERROR,
235 : (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
236 : errmsg("crypt(3) returned NULL")));
237 :
238 88 : res = cstring_to_text(cres);
239 :
240 88 : pfree(resbuf);
241 :
242 88 : PG_FREE_IF_COPY(arg0, 0);
243 88 : PG_FREE_IF_COPY(arg1, 1);
244 :
245 88 : PG_RETURN_TEXT_P(res);
246 : }
247 :
248 : /* SQL function: pg_encrypt(bytea, bytea, text) returns bytea */
249 14 : PG_FUNCTION_INFO_V1(pg_encrypt);
250 :
251 : Datum
252 134 : pg_encrypt(PG_FUNCTION_ARGS)
253 : {
254 : int err;
255 : bytea *data,
256 : *key,
257 : *res;
258 : text *type;
259 : PX_Combo *c;
260 : unsigned dlen,
261 : klen,
262 : rlen;
263 :
264 134 : type = PG_GETARG_TEXT_PP(2);
265 134 : c = find_provider(type, (PFN) px_find_combo, "Cipher", 0);
266 :
267 132 : data = PG_GETARG_BYTEA_PP(0);
268 132 : key = PG_GETARG_BYTEA_PP(1);
269 132 : dlen = VARSIZE_ANY_EXHDR(data);
270 132 : klen = VARSIZE_ANY_EXHDR(key);
271 :
272 132 : rlen = px_combo_encrypt_len(c, dlen);
273 132 : res = palloc(VARHDRSZ + rlen);
274 :
275 132 : err = px_combo_init(c, (uint8 *) VARDATA_ANY(key), klen, NULL, 0);
276 132 : if (!err)
277 132 : err = px_combo_encrypt(c, (uint8 *) VARDATA_ANY(data), dlen,
278 : (uint8 *) VARDATA(res), &rlen);
279 132 : px_combo_free(c);
280 :
281 132 : PG_FREE_IF_COPY(data, 0);
282 132 : PG_FREE_IF_COPY(key, 1);
283 132 : PG_FREE_IF_COPY(type, 2);
284 :
285 132 : if (err)
286 : {
287 2 : pfree(res);
288 2 : ereport(ERROR,
289 : (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
290 : errmsg("encrypt error: %s", px_strerror(err))));
291 : }
292 :
293 130 : SET_VARSIZE(res, VARHDRSZ + rlen);
294 130 : PG_RETURN_BYTEA_P(res);
295 : }
296 :
297 : /* SQL function: pg_decrypt(bytea, bytea, text) returns bytea */
298 12 : PG_FUNCTION_INFO_V1(pg_decrypt);
299 :
300 : Datum
301 28 : pg_decrypt(PG_FUNCTION_ARGS)
302 : {
303 : int err;
304 : bytea *data,
305 : *key,
306 : *res;
307 : text *type;
308 : PX_Combo *c;
309 : unsigned dlen,
310 : klen,
311 : rlen;
312 :
313 28 : type = PG_GETARG_TEXT_PP(2);
314 28 : c = find_provider(type, (PFN) px_find_combo, "Cipher", 0);
315 :
316 28 : data = PG_GETARG_BYTEA_PP(0);
317 28 : key = PG_GETARG_BYTEA_PP(1);
318 28 : dlen = VARSIZE_ANY_EXHDR(data);
319 28 : klen = VARSIZE_ANY_EXHDR(key);
320 :
321 28 : rlen = px_combo_decrypt_len(c, dlen);
322 28 : res = palloc(VARHDRSZ + rlen);
323 :
324 28 : err = px_combo_init(c, (uint8 *) VARDATA_ANY(key), klen, NULL, 0);
325 28 : if (!err)
326 28 : err = px_combo_decrypt(c, (uint8 *) VARDATA_ANY(data), dlen,
327 : (uint8 *) VARDATA(res), &rlen);
328 :
329 28 : px_combo_free(c);
330 :
331 28 : if (err)
332 2 : ereport(ERROR,
333 : (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
334 : errmsg("decrypt error: %s", px_strerror(err))));
335 :
336 26 : SET_VARSIZE(res, VARHDRSZ + rlen);
337 :
338 26 : PG_FREE_IF_COPY(data, 0);
339 26 : PG_FREE_IF_COPY(key, 1);
340 26 : PG_FREE_IF_COPY(type, 2);
341 :
342 26 : PG_RETURN_BYTEA_P(res);
343 : }
344 :
345 : /* SQL function: pg_encrypt_iv(bytea, bytea, bytea, text) returns bytea */
346 12 : PG_FUNCTION_INFO_V1(pg_encrypt_iv);
347 :
348 : Datum
349 12 : pg_encrypt_iv(PG_FUNCTION_ARGS)
350 : {
351 : int err;
352 : bytea *data,
353 : *key,
354 : *iv,
355 : *res;
356 : text *type;
357 : PX_Combo *c;
358 : unsigned dlen,
359 : klen,
360 : ivlen,
361 : rlen;
362 :
363 12 : type = PG_GETARG_TEXT_PP(3);
364 12 : c = find_provider(type, (PFN) px_find_combo, "Cipher", 0);
365 :
366 12 : data = PG_GETARG_BYTEA_PP(0);
367 12 : key = PG_GETARG_BYTEA_PP(1);
368 12 : iv = PG_GETARG_BYTEA_PP(2);
369 12 : dlen = VARSIZE_ANY_EXHDR(data);
370 12 : klen = VARSIZE_ANY_EXHDR(key);
371 12 : ivlen = VARSIZE_ANY_EXHDR(iv);
372 :
373 12 : rlen = px_combo_encrypt_len(c, dlen);
374 12 : res = palloc(VARHDRSZ + rlen);
375 :
376 12 : err = px_combo_init(c, (uint8 *) VARDATA_ANY(key), klen,
377 : (uint8 *) VARDATA_ANY(iv), ivlen);
378 12 : if (!err)
379 12 : err = px_combo_encrypt(c, (uint8 *) VARDATA_ANY(data), dlen,
380 : (uint8 *) VARDATA(res), &rlen);
381 :
382 12 : px_combo_free(c);
383 :
384 12 : if (err)
385 0 : ereport(ERROR,
386 : (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
387 : errmsg("encrypt_iv error: %s", px_strerror(err))));
388 :
389 12 : SET_VARSIZE(res, VARHDRSZ + rlen);
390 :
391 12 : PG_FREE_IF_COPY(data, 0);
392 12 : PG_FREE_IF_COPY(key, 1);
393 12 : PG_FREE_IF_COPY(iv, 2);
394 12 : PG_FREE_IF_COPY(type, 3);
395 :
396 12 : PG_RETURN_BYTEA_P(res);
397 : }
398 :
399 : /* SQL function: pg_decrypt_iv(bytea, bytea, bytea, text) returns bytea */
400 12 : PG_FUNCTION_INFO_V1(pg_decrypt_iv);
401 :
402 : Datum
403 16 : pg_decrypt_iv(PG_FUNCTION_ARGS)
404 : {
405 : int err;
406 : bytea *data,
407 : *key,
408 : *iv,
409 : *res;
410 : text *type;
411 : PX_Combo *c;
412 : unsigned dlen,
413 : klen,
414 : rlen,
415 : ivlen;
416 :
417 16 : type = PG_GETARG_TEXT_PP(3);
418 16 : c = find_provider(type, (PFN) px_find_combo, "Cipher", 0);
419 :
420 16 : data = PG_GETARG_BYTEA_PP(0);
421 16 : key = PG_GETARG_BYTEA_PP(1);
422 16 : iv = PG_GETARG_BYTEA_PP(2);
423 16 : dlen = VARSIZE_ANY_EXHDR(data);
424 16 : klen = VARSIZE_ANY_EXHDR(key);
425 16 : ivlen = VARSIZE_ANY_EXHDR(iv);
426 :
427 16 : rlen = px_combo_decrypt_len(c, dlen);
428 16 : res = palloc(VARHDRSZ + rlen);
429 :
430 16 : err = px_combo_init(c, (uint8 *) VARDATA_ANY(key), klen,
431 : (uint8 *) VARDATA_ANY(iv), ivlen);
432 16 : if (!err)
433 16 : err = px_combo_decrypt(c, (uint8 *) VARDATA_ANY(data), dlen,
434 : (uint8 *) VARDATA(res), &rlen);
435 :
436 16 : px_combo_free(c);
437 :
438 16 : if (err)
439 2 : ereport(ERROR,
440 : (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
441 : errmsg("decrypt_iv error: %s", px_strerror(err))));
442 :
443 14 : SET_VARSIZE(res, VARHDRSZ + rlen);
444 :
445 14 : PG_FREE_IF_COPY(data, 0);
446 14 : PG_FREE_IF_COPY(key, 1);
447 14 : PG_FREE_IF_COPY(iv, 2);
448 14 : PG_FREE_IF_COPY(type, 3);
449 :
450 14 : PG_RETURN_BYTEA_P(res);
451 : }
452 :
453 : /* SQL function: pg_random_bytes(int4) returns bytea */
454 2 : PG_FUNCTION_INFO_V1(pg_random_bytes);
455 :
456 : Datum
457 0 : pg_random_bytes(PG_FUNCTION_ARGS)
458 : {
459 0 : int len = PG_GETARG_INT32(0);
460 : bytea *res;
461 :
462 0 : if (len < 1 || len > 1024)
463 0 : ereport(ERROR,
464 : (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
465 : errmsg("Length not in range")));
466 :
467 0 : res = palloc(VARHDRSZ + len);
468 0 : SET_VARSIZE(res, VARHDRSZ + len);
469 :
470 : /* generate result */
471 0 : if (!pg_strong_random(VARDATA(res), len))
472 0 : px_THROW_ERROR(PXE_NO_RANDOM);
473 :
474 0 : PG_RETURN_BYTEA_P(res);
475 : }
476 :
477 : /* SQL function: gen_random_uuid() returns uuid */
478 2 : PG_FUNCTION_INFO_V1(pg_random_uuid);
479 :
480 : Datum
481 0 : pg_random_uuid(PG_FUNCTION_ARGS)
482 : {
483 : /* redirect to built-in function */
484 0 : return gen_random_uuid(fcinfo);
485 : }
486 :
487 2 : PG_FUNCTION_INFO_V1(pg_check_fipsmode);
488 :
489 : Datum
490 0 : pg_check_fipsmode(PG_FUNCTION_ARGS)
491 : {
492 0 : PG_RETURN_BOOL(CheckFIPSMode());
493 : }
494 :
495 : static void *
496 308 : find_provider(text *name,
497 : PFN provider_lookup,
498 : const char *desc, int silent)
499 : {
500 : void *res;
501 : char *buf;
502 : int err;
503 :
504 308 : buf = downcase_truncate_identifier(VARDATA_ANY(name),
505 308 : VARSIZE_ANY_EXHDR(name),
506 : false);
507 :
508 308 : err = provider_lookup(buf, &res);
509 :
510 308 : if (err && !silent)
511 6 : ereport(ERROR,
512 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
513 : errmsg("Cannot use \"%s\": %s", buf, px_strerror(err))));
514 :
515 302 : pfree(buf);
516 :
517 302 : return err ? NULL : res;
518 : }
|