mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +03:00 
			
		
		
		
	pgp_sym_encrypt's option is spelled "sess-key", not "enable-session-key". Spotted by Jeff Janes. In passing, improve a comment in pgp-pgsql.c to make it clearer that the debugging options are intentionally undocumented.
		
			
				
	
	
		
			906 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			906 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * pgp-pgsql.c
 | 
						|
 *		PostgreSQL wrappers for pgp.
 | 
						|
 *
 | 
						|
 * Copyright (c) 2005 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/pgp-pgsql.c
 | 
						|
 */
 | 
						|
 | 
						|
#include "postgres.h"
 | 
						|
 | 
						|
#include "mb/pg_wchar.h"
 | 
						|
#include "utils/builtins.h"
 | 
						|
 | 
						|
#include "mbuf.h"
 | 
						|
#include "px.h"
 | 
						|
#include "pgp.h"
 | 
						|
 | 
						|
/*
 | 
						|
 * public functions
 | 
						|
 */
 | 
						|
PG_FUNCTION_INFO_V1(pgp_sym_encrypt_bytea);
 | 
						|
PG_FUNCTION_INFO_V1(pgp_sym_encrypt_text);
 | 
						|
PG_FUNCTION_INFO_V1(pgp_sym_decrypt_bytea);
 | 
						|
PG_FUNCTION_INFO_V1(pgp_sym_decrypt_text);
 | 
						|
 | 
						|
PG_FUNCTION_INFO_V1(pgp_pub_encrypt_bytea);
 | 
						|
PG_FUNCTION_INFO_V1(pgp_pub_encrypt_text);
 | 
						|
PG_FUNCTION_INFO_V1(pgp_pub_decrypt_bytea);
 | 
						|
PG_FUNCTION_INFO_V1(pgp_pub_decrypt_text);
 | 
						|
 | 
						|
PG_FUNCTION_INFO_V1(pgp_key_id_w);
 | 
						|
 | 
						|
PG_FUNCTION_INFO_V1(pg_armor);
 | 
						|
PG_FUNCTION_INFO_V1(pg_dearmor);
 | 
						|
 | 
						|
/*
 | 
						|
 * Mix a block of data into RNG.
 | 
						|
 */
 | 
						|
static void
 | 
						|
