#include "strfuncs.h"
#include "strnum.h"
+#include "auth-gs2.h"
#include "auth-scram-server.h"
/* s-nonce length */
pool_unref(&server->pool);
}
-static const char *auth_scram_unescape_username(const char *in)
-{
- string_t *out;
-
- /* RFC 5802, Section 5.1:
-
- The characters ',' or '=' in usernames are sent as '=2C' and '=3D'
- respectively. If the server receives a username that contains '='
- not followed by either '2C' or '3D', then the server MUST fail the
- authentication.
- */
-
- out = t_str_new(64);
- for (; *in != '\0'; in++) {
- i_assert(in[0] != ','); /* strsplit should have caught this */
-
- if (in[0] == '=') {
- if (in[1] == '2' && in[2] == 'C')
- str_append_c(out, ',');
- else if (in[1] == '3' && in[2] == 'D')
- str_append_c(out, '=');
- else
- return NULL;
- in += 2;
- } else {
- str_append_c(out, *in);
- }
- }
- return str_c(out);
-}
-
static int
auth_scram_parse_client_first(struct auth_scram_server *server,
const unsigned char *data, size_t size,
const char **login_username_r,
const char **error_r)
{
- const char *login_username = NULL;
- const char *data_cstr, *p;
- const char *gs2_header, *gs2_cbind_flag, *authzid;
- const char *cfm_bare, *username, *nonce;
+ struct auth_gs2_header gs2_header;
+ const unsigned char *gs2_header_end;
+ const char *cfm_bare, *login_username = NULL, *username, *nonce;
const char *const *fields;
-
- data_cstr = gs2_header = t_strndup(data, size);
+ const char *error;
/* RFC 5802, Section 7:
;; MUST be ignored.
attr-val = ALPHA "=" value
*/
- p = strchr(data_cstr, ',');
- if (p == NULL) {
- *error_r = "Invalid initial client message: "
- "Missing first ',' in GS2 header";
- return -1;
- }
- gs2_cbind_flag = t_strdup_until(data_cstr, p);
- data_cstr = p + 1;
- p = strchr(data_cstr, ',');
- if (p == NULL) {
- *error_r = "Invalid initial client message: "
- "Missing second ',' in GS2 header";
+ if (auth_gs2_header_decode(data, size, FALSE,
+ &gs2_header, &gs2_header_end, &error) < 0) {
+ *error_r = t_strdup_printf("Invalid initial client message: %s",
+ error);
return -1;
}
- authzid = t_strdup_until(data_cstr, p);
- gs2_header = t_strdup_until(gs2_header, p + 1);
- cfm_bare = p + 1;
+ size_t gs2_header_size = gs2_header_end - data;
+ size_t cfm_bare_size = size - gs2_header_size;
+
+ cfm_bare = t_strndup(gs2_header_end, cfm_bare_size);
fields = t_strsplit(cfm_bare, ",");
if (str_array_length(fields) < 2) {
*error_r = "Invalid initial client message: "
enum auth_scram_cbind_server_support cbind_support =
server->set.cbind_support;
- switch (gs2_cbind_flag[0]) {
- case 'p':
- if (gs2_cbind_flag[1] != '=' || gs2_cbind_flag[2] == '\0') {
- *error_r = "Invalid GS2 header";
- return -1;
- }
+ switch (gs2_header.cbind.status) {
+ case AUTH_GS2_CBIND_STATUS_PROVIDED:
if (cbind_support == AUTH_SCRAM_CBIND_SERVER_SUPPORT_NONE) {
*error_r = "Channel binding not supported";
return -1;
}
- auth_scram_server_start_channel_binding(server,
- &gs2_cbind_flag[2]);
+ auth_scram_server_start_channel_binding(
+ server, gs2_header.cbind.name);
break;
- case 'y':
+ case AUTH_GS2_CBIND_STATUS_NO_SERVER_SUPPORT:
/* RFC 5802, Section 6:
If the flag is set to "y" and the server supports channel
return -1;
}
break;
- case 'n':
+ case AUTH_GS2_CBIND_STATUS_NO_CLIENT_SUPPORT:
if (cbind_support == AUTH_SCRAM_CBIND_SERVER_SUPPORT_REQUIRED) {
*error_r = "Channel binding required";
return -1;
/* authzid = "a=" saslname
;; Protocol specific.
*/
- if (authzid[0] == '\0')
- ;
- else if (authzid[0] == 'a' && authzid[1] == '=') {
- /* Unescape authzid */
- login_username = auth_scram_unescape_username(authzid + 2);
-
- if (login_username == NULL) {
- *error_r = "authzid escaping is invalid";
- return -1;
- }
- } else {
- *error_r = "Invalid authzid field";
- return -1;
- }
+ if (gs2_header.authzid != NULL)
+ login_username = gs2_header.authzid;
/* reserved-mext = "m=" 1*(value-char)
*/
/* username = "n=" saslname
*/
if (username[0] == 'n' && username[1] == '=') {
+ const char *uname_enc = username + 2;
+ size_t uname_enc_size = strlen(uname_enc);
+
/* Unescape username */
- username = auth_scram_unescape_username(username + 2);
- if (username == NULL) {
+ if (auth_gs2_decode_username((const unsigned char *)uname_enc,
+ uname_enc_size, &username) < 0) {
*error_r = "Username escaping is invalid";
return -1;
}
*username_r = username;
*login_username_r = login_username;
- server->gs2_header = p_strdup(server->pool, gs2_header);
- server->client_first_message_bare =
- p_strdup(server->pool, cfm_bare);
+ server->gs2_header = p_strndup(server->pool, data, gs2_header_size);
+ server->client_first_message_bare = p_strdup(server->pool, cfm_bare);
return 0;
}