Unify SMTP authentication with Cyrus SASL
This commit is contained in:
@@ -30,6 +30,16 @@ source IPv4 or IPv6 network. Requiring app passwords remains a separate,
|
||||
explicit per-user policy; enabling TOTP does not silently change mail-client
|
||||
authentication.
|
||||
|
||||
SMTP submission, IMAP, POP3 and ManageSieve use one Cyrus SASL server layer.
|
||||
Its Bongo callback applies the same account lookup, service-specific app
|
||||
password policy, source-network restriction and authentication audit trail to
|
||||
every protocol. Password-bearing mechanisms are advertised only after TLS and
|
||||
the advertised list is generated from mechanisms that are both installed and
|
||||
enabled by Bongo. Clients cannot select an installed Cyrus mechanism that
|
||||
Bongo did not advertise. Bongo 0.7 enables `PLAIN` and `LOGIN` for TLS-protected
|
||||
compatibility clients; adding a mechanism requires implementing its credential
|
||||
callback and policy first, not merely installing another Cyrus plugin.
|
||||
|
||||
## LDAP master authentication
|
||||
|
||||
LDAP can be enabled as the primary source for normal account passwords. It is
|
||||
|
||||
@@ -13,6 +13,7 @@ target_link_libraries(bongosmtp
|
||||
bongojson
|
||||
bongomsgapi
|
||||
bongomailauth
|
||||
bongosasl
|
||||
bongocollectoraccounts
|
||||
)
|
||||
|
||||
|
||||
@@ -67,8 +67,9 @@ SMTPFormatEhlo(char *buffer, size_t buffer_size,
|
||||
!Append(buffer, buffer_size, &length, "250-ETRN\r\n")) ||
|
||||
(options->starttls &&
|
||||
!Append(buffer, buffer_size, &length, "250-STARTTLS\r\n")) ||
|
||||
(options->auth &&
|
||||
!Append(buffer, buffer_size, &length, "250-AUTH PLAIN LOGIN\r\n")) ||
|
||||
(options->auth_mechanisms && *options->auth_mechanisms &&
|
||||
!Append(buffer, buffer_size, &length, "250-AUTH %s\r\n",
|
||||
options->auth_mechanisms)) ||
|
||||
(options->smtputf8 &&
|
||||
!Append(buffer, buffer_size, &length, "250-SMTPUTF8\r\n")) ||
|
||||
!Append(buffer, buffer_size, &length, "250-HELP\r\n") ||
|
||||
|
||||
@@ -29,7 +29,7 @@ typedef struct {
|
||||
int message_limit;
|
||||
int etrn;
|
||||
int starttls;
|
||||
int auth;
|
||||
const char *auth_mechanisms;
|
||||
int smtputf8;
|
||||
int expn;
|
||||
} SMTPCapabilityOptions;
|
||||
|
||||
+175
-105
@@ -42,6 +42,7 @@
|
||||
#include <nmap.h>
|
||||
|
||||
#include <bongoagent.h>
|
||||
#include <bongosasl.h>
|
||||
#include <bongoutil.h>
|
||||
#include "mailauth.h"
|
||||
#include "smtpd.h"
|
||||
@@ -719,20 +720,35 @@ IsTransportRecipient(const char *address)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOL
|
||||
AuthenticateUser(ConnectionStruct *client, const char *user, const char *password)
|
||||
static int
|
||||
SMTPVerifyPassword(void *context, const char *user, const char *password,
|
||||
size_t passwordLength)
|
||||
{
|
||||
ConnectionStruct *client = context;
|
||||
char source[INET_ADDRSTRLEN];
|
||||
char *copy;
|
||||
int valid;
|
||||
|
||||
if (user == NULL || password == NULL || *user == '\0' ||
|
||||
if (client == NULL || user == NULL || password == NULL || *user == '\0' ||
|
||||
passwordLength > 4096U ||
|
||||
!inet_ntop(AF_INET, &client->client.conn->socketAddress.sin_addr,
|
||||
source, sizeof(source)) ||
|
||||
MsgAuthVerifyServicePassword(user, password, MSG_AUTH_SERVICE_SMTP,
|
||||
source) != 0 || MsgAuthFindUser(user) != 0) {
|
||||
Log(LOG_NOTICE, "Bongo authentication failed: service=smtp source=%s",
|
||||
LOGIP(client->client.conn->socketAddress));
|
||||
return FALSE;
|
||||
}
|
||||
source, sizeof(source))) return 0;
|
||||
copy = malloc(passwordLength + 1U);
|
||||
if (copy == NULL) return 0;
|
||||
memcpy(copy, password, passwordLength);
|
||||
copy[passwordLength] = '\0';
|
||||
valid = MsgAuthVerifyServicePassword(user, copy, MSG_AUTH_SERVICE_SMTP,
|
||||
source) == 0 &&
|
||||
MsgAuthFindUser(user) == 0;
|
||||
memset(copy, 0, passwordLength + 1U);
|
||||
free(copy);
|
||||
return valid;
|
||||
}
|
||||
|
||||
static BOOL
|
||||
SMTPRecordAuthenticatedUser(ConnectionStruct *client, const char *user)
|
||||
{
|
||||
if (client == NULL || user == NULL || *user == '\0') return FALSE;
|
||||
|
||||
Log(LOG_NOTICE, "Successful login for user %s at host %s", user,
|
||||
LOGIP(client->client.conn->socketAddress));
|
||||
@@ -741,6 +757,131 @@ AuthenticateUser(ConnectionStruct *client, const char *user, const char *passwor
|
||||
return client->AuthFrom != NULL;
|
||||
}
|
||||
|
||||
enum {
|
||||
SMTP_SASL_CONNECTION_CLOSED = -1,
|
||||
SMTP_SASL_FAILED = 0,
|
||||
SMTP_SASL_COMPLETE = 1,
|
||||
SMTP_SASL_CANCELLED = 2
|
||||
};
|
||||
|
||||
static int
|
||||
SMTPAuthenticateSasl(ConnectionStruct *client, char *arguments)
|
||||
{
|
||||
BongoSaslSession *session = NULL;
|
||||
const void *output = NULL;
|
||||
const char *username;
|
||||
char mechanism[64];
|
||||
char decoded[BUFSIZE + 1];
|
||||
char encoded[(BUFSIZE + 1) * 2];
|
||||
char *initial;
|
||||
size_t mechanismLength = 0U;
|
||||
size_t decodedLength = 0U;
|
||||
size_t encodedLength = 0U;
|
||||
size_t outputLength = 0U;
|
||||
int saslResult;
|
||||
int result = SMTP_SASL_FAILED;
|
||||
|
||||
if (client == NULL || arguments == NULL ||
|
||||
!isspace((unsigned char)*arguments)) return SMTP_SASL_FAILED;
|
||||
while (isspace((unsigned char)*arguments)) arguments++;
|
||||
while (arguments[mechanismLength] != '\0' &&
|
||||
!isspace((unsigned char)arguments[mechanismLength])) {
|
||||
mechanismLength++;
|
||||
}
|
||||
if (mechanismLength == 0U || mechanismLength >= sizeof(mechanism)) {
|
||||
return SMTP_SASL_FAILED;
|
||||
}
|
||||
memcpy(mechanism, arguments, mechanismLength);
|
||||
mechanism[mechanismLength] = '\0';
|
||||
for (size_t index = 0U; index < mechanismLength; index++) {
|
||||
mechanism[index] = (char)toupper((unsigned char)mechanism[index]);
|
||||
}
|
||||
initial = arguments + mechanismLength;
|
||||
while (isspace((unsigned char)*initial)) initial++;
|
||||
if (!strcmp(initial, "*")) {
|
||||
ConnWrite(client->client.conn,
|
||||
"501 5.7.0 Authentication canceled\r\n",
|
||||
sizeof("501 5.7.0 Authentication canceled\r\n") - 1U);
|
||||
return SMTP_SASL_CANCELLED;
|
||||
}
|
||||
if (!BongoSaslSessionCreate(&session, "smtp", BongoGlobals.hostname,
|
||||
NULL, NULL, 1, SMTPVerifyPassword, client)) {
|
||||
return SMTP_SASL_FAILED;
|
||||
}
|
||||
if (*initial != '\0' && strcmp(initial, "=") &&
|
||||
!BongoSaslDecode64(initial, strlen(initial), decoded, sizeof(decoded),
|
||||
&decodedLength)) goto finish;
|
||||
|
||||
saslResult = BongoSaslStart(session, mechanism,
|
||||
*initial != '\0' ? decoded : NULL,
|
||||
decodedLength, &output, &outputLength);
|
||||
while (saslResult == BONGO_SASL_CONTINUE) {
|
||||
encodedLength = 0U;
|
||||
if (outputLength != 0U &&
|
||||
!BongoSaslEncode64(output, outputLength, encoded, sizeof(encoded),
|
||||
&encodedLength)) goto finish;
|
||||
if (ConnWriteF(client->client.conn, "334 %.*s\r\n", (int)encodedLength,
|
||||
outputLength != 0U ? encoded : "") == -1 ||
|
||||
ConnFlush(client->client.conn) == -1) {
|
||||
result = SMTP_SASL_CONNECTION_CLOSED;
|
||||
goto finish;
|
||||
}
|
||||
if (ConnReadToAllocatedBuffer(client->client.conn,
|
||||
&client->client.buffer,
|
||||
&client->client.buflen) <= 0) {
|
||||
result = SMTP_SASL_CONNECTION_CLOSED;
|
||||
goto finish;
|
||||
}
|
||||
if (!strcmp(client->client.buffer, "*")) {
|
||||
ConnWrite(client->client.conn,
|
||||
"501 5.7.0 Authentication canceled\r\n",
|
||||
sizeof("501 5.7.0 Authentication canceled\r\n") - 1U);
|
||||
result = SMTP_SASL_CANCELLED;
|
||||
goto finish;
|
||||
}
|
||||
if (!BongoSaslDecode64(client->client.buffer,
|
||||
strlen(client->client.buffer), decoded,
|
||||
sizeof(decoded), &decodedLength)) goto finish;
|
||||
saslResult = BongoSaslStep(session, decoded, decodedLength,
|
||||
&output, &outputLength);
|
||||
}
|
||||
username = BongoSaslUsername(session);
|
||||
if (saslResult == BONGO_SASL_COMPLETE && username != NULL &&
|
||||
SMTPRecordAuthenticatedUser(client, username)) {
|
||||
result = SMTP_SASL_COMPLETE;
|
||||
}
|
||||
|
||||
finish:
|
||||
memset(decoded, 0, sizeof(decoded));
|
||||
memset(encoded, 0, sizeof(encoded));
|
||||
if (client->client.buffer != NULL && client->client.buflen != 0U) {
|
||||
memset(client->client.buffer, 0, client->client.buflen);
|
||||
}
|
||||
BongoSaslSessionDestroy(&session);
|
||||
return result;
|
||||
}
|
||||
|
||||
static BOOL
|
||||
SMTPGetSaslMechanisms(ConnectionStruct *client, char *buffer, size_t bufferSize)
|
||||
{
|
||||
BongoSaslSession *session = NULL;
|
||||
const char *mechanisms = NULL;
|
||||
BOOL available = FALSE;
|
||||
|
||||
if (client == NULL || buffer == NULL || bufferSize == 0U ||
|
||||
!BongoSaslSessionCreate(&session, "smtp", BongoGlobals.hostname,
|
||||
NULL, NULL, 1, SMTPVerifyPassword, client)) {
|
||||
return FALSE;
|
||||
}
|
||||
if (BongoSaslMechanisms(session, &mechanisms) && mechanisms != NULL &&
|
||||
*mechanisms != '\0' && strlen(mechanisms) < bufferSize) {
|
||||
strcpy(buffer, mechanisms);
|
||||
available = TRUE;
|
||||
}
|
||||
BongoSaslSessionDestroy(&session);
|
||||
return available;
|
||||
}
|
||||
|
||||
static BOOL
|
||||
SubmissionSenderAllowed(const char *user, const char *sender)
|
||||
{
|
||||
@@ -766,42 +907,6 @@ SubmissionSenderAllowed(const char *user, const char *sender)
|
||||
return found == 1;
|
||||
}
|
||||
|
||||
static BOOL
|
||||
ParsePlainCredentials(char *encoded, const char **user, const char **password)
|
||||
{
|
||||
size_t encodedLength;
|
||||
size_t decodedLimit;
|
||||
char *decoded;
|
||||
char *authcid;
|
||||
char *separator;
|
||||
|
||||
if (encoded == NULL || user == NULL || password == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
encodedLength = strlen(encoded);
|
||||
if (encodedLength == 0 || encodedLength > BUFSIZE) {
|
||||
return FALSE;
|
||||
}
|
||||
decodedLimit = ((encodedLength + 3U) / 4U) * 3U;
|
||||
decoded = DecodeBase64(encoded);
|
||||
if (decoded == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
separator = memchr(decoded, '\0', decodedLimit);
|
||||
if (separator == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
authcid = separator + 1;
|
||||
separator = memchr(authcid, '\0', decodedLimit - (size_t)(authcid - decoded));
|
||||
if (separator == NULL || authcid == separator) {
|
||||
return FALSE;
|
||||
}
|
||||
*user = authcid;
|
||||
*password = separator + 1;
|
||||
return (size_t)(*password - decoded) < decodedLimit;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char To[MAXEMAILNAMESIZE+1];
|
||||
@@ -1133,9 +1238,7 @@ HandleConnection (void *param)
|
||||
}
|
||||
|
||||
case 'A':{ /* AUTH */
|
||||
const char *user = NULL;
|
||||
const char *password = NULL;
|
||||
BOOL authenticated = FALSE;
|
||||
int authResult;
|
||||
|
||||
if (AllowAuth == FALSE || !Client->client.conn->ssl.enable) {
|
||||
ConnWrite (Client->client.conn, MSG500UNKNOWN, MSG500UNKNOWN_LEN);
|
||||
@@ -1147,67 +1250,25 @@ HandleConnection (void *param)
|
||||
ConnWrite (Client->client.conn, MSG503BADORDER, MSG503BADORDER_LEN);
|
||||
break;
|
||||
}
|
||||
|
||||
if (XplStrNCaseCmp(Client->Command + 5, "PLAIN", 5) == 0) {
|
||||
char *encoded = Client->Command + 10;
|
||||
|
||||
while (isspace((unsigned char)*encoded)) {
|
||||
encoded++;
|
||||
}
|
||||
if (*encoded == '\0') {
|
||||
ConnWrite(Client->client.conn, "334 \r\n", 6);
|
||||
ConnFlush(Client->client.conn);
|
||||
if (ConnReadToAllocatedBuffer(Client->client.conn,
|
||||
&Client->client.buffer, &Client->client.buflen) <= 0) {
|
||||
return EndClientConnection(Client);
|
||||
}
|
||||
encoded = Client->client.buffer;
|
||||
}
|
||||
if (strcmp(encoded, "*") == 0) {
|
||||
ConnWrite(Client->client.conn,
|
||||
"501 5.7.0 Authentication canceled\r\n", 35);
|
||||
break;
|
||||
}
|
||||
if (ParsePlainCredentials(encoded, &user, &password)) {
|
||||
authenticated = AuthenticateUser(Client, user, password);
|
||||
}
|
||||
} else if (XplStrNCaseCmp(Client->Command + 5, "LOGIN", 5) == 0) {
|
||||
if (!isspace((unsigned char)Client->Command[10])) {
|
||||
ConnWrite (Client->client.conn, "334 VXNlcm5hbWU6\r\n", 18);
|
||||
ConnFlush (Client->client.conn);
|
||||
if (ConnReadToAllocatedBuffer(Client->client.conn,
|
||||
&Client->client.buffer, &Client->client.buflen) <= 0) {
|
||||
return EndClientConnection(Client);
|
||||
}
|
||||
snprintf(Reply, sizeof(Reply), "%s", Client->client.buffer);
|
||||
} else {
|
||||
snprintf(Reply, sizeof(Reply), "%s", Client->Command + 11);
|
||||
}
|
||||
if (strcmp(Reply, "*") != 0) {
|
||||
user = DecodeBase64(Reply);
|
||||
ConnWrite(Client->client.conn, "334 UGFzc3dvcmQ6\r\n", 18);
|
||||
ConnFlush(Client->client.conn);
|
||||
if (ConnReadToAllocatedBuffer(Client->client.conn,
|
||||
&Client->client.buffer, &Client->client.buflen) <= 0) {
|
||||
return EndClientConnection(Client);
|
||||
}
|
||||
if (strcmp(Client->client.buffer, "*") != 0) {
|
||||
password = DecodeBase64(Client->client.buffer);
|
||||
authenticated = AuthenticateUser(Client, user, password);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ConnWrite(Client->client.conn, MSG504BADAUTH, MSG504BADAUTH_LEN);
|
||||
if (Client->State != STATE_HELO || !Client->IsEHLO) {
|
||||
ConnWrite(Client->client.conn, MSG503BADORDER,
|
||||
MSG503BADORDER_LEN);
|
||||
break;
|
||||
}
|
||||
|
||||
if (authenticated) {
|
||||
authResult = SMTPAuthenticateSasl(Client, Client->Command + 4);
|
||||
if (authResult == SMTP_SASL_CONNECTION_CLOSED) {
|
||||
return EndClientConnection(Client);
|
||||
}
|
||||
if (authResult == SMTP_SASL_COMPLETE) {
|
||||
Client->State = STATE_AUTH;
|
||||
IsAuthed = TRUE;
|
||||
IsTrusted = TRUE;
|
||||
ConnWrite(Client->client.conn,
|
||||
"235 2.7.0 Authentication successful\r\n", 37);
|
||||
} else {
|
||||
} else if (authResult == SMTP_SASL_FAILED) {
|
||||
Log(LOG_NOTICE,
|
||||
"Bongo authentication failed: service=smtp source=%s",
|
||||
LOGIP(Client->client.conn->socketAddress));
|
||||
ConnWrite(Client->client.conn,
|
||||
"535 5.7.8 Authentication credentials invalid\r\n", 46);
|
||||
}
|
||||
@@ -2330,6 +2391,7 @@ HandleConnection (void *param)
|
||||
case 'H':{ /* EHLO */
|
||||
if (Client->State == STATE_FRESH) {
|
||||
SMTPCapabilityOptions capabilities;
|
||||
char mechanisms[256];
|
||||
size_t answerLength;
|
||||
|
||||
XplRWReadLockAcquire (&ConfigLock);
|
||||
@@ -2340,8 +2402,11 @@ HandleConnection (void *param)
|
||||
(SMTP.allow_client_ssl || Client->InternalRelay || Client->Submission) &&
|
||||
(Client->InternalRelay ? InternalSSLContext != NULL : SSLContext != NULL) &&
|
||||
!Client->client.conn->ssl.enable;
|
||||
capabilities.auth = AllowAuth == TRUE &&
|
||||
Client->client.conn->ssl.enable;
|
||||
capabilities.auth_mechanisms =
|
||||
AllowAuth == TRUE && Client->client.conn->ssl.enable &&
|
||||
SMTPGetSaslMechanisms(Client, mechanisms,
|
||||
sizeof(mechanisms))
|
||||
? mechanisms : NULL;
|
||||
capabilities.smtputf8 = SMTP.smtp_utf8_enabled;
|
||||
capabilities.expn = SMTP.allow_expn;
|
||||
answerLength = SMTPFormatEhlo(Answer, sizeof(Answer),
|
||||
@@ -5026,6 +5091,7 @@ SMTPServer (void *ignored)
|
||||
LogShutdown ();
|
||||
|
||||
XplRWLockDestroy (&ConfigLock);
|
||||
BongoSaslShutdown();
|
||||
MsgShutdown ();
|
||||
|
||||
ConnShutdown ();
|
||||
@@ -5310,6 +5376,10 @@ int XplServiceMain (int argc, char *argv[])
|
||||
|
||||
MsgInit();
|
||||
MsgAuthInit();
|
||||
if (!BongoSaslInitialize()) {
|
||||
Log(LOG_ERROR, "Unable to initialize Cyrus SASL");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// FIXME: Connio socket timeout needs to be a run-time tunable. bug #9924
|
||||
// ConnStartup (SMTP.socket_timeout, TRUE);
|
||||
|
||||
@@ -46,7 +46,7 @@ main(void)
|
||||
|
||||
options.etrn = 1;
|
||||
options.starttls = 1;
|
||||
options.auth = 1;
|
||||
options.auth_mechanisms = "PLAIN LOGIN";
|
||||
options.smtputf8 = 1;
|
||||
options.expn = 1;
|
||||
assert(SMTPFormatEhlo(buffer, sizeof(buffer), &options) > 0);
|
||||
@@ -56,6 +56,10 @@ main(void)
|
||||
assert(strstr(buffer, "250-SMTPUTF8\r\n"));
|
||||
assert(strstr(buffer, "250-EXPN\r\n"));
|
||||
|
||||
options.auth_mechanisms = "SCRAM-SHA-256";
|
||||
assert(SMTPFormatEhlo(buffer, sizeof(buffer), &options) > 0);
|
||||
assert(strstr(buffer, "250-AUTH SCRAM-SHA-256\r\n"));
|
||||
|
||||
options.message_limit = 0;
|
||||
assert(SMTPFormatEhlo(buffer, sizeof(buffer), &options) > 0);
|
||||
assert(strstr(buffer, "250 SIZE\r\n"));
|
||||
|
||||
@@ -5,5 +5,5 @@ install(TARGETS bongosasl DESTINATION ${LIB_INSTALL_DIR})
|
||||
if(BUILD_TESTING)
|
||||
add_executable(bongo-sasl-test tests/sasl-test.c)
|
||||
target_link_libraries(bongo-sasl-test PRIVATE bongosasl)
|
||||
add_test(NAME sasl-plain COMMAND bongo-sasl-test)
|
||||
add_test(NAME sasl-password-mechanisms COMMAND bongo-sasl-test)
|
||||
endif()
|
||||
|
||||
+25
-1
@@ -25,6 +25,7 @@
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
|
||||
struct BongoSaslSession {
|
||||
sasl_conn_t *connection;
|
||||
@@ -135,6 +136,28 @@ exchange_result(int result, const char *sasl_output, unsigned sasl_length,
|
||||
return BONGO_SASL_FAIL;
|
||||
}
|
||||
|
||||
static int
|
||||
mechanism_allowed(BongoSaslSession *session, const char *mechanism)
|
||||
{
|
||||
const char *allowed;
|
||||
const char *cursor;
|
||||
size_t mechanism_length;
|
||||
|
||||
if (!session || !mechanism || !*mechanism ||
|
||||
!BongoSaslMechanisms(session, &allowed)) return 0;
|
||||
mechanism_length = strlen(mechanism);
|
||||
for (cursor = allowed; *cursor;) {
|
||||
const char *end;
|
||||
while (*cursor == ' ') cursor++;
|
||||
end = strchr(cursor, ' ');
|
||||
if (!end) end = cursor + strlen(cursor);
|
||||
if ((size_t)(end - cursor) == mechanism_length &&
|
||||
!strncasecmp(cursor, mechanism, mechanism_length)) return 1;
|
||||
cursor = end;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
BongoSaslStart(BongoSaslSession *session, const char *mechanism,
|
||||
const void *input, size_t input_length,
|
||||
@@ -143,7 +166,8 @@ BongoSaslStart(BongoSaslSession *session, const char *mechanism,
|
||||
const char *sasl_output = NULL;
|
||||
unsigned sasl_length = 0;
|
||||
int result;
|
||||
if (!session || !mechanism || input_length > (size_t)UINT_MAX) return BONGO_SASL_FAIL;
|
||||
if (!session || !mechanism || input_length > (size_t)UINT_MAX ||
|
||||
!mechanism_allowed(session, mechanism)) return BONGO_SASL_FAIL;
|
||||
result = sasl_server_start(session->connection, mechanism, input,
|
||||
(unsigned)input_length, &sasl_output, &sasl_length);
|
||||
return exchange_result(result, sasl_output, sasl_length, output, output_length);
|
||||
|
||||
@@ -46,11 +46,30 @@ int main(void)
|
||||
if (!BongoSaslSessionCreate(&session, "sieve", "mail.example.test",
|
||||
"127.0.0.1;4190", "127.0.0.1;12345", 1,
|
||||
verify, NULL)) goto finish;
|
||||
if (!BongoSaslMechanisms(session, &mechanisms) || !strstr(mechanisms, "PLAIN")) goto finish;
|
||||
if (!BongoSaslMechanisms(session, &mechanisms) ||
|
||||
!strstr(mechanisms, "PLAIN") || !strstr(mechanisms, "LOGIN")) goto finish;
|
||||
if (BongoSaslStart(session, "PLAIN", plain, sizeof(plain) - 1,
|
||||
&output, &output_length) != BONGO_SASL_COMPLETE) goto finish;
|
||||
if (!BongoSaslUsername(session) || strcmp(BongoSaslUsername(session),
|
||||
"alice@example.test")) goto finish;
|
||||
BongoSaslSessionDestroy(&session);
|
||||
if (!BongoSaslSessionCreate(&session, "smtp", "mail.example.test",
|
||||
"127.0.0.1;587", "127.0.0.1;12345", 1,
|
||||
verify, NULL)) goto finish;
|
||||
if (BongoSaslStart(session, "SCRAM-SHA-256", NULL, 0, &output,
|
||||
&output_length) != BONGO_SASL_FAIL) goto finish;
|
||||
BongoSaslSessionDestroy(&session);
|
||||
if (!BongoSaslSessionCreate(&session, "smtp", "mail.example.test",
|
||||
"127.0.0.1;587", "127.0.0.1;12345", 1,
|
||||
verify, NULL)) goto finish;
|
||||
if (BongoSaslStart(session, "login", NULL, 0, &output,
|
||||
&output_length) != BONGO_SASL_CONTINUE ||
|
||||
BongoSaslStep(session, "alice@example.test", 18, &output,
|
||||
&output_length) != BONGO_SASL_CONTINUE ||
|
||||
BongoSaslStep(session, "secret", 6, &output,
|
||||
&output_length) != BONGO_SASL_COMPLETE ||
|
||||
!BongoSaslUsername(session) ||
|
||||
strcmp(BongoSaslUsername(session), "alice@example.test")) goto finish;
|
||||
if (!BongoSaslEncode64(plain, sizeof(plain) - 1, encoded, sizeof(encoded),
|
||||
&encoded_length) ||
|
||||
!BongoSaslDecode64(encoded, encoded_length, decoded, sizeof(decoded),
|
||||
|
||||
Reference in New Issue
Block a user