add_block_entropy(PX_MD *md, text *data)
 | 
						|
{
 | 
						|
	uint8		sha1[20];
 | 
						|
 | 
						|
	px_md_reset(md);
 | 
						|
	px_md_update(md, (uint8 *) VARDATA(data), VARSIZE(data) - VARHDRSZ);
 | 
						|
	px_md_finish(md, sha1);
 | 
						|
 | 
						|
	px_add_entropy(sha1, 20);
 | 
						|
 | 
						|
	px_memset(sha1, 0, 20);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Mix user data into RNG.  It is for user own interests to have
 | 
						|
 * RNG state shuffled.
 | 
						|
 */
 | 
						|
static void
 | 
						|
add_entropy(text *data1, text *data2, text *data3)
 | 
						|
{
 | 
						|
	PX_MD	   *md;
 | 
						|
	uint8		rnd[3];
 | 
						|
 | 
						|
	if (!data1 && !data2 && !data3)
 | 
						|
		return;
 | 
						|
 | 
						|
	if (px_get_random_bytes(rnd, 3) < 0)
 | 
						|
		return;
 | 
						|
 | 
						|
	if (px_find_digest("sha1", &md) < 0)
 | 
						|
		return;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Try to make the feeding unpredictable.
 | 
						|
	 *
 | 
						|
	 * Prefer data over keys, as it's rather likely that key is same in
 | 
						|
	 * several calls.
 | 
						|
	 */
 | 
						|
 | 
						|
	/* chance: 7/8 */
 | 
						|
	if (data1 && rnd[0] >= 32)
 | 
						|
		add_block_entropy(md, data1);
 | 
						|
 | 
						|
	/* chance: 5/8 */
 | 
						|
	if (data2 && rnd[1] >= 160)
 | 
						|
		add_block_entropy(md, data2);
 | 
						|
 | 
						|
	/* chance: 5/8 */
 | 
						|
	if (data3 && rnd[2] >= 160)
 | 
						|
		add_block_entropy(md, data3);
 | 
						|
 | 
						|
	px_md_free(md);
 | 
						|
	px_memset(rnd, 0, sizeof(rnd));
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * returns src in case of no conversion or error
 | 
						|
 */
 | 
						|
static text *
 | 
						|
convert_charset(text *src, int cset_from, int cset_to)
 | 
						|
{
 | 
						|
	int			src_len = VARSIZE(src) - VARHDRSZ;
 | 
						|
	unsigned char *dst;
 | 
						|
	unsigned char *csrc = (unsigned char *) VARDATA(src);
 | 
						|
	text	   *res;
 | 
						|
 | 
						|
	dst = pg_do_encoding_conversion(csrc, src_len, cset_from, cset_to);
 | 
						|
	if (dst == csrc)
 | 
						|
		return src;
 | 
						|
 | 
						|
	res = cstring_to_text((char *) dst);
 | 
						|
	pfree(dst);
 | 
						|
	return res;
 | 
						|
}
 | 
						|
 | 
						|
static text *
 | 
						|
convert_from_utf8(text *src)
 | 
						|
{
 | 
						|
	return convert_charset(src, PG_UTF8, GetDatabaseEncoding());
 | 
						|
}
 | 
						|
 | 
						|
static text *
 | 
						|
convert_to_utf8(text *src)
 | 
						|
{
 | 
						|
	return convert_charset(src, GetDatabaseEncoding(), PG_UTF8);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
clear_and_pfree(text *p)
 | 
						|
{
 | 
						|
	px_memset(p, 0, VARSIZE(p));
 | 
						|
	pfree(p);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * expect-* arguments storage
 | 
						|
 */
 | 
						|
struct debug_expect
 | 
						|
{
 | 
						|
	int			debug;
 | 
						|
	int			expect;
 | 
						|
	int			cipher_algo;
 | 
						|
	int			s2k_mode;
 | 
						|
	int			s2k_cipher_algo;
 | 
						|
	int			s2k_digest_algo;
 | 
						|
	int			compress_algo;
 | 
						|
	int			use_sess_key;
 | 
						|
	int			disable_mdc;
 | 
						|
	int			unicode_mode;
 | 
						|
};
 | 
						|
 | 
						|
static void
 | 
						|
fill_expect(struct debug_expect * ex, int text_mode)
 | 
						|
{
 | 
						|
	ex->debug = 0;
 | 
						|
	ex->expect = 0;
 | 
						|
	ex->cipher_algo = -1;
 | 
						|
	ex->s2k_mode = -1;
 | 
						|
	ex->s2k_cipher_algo = -1;
 | 
						|
	ex->s2k_digest_algo = -1;
 | 
						|
	ex->compress_algo = -1;
 | 
						|
	ex->use_sess_key = -1;
 | 
						|
	ex->disable_mdc = -1;
 | 
						|
	ex->unicode_mode = -1;
 | 
						|
}
 | 
						|
 | 
						|
#define EX_MSG(arg) \
 | 
						|
	ereport(NOTICE, (errmsg( \
 | 
						|
		"pgp_decrypt: unexpected %s: expected %d got %d", \
 | 
						|
		CppAsString(arg), ex->arg, ctx->arg)))
 | 
						|
 | 
						|
#define EX_CHECK(arg) do { \
 | 
						|
		if (ex->arg >= 0 && ex->arg != ctx->arg) EX_MSG(arg); \
 | 
						|
	} while (0)
 | 
						|
 | 
						|
static void
 | 
						|
check_expect(PGP_Context *ctx, struct debug_expect * ex)
 | 
						|
{
 | 
						|
	EX_CHECK(cipher_algo);
 | 
						|
	EX_CHECK(s2k_mode);
 | 
						|
	EX_CHECK(s2k_digest_algo);
 | 
						|
	EX_CHECK(use_sess_key);
 | 
						|
	if (ctx->use_sess_key)
 | 
						|
		EX_CHECK(s2k_cipher_algo);
 | 
						|
	EX_CHECK(disable_mdc);
 | 
						|
	EX_CHECK(compress_algo);
 | 
						|
	EX_CHECK(unicode_mode);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
show_debug(const char *msg)
 | 
						|
{
 | 
						|
	ereport(NOTICE, (errmsg("dbg: %s", msg)));
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
set_arg(PGP_Context *ctx, char *key, char *val,
 | 
						|
		struct debug_expect * ex)
 | 
						|
{
 | 
						|
	int			res = 0;
 | 
						|
 | 
						|
	if (strcmp(key, "cipher-algo") == 0)
 | 
						|
		res = pgp_set_cipher_algo(ctx, val);
 | 
						|
	else if (strcmp(key, "disable-mdc") == 0)
 | 
						|
		res = pgp_disable_mdc(ctx, atoi(val));
 | 
						|
	else if (strcmp(key, "sess-key") == 0)
 | 
						|
		res = pgp_set_sess_key(ctx, atoi(val));
 | 
						|
	else if (strcmp(key, "s2k-mode") == 0)
 | 
						|
		res = pgp_set_s2k_mode(ctx, atoi(val));
 | 
						|
	else if (strcmp(key, "s2k-digest-algo") == 0)
 | 
						|
		res = pgp_set_s2k_digest_algo(ctx, val);
 | 
						|
	else if (strcmp(key, "s2k-cipher-algo") == 0)
 | 
						|
		res = pgp_set_s2k_cipher_algo(ctx, val);
 | 
						|
	else if (strcmp(key, "compress-algo") == 0)
 | 
						|
		res = pgp_set_compress_algo(ctx, atoi(val));
 | 
						|
	else if (strcmp(key, "compress-level") == 0)
 | 
						|
		res = pgp_set_compress_level(ctx, atoi(val));
 | 
						|
	else if (strcmp(key, "convert-crlf") == 0)
 | 
						|
		res = pgp_set_convert_crlf(ctx, atoi(val));
 | 
						|
	else if (strcmp(key, "unicode-mode") == 0)
 | 
						|
		res = pgp_set_unicode_mode(ctx, atoi(val));
 | 
						|
	/*
 | 
						|
	 * The remaining options are for debugging/testing and are therefore not
 | 
						|
	 * documented in the user-facing docs.
 | 
						|
	 */
 | 
						|
	else if (ex != NULL && strcmp(key, "debug") == 0)
 | 
						|
		ex->debug = atoi(val);
 | 
						|
	else if (ex != NULL && strcmp(key, "expect-cipher-algo") == 0)
 | 
						|
	{
 | 
						|
		ex->expect = 1;
 | 
						|
		ex->cipher_algo = pgp_get_cipher_code(val);
 | 
						|
	}
 | 
						|
	else if (ex != NULL && strcmp(key, "expect-disable-mdc") == 0)
 | 
						|
	{
 | 
						|
		ex->expect = 1;
 | 
						|
		ex->disable_mdc = atoi(val);
 | 
						|
	}
 | 
						|
	else if (ex != NULL && strcmp(key, "expect-sess-key") == 0)
 | 
						|
	{
 | 
						|
		ex->expect = 1;
 | 
						|
		ex->use_sess_key = atoi(val);
 | 
						|
	}
 | 
						|
	else if (ex != NULL && strcmp(key, "expect-s2k-mode") == 0)
 | 
						|
	{
 | 
						|
		ex->expect = 1;
 | 
						|
		ex->s2k_mode = atoi(val);
 | 
						|
	}
 | 
						|
	else if (ex != NULL && strcmp(key, "expect-s2k-digest-algo") == 0)
 | 
						|
	{
 | 
						|
		ex->expect = 1;
 | 
						|
		ex->s2k_digest_algo = pgp_get_digest_code(val);
 | 
						|
	}
 | 
						|
	else if (ex != NULL && strcmp(key, "expect-s2k-cipher-algo") == 0)
 | 
						|
	{
 | 
						|
		ex->expect = 1;
 | 
						|
		ex->s2k_cipher_algo = pgp_get_cipher_code(val);
 | 
						|
	}
 | 
						|
	else if (ex != NULL && strcmp(key, "expect-compress-algo") == 0)
 | 
						|
	{
 | 
						|
		ex->expect = 1;
 | 
						|
		ex->compress_algo = atoi(val);
 | 
						|
	}
 | 
						|
	else if (ex != NULL && strcmp(key, "expect-unicode-mode") == 0)
 | 
						|
	{
 | 
						|
		ex->expect = 1;
 | 
						|
		ex->unicode_mode = atoi(val);
 | 
						|
	}
 | 
						|
	else
 | 
						|
		res = PXE_ARGUMENT_ERROR;
 | 
						|
 | 
						|
	return res;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Find next word.  Handle ',' and '=' as words.  Skip whitespace.
 | 
						|
 * Put word info into res_p, res_len.
 | 
						|
 * Returns ptr to next word.
 | 
						|
 */
 | 
						|
static char *
 | 
						|
getword(char *p, char **res_p, int *res_len)
 | 
						|
{
 | 
						|
	/* whitespace at start */
 | 
						|
	while (*p && (*p == ' ' || *p == '\t' || *p == '\n'))
 | 
						|
		p++;
 | 
						|
 | 
						|
	/* word data */
 | 
						|
	*res_p = p;
 | 
						|
	if (*p == '=' || *p == ',')
 | 
						|
		p++;
 | 
						|
	else
 | 
						|
		while (*p && !(*p == ' ' || *p == '\t' || *p == '\n'
 | 
						|
					   || *p == '=' || *p == ','))
 | 
						|
			p++;
 | 
						|
 | 
						|
	/* word end */
 | 
						|
	*res_len = p - *res_p;
 | 
						|
 | 
						|
	/* whitespace at end */
 | 
						|
	while (*p && (*p == ' ' || *p == '\t' || *p == '\n'))
 | 
						|
		p++;
 | 
						|
 | 
						|
	return p;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Convert to lowercase asciiz string.
 | 
						|
 */
 | 
						|
static char *
 | 
						|
downcase_convert(const uint8 *s, int len)
 | 
						|
{
 | 
						|
	int			c,
 | 
						|
				i;
 | 
						|
	char	   *res = palloc(len + 1);
 | 
						|
 | 
						|
	for (i = 0; i < len; i++)
 | 
						|
	{
 | 
						|
		c = s[i];
 | 
						|
		if (c >= 'A' && c <= 'Z')
 | 
						|
			c += 'a' - 'A';
 | 
						|
		res[i] = c;
 | 
						|
	}
 | 
						|
	res[len] = 0;
 | 
						|
	return res;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
parse_args(PGP_Context *ctx, uint8 *args, int arg_len,
 | 
						|
		   struct debug_expect * ex)
 | 
						|
{
 | 
						|
	char	   *str = downcase_convert(args, arg_len);
 | 
						|
	char	   *key,
 | 
						|
			   *val;
 | 
						|
	int			key_len,
 | 
						|
				val_len;
 | 
						|
	int			res = 0;
 | 
						|
	char	   *p = str;
 | 
						|
 | 
						|
	while (*p)
 | 
						|
	{
 | 
						|
		res = PXE_ARGUMENT_ERROR;
 | 
						|
		p = getword(p, &key, &key_len);
 | 
						|
		if (*p++ != '=')
 | 
						|
			break;
 | 
						|
		p = getword(p, &val, &val_len);
 | 
						|
		if (*p == '\0')
 | 
						|
			;
 | 
						|
		else if (*p++ != ',')
 | 
						|
			break;
 | 
						|
 | 
						|
		if (*key == 0 || *val == 0 || val_len == 0)
 | 
						|
			break;
 | 
						|
 | 
						|
		key[key_len] = 0;
 | 
						|
		val[val_len] = 0;
 | 
						|
 | 
						|
		res = set_arg(ctx, key, val, ex);
 | 
						|
		if (res < 0)
 | 
						|
			break;
 | 
						|
	}
 | 
						|
	pfree(str);
 | 
						|
	return res;
 | 
						|
}
 | 
						|
 | 
						|
static MBuf *
 | 
						|
create_mbuf_from_vardata(text *data)
 | 
						|
{
 | 
						|
	return mbuf_create_from_data((uint8 *) VARDATA(data),
 | 
						|
								 VARSIZE(data) - VARHDRSZ);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
init_work(PGP_Context **ctx_p, int is_text,
 | 
						|
		  text *args, struct debug_expect * ex)
 | 
						|
{
 | 
						|
	int			err = pgp_init(ctx_p);
 | 
						|
 | 
						|
	fill_expect(ex, is_text);
 | 
						|
 | 
						|
	if (err == 0 && args != NULL)
 | 
						|
		err = parse_args(*ctx_p, (uint8 *) VARDATA(args),
 | 
						|
						 VARSIZE(args) - VARHDRSZ, ex);
 | 
						|
 | 
						|
	if (err)
 | 
						|
	{
 | 
						|
		ereport(ERROR,
 | 
						|
				(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
 | 
						|
				 errmsg("%s", px_strerror(err))));
 | 
						|
	}
 | 
						|
 | 
						|
	if (ex->debug)
 | 
						|
		px_set_debug_handler(show_debug);
 | 
						|
 | 
						|
	pgp_set_text_mode(*ctx_p, is_text);
 | 
						|
}
 | 
						|
 | 
						|
static bytea *
 | 
						|
encrypt_internal(int is_pubenc, int is_text,
 | 
						|
				 text *data, text *key, text *args)
 | 
						|
{
 | 
						|
	MBuf	   *src,
 | 
						|
			   *dst;
 | 
						|
	uint8		tmp[VARHDRSZ];
 | 
						|
	uint8	   *restmp;
 | 
						|
	bytea	   *res;
 | 
						|
	int			res_len;
 | 
						|
	PGP_Context *ctx;
 | 
						|
	int			err;
 | 
						|
	struct debug_expect ex;
 | 
						|
	text	   *tmp_data = NULL;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Add data and key info RNG.
 | 
						|
	 */
 | 
						|
	add_entropy(data, key, NULL);
 | 
						|
 | 
						|
	init_work(&ctx, is_text, args, &ex);
 | 
						|
 | 
						|
	if (is_text && pgp_get_unicode_mode(ctx))
 | 
						|
	{
 | 
						|
		tmp_data = convert_to_utf8(data);
 | 
						|
		if (tmp_data == data)
 | 
						|
			tmp_data = NULL;
 | 
						|
		else
 | 
						|
			data = tmp_data;
 | 
						|
	}
 | 
						|
 | 
						|
	src = create_mbuf_from_vardata(data);
 | 
						|
	dst = mbuf_create(VARSIZE(data) + 128);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * reserve room for header
 | 
						|
	 */
 | 
						|
	mbuf_append(dst, tmp, VARHDRSZ);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * set key
 | 
						|
	 */
 | 
						|
	if (is_pubenc)
 | 
						|
	{
 | 
						|
		MBuf	   *kbuf = create_mbuf_from_vardata(key);
 | 
						|
 | 
						|
		err = pgp_set_pubkey(ctx, kbuf,
 | 
						|
							 NULL, 0, 0);
 | 
						|
		mbuf_free(kbuf);
 | 
						|
	}
 | 
						|
	else
 | 
						|
		err = pgp_set_symkey(ctx, (uint8 *) VARDATA(key),
 | 
						|
							 VARSIZE(key) - VARHDRSZ);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * encrypt
 | 
						|
	 */
 | 
						|
	if (err >= 0)
 | 
						|
		err = pgp_encrypt(ctx, src, dst);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * check for error
 | 
						|
	 */
 | 
						|
	if (err)
 | 
						|
	{
 | 
						|
		if (ex.debug)
 | 
						|
			px_set_debug_handler(NULL);
 | 
						|
		if (tmp_data)
 | 
						|
			clear_and_pfree(tmp_data);
 | 
						|
		pgp_free(ctx);
 | 
						|
		mbuf_free(src);
 | 
						|
		mbuf_free(dst);
 | 
						|
		ereport(ERROR,
 | 
						|
				(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
 | 
						|
				 errmsg("%s", px_strerror(err))));
 | 
						|
	}
 | 
						|
 | 
						|
	/* res_len includes VARHDRSZ */
 | 
						|
	res_len = mbuf_steal_data(dst, &restmp);
 | 
						|
	res = (bytea *) restmp;
 | 
						|
	SET_VARSIZE(res, res_len);
 | 
						|
 | 
						|
	if (tmp_data)
 | 
						|
		clear_and_pfree(tmp_data);
 | 
						|
	pgp_free(ctx);
 | 
						|
	mbuf_free(src);
 | 
						|
	mbuf_free(dst);
 | 
						|
 | 
						|
	px_set_debug_handler(NULL);
 | 
						|
 | 
						|
	return res;
 | 
						|
}
 | 
						|
 | 
						|
static bytea *
 | 
						|
decrypt_internal(int is_pubenc, int need_text, text *data,
 | 
						|
				 text *key, text *keypsw, text *args)
 | 
						|
{
 | 
						|
	int			err;
 | 
						|
	MBuf	   *src = NULL,
 | 
						|
			   *dst = NULL;
 | 
						|
	uint8		tmp[VARHDRSZ];
 | 
						|
	uint8	   *restmp;
 | 
						|
	bytea	   *res;
 | 
						|
	int			res_len;
 | 
						|
	PGP_Context *ctx = NULL;
 | 
						|
	struct debug_expect ex;
 | 
						|
	int			got_unicode = 0;
 | 
						|
 | 
						|
 | 
						|
	init_work(&ctx, need_text, args, &ex);
 | 
						|
 | 
						|
	src = mbuf_create_from_data((uint8 *) VARDATA(data),
 | 
						|
								VARSIZE(data) - VARHDRSZ);
 | 
						|
	dst = mbuf_create(VARSIZE(data) + 2048);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * reserve room for header
 | 
						|
	 */
 | 
						|
	mbuf_append(dst, tmp, VARHDRSZ);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * set key
 | 
						|
	 */
 | 
						|
	if (is_pubenc)
 | 
						|
	{
 | 
						|
		uint8	   *psw = NULL;
 | 
						|
		int			psw_len = 0;
 | 
						|
		MBuf	   *kbuf;
 | 
						|
 | 
						|
		if (keypsw)
 | 
						|
		{
 | 
						|
			psw = (uint8 *) VARDATA(keypsw);
 | 
						|
			psw_len = VARSIZE(keypsw) - VARHDRSZ;
 | 
						|
		}
 | 
						|
		kbuf = create_mbuf_from_vardata(key);
 | 
						|
		err = pgp_set_pubkey(ctx, kbuf, psw, psw_len, 1);
 | 
						|
		mbuf_free(kbuf);
 | 
						|
	}
 | 
						|
	else
 | 
						|
		err = pgp_set_symkey(ctx, (uint8 *) VARDATA(key),
 | 
						|
							 VARSIZE(key) - VARHDRSZ);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * decrypt
 | 
						|
	 */
 | 
						|
	if (err >= 0)
 | 
						|
		err = pgp_decrypt(ctx, src, dst);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * failed?
 | 
						|
	 */
 | 
						|
	if (err < 0)
 | 
						|
		goto out;
 | 
						|
 | 
						|
	if (ex.expect)
 | 
						|
		check_expect(ctx, &ex);
 | 
						|
 | 
						|
	/* remember the setting */
 | 
						|
	got_unicode = pgp_get_unicode_mode(ctx);
 | 
						|
 | 
						|
out:
 | 
						|
	if (src)
 | 
						|
		mbuf_free(src);
 | 
						|
	if (ctx)
 | 
						|
		pgp_free(ctx);
 | 
						|
 | 
						|
	if (err)
 | 
						|
	{
 | 
						|
		px_set_debug_handler(NULL);
 | 
						|
		if (dst)
 | 
						|
			mbuf_free(dst);
 | 
						|
		ereport(ERROR,
 | 
						|
				(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
 | 
						|
				 errmsg("%s", px_strerror(err))));
 | 
						|
	}
 | 
						|
 | 
						|
	res_len = mbuf_steal_data(dst, &restmp);
 | 
						|
	mbuf_free(dst);
 | 
						|
 | 
						|
	/* res_len includes VARHDRSZ */
 | 
						|
	res = (bytea *) restmp;
 | 
						|
	SET_VARSIZE(res, res_len);
 | 
						|
 | 
						|
	if (need_text && got_unicode)
 | 
						|
	{
 | 
						|
		text	   *utf = convert_from_utf8(res);
 | 
						|
 | 
						|
		if (utf != res)
 | 
						|
		{
 | 
						|
			clear_and_pfree(res);
 | 
						|
			res = utf;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	px_set_debug_handler(NULL);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * add successful decryptions also into RNG
 | 
						|
	 */
 | 
						|
	add_entropy(res, key, keypsw);
 | 
						|
 | 
						|
	return res;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Wrappers for symmetric-key functions
 | 
						|
 */
 | 
						|
Datum
 | 
						|
pgp_sym_encrypt_bytea(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	bytea	   *data,
 | 
						|
			   *key;
 | 
						|
	text	   *arg = NULL;
 | 
						|
	text	   *res;
 | 
						|
 | 
						|
	data = PG_GETARG_BYTEA_P(0);
 | 
						|
	key = PG_GETARG_BYTEA_P(1);
 | 
						|
	if (PG_NARGS() > 2)
 | 
						|
		arg = PG_GETARG_BYTEA_P(2);
 | 
						|
 | 
						|
	res = encrypt_internal(0, 0, data, key, arg);
 | 
						|
 | 
						|
	PG_FREE_IF_COPY(data, 0);
 | 
						|
	PG_FREE_IF_COPY(key, 1);
 | 
						|
	if (PG_NARGS() > 2)
 | 
						|
		PG_FREE_IF_COPY(arg, 2);
 | 
						|
	PG_RETURN_TEXT_P(res);
 | 
						|
}
 | 
						|
 | 
						|
Datum
 | 
						|
pgp_sym_encrypt_text(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	bytea	   *data,
 | 
						|
			   *key;
 | 
						|
	text	   *arg = NULL;
 | 
						|
	text	   *res;
 | 
						|
 | 
						|
	data = PG_GETARG_BYTEA_P(0);
 | 
						|
	key = PG_GETARG_BYTEA_P(1);
 | 
						|
	if (PG_NARGS() > 2)
 | 
						|
		arg = PG_GETARG_BYTEA_P(2);
 | 
						|
 | 
						|
	res = encrypt_internal(0, 1, data, key, arg);
 | 
						|
 | 
						|
	PG_FREE_IF_COPY(data, 0);
 | 
						|
	PG_FREE_IF_COPY(key, 1);
 | 
						|
	if (PG_NARGS() > 2)
 | 
						|
		PG_FREE_IF_COPY(arg, 2);
 | 
						|
	PG_RETURN_TEXT_P(res);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
Datum
 | 
						|
pgp_sym_decrypt_bytea(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	bytea	   *data,
 | 
						|
			   *key;
 | 
						|
	text	   *arg = NULL;
 | 
						|
	text	   *res;
 | 
						|
 | 
						|
	data = PG_GETARG_BYTEA_P(0);
 | 
						|
	key = PG_GETARG_BYTEA_P(1);
 | 
						|
	if (PG_NARGS() > 2)
 | 
						|
		arg = PG_GETARG_BYTEA_P(2);
 | 
						|
 | 
						|
	res = decrypt_internal(0, 0, data, key, NULL, arg);
 | 
						|
 | 
						|
	PG_FREE_IF_COPY(data, 0);
 | 
						|
	PG_FREE_IF_COPY(key, 1);
 | 
						|
	if (PG_NARGS() > 2)
 | 
						|
		PG_FREE_IF_COPY(arg, 2);
 | 
						|
	PG_RETURN_TEXT_P(res);
 | 
						|
}
 | 
						|
 | 
						|
Datum
 | 
						|
pgp_sym_decrypt_text(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	bytea	   *data,
 | 
						|
			   *key;
 | 
						|
	text	   *arg = NULL;
 | 
						|
	text	   *res;
 | 
						|
 | 
						|
	data = PG_GETARG_BYTEA_P(0);
 | 
						|
	key = PG_GETARG_BYTEA_P(1);
 | 
						|
	if (PG_NARGS() > 2)
 | 
						|
		arg = PG_GETARG_BYTEA_P(2);
 | 
						|
 | 
						|
	res = decrypt_internal(0, 1, data, key, NULL, arg);
 | 
						|
 | 
						|
	PG_FREE_IF_COPY(data, 0);
 | 
						|
	PG_FREE_IF_COPY(key, 1);
 | 
						|
	if (PG_NARGS() > 2)
 | 
						|
		PG_FREE_IF_COPY(arg, 2);
 | 
						|
	PG_RETURN_TEXT_P(res);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Wrappers for public-key functions
 | 
						|
 */
 | 
						|
 | 
						|
Datum
 | 
						|
pgp_pub_encrypt_bytea(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	bytea	   *data,
 | 
						|
			   *key;
 | 
						|
	text	   *arg = NULL;
 | 
						|
	text	   *res;
 | 
						|
 | 
						|
	data = PG_GETARG_BYTEA_P(0);
 | 
						|
	key = PG_GETARG_BYTEA_P(1);
 | 
						|
	if (PG_NARGS() > 2)
 | 
						|
		arg = PG_GETARG_BYTEA_P(2);
 | 
						|
 | 
						|
	res = encrypt_internal(1, 0, data, key, arg);
 | 
						|
 | 
						|
	PG_FREE_IF_COPY(data, 0);
 | 
						|
	PG_FREE_IF_COPY(key, 1);
 | 
						|
	if (PG_NARGS() > 2)
 | 
						|
		PG_FREE_IF_COPY(arg, 2);
 | 
						|
	PG_RETURN_TEXT_P(res);
 | 
						|
}
 | 
						|
 | 
						|
Datum
 | 
						|
pgp_pub_encrypt_text(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	bytea	   *data,
 | 
						|
			   *key;
 | 
						|
	text	   *arg = NULL;
 | 
						|
	text	   *res;
 | 
						|
 | 
						|
	data = PG_GETARG_BYTEA_P(0);
 | 
						|
	key = PG_GETARG_BYTEA_P(1);
 | 
						|
	if (PG_NARGS() > 2)
 | 
						|
		arg = PG_GETARG_BYTEA_P(2);
 | 
						|
 | 
						|
	res = encrypt_internal(1, 1, data, key, arg);
 | 
						|
 | 
						|
	PG_FREE_IF_COPY(data, 0);
 | 
						|
	PG_FREE_IF_COPY(key, 1);
 | 
						|
	if (PG_NARGS() > 2)
 | 
						|
		PG_FREE_IF_COPY(arg, 2);
 | 
						|
	PG_RETURN_TEXT_P(res);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
Datum
 | 
						|
pgp_pub_decrypt_bytea(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	bytea	   *data,
 | 
						|
			   *key;
 | 
						|
	text	   *psw = NULL,
 | 
						|
			   *arg = NULL;
 | 
						|
	text	   *res;
 | 
						|
 | 
						|
	data = PG_GETARG_BYTEA_P(0);
 | 
						|
	key = PG_GETARG_BYTEA_P(1);
 | 
						|
	if (PG_NARGS() > 2)
 | 
						|
		psw = PG_GETARG_BYTEA_P(2);
 | 
						|
	if (PG_NARGS() > 3)
 | 
						|
		arg = PG_GETARG_BYTEA_P(3);
 | 
						|
 | 
						|
	res = decrypt_internal(1, 0, data, key, psw, arg);
 | 
						|
 | 
						|
	PG_FREE_IF_COPY(data, 0);
 | 
						|
	PG_FREE_IF_COPY(key, 1);
 | 
						|
	if (PG_NARGS() > 2)
 | 
						|
		PG_FREE_IF_COPY(psw, 2);
 | 
						|
	if (PG_NARGS() > 3)
 | 
						|
		PG_FREE_IF_COPY(arg, 3);
 | 
						|
	PG_RETURN_TEXT_P(res);
 | 
						|
}
 | 
						|
 | 
						|
Datum
 | 
						|
pgp_pub_decrypt_text(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	bytea	   *data,
 | 
						|
			   *key;
 | 
						|
	text	   *psw = NULL,
 | 
						|
			   *arg = NULL;
 | 
						|
	text	   *res;
 | 
						|
 | 
						|
	data = PG_GETARG_BYTEA_P(0);
 | 
						|
	key = PG_GETARG_BYTEA_P(1);
 | 
						|
	if (PG_NARGS() > 2)
 | 
						|
		psw = PG_GETARG_BYTEA_P(2);
 | 
						|
	if (PG_NARGS() > 3)
 | 
						|
		arg = PG_GETARG_BYTEA_P(3);
 | 
						|
 | 
						|
	res = decrypt_internal(1, 1, data, key, psw, arg);
 | 
						|
 | 
						|
	PG_FREE_IF_COPY(data, 0);
 | 
						|
	PG_FREE_IF_COPY(key, 1);
 | 
						|
	if (PG_NARGS() > 2)
 | 
						|
		PG_FREE_IF_COPY(psw, 2);
 | 
						|
	if (PG_NARGS() > 3)
 | 
						|
		PG_FREE_IF_COPY(arg, 3);
 | 
						|
	PG_RETURN_TEXT_P(res);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * Wrappers for PGP ascii armor
 | 
						|
 */
 | 
						|
 | 
						|
Datum
 | 
						|
pg_armor(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	bytea	   *data;
 | 
						|
	text	   *res;
 | 
						|
	int			data_len,
 | 
						|
				res_len,
 | 
						|
				guess_len;
 | 
						|
 | 
						|
	data = PG_GETARG_BYTEA_P(0);
 | 
						|
	data_len = VARSIZE(data) - VARHDRSZ;
 | 
						|
 | 
						|
	guess_len = pgp_armor_enc_len(data_len);
 | 
						|
	res = palloc(VARHDRSZ + guess_len);
 | 
						|
 | 
						|
	res_len = pgp_armor_encode((uint8 *) VARDATA(data), data_len,
 | 
						|
							   (uint8 *) VARDATA(res));
 | 
						|
	if (res_len > guess_len)
 | 
						|
		ereport(ERROR,
 | 
						|
				(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
 | 
						|
				 errmsg("Overflow - encode estimate too small")));
 | 
						|
	SET_VARSIZE(res, VARHDRSZ + res_len);
 | 
						|
 | 
						|
	PG_FREE_IF_COPY(data, 0);
 | 
						|
	PG_RETURN_TEXT_P(res);
 | 
						|
}
 | 
						|
 | 
						|
Datum
 | 
						|
pg_dearmor(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	text	   *data;
 | 
						|
	bytea	   *res;
 | 
						|
	int			data_len,
 | 
						|
				res_len,
 | 
						|
				guess_len;
 | 
						|
 | 
						|
	data = PG_GETARG_TEXT_P(0);
 | 
						|
	data_len = VARSIZE(data) - VARHDRSZ;
 | 
						|
 | 
						|
	guess_len = pgp_armor_dec_len(data_len);
 | 
						|
	res = palloc(VARHDRSZ + guess_len);
 | 
						|
 | 
						|
	res_len = pgp_armor_decode((uint8 *) VARDATA(data), data_len,
 | 
						|
							   (uint8 *) VARDATA(res));
 | 
						|
	if (res_len < 0)
 | 
						|
		ereport(ERROR,
 | 
						|
				(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
 | 
						|
				 errmsg("%s", px_strerror(res_len))));
 | 
						|
	if (res_len > guess_len)
 | 
						|
		ereport(ERROR,
 | 
						|
				(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
 | 
						|
				 errmsg("Overflow - decode estimate too small")));
 | 
						|
	SET_VARSIZE(res, VARHDRSZ + res_len);
 | 
						|
 | 
						|
	PG_FREE_IF_COPY(data, 0);
 | 
						|
	PG_RETURN_TEXT_P(res);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Wrappers for PGP key id
 | 
						|
 */
 | 
						|
 | 
						|
Datum
 | 
						|
pgp_key_id_w(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	bytea	   *data;
 | 
						|
	text	   *res;
 | 
						|
	int			res_len;
 | 
						|
	MBuf	   *buf;
 | 
						|
 | 
						|
	data = PG_GETARG_BYTEA_P(0);
 | 
						|
	buf = create_mbuf_from_vardata(data);
 | 
						|
	res = palloc(VARHDRSZ + 17);
 | 
						|
 | 
						|
	res_len = pgp_get_keyid(buf, VARDATA(res));
 | 
						|
	mbuf_free(buf);
 | 
						|
	if (res_len < 0)
 | 
						|
		ereport(ERROR,
 | 
						|
				(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
 | 
						|
				 errmsg("%s", px_strerror(res_len))));
 | 
						|
	SET_VARSIZE(res, VARHDRSZ + res_len);
 | 
						|
 | 
						|
	PG_FREE_IF_COPY(data, 0);
 | 
						|
	PG_RETURN_TEXT_P(res);
 | 
						|
}
 |