/* * 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_ERR_GENERIC, "Some PX error (not specified)"}, {PXE_NO_HASH, "No such hash algorithm"}, {PXE_NO_CIPHER, "No such cipher algorithm"}, {PXE_NOTBLOCKSIZE, "Data not a multiple of block size"}, {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_DEV_READ_ERROR, "Error reading from random device"}, {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_MCRYPT_INTERNAL, "mcrypt internal error"}, {PXE_NO_RANDOM, "No strong random source"}, {PXE_DECRYPT_FAILED, "Decryption 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) { #ifdef HAVE_STRONG_RANDOM ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("could not generate a random number"))); #else ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("generating random data is not supported by this build"), errdetail("This functionality requires a source of strong random numbers"), errhint("You need to rebuild PostgreSQL using --enable-strong-random"))); #endif } 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 = px_alloc(ivs); memset(ivbuf, 0, ivs); if (ivlen > ivs) memcpy(ivbuf, iv, ivs); else memcpy(ivbuf, iv, ivlen); } if (klen > ks) klen = ks; keybuf = px_alloc(ks); memset(keybuf, 0, ks); memcpy(keybuf, key, klen); err = px_cipher_init(c, keybuf, klen, ivbuf); if (ivbuf) px_free(ivbuf); px_free(keybuf); return err; } static int combo_encrypt(PX_Combo *cx, const uint8 *data, unsigned dlen, uint8 *res, unsigned *rlen) { int err = 0; uint8 *bbuf; unsigned bs, bpos, i, pad; PX_Cipher *c = cx->cipher; bbuf = NULL; bs = px_cipher_block_size(c); /* encrypt */ if (bs > 1) { bbuf = px_alloc(bs * 4); bpos = dlen % bs; *rlen = dlen - bpos; memcpy(bbuf, data + *rlen, bpos); /* encrypt full-block data */ if (*rlen) { err = px_cipher_encrypt(c, data, *rlen, res); if (err) goto out; } /* bbuf has now bpos bytes of stuff */ if (cx->padding) { pad = bs - (bpos % bs); for (i = 0; i < pad; i++) bbuf[bpos++] = pad; } else if (bpos % bs) { /* ERROR? */ pad = bs - (bpos % bs); for (i = 0; i < pad; i++) bbuf[bpos++] = 0; } /* encrypt the rest - pad */ if (bpos) { err = px_cipher_encrypt(c, bbuf, bpos, res + *rlen); *rlen += bpos; } } else { /* stream cipher/mode - no pad needed */ err = px_cipher_encrypt(c, data, dlen, res); if (err) goto out; *rlen = dlen; } out: if (bbuf) px_free(bbuf); return err; } static int combo_decrypt(PX_Combo *cx, const uint8 *data, unsigned dlen, uint8 *res, unsigned *rlen) { unsigned bs, i, pad; unsigned pad_ok; PX_Cipher *c = cx->cipher; /* decide whether zero-length input is allowed */ if (dlen == 0) { /* with padding, empty ciphertext is not allowed */ if (cx->padding) return PXE_DECRYPT_FAILED; /* without padding, report empty result */ *rlen = 0; return 0; } bs = px_cipher_block_size(c); if (bs > 1 && (dlen % bs) != 0) goto block_error; /* decrypt */ *rlen = dlen; px_cipher_decrypt(c, data, dlen, res); /* unpad */ if (bs > 1 && cx->padding) { pad = res[*rlen - 1]; pad_ok = 0; if (pad > 0 && pad <= bs && pad <= *rlen) { pad_ok = 1; for (i = *rlen - pad; i < *rlen; i++) if (res[i] != pad) { pad_ok = 0; break; } } if (pad_ok) *rlen -= pad; } return 0; block_error: return PXE_NOTBLOCKSIZE; } static void combo_free(PX_Combo *cx) { if (cx->cipher) px_cipher_free(cx->cipher); px_memset(cx, 0, sizeof(*cx)); px_free(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 = px_alloc(sizeof(*cx)); memset(cx, 0, sizeof(*cx)); buf = px_alloc(strlen(name) + 1); strcpy(buf, name); err = parse_cipher_name(buf, &s_cipher, &s_pad); if (err) { px_free(buf); px_free(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; px_free(buf); *res = cx; return 0; err1: if (cx->cipher) px_cipher_free(cx->cipher); px_free(cx); px_free(buf); return PXE_NO_CIPHER; }