mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-29 22:49:41 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			489 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			489 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * pgp-armor.c
 | |
|  *		PGP ascii-armor.
 | |
|  *
 | |
|  * 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-armor.c
 | |
|  */
 | |
| 
 | |
| #include "postgres.h"
 | |
| 
 | |
| #include "px.h"
 | |
| #include "pgp.h"
 | |
| 
 | |
| /*
 | |
|  * BASE64 - duplicated :(
 | |
|  */
 | |
| 
 | |
| static const unsigned char _base64[] =
 | |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 | |
| 
 | |
| static int
 | |
| b64_encode(const uint8 *src, unsigned len, uint8 *dst)
 | |
| {
 | |
| 	uint8	   *p,
 | |
| 			   *lend = dst + 76;
 | |
| 	const uint8 *s,
 | |
| 			   *end = src + len;
 | |
| 	int			pos = 2;
 | |
| 	unsigned long buf = 0;
 | |
| 
 | |
| 	s = src;
 | |
| 	p = dst;
 | |
| 
 | |
| 	while (s < end)
 | |
| 	{
 | |
| 		buf |= *s << (pos << 3);
 | |
| 		pos--;
 | |
| 		s++;
 | |
| 
 | |
| 		/*
 | |
| 		 * write it out
 | |
| 		 */
 | |
| 		if (pos < 0)
 | |
| 		{
 | |
| 			*p++ = _base64[(buf >> 18) & 0x3f];
 | |
| 			*p++ = _base64[(buf >> 12) & 0x3f];
 | |
| 			*p++ = _base64[(buf >> 6) & 0x3f];
 | |
| 			*p++ = _base64[buf & 0x3f];
 | |
| 
 | |
| 			pos = 2;
 | |
| 			buf = 0;
 | |
| 		}
 | |
| 		if (p >= lend)
 | |
| 		{
 | |
| 			*p++ = '\n';
 | |
| 			lend = p + 76;
 | |
| 		}
 | |
| 	}
 | |
| 	if (pos != 2)
 | |
| 	{
 | |
| 		*p++ = _base64[(buf >> 18) & 0x3f];
 | |
| 		*p++ = _base64[(buf >> 12) & 0x3f];
 | |
| 		*p++ = (pos == 0) ? _base64[(buf >> 6) & 0x3f] : '=';
 | |
| 		*p++ = '=';
 | |
| 	}
 | |
| 
 | |
| 	return p - dst;
 | |
| }
 | |
| 
 | |
| /* probably should use lookup table */
 | |
| static int
 | |
| b64_decode(const uint8 *src, unsigned len, uint8 *dst)
 | |
