/* * 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 "catalog/pg_type.h" #include "common/string.h" #include "funcapi.h" #include "lib/stringinfo.h" #include "mb/pg_wchar.h" #include "mbuf.h" #include "pgp.h" #include "px.h" #include "utils/array.h" #include "utils/builtins.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); PG_FUNCTION_INFO_V1(pgp_armor_headers); /* * 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_ANY_EXHDR(src); unsigned char *dst; unsigned char *csrc = (unsigned char *) VARDATA_ANY(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_ANY(p)); pfree(p); } /* * expect-* arguments storage */ struct debug_expect { int debug; int expect; int cipher_algo; int s2k_mode; int s2k_count; 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_count = -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_count); 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-count") == 0) res = pgp_set_s2k_count(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-count") == 0) { ex->expect = 1; ex->s2k_count = 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_ANY(data), VARSIZE_ANY_EXHDR(data)); } 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_ANY(args), VARSIZE_ANY_EXHDR(args), ex); if (err) px_THROW_ERROR(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; 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_ANY(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_ANY(key), VARSIZE_ANY_EXHDR(key)); /* * 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); px_THROW_ERROR(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_ANY(data), VARSIZE_ANY_EXHDR(data)); dst = mbuf_create(VARSIZE_ANY(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_ANY(keypsw); psw_len = VARSIZE_ANY_EXHDR(keypsw); } 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_ANY(key), VARSIZE_ANY_EXHDR(key)); /* decrypt */ if (err >= 0) { err = pgp_decrypt(ctx, src, dst); if (ex.expect) check_expect(ctx, &ex); /* remember the setting */ got_unicode = pgp_get_unicode_mode(ctx); } mbuf_free(src); pgp_free(ctx); if (err) { px_set_debug_handler(NULL); mbuf_free(dst); px_THROW_ERROR(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); return res; } /* * Wrappers for symmetric-key functions */ Datum pgp_sym_encrypt_bytea(PG_FUNCTION_ARGS) { bytea *data; text *arg = NULL; text *res, *key; data = PG_GETARG_BYTEA_PP(0); key = PG_GETARG_TEXT_PP(1); if (PG_NARGS() > 2) arg = PG_GETARG_TEXT_PP(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) { text *data, *key; text *arg = NULL; text *res; data = PG_GETARG_TEXT_PP(0); key = PG_GETARG_TEXT_PP(1); if (PG_NARGS() > 2) arg = PG_GETARG_TEXT_PP(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; text *arg = NULL; text *res, *key; data = PG_GETARG_BYTEA_PP(0); key = PG_GETARG_TEXT_PP(1); if (PG_NARGS() > 2) arg = PG_GETARG_TEXT_PP(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; text *arg = NULL; text *res, *key; data = PG_GETARG_BYTEA_PP(0); key = PG_GETARG_TEXT_PP(1); if (PG_NARGS() > 2) arg = PG_GETARG_TEXT_PP(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_PP(0); key = PG_GETARG_BYTEA_PP(1); if (PG_NARGS() > 2) arg = PG_GETARG_TEXT_PP(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 *key; text *arg = NULL; text *res, *data; data = PG_GETARG_TEXT_PP(0); key = PG_GETARG_BYTEA_PP(1); if (PG_NARGS() > 2) arg = PG_GETARG_TEXT_PP(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_PP(0); key = PG_GETARG_BYTEA_PP(1); if (PG_NARGS() > 2) psw = PG_GETARG_TEXT_PP(2); if (PG_NARGS() > 3) arg = PG_GETARG_TEXT_PP(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_PP(0); key = PG_GETARG_BYTEA_PP(1); if (PG_NARGS() > 2) psw = PG_GETARG_TEXT_PP(2); if (PG_NARGS() > 3) arg = PG_GETARG_TEXT_PP(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 */ /* * Helper function for pg_armor. Converts arrays of keys and values into * plain C arrays, and checks that they don't contain invalid characters. */ static int parse_key_value_arrays(ArrayType *key_array, ArrayType *val_array, char ***p_keys, char ***p_values) { int nkdims = ARR_NDIM(key_array); int nvdims = ARR_NDIM(val_array); char **keys, **values; Datum *key_datums, *val_datums; bool *key_nulls, *val_nulls; int key_count, val_count; int i; if (nkdims > 1 || nkdims != nvdims) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("wrong number of array subscripts"))); if (nkdims == 0) return 0; deconstruct_array_builtin(key_array, TEXTOID, &key_datums, &key_nulls, &key_count); deconstruct_array_builtin(val_array, TEXTOID, &val_datums, &val_nulls, &val_count); if (key_count != val_count) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("mismatched array dimensions"))); keys = (char **) palloc(sizeof(char *) * key_count); values = (char **) palloc(sizeof(char *) * val_count); for (i = 0; i < key_count; i++) { char *v; /* Check that the key doesn't contain anything funny */ if (key_nulls[i]) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("null value not allowed for header key"))); v = TextDatumGetCString(key_datums[i]); if (!pg_is_ascii(v)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("header key must not contain non-ASCII characters"))); if (strstr(v, ": ")) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("header key must not contain \": \""))); if (strchr(v, '\n')) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("header key must not contain newlines"))); keys[i] = v; /* And the same for the value */ if (val_nulls[i]) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("null value not allowed for header value"))); v = TextDatumGetCString(val_datums[i]); if (!pg_is_ascii(v)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("header value must not contain non-ASCII characters"))); if (strchr(v, '\n')) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("header value must not contain newlines"))); values[i] = v; } *p_keys = keys; *p_values = values; return key_count; } Datum pg_armor(PG_FUNCTION_ARGS) { bytea *data; text *res; int data_len; StringInfoData buf; int num_headers; char **keys = NULL, **values = NULL; data = PG_GETARG_BYTEA_PP(0); data_len = VARSIZE_ANY_EXHDR(data); if (PG_NARGS() == 3) { num_headers = parse_key_value_arrays(PG_GETARG_ARRAYTYPE_P(1), PG_GETARG_ARRAYTYPE_P(2), &keys, &values); } else if (PG_NARGS() == 1) num_headers = 0; else elog(ERROR, "unexpected number of arguments %d", PG_NARGS()); initStringInfo(&buf); pgp_armor_encode((uint8 *) VARDATA_ANY(data), data_len, &buf, num_headers, keys, values); res = palloc(VARHDRSZ + buf.len); SET_VARSIZE(res, VARHDRSZ + buf.len); memcpy(VARDATA(res), buf.data, buf.len); pfree(buf.data); PG_FREE_IF_COPY(data, 0); PG_RETURN_TEXT_P(res); } Datum pg_dearmor(PG_FUNCTION_ARGS) { text *data; bytea *res; int data_len; int ret; StringInfoData buf; data = PG_GETARG_TEXT_PP(0); data_len = VARSIZE_ANY_EXHDR(data); initStringInfo(&buf); ret = pgp_armor_decode((uint8 *) VARDATA_ANY(data), data_len, &buf); if (ret < 0) px_THROW_ERROR(ret); res = palloc(VARHDRSZ + buf.len); SET_VARSIZE(res, VARHDRSZ + buf.len); memcpy(VARDATA(res), buf.data, buf.len); pfree(buf.data); PG_FREE_IF_COPY(data, 0); PG_RETURN_TEXT_P(res); } /* cross-call state for pgp_armor_headers */ typedef struct { int nheaders; char **keys; char **values; } pgp_armor_headers_state; Datum pgp_armor_headers(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; pgp_armor_headers_state *state; char *utf8key; char *utf8val; HeapTuple tuple; TupleDesc tupdesc; AttInMetadata *attinmeta; if (SRF_IS_FIRSTCALL()) { text *data = PG_GETARG_TEXT_PP(0); int res; MemoryContext oldcontext; funcctx = SRF_FIRSTCALL_INIT(); /* we need the state allocated in the multi call context */ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); attinmeta = TupleDescGetAttInMetadata(tupdesc); funcctx->attinmeta = attinmeta; state = (pgp_armor_headers_state *) palloc(sizeof(pgp_armor_headers_state)); res = pgp_extract_armor_headers((uint8 *) VARDATA_ANY(data), VARSIZE_ANY_EXHDR(data), &state->nheaders, &state->keys, &state->values); if (res < 0) px_THROW_ERROR(res); MemoryContextSwitchTo(oldcontext); funcctx->user_fctx = state; } funcctx = SRF_PERCALL_SETUP(); state = (pgp_armor_headers_state *) funcctx->user_fctx; if (funcctx->call_cntr >= state->nheaders) SRF_RETURN_DONE(funcctx); else { char *values[2]; /* we assume that the keys (and values) are in UTF-8. */ utf8key = state->keys[funcctx->call_cntr]; utf8val = state->values[funcctx->call_cntr]; values[0] = pg_any_to_server(utf8key, strlen(utf8key), PG_UTF8); values[1] = pg_any_to_server(utf8val, strlen(utf8val), PG_UTF8); /* build a tuple */ tuple = BuildTupleFromCStrings(funcctx->attinmeta, values); SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); } } /* * 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_PP(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) px_THROW_ERROR(res_len); SET_VARSIZE(res, VARHDRSZ + res_len); PG_FREE_IF_COPY(data, 0); PG_RETURN_TEXT_P(res); }