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:21
@ SASLPREP_INVALID_UTF8
Definition: saslprep.h:24
@ SASLPREP_PROHIBITED
Definition: saslprep.h:25
@ SASLPREP_OOM
Definition: saslprep.h:23
@ SASLPREP_SUCCESS
Definition: saslprep.h:22

Function Documentation

◆ pg_saslprep()

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

Definition at line 1046 of file saslprep.c.

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

References ALLOC, Assert, buf, commonly_mapped_to_nothing_ranges, FREE, i, input, IS_CODE_IN_TABLE, LCat_codepoint_ranges, non_ascii_space_ranges, output, pg_is_ascii(), 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(), scram_init(), and scram_verify_plain_password().