/* * px.c * Various cryptographic stuff for PostgreSQL. * * Copyright (c) 2001 Marko Kreen * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * contrib/pgcrypto/px.c */ #include "postgres.h" #include "px.h" struct error_desc { int err; const char *desc; }; static const struct error_desc px_err_list[] = { {PXE_OK, "Everything ok"}, {PXE_NO_HASH, "No such hash algorithm"}, {PXE_NO_CIPHER, "No such cipher algorithm"}, {PXE_BAD_OPTION, "Unknown option"}, {PXE_BAD_FORMAT, "Badly formatted type"}, {PXE_KEY_TOO_BIG, "Key was too big"}, {PXE_CIPHER_INIT, "Cipher cannot be initialized"}, {PXE_HASH_UNUSABLE_FOR_HMAC, "This hash algorithm is unusable for HMAC"}, {PXE_BUG, "pgcrypto bug"}, {PXE_ARGUMENT_ERROR, "Illegal argument to function"}, {PXE_UNKNOWN_SALT_ALGO, "Unknown salt algorithm"}, {PXE_BAD_SALT_ROUNDS, "Incorrect number of rounds"}, {PXE_NO_RANDOM, "Failed to generate strong random bits"}, {PXE_DECRYPT_FAILED, "Decryption failed"}, {PXE_ENCRYPT_FAILED, "Encryption failed"}, {PXE_PGP_CORRUPT_DATA, "Wrong key or corrupt data"}, {PXE_PGP_CORRUPT_ARMOR, "Corrupt ascii-armor"}, {PXE_PGP_UNSUPPORTED_COMPR, "Unsupported compression algorithm"}, {PXE_PGP_UNSUPPORTED_CIPHER, "Unsupported cipher algorithm"}, {PXE_PGP_UNSUPPORTED_HASH, "Unsupported digest algorithm"}, {PXE_PGP_COMPRESSION_ERROR, "Compression error"}, {PXE_PGP_NOT_TEXT, "Not text data"}, {PXE_PGP_UNEXPECTED_PKT, "Unexpected packet in key data"}, {PXE_PGP_MATH_FAILED, "Math operation failed"}, {PXE_PGP_SHORT_ELGAMAL_KEY, "Elgamal keys must be at least 1024 bits long"}, {PXE_PGP_UNKNOWN_PUBALGO, "Unknown public-key encryption algorithm"}, {PXE_PGP_WRONG_KEY, "Wrong key"}, {PXE_PGP_MULTIPLE_KEYS, "Several keys given - pgcrypto does not handle keyring"}, {PXE_PGP_EXPECT_PUBLIC_KEY, "Refusing to encrypt with secret key"}, {PXE_PGP_EXPECT_SECRET_KEY, "Cannot decrypt with public key"}, {PXE_PGP_NOT_V4_KEYPKT, "Only V4 key packets are supported"}, {PXE_PGP_KEYPKT_CORRUPT, "Corrupt key packet"}, {PXE_PGP_NO_USABLE_KEY, "No encryption key found"}, {PXE_PGP_NEED_SECRET_PSW, "Need password for secret key"}, {PXE_PGP_BAD_S2K_MODE, "Bad S2K mode"}, {PXE_PGP_UNSUPPORTED_PUBALGO, "Unsupported public key algorithm"}, {PXE_PGP_MULTIPLE_SUBKEYS, "Several subkeys not supported"}, {0, NULL}, }; /* * Call ereport(ERROR, ...), with an error code and message corresponding to * the PXE_* error code given as argument. * * This is similar to px_strerror(err), but for some errors, we fill in the * error code and detail fields more appropriately. */ void px_THROW_ERROR(int err) { if (err == PXE_NO_RANDOM) { ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("could not generate a random number"))); } else { /* For other errors, use the message from the above list. */ ereport(ERROR, (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), errmsg("%s", px_strerror(err)))); } } const char * px_strerror(int err) { const struct error_desc *e; for (e = px_err_list; e->desc; e++) if (e->err == err) return e->desc; return "Bad error code"; } /* memset that must not be optimized away */ void px_memset(void *ptr, int c, size_t len) { memset(ptr, c, len); } const char * px_resolve_alias(const PX_Alias *list, const char *name) { while (list->name) { if (pg_strcasecmp(list->alias, name) == 0) return list->name; list++; } return name; } static void (*debug_handler) (const char *) = NULL; void px_set_debug_handler(void (*handler) (const char *)) { debug_handler = handler; } void px_debug(const char *fmt,...) { va_list ap; va_start(ap, fmt); if (debug_handler) { char buf[512]; vsnprintf(buf, sizeof(buf), fmt, ap); debug_handler(buf); } va_end(ap); } /* * combo - cipher + padding (+ checksum) */ static unsigned combo_encrypt_len(PX_Combo *cx, unsigned dlen) { return dlen + 512; } static unsigned combo_decrypt_len(PX_Combo *cx, unsigned dlen) { return dlen; } static int combo_init(PX_Combo *cx, const uint8 *key, unsigned klen, const uint8 *iv, unsigned ivlen) { int err; unsigned ks, ivs; PX_Cipher *c = cx->cipher; uint8 *ivbuf = NULL; uint8 *keybuf; ks = px_cipher_key_size(c); ivs = px_cipher_iv_size(c); if (ivs > 0) { ivbuf = palloc0(ivs); if (ivlen > ivs) memcpy(ivbuf, iv, ivs); else if (ivlen > 0) memcpy(ivbuf, iv, ivlen); } if (klen > ks) klen = ks; keybuf = palloc0(ks); memcpy(keybuf, key, klen); err = px_cipher_init(c, keybuf, klen, ivbuf); if (ivbuf) pfree(ivbuf); pfree(keybuf); return err; } static int combo_encrypt(PX_Combo *cx, const uint8 *data, unsigned dlen, uint8 *res, unsigned *rlen) { return px_cipher_encrypt(cx->cipher, cx->padding, data, dlen, res, rlen); } static int combo_decrypt(PX_Combo *cx, const uint8 *data, unsigned dlen, uint8 *res, unsigned *rlen) { return px_cipher_decrypt(cx->cipher, cx->padding, data, dlen, res, rlen); } static void combo_free(PX_Combo *cx) { if (cx->cipher) px_cipher_free(cx->cipher); px_memset(cx, 0, sizeof(*cx)); pfree(cx); } /* PARSER */ static int parse_cipher_name(char *full, char **cipher, char **pad) { char *p, *p2, *q; *cipher = full; *pad = NULL; p = strchr(full, '/'); if (p != NULL) *p++ = 0; while (p != NULL) { if ((q = strchr(p, '/')) != NULL) *q++ = 0; if (!*p) { p = q; continue; } p2 = strchr(p, ':'); if (p2 != NULL) { *p2++ = 0; if (strcmp(p, "pad") == 0) *pad = p2; else return PXE_BAD_OPTION; } else return PXE_BAD_FORMAT; p = q; } return 0; } /* provider */ int px_find_combo(const char *name, PX_Combo **res) { int err; char *buf, *s_cipher, *s_pad; PX_Combo *cx; cx = palloc0(sizeof(*cx)); buf = pstrdup(name); err = parse_cipher_name(buf, &s_cipher, &s_pad); if (err) { pfree(buf); pfree(cx); return err; } err = px_find_cipher(s_cipher, &cx->cipher); if (err) goto err1; if (s_pad != NULL) { if (strcmp(s_pad, "pkcs") == 0) cx->padding = 1; else if (strcmp(s_pad, "none") == 0) cx->padding = 0; else goto err1; } else cx->padding = 1; cx->init = combo_init; cx->encrypt = combo_encrypt; cx->decrypt = combo_decrypt; cx->encrypt_len = combo_encrypt_len; cx->decrypt_len = combo_decrypt_len; cx->free = combo_free; pfree(buf); *res = cx; return 0; err1: if (cx->cipher) px_cipher_free(cx->cipher); pfree(cx); pfree(buf); return PXE_NO_CIPHER; }