PostgreSQL Source Code  git master
saslprep.c File Reference
#include "postgres.h"
#include "common/saslprep.h"
#include "common/unicode_norm.h"
#include "mb/pg_wchar.h"
Include dependency graph for saslprep.c:

Go to the source code of this file.

Macros

#define MAX_PASSWORD_LENGTH   1024
 
#define STRDUP(s)   pstrdup(s)
 
#define ALLOC(size)   palloc(size)
 
#define FREE(size)   pfree(size)
 
#define IS_CODE_IN_TABLE(code, map)   is_code_in_table(code, map, lengthof(map))
 

Functions

static int codepoint_range_cmp (const void *a, const void *b)
 
static bool is_code_in_table (pg_wchar code, const pg_wchar *map, int mapsize)
 
static int pg_utf8_string_len (const char *source)
 
static bool pg_is_ascii_string (const char *p)
 
pg_saslprep_rc pg_saslprep (const char *input, char **output)
 

Variables

static const pg_wchar non_ascii_space_ranges []
 
static const pg_wchar commonly_mapped_to_nothing_ranges []
 
static const pg_wchar prohibited_output_ranges []
 
static const pg_wchar unassigned_codepoint_ranges []
 
static const pg_wchar RandALCat_codepoint_ranges []
 
static const pg_wchar LCat_codepoint_ranges []
 

Macro Definition Documentation

◆ ALLOC

#define ALLOC (   size)    palloc(size)

Definition at line 44 of file saslprep.c.

Referenced by pg_saslprep().

◆ FREE

◆ IS_CODE_IN_TABLE

#define IS_CODE_IN_TABLE (   code,
  map 
)    is_code_in_table(code, map, lengthof(map))

Definition at line 971 of file saslprep.c.

Referenced by pg_saslprep().

◆ MAX_PASSWORD_LENGTH

#define MAX_PASSWORD_LENGTH   1024

Definition at line 36 of file saslprep.c.

Referenced by pg_saslprep().

◆ STRDUP

#define STRDUP (   s)    pstrdup(s)

Definition at line 43 of file saslprep.c.

Referenced by pg_saslprep().

Function Documentation

◆ codepoint_range_cmp()

static int codepoint_range_cmp ( const void *  a,
const void *  b 
)
static

Definition at line 974 of file saslprep.c.

References sort-test::key, and range().

Referenced by is_code_in_table().

975 {
976  const pg_wchar *key = (const pg_wchar *) a;
977  const pg_wchar *range = (const pg_wchar *) b;
978 
979  if (*key < range[0])
980  return -1; /* less than lower bound */
981  if (*key > range[1])
982  return 1; /* greater than upper bound */
983 
984  return 0; /* within range */
985 }
static struct cvec * range(struct vars *v, chr a, chr b, int cases)
Definition: regc_locale.c:416
unsigned int pg_wchar
Definition: mbprint.c:31

◆ is_code_in_table()

static bool is_code_in_table ( pg_wchar  code,
const pg_wchar map,
int  mapsize 
)
static

Definition at line 988 of file saslprep.c.

References Assert, and codepoint_range_cmp().

