mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-31 10:30:33 +03:00 
			
		
		
		
	The PXE_CIPHER_INIT error is used to report initialization errors, so appending a questionmark to the error isn't entirely accurate (using a space before the questionmark doubly so). Discussion: https://postgr.es/m/C89D932C-501E-4473-9750-638CFCD9095E@yesql.se
		
			
				
	
	
		
			342 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			342 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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);
 | |
| 	memset(keybuf, 0, 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;
 | |
| }
 |