/*
* SCRAM-SHA-1 SASL authentication, see RFC-5802
*
* Copyright (c) 2011-2016 Florian Zeitz <florob@babelmonkeys.de>
*
* This software is released under the MIT license.
*/
#include <limits.h>
#include "auth-common.h"
#include "base64.h"
#include "buffer.h"
#include "hmac.h"
#include "sha1.h"
#include "randgen.h"
#include "safe-memset.h"
#include "str.h"
#include "strfuncs.h"
#include "strnum.h"
#include "password-scheme.h"
#include "mech.h"
/* s-nonce length */
struct scram_auth_request {
/* sent: */
const char *server_first_message;
const char *snonce;
/* received: */
const char *gs2_cbind_flag;
const char *cnonce;
const char *client_first_message_bare;
const char *client_final_message_without_proof;
/* stored */
};
{
size_t i;
/* make sure snonce is printable and does not contain ',' */
for (i = 0; i < sizeof(snonce)-1; i++) {
if (snonce[i] == ',')
snonce[i] = '~';
}
}
{
const char *auth_message;
}
{
if (in[0] == '=') {
else
return NULL;
in += 2;
} else {
}
}
}
const char **error_r)
{
*error_r = "Invalid initial client message";
return FALSE;
}
gs2_cbind_flag = fields[0];
/* Order of fields is fixed:
client-first-message = gs2-header client-first-message-bare
gs2-header = gs2-cbind-flag "," [ authzid ] ","
gs2-cbind-flag = ("p=" cb-name) / "n" / "y"
client-first-message-bare = [reserved-mext ","]
username "," nonce ["," extensions]
reserved-mext = "m=" 1*(value-char)
username = "n=" saslname
nonce = "r=" c-nonce [s-nonce]
extensions = attr-val *("," attr-val)
;; All extensions are optional,
;; i.e., unrecognized attributes
;; not defined in this document
;; MUST be ignored.
attr-val = ALPHA "=" value
*/
switch (gs2_cbind_flag[0]) {
case 'p':
*error_r = "Channel binding not supported";
return FALSE;
case 'y':
case 'n':
break;
default:
*error_r = "Invalid GS2 header";
return FALSE;
}
if (authzid[0] == '\0')
;
/* Unescape authzid */
if (login_username == NULL) {
*error_r = "authzid escaping is invalid";
return FALSE;
}
} else {
*error_r = "Invalid authzid field";
return FALSE;
}
if (username[0] == 'm') {
*error_r = "Mandatory extension(s) not supported";
return FALSE;
}
/* Unescape username */
*error_r = "Username escaping is invalid";
return FALSE;
}
return FALSE;
} else {
*error_r = "Invalid username field";
return FALSE;
}
if (login_username != NULL) {
return FALSE;
}
else {
*error_r = "Invalid client nonce";
return FALSE;
}
/* This works only without channel binding support,
otherwise the GS2 header doesn't have a fixed length */
return TRUE;
}
{
const char *auth_message;
size_t i;
for (i = 0; i < sizeof(client_signature); i++)
client_key[i] =
}
struct auth_request *auth_request)
{
(struct scram_auth_request *)auth_request;
unsigned int iter_count;
switch (result) {
case PASSDB_RESULT_OK:
"%s", error);
break;
}
break;
break;
default:
break;
}
}
const char **error_r)
{
unsigned int field_count;
if (field_count < 3) {
*error_r = "Invalid final client message";
return FALSE;
}
*error_r = "Invalid channel binding data";
return FALSE;
}
*error_r = "Wrong nonce";
return FALSE;
}
*error_r = "Invalid base64 encoding";
return FALSE;
}
*error_r = "Invalid ClientProof length";
return FALSE;
}
} else {
*error_r = "Invalid ClientProof";
return FALSE;
}
return TRUE;
}
const unsigned char *data,
{
(struct scram_auth_request *)auth_request;
const char *server_final_message;
/* Received client-first-message */
"SCRAM-SHA-1",
return;
}
} else {
/* Received client-final-message */
&error)) {
if (!verify_credentials(request)) {
} else {
return;
}
}
}
}
{
return &request->auth_request;
}
"SCRAM-SHA-1",
};