| {
 | |
| 	const uint8 *srcend = src + len,
 | |
| 			   *s = src;
 | |
| 	uint8	   *p = dst;
 | |
| 	char		c;
 | |
| 	unsigned	b = 0;
 | |
| 	unsigned long buf = 0;
 | |
| 	int			pos = 0,
 | |
| 				end = 0;
 | |
| 
 | |
| 	while (s < srcend)
 | |
| 	{
 | |
| 		c = *s++;
 | |
| 		if (c >= 'A' && c <= 'Z')
 | |
| 			b = c - 'A';
 | |
| 		else if (c >= 'a' && c <= 'z')
 | |
| 			b = c - 'a' + 26;
 | |
| 		else if (c >= '0' && c <= '9')
 | |
| 			b = c - '0' + 52;
 | |
| 		else if (c == '+')
 | |
| 			b = 62;
 | |
| 		else if (c == '/')
 | |
| 			b = 63;
 | |
| 		else if (c == '=')
 | |
| 		{
 | |
| 			/*
 | |
| 			 * end sequence
 | |
| 			 */
 | |
| 			if (!end)
 | |
| 			{
 | |
| 				if (pos == 2)
 | |
| 					end = 1;
 | |
| 				else if (pos == 3)
 | |
| 					end = 2;
 | |
| 				else
 | |
| 					return PXE_PGP_CORRUPT_ARMOR;
 | |
| 			}
 | |
| 			b = 0;
 | |
| 		}
 | |
| 		else if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
 | |
| 			continue;
 | |
| 		else
 | |
| 			return PXE_PGP_CORRUPT_ARMOR;
 | |
| 
 | |
| 		/*
 | |
| 		 * add it to buffer
 | |
| 		 */
 | |
| 		buf = (buf << 6) + b;
 | |
| 		pos++;
 | |
| 		if (pos == 4)
 | |
| 		{
 | |
| 			*p++ = (buf >> 16) & 255;
 | |
| 			if (end == 0 || end > 1)
 | |
| 				*p++ = (buf >> 8) & 255;
 | |
| 			if (end == 0 || end > 2)
 | |
| 				*p++ = buf & 255;
 | |
| 			buf = 0;
 | |
| 			pos = 0;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (pos != 0)
 | |
| 		return PXE_PGP_CORRUPT_ARMOR;
 | |
| 	return p - dst;
 | |
| }
 | |
| 
 | |
| static unsigned
 | |
| b64_enc_len(unsigned srclen)
 | |
| {
 | |
| 	/*
 | |
| 	 * 3 bytes will be converted to 4, linefeed after 76 chars
 | |
| 	 */
 | |
| 	return (srclen + 2) * 4 / 3 + srclen / (76 * 3 / 4);
 | |
| }
 | |
| 
 | |
| static unsigned
 | |
| b64_dec_len(unsigned srclen)
 | |
| {
 | |
| 	return (srclen * 3) >> 2;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * PGP armor
 | |
|  */
 | |
| 
 | |
| static const char *armor_header = "-----BEGIN PGP MESSAGE-----\n";
 | |
| static const char *armor_footer = "\n-----END PGP MESSAGE-----\n";
 | |
| 
 | |
| /* CRC24 implementation from rfc2440 */
 | |
| #define CRC24_INIT 0x00b704ceL
 | |
| #define CRC24_POLY 0x01864cfbL
 | |
| static long
 | |
| crc24(const uint8 *data, unsigned len)
 | |
| {
 | |
| 	unsigned	crc = CRC24_INIT;
 | |
| 	int			i;
 | |
| 
 | |
| 	while (len--)
 | |
| 	{
 | |
| 		crc ^= (*data++) << 16;
 | |
| 		for (i = 0; i < 8; i++)
 | |
| 		{
 | |
| 			crc <<= 1;
 | |
| 			if (crc & 0x1000000)
 | |
| 				crc ^= CRC24_POLY;
 | |
| 		}
 | |
| 	}
 | |
| 	return crc & 0xffffffL;
 | |
| }
 | |
| 
 | |
| void
 | |
| pgp_armor_encode(const uint8 *src, unsigned len, StringInfo dst,
 | |
| 				 int num_headers, char **keys, char **values)
 | |
| {
 | |
| 	int			n;
 | |
| 	int			res;
 | |
| 	unsigned	b64len;
 | |
| 	unsigned	crc = crc24(src, len);
 | |
| 
 | |
| 	appendStringInfoString(dst, armor_header);
 | |
| 
 | |
| 	for (n = 0; n < num_headers; n++)
 | |
| 		appendStringInfo(dst, "%s: %s\n", keys[n], values[n]);
 | |
| 	appendStringInfoChar(dst, '\n');
 | |
| 
 | |
| 	/* make sure we have enough room to b64_encode() */
 | |
| 	b64len = b64_enc_len(len);
 | |
| 	enlargeStringInfo(dst, (int) b64len);
 | |
| 
 | |
| 	res = b64_encode(src, len, (uint8 *) dst->data + dst->len);
 | |
| 	if (res > b64len)
 | |
| 		elog(FATAL, "overflow - encode estimate too small");
 | |
| 	dst->len += res;
 | |
| 
 | |
| 	if (*(dst->data + dst->len - 1) != '\n')
 | |
| 		appendStringInfoChar(dst, '\n');
 | |
| 
 | |
| 	appendStringInfoChar(dst, '=');
 | |
| 	appendStringInfoChar(dst, _base64[(crc >> 18) & 0x3f]);
 | |
| 	appendStringInfoChar(dst, _base64[(crc >> 12) & 0x3f]);
 | |
| 	appendStringInfoChar(dst, _base64[(crc >> 6) & 0x3f]);
 | |
| 	appendStringInfoChar(dst, _base64[crc & 0x3f]);
 | |
| 
 | |
| 	appendStringInfoString(dst, armor_footer);
 | |
| }
 | |
| 
 | |
| static const uint8 *
 | |
| find_str(const uint8 *data, const uint8 *data_end, const char *str, int strlen)
 | |
| {
 | |
| 	const uint8 *p = data;
 | |
| 
 | |
| 	if (!strlen)
 | |
| 		return NULL;
 | |
| 	if (data_end - data < strlen)
 | |
| 		return NULL;
 | |
| 	while (p < data_end)
 | |
| 	{
 | |
| 		p = memchr(p, str[0], data_end - p);
 | |
| 		if (p == NULL)
 | |
| 			return NULL;
 | |
| 		if (p + strlen > data_end)
 | |
| 			return NULL;
 | |
| 		if (memcmp(p, str, strlen) == 0)
 | |
| 			return p;
 | |
| 		p++;
 | |
| 	}
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| static int
 | |
| find_header(const uint8 *data, const uint8 *datend,
 | |
| 			const uint8 **start_p, int is_end)
 | |
| {
 | |
| 	const uint8 *p = data;
 | |
| 	static const char *start_sep = "-----BEGIN";
 | |
| 	static const char *end_sep = "-----END";
 | |
| 	const char *sep = is_end ? end_sep : start_sep;
 | |
| 
 | |
| 	/* find header line */
 | |
| 	while (1)
 | |
| 	{
 | |
| 		p = find_str(p, datend, sep, strlen(sep));
 | |
| 		if (p == NULL)
 | |
| 			return PXE_PGP_CORRUPT_ARMOR;
 | |
| 		/* it must start at beginning of line */
 | |
| 		if (p == data || *(p - 1) == '\n')
 | |
| 			break;
 | |
| 		p += strlen(sep);
 | |
| 	}
 | |
| 	*start_p = p;
 | |
| 	p += strlen(sep);
 | |
| 
 | |
| 	/* check if header text ok */
 | |
| 	for (; p < datend && *p != '-'; p++)
 | |
| 	{
 | |
| 		/* various junk can be there, but definitely not line-feed	*/
 | |
| 		if (*p >= ' ')
 | |
| 			continue;
 | |
| 		return PXE_PGP_CORRUPT_ARMOR;
 | |
| 	}
 | |
| 	if (datend - p < 5 || memcmp(p, sep, 5) != 0)
 | |
| 		return PXE_PGP_CORRUPT_ARMOR;
 | |
| 	p += 5;
 | |
| 
 | |
| 	/* check if at end of line */
 | |
| 	if (p < datend)
 | |
| 	{
 | |
| 		if (*p != '\n' && *p != '\r')
 | |
| 			return PXE_PGP_CORRUPT_ARMOR;
 | |
| 		if (*p == '\r')
 | |
| 			p++;
 | |
| 		if (p < datend && *p == '\n')
 | |
| 			p++;
 | |
| 	}
 | |
| 	return p - *start_p;
 | |
| }
 | |
| 
 | |
| int
 | |
| pgp_armor_decode(const uint8 *src, int len, StringInfo dst)
 | |
| {
 | |
| 	const uint8 *p = src;
 | |
| 	const uint8 *data_end = src + len;
 | |
| 	long		crc;
 | |
| 	const uint8 *base64_start,
 | |
| 			   *armor_end;
 | |
| 	const uint8 *base64_end = NULL;
 | |
| 	uint8		buf[4];
 | |
| 	int			hlen;
 | |
| 	int			blen;
 | |
| 	int			res = PXE_PGP_CORRUPT_ARMOR;
 | |
| 
 | |
| 	/* armor start */
 | |
| 	hlen = find_header(src, data_end, &p, 0);
 | |
| 	if (hlen <= 0)
 | |
| 		goto out;
 | |
| 	p += hlen;
 | |
| 
 | |
| 	/* armor end */
 | |
| 	hlen = find_header(p, data_end, &armor_end, 1);
 | |
| 	if (hlen <= 0)
 | |
| 		goto out;
 | |
| 
 | |
| 	/* skip comments - find empty line */
 | |
| 	while (p < armor_end && *p != '\n' && *p != '\r')
 | |
| 	{
 | |
| 		p = memchr(p, '\n', armor_end - p);
 | |
| 		if (!p)
 | |
| 			goto out;
 | |
| 
 | |
| 		/* step to start of next line */
 | |
| 		p++;
 | |
| 	}
 | |
| 	base64_start = p;
 | |
| 
 | |
| 	/* find crc pos */
 | |
| 	for (p = armor_end; p >= base64_start; p--)
 | |
| 		if (*p == '=')
 | |
| 		{
 | |
| 			base64_end = p - 1;
 | |
| 			break;
 | |
| 		}
 | |
| 	if (base64_end == NULL)
 | |
| 		goto out;
 | |
| 
 | |
| 	/* decode crc */
 | |
| 	if (b64_decode(p + 1, 4, buf) != 3)
 | |
| 		goto out;
 | |
| 	crc = (((long) buf[0]) << 16) + (((long) buf[1]) << 8) + (long) buf[2];
 | |
| 
 | |
| 	/* decode data */
 | |
| 	blen = (int) b64_dec_len(len);
 | |
| 	enlargeStringInfo(dst, blen);
 | |
| 	res = b64_decode(base64_start, base64_end - base64_start, (uint8 *) dst->data);
 | |
| 	if (res > blen)
 | |
| 		elog(FATAL, "overflow - decode estimate too small");
 | |
| 	if (res >= 0)
 | |
| 	{
 | |
| 		if (crc24((uint8 *) dst->data, res) == crc)
 | |
| 			dst->len += res;
 | |
| 		else
 | |
| 			res = PXE_PGP_CORRUPT_ARMOR;
 | |
| 	}
 | |
| out:
 | |
| 	return res;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Extracts all armor headers from an ASCII-armored input.
 | |
|  *
 | |
|  * Returns 0 on success, or PXE_* error code on error. On success, the
 | |
|  * number of headers and their keys and values are returned in *nheaders,
 | |
|  * *nkeys and *nvalues.
 | |
|  */
 | |
| int
 | |
| pgp_extract_armor_headers(const uint8 *src, unsigned len,
 | |
| 						  int *nheaders, char ***keys, char ***values)
 | |
| {
 | |
| 	const uint8 *data_end = src + len;
 | |
| 	const uint8 *p;
 | |
| 	const uint8 *base64_start;
 | |
| 	const uint8 *armor_start;
 | |
| 	const uint8 *armor_end;
 | |
| 	Size		armor_len;
 | |
| 	char	   *line;
 | |
| 	char	   *nextline;
 | |
| 	char	   *eol,
 | |
| 			   *colon;
 | |
| 	int			hlen;
 | |
| 	char	   *buf;
 | |
| 	int			hdrlines;
 | |
| 	int			n;
 | |
| 
 | |
| 	/* armor start */
 | |
| 	hlen = find_header(src, data_end, &armor_start, 0);
 | |
| 	if (hlen <= 0)
 | |
| 		return PXE_PGP_CORRUPT_ARMOR;
 | |
| 	armor_start += hlen;
 | |
| 
 | |
| 	/* armor end */
 | |
| 	hlen = find_header(armor_start, data_end, &armor_end, 1);
 | |
| 	if (hlen <= 0)
 | |
| 		return PXE_PGP_CORRUPT_ARMOR;
 | |
| 
 | |
| 	/* Count the number of armor header lines. */
 | |
| 	hdrlines = 0;
 | |
| 	p = armor_start;
 | |
| 	while (p < armor_end && *p != '\n' && *p != '\r')
 | |
| 	{
 | |
| 		p = memchr(p, '\n', armor_end - p);
 | |
| 		if (!p)
 | |
| 			return PXE_PGP_CORRUPT_ARMOR;
 | |
| 
 | |
| 		/* step to start of next line */
 | |
| 		p++;
 | |
| 		hdrlines++;
 | |
| 	}
 | |
| 	base64_start = p;
 | |
| 
 | |
| 	/*
 | |
| 	 * Make a modifiable copy of the part of the input that contains the
 | |
| 	 * headers. The returned key/value pointers will point inside the buffer.
 | |
| 	 */
 | |
| 	armor_len = base64_start - armor_start;
 | |
| 	buf = palloc(armor_len + 1);
 | |
| 	memcpy(buf, armor_start, armor_len);
 | |
| 	buf[armor_len] = '\0';
 | |
| 
 | |
| 	/* Allocate return arrays */
 | |
| 	*keys = (char **) palloc(hdrlines * sizeof(char *));
 | |
| 	*values = (char **) palloc(hdrlines * sizeof(char *));
 | |
| 
 | |
| 	/*
 | |
| 	 * Split the header lines at newlines and ": " separators, and collect
 | |
| 	 * pointers to the keys and values in the return arrays.
 | |
| 	 */
 | |
| 	n = 0;
 | |
| 	line = buf;
 | |
| 	for (;;)
 | |
| 	{
 | |
| 		/* find end of line */
 | |
| 		eol = strchr(line, '\n');
 | |
| 		if (!eol)
 | |
| 			break;
 | |
| 		nextline = eol + 1;
 | |
| 		/* if the line ends in CR + LF, strip the CR */
 | |
| 		if (eol > line && *(eol - 1) == '\r')
 | |
| 			eol--;
 | |
| 		*eol = '\0';
 | |
| 
 | |
| 		/* find colon+space separating the key and value */
 | |
| 		colon = strstr(line, ": ");
 | |
| 		if (!colon)
 | |
| 			return PXE_PGP_CORRUPT_ARMOR;
 | |
| 		*colon = '\0';
 | |
| 
 | |
| 		/* shouldn't happen, we counted the number of lines beforehand */
 | |
| 		if (n >= hdrlines)
 | |
| 			elog(ERROR, "unexpected number of armor header lines");
 | |
| 
 | |
| 		(*keys)[n] = line;
 | |
| 		(*values)[n] = colon + 2;
 | |
| 		n++;
 | |
| 
 | |
| 		/* step to start of next line */
 | |
| 		line = nextline;
 | |
| 	}
 | |
| 
 | |
| 	if (n != hdrlines)
 | |
| 		elog(ERROR, "unexpected number of armor header lines");
 | |
| 
 | |
| 	*nheaders = n;
 | |
| 	return 0;
 | |
| }
 |