|
17 | 17 | * by the SASLprep profile, we skip the SASLprep pre-processing and use
|
18 | 18 | * the raw bytes in calculating the hash.
|
19 | 19 | *
|
20 |
| - * - Channel binding is not supported yet. |
21 |
| - * |
22 | 20 | *
|
23 | 21 | * The password stored in pg_authid consists of the iteration count, salt,
|
24 | 22 | * StoredKey and ServerKey.
|
@@ -112,6 +110,11 @@ typedef struct
|
112 | 110 |
|
113 | 111 | const char *username; /* username from startup packet */
|
114 | 112 |
|
| 113 | + bool ssl_in_use; |
| 114 | + const char *tls_finished_message; |
| 115 | + size_t tls_finished_len; |
| 116 | + char *channel_binding_type; |
| 117 | + |
115 | 118 | int iterations;
|
116 | 119 | char *salt; /* base64-encoded */
|
117 | 120 | uint8 StoredKey[SCRAM_KEY_LEN];
|
@@ -168,14 +171,22 @@ static char *scram_mock_salt(const char *username);
|
168 | 171 | * it will fail, as if an incorrect password was given.
|
169 | 172 | */
|
170 | 173 | void *
|
171 |
| -pg_be_scram_init(const char *username, const char *shadow_pass) |
| 174 | +pg_be_scram_init(const char *username, |
| 175 | + const char *shadow_pass, |
| 176 | + bool ssl_in_use, |
| 177 | + const char *tls_finished_message, |
| 178 | + size_t tls_finished_len) |
172 | 179 | {
|
173 | 180 | scram_state *state;
|
174 | 181 | bool got_verifier;
|
175 | 182 |
|
176 | 183 | state = (scram_state *) palloc0(sizeof(scram_state));
|
177 | 184 | state->state = SCRAM_AUTH_INIT;
|
178 | 185 | state->username = username;
|
| 186 | + state->ssl_in_use = ssl_in_use; |
| 187 | + state->tls_finished_message = tls_finished_message; |
| 188 | + state->tls_finished_len = tls_finished_len; |
| 189 | + state->channel_binding_type = NULL; |
179 | 190 |
|
180 | 191 | /*
|
181 | 192 | * Parse the stored password verifier.
|
@@ -773,45 +784,96 @@ read_client_first_message(scram_state *state, char *input)
|
773 | 784 | *------
|
774 | 785 | */
|
775 | 786 |
|
776 |
| - /* read gs2-cbind-flag */ |
| 787 | + /* |
| 788 | + * Read gs2-cbind-flag. (For details see also RFC 5802 Section 6 "Channel |
| 789 | + * Binding".) |
| 790 | + */ |
777 | 791 | switch (*input)
|
778 | 792 | {
|
779 | 793 | case 'n':
|
780 |
| - /* Client does not support channel binding */ |
| 794 | + /* |
| 795 | + * The client does not support channel binding or has simply |
| 796 | + * decided to not use it. In that case just let it go. |
| 797 | + */ |
| 798 | + input++; |
| 799 | + if (*input != ',') |
| 800 | + ereport(ERROR, |
| 801 | + (errcode(ERRCODE_PROTOCOL_VIOLATION), |
| 802 | + errmsg("malformed SCRAM message"), |
| 803 | + errdetail("Comma expected, but found character \"%s\".", |
| 804 | + sanitize_char(*input)))); |
781 | 805 | input++;
|
782 | 806 | break;
|
783 | 807 | case 'y':
|
784 |
| - /* Client supports channel binding, but we're not doing it today */ |
| 808 | + /* |
| 809 | + * The client supports channel binding and thinks that the server |
| 810 | + * does not. In this case, the server must fail authentication if |
| 811 | + * it supports channel binding, which in this implementation is |
| 812 | + * the case if a connection is using SSL. |
| 813 | + */ |
| 814 | + if (state->ssl_in_use) |
| 815 | + ereport(ERROR, |
| 816 | + (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), |
| 817 | + errmsg("SCRAM channel binding negotiation error"), |
| 818 | + errdetail("The client supports SCRAM channel binding but thinks the server does not. " |
| 819 | + "However, this server does support channel binding."))); |
| 820 | + input++; |
| 821 | + if (*input != ',') |
| 822 | + ereport(ERROR, |
| 823 | + (errcode(ERRCODE_PROTOCOL_VIOLATION), |
| 824 | + errmsg("malformed SCRAM message"), |
| 825 | + errdetail("Comma expected, but found character \"%s\".", |
| 826 | + sanitize_char(*input)))); |
785 | 827 | input++;
|
786 | 828 | break;
|
787 | 829 | case 'p':
|
788 |
| - |
789 | 830 | /*
|
790 |
| - * Client requires channel binding. We don't support it. |
791 |
| - * |
792 |
| - * RFC 5802 specifies a particular error code, |
793 |
| - * e=server-does-support-channel-binding, for this. But it can |
794 |
| - * only be sent in the server-final message, and we don't want to |
795 |
| - * go through the motions of the authentication, knowing it will |
796 |
| - * fail, just to send that error message. |
| 831 | + * The client requires channel binding. Channel binding type |
| 832 | + * follows, e.g., "p=tls-unique". |
797 | 833 | */
|
798 |
| - ereport(ERROR, |
799 |
| - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
800 |
| - errmsg("client requires SCRAM channel binding, but it is not supported"))); |
| 834 | + { |
| 835 | + char *channel_binding_type; |
| 836 | + |
| 837 | + if (!state->ssl_in_use) |
| 838 | + { |
| 839 | + /* |
| 840 | + * Without SSL, we don't support channel binding. |
| 841 | + * |
| 842 | + * RFC 5802 specifies a particular error code, |
| 843 | + * e=server-does-support-channel-binding, for this. But |
| 844 | + * it can only be sent in the server-final message, and we |
| 845 | + * don't want to go through the motions of the |
| 846 | + * authentication, knowing it will fail, just to send that |
| 847 | + * error message. |
| 848 | + */ |
| 849 | + ereport(ERROR, |
| 850 | + (errcode(ERRCODE_PROTOCOL_VIOLATION), |
| 851 | + errmsg("client requires SCRAM channel binding, but it is not supported"))); |
| 852 | + } |
| 853 | + |
| 854 | + /* |
| 855 | + * Read value provided by client; only tls-unique is supported |
| 856 | + * for now. (It is not safe to print the name of an |
| 857 | + * unsupported binding type in the error message. Pranksters |
| 858 | + * could print arbitrary strings into the log that way.) |
| 859 | + */ |
| 860 | + channel_binding_type = read_attr_value(&input, 'p'); |
| 861 | + if (strcmp(channel_binding_type, SCRAM_CHANNEL_BINDING_TLS_UNIQUE) != 0) |
| 862 | + ereport(ERROR, |
| 863 | + (errcode(ERRCODE_PROTOCOL_VIOLATION), |
| 864 | + (errmsg("unsupported SCRAM channel-binding type")))); |
| 865 | + |
| 866 | + /* Save the name for handling of subsequent messages */ |
| 867 | + state->channel_binding_type = pstrdup(channel_binding_type); |
| 868 | + } |
| 869 | + break; |
801 | 870 | default:
|
802 | 871 | ereport(ERROR,
|
803 | 872 | (errcode(ERRCODE_PROTOCOL_VIOLATION),
|
804 | 873 | errmsg("malformed SCRAM message"),
|
805 | 874 | errdetail("Unexpected channel-binding flag \"%s\".",
|
806 | 875 | sanitize_char(*input))));
|
807 | 876 | }
|
808 |
| - if (*input != ',') |
809 |
| - ereport(ERROR, |
810 |
| - (errcode(ERRCODE_PROTOCOL_VIOLATION), |
811 |
| - errmsg("malformed SCRAM message"), |
812 |
| - errdetail("Comma expected, but found character \"%s\".", |
813 |
| - sanitize_char(*input)))); |
814 |
| - input++; |
815 | 877 |
|
816 | 878 | /*
|
817 | 879 | * Forbid optional authzid (authorization identity). We don't support it.
|
@@ -1032,14 +1094,73 @@ read_client_final_message(scram_state *state, char *input)
|
1032 | 1094 | */
|
1033 | 1095 |
|
1034 | 1096 | /*
|
1035 |
| - * Read channel-binding. We don't support channel binding, so it's |
1036 |
| - * expected to always be "biws", which is "n,,", base64-encoded. |
| 1097 | + * Read channel binding. This repeats the channel-binding flags and is |
| 1098 | + * then followed by the actual binding data depending on the type. |
1037 | 1099 | */
|
1038 | 1100 | channel_binding = read_attr_value(&p, 'c');
|
1039 |
| - if (strcmp(channel_binding, "biws") != 0) |
1040 |
| - ereport(ERROR, |
1041 |
| - (errcode(ERRCODE_PROTOCOL_VIOLATION), |
1042 |
| - (errmsg("unexpected SCRAM channel-binding attribute in client-final-message")))); |
| 1101 | + if (state->channel_binding_type) |
| 1102 | + { |
| 1103 | + const char *cbind_data = NULL; |
| 1104 | + size_t cbind_data_len = 0; |
| 1105 | + size_t cbind_header_len; |
| 1106 | + char *cbind_input; |
| 1107 | + size_t cbind_input_len; |
| 1108 | + char *b64_message; |
| 1109 | + int b64_message_len; |
| 1110 | + |
| 1111 | + /* |
| 1112 | + * Fetch data appropriate for channel binding type |
| 1113 | + */ |
| 1114 | + if (strcmp(state->channel_binding_type, SCRAM_CHANNEL_BINDING_TLS_UNIQUE) == 0) |
| 1115 | + { |
| 1116 | + cbind_data = state->tls_finished_message; |
| 1117 | + cbind_data_len = state->tls_finished_len; |
| 1118 | + } |
| 1119 | + else |
| 1120 | + { |
| 1121 | + /* should not happen */ |
| 1122 | + elog(ERROR, "invalid channel binding type"); |
| 1123 | + } |
| 1124 | + |
| 1125 | + /* should not happen */ |
| 1126 | + if (cbind_data == NULL || cbind_data_len == 0) |
| 1127 | + elog(ERROR, "empty channel binding data for channel binding type \"%s\"", |
| 1128 | + state->channel_binding_type); |
| 1129 | + |
| 1130 | + cbind_header_len = 4 + strlen(state->channel_binding_type); /* p=type,, */ |
| 1131 | + cbind_input_len = cbind_header_len + cbind_data_len; |
| 1132 | + cbind_input = palloc(cbind_input_len); |
| 1133 | + snprintf(cbind_input, cbind_input_len, "p=%s,,", state->channel_binding_type); |
| 1134 | + memcpy(cbind_input + cbind_header_len, cbind_data, cbind_data_len); |
| 1135 | + |
| 1136 | + b64_message = palloc(pg_b64_enc_len(cbind_input_len) + 1); |
| 1137 | + b64_message_len = pg_b64_encode(cbind_input, cbind_input_len, |
| 1138 | + b64_message); |
| 1139 | + b64_message[b64_message_len] = '\0'; |
| 1140 | + |
| 1141 | + /* |
| 1142 | + * Compare the value sent by the client with the value expected by |
| 1143 | + * the server. |
| 1144 | + */ |
| 1145 | + if (strcmp(channel_binding, b64_message) != 0) |
| 1146 | + ereport(ERROR, |
| 1147 | + (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), |
| 1148 | + (errmsg("SCRAM channel binding check failed")))); |
| 1149 | + } |
| 1150 | + else |
| 1151 | + { |
| 1152 | + /* |
| 1153 | + * If we are not using channel binding, the binding data is expected |
| 1154 | + * to always be "biws", which is "n,," base64-encoded, or "eSws", |
| 1155 | + * which is "y,,". |
| 1156 | + */ |
| 1157 | + if (strcmp(channel_binding, "biws") != 0 && |
| 1158 | + strcmp(channel_binding, "eSws") != 0) |
| 1159 | + ereport(ERROR, |
| 1160 | + (errcode(ERRCODE_PROTOCOL_VIOLATION), |
| 1161 | + (errmsg("unexpected SCRAM channel-binding attribute in client-final-message")))); |
| 1162 | + } |
| 1163 | + |
1043 | 1164 | state->client_final_nonce = read_attr_value(&p, 'r');
|
1044 | 1165 |
|
1045 | 1166 | /* ignore optional extensions */
|
|
0 commit comments