PostgreSQL Source Code  git master
saslprep.h File Reference
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Enumerations

enum  pg_saslprep_rc { SASLPREP_SUCCESS = 0, SASLPREP_OOM = -1, SASLPREP_INVALID_UTF8 = -2, SASLPREP_PROHIBITED = -3 }
 

Functions

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

Enumeration Type Documentation

◆ pg_saslprep_rc

Enumerator
SASLPREP_SUCCESS 
SASLPREP_OOM 
SASLPREP_INVALID_UTF8 
SASLPREP_PROHIBITED 

Definition at line 20 of file saslprep.h.

21 {
22  SASLPREP_SUCCESS = 0,
23  SASLPREP_OOM = -1, /* out of memory (only in frontend) */
24  SASLPREP_INVALID_UTF8 = -2, /* input is not a valid UTF-8 string */
25  SASLPREP_PROHIBITED = -3 /* output would contain prohibited characters */
pg_saslprep_rc
Definition: saslprep.h:20

Function Documentation

◆ pg_saslprep()

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

Definition at line 1059 of file saslprep.c.

References ALLOC, Assert, buf, commonly_mapped_to_nothing_ranges, FREE, i, IS_CODE_IN_TABLE, LCat_codepoint_ranges, 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().

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