989 {
990  Assert(mapsize % 2 == 0);
991 
992  if (code < map[0] || code > map[mapsize - 1])
993  return false;
994 
995  if (bsearch(&code, map, mapsize / 2, sizeof(pg_wchar) * 2,
997  return true;
998  else
999  return false;
1000 }
unsigned int pg_wchar
Definition: mbprint.c:31
#define Assert(condition)
Definition: c.h:738
static int codepoint_range_cmp(const void *a, const void *b)
Definition: saslprep.c:974

◆ pg_is_ascii_string()

static bool pg_is_ascii_string ( const char *  p)
static

Definition at line 1032 of file saslprep.c.

References IS_HIGHBIT_SET.

Referenced by pg_saslprep().

1033 {
1034  while (*p)
1035  {
1036  if (IS_HIGHBIT_SET(*p))
1037  return false;
1038  p++;
1039  }
1040  return true;
1041 }
#define IS_HIGHBIT_SET(ch)
Definition: c.h:1119

◆ pg_saslprep()

pg_saslprep_rc pg_saslprep ( const char *  input,
char **  output 
)

Definition at line 1065 of file saslprep.c.

References ALLOC, Assert, buf, commonly_mapped_to_nothing_ranges, ereport, errcode(), errmsg(), ERROR, FREE, i, IS_CODE_IN_TABLE, LCat_codepoint_ranges, MAX_PASSWORD_LENGTH, non_ascii_space_ranges, pg_is_ascii_string(), pg_utf8_string_len(), pg_utf_mblen(), prohibited_output_ranges, RandALCat_codepoint_ranges, SASLPREP_INVALID_UTF8, SASLPREP_OOM, SASLPREP_PROHIBITED, SASLPREP_SUCCESS, STRDUP, unassigned_codepoint_ranges, UNICODE_NFKC, unicode_normalize(), unicode_to_utf8(), and utf8_to_unicode().

Referenced by pg_be_scram_build_secret(), pg_fe_scram_build_secret(), pg_fe_scram_init(), and scram_verify_plain_password().

1066 {
1067  pg_wchar *input_chars = NULL;
1068  pg_wchar *output_chars = NULL;
1069  int input_size;
1070  char *result;
1071  int result_size;
1072  int count;
1073  int i;
1074  bool contains_RandALCat;
1075  unsigned char *p;
1076  pg_wchar *wp;
1077 
1078  /* Ensure we return *output as NULL on failure */
1079  *output = NULL;
1080 
1081  /* Check that the password isn't stupendously long */
1082  if (strlen(input) > MAX_PASSWORD_LENGTH)
1083  {
1084 #ifndef FRONTEND
1085  ereport(ERROR,
1086  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1087  errmsg("password too long")));
1088 #else
1089  return SASLPREP_OOM;
1090 #endif
1091  }
1092 
1093  /*
1094  * Quick check if the input is pure ASCII. An ASCII string requires no
1095  * further processing.
1096  */
1097  if (pg_is_ascii_string(input))
1098  {
1099  *output = STRDUP(input);
1100  if (!(*output))
1101  goto oom;
1102  return SASLPREP_SUCCESS;
1103  }
1104 
1105  /*
1106  * Convert the input from UTF-8 to an array of Unicode codepoints.
1107  *
1108  * This also checks that the input is a legal UTF-8 string.
1109  */
1110  input_size = pg_utf8_string_len(input);
1111  if (input_size < 0)
1112  return SASLPREP_INVALID_UTF8;
1113 
1114  input_chars = ALLOC((input_size + 1) * sizeof(pg_wchar));
1115  if (!input_chars)
1116  goto oom;
1117 
1118  p = (unsigned char *) input;
1119  for (i = 0; i < input_size; i++)
1120  {
1121  input_chars[i] = utf8_to_unicode(p);
1122  p += pg_utf_mblen(p);
1123  }
1124  input_chars[i] = (pg_wchar) '\0';
1125 
1126  /*
1127  * The steps below correspond to the steps listed in [RFC3454], Section
1128  * "2. Preparation Overview"
1129  */
1130 
1131  /*
1132  * 1) Map -- For each character in the input, check if it has a mapping
1133  * and, if so, replace it with its mapping.
1134  */
1135  count = 0;
1136  for (i = 0; i < input_size; i++)
1137  {
1138  pg_wchar code = input_chars[i];
1139 
1141  input_chars[count++] = 0x0020;
1143  {
1144  /* map to nothing */
1145  }
1146  else
1147  input_chars[count++] = code;
1148  }
1149  input_chars[count] = (pg_wchar) '\0';
1150  input_size = count;
1151 
1152  if (input_size == 0)
1153  goto prohibited; /* don't allow empty password */
1154 
1155  /*
1156  * 2) Normalize -- Normalize the result of step 1 using Unicode
1157  * normalization.
1158  */
1159  output_chars = unicode_normalize(UNICODE_NFKC, input_chars);
1160  if (!output_chars)
1161  goto oom;
1162 
1163  /*
1164  * 3) Prohibit -- Check for any characters that are not allowed in the
1165  * output. If any are found, return an error.
1166  */
1167  for (i = 0; i < input_size; i++)
1168  {
1169  pg_wchar code = input_chars[i];
1170 
1172  goto prohibited;
1174  goto prohibited;
1175  }
1176 
1177  /*
1178  * 4) Check bidi -- Possibly check for right-to-left characters, and if
1179  * any are found, make sure that the whole string satisfies the
1180  * requirements for bidirectional strings. If the string does not satisfy
1181  * the requirements for bidirectional strings, return an error.
1182  *
1183  * [RFC3454], Section "6. Bidirectional Characters" explains in more
1184  * detail what that means:
1185  *
1186  * "In any profile that specifies bidirectional character handling, all
1187  * three of the following requirements MUST be met:
1188  *
1189  * 1) The characters in section 5.8 MUST be prohibited.
1190  *
1191  * 2) If a string contains any RandALCat character, the string MUST NOT
1192  * contain any LCat character.
1193  *
1194  * 3) If a string contains any RandALCat character, a RandALCat character
1195  * MUST be the first character of the string, and a RandALCat character
1196  * MUST be the last character of the string."
1197  */
1198  contains_RandALCat = false;
1199  for (i = 0; i < input_size; i++)
1200  {
1201  pg_wchar code = input_chars[i];
1202 
1204  {
1205  contains_RandALCat = true;
1206  break;
1207  }
1208  }
1209 
1210  if (contains_RandALCat)
1211  {
1212  pg_wchar first = input_chars[0];
1213  pg_wchar last = input_chars[input_size - 1];
1214 
1215  for (i = 0; i < input_size; i++)
1216  {
1217  pg_wchar code = input_chars[i];
1218 
1220  goto prohibited;
1221  }
1222 
1225  goto prohibited;
1226  }
1227 
1228  /*
1229  * Finally, convert the result back to UTF-8.
1230  */
1231  result_size = 0;
1232  for (wp = output_chars; *wp; wp++)
1233  {
1234  unsigned char buf[4];
1235 
1236  unicode_to_utf8(*wp, buf);
1237  result_size += pg_utf_mblen(buf);
1238  }
1239 
1240  result = ALLOC(result_size + 1);
1241  if (!result)
1242  goto oom;
1243 
1244  /*
1245  * There are no error exits below here, so the error exit paths don't need
1246  * to worry about possibly freeing "result".
1247  */
1248  p = (unsigned char *) result;
1249  for (wp = output_chars; *wp; wp++)
1250  {
1251  unicode_to_utf8(*wp, p);
1252  p += pg_utf_mblen(p);
1253  }
1254  Assert((char *) p == result + result_size);
1255  *p = '\0';
1256 
1257  FREE(input_chars);
1258  FREE(output_chars);
1259 
1260  *output = result;
1261  return SASLPREP_SUCCESS;
1262 
1263 prohibited:
1264  if (input_chars)
1265  FREE(input_chars);
1266  if (output_chars)
1267  FREE(output_chars);
1268 
1269  return SASLPREP_PROHIBITED;
1270 
1271 oom:
1272  if (input_chars)
1273  FREE(input_chars);
1274  if (output_chars)
1275  FREE(output_chars);
1276 
1277  return SASLPREP_OOM;
1278 }
static const pg_wchar commonly_mapped_to_nothing_ranges[]
Definition: saslprep.c:87
#define ALLOC(size)
Definition: saslprep.c:44
static const pg_wchar prohibited_output_ranges[]
Definition: saslprep.c:122
pg_wchar utf8_to_unicode(const unsigned char *c)
Definition: wchar.c:686
unsigned char * unicode_to_utf8(pg_wchar c, unsigned char *utf8string)
Definition: wchar.c:483
static int pg_utf8_string_len(const char *source)
Definition: saslprep.c:1008
static void output(uint64 loop_count)
static const pg_wchar non_ascii_space_ranges[]
Definition: saslprep.c:72
#define STRDUP(s)
Definition: saslprep.c:43
#define MAX_PASSWORD_LENGTH
Definition: saslprep.c:36
int errcode(int sqlerrcode)
Definition: elog.c:610
static const pg_wchar unassigned_codepoint_ranges[]
Definition: saslprep.c:163
#define ERROR
Definition: elog.h:43
#define IS_CODE_IN_TABLE(code, map)
Definition: saslprep.c:971
static char * buf
Definition: pg_test_fsync.c:67
unsigned int pg_wchar
Definition: mbprint.c:31
static bool pg_is_ascii_string(const char *p)
Definition: saslprep.c:1032
static const pg_wchar LCat_codepoint_ranges[]
Definition: saslprep.c:603
#define ereport(elevel,...)
Definition: elog.h:144
#define Assert(condition)
Definition: c.h:738
int pg_utf_mblen(const unsigned char *s)
Definition: wchar.c:549
int errmsg(const char *fmt,...)
Definition: elog.c:824
int i
static const pg_wchar RandALCat_codepoint_ranges[]
Definition: saslprep.c:564
#define FREE(size)
Definition: saslprep.c:45
pg_wchar * unicode_normalize(UnicodeNormalizationForm form, const pg_wchar *input)
Definition: unicode_norm.c:312

◆ pg_utf8_string_len()

static int pg_utf8_string_len ( const char *  source)
static

Definition at line 1008 of file saslprep.c.

References pg_utf8_islegal(), and pg_utf_mblen().

Referenced by pg_saslprep().

1009 {
1010  const unsigned char *p = (const unsigned char *) source;
1011  int l;
1012  int num_chars = 0;
1013 
1014  while (*p)
1015  {
1016  l = pg_utf_mblen(p);
1017 
1018  if (!pg_utf8_islegal(p, l))
1019  return -1;
1020 
1021  p += l;
1022  num_chars++;
1023  }
1024 
1025  return num_chars;
1026 }
bool pg_utf8_islegal(const unsigned char *source, int length)
Definition: wchar.c:1442
int pg_utf_mblen(const unsigned char *s)
Definition: wchar.c:549

Variable Documentation

◆ commonly_mapped_to_nothing_ranges

const pg_wchar commonly_mapped_to_nothing_ranges[]
static
Initial value:
=
{
0x00AD, 0x00AD,
0x034F, 0x034F,
0x1806, 0x1806,
0x180B, 0x180D,
0x200B, 0x200D,
0x2060, 0x2060,
0xFE00, 0xFE0F,
0xFEFF, 0xFEFF
}

Definition at line 87 of file saslprep.c.

Referenced by pg_saslprep().

◆ LCat_codepoint_ranges

const pg_wchar LCat_codepoint_ranges[]
static

Definition at line 603 of file saslprep.c.

Referenced by pg_saslprep().

◆ non_ascii_space_ranges

const pg_wchar non_ascii_space_ranges[]
static
Initial value:
=
{
0x00A0, 0x00A0,
0x1680, 0x1680,
0x2000, 0x200B,
0x202F, 0x202F,
0x205F, 0x205F,
0x3000, 0x3000
}

Definition at line 72 of file saslprep.c.

Referenced by pg_saslprep().

◆ prohibited_output_ranges

const pg_wchar prohibited_output_ranges[]
static

Definition at line 122 of file saslprep.c.

Referenced by pg_saslprep().

◆ RandALCat_codepoint_ranges

const pg_wchar RandALCat_codepoint_ranges[]
static

Definition at line 564 of file saslprep.c.

Referenced by pg_saslprep().

◆ unassigned_codepoint_ranges

const pg_wchar unassigned_codepoint_ranges[]
static

Definition at line 163 of file saslprep.c.

Referenced by pg_saslprep().