Skip to content

Commit

Permalink
added XOAUTH2 authentication to qmail-smtpd, qmail-remote
Browse files Browse the repository at this point in the history
  • Loading branch information
mbhangui committed Jul 15, 2024
1 parent 3cc58ae commit 39444f1
Show file tree
Hide file tree
Showing 9 changed files with 353 additions and 125 deletions.
9 changes: 7 additions & 2 deletions indimail-mta-x/indimail-env.9
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ bouncing the mail after discovring that the account doesn't exist
.TP
\fIAUTH_SMTP\fR
If set, \fBqmail-remote\fR(8) will use authenticated SMTP with the remote
host before starting delivery.
host before starting delivery. Supported values are LOGIN, PLAIN, XOAUTH2,
CRAM-MD5, CRAM-RIPEMD, CRAM-SHA1, CRAM-SHA224, CRAM-SHA256, CRAM-SHA384,
CRAM-SHA512, DIGEST-MD5, SCRAM-SHA1, SCRAM-SHA-1-PLUS, SCRAM-SHA256,
SCRAM-SHA-256-PLUS

.TP
\fIBADEXT\fR
Expand Down Expand Up @@ -247,7 +250,9 @@ Used by various indimail-virtual programs and \fBqmail-smtpd\fR(8) when domain
component is missing in addresses.

.TP
\fIDISABLE_AUTH_LOGIN\fR, \fIDISABLE_AUTH_PLAIN\fR, \fIDISABLE_CRAM_MD5\fR, \fIDISABLE_CRAM_RIPEMD\fR,
\fIDISABLE_AUTH_LOGIN\fR, \fIDISABLE_AUTH_PLAIN\fR, \fIDISABLE_AUTH_OAUTH2\fR,
.TP
\fIDISABLE_CRAM_MD5\fR, \fIDISABLE_CRAM_RIPEMD\fR,
.TP
\fIDISABLE_CRAM_SHA1\fR, \fIDISABLE_CRAM_SHA224\fR, \fIDISABLE_CRAM_SHA256\fR, \fIDSABLE_CRAM_SHA384\fR,
.TP
Expand Down
47 changes: 30 additions & 17 deletions indimail-mta-x/qmail-remote.9
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ or \fISYSCONF/control/smtproutes\fR control file. This is described in
detail in the section \fBCONTROL FILES\fR. \fBqmail-remote\fR uses libgsasl
to use GNU Simple Authentication and Security Layer detailed here
https://www.gnu.org/software/gsasl. \fBqmail-remote\fR supports LOGIN,
PLAIN, CRAM-MD5, CRAM-SHA1, CRAM-SHA224, CRAM-SHA256, CRAM-SHA384,
PLAIN, XOAUTH2, CRAM-MD5, CRAM-SHA1, CRAM-SHA224, CRAM-SHA256, CRAM-SHA384,
CRAM-RIPEMD, DIGEST-MD5 authentication using built-in functions and
SCRAM-SHA-1, SCRAM-SHA-256, SCRAM-SHA-1-PLUS, SCRAM-SHA-256-PLUS
authentication provided by libgsasl. Note that DIGEST-MD5 has been moved to
Expand Down Expand Up @@ -176,32 +176,44 @@ no supported AUTH method found, continuing without authentication.
Z
Connected to
.I host
but authentication was rejected (AUTH PLAIN).
but unable to base64encode (plain).

.TP 5
Z
Connected to
.I host
but unable to base64encode (plain).
but unable to base64encode (challenge).

.TP 5
Z
Connected to
.I host
but unable to base64encode (username+digest).

.TP 5
Z
Connected to
.I host
but authentication was rejected (plain)."
but unable to base64encode (oauth2).

.TP 5
Z
Connected to
.I host
but authentication was rejected (AUTH LOGIN).
but unable to base64encode (oauth2 error).

.TP 5
Z
Connected to
.I host
but unable to base64encode user.

.TP 5
Z
Connected to
.I host
but unable to base64encode password.

.TP 5
Z
Connected to
Expand All @@ -224,8 +236,8 @@ but authentication was rejected (password).
Z
Connected to
.I host
but authentication was rejected (AUTH LOGIN, PLAIN, CRAM-MD5, CRAM-SHA1,
CRAM-SHA224, CRAM-SHA256, CRAM-SHA384, CRAM-SHA512, CRAM-RIPEMD,
but authentication was rejected (AUTH LOGIN, PLAIN, XOAUTH2, CRAM-MD5,
CRAM-SHA1, CRAM-SHA224, CRAM-SHA256, CRAM-SHA384, CRAM-SHA512, CRAM-RIPEMD,
DIGEST-MD5, SCRAM-SHA-1, SCRAM-SHA-256, SCRAM-SHA-1-PLUS,
SCRAM-SHA-256-PLUS)

Expand Down Expand Up @@ -577,7 +589,7 @@ Here \fIaddr\fR is an address;
\fBqmail-remote\fR will use for authenticated SMTP.
If there are several assignments for the same \fIaddr\fR address,
\fBqmail-remote\R will use the first one.
\fBqmail-remote\fR will use the first one.
\fIaddr\fR is interpreted without regard to case.
Expand Down Expand Up @@ -614,11 +626,11 @@ You can create \fISYSCONF/control/remote_auth.cdb\fR from
\fBAUTH_SMTP\fR can have the values SCRAM-SHA-256-PLUS, SCRAM-SHA-256,
SCRAM-SHA-1-PLUS, SCRAM-SHA-1, DIGEST-MD5, CRAM-RIPEMD, CRAM-SHA512,
CRAM-SHA384, CRAM-SHA256, CRAM-SHA224, CRAM-SHA1, CRAM-MD5, LOGIN or PLAIN
to use the desired authentication method. AUTH_SMTP can also be an empty
string to let qmail-remote choose an authentication method in the order
listed above. If no method from the above list is found supported by the
remote server, qmail-remote will continue without using authentication.
CRAM-SHA384, CRAM-SHA256, CRAM-SHA224, CRAM-SHA1, CRAM-MD5, LOGIN, PLAIN,
XOAUTH2 to use the desired authentication method. AUTH_SMTP can also be an
empty string to let qmail-remote choose an authentication method in the
order listed above. If no method from the above list is found supported by
the remote server, qmail-remote will continue without using authentication.
Remember that authenticated smtp gets disabled if username and password
have not been configured in \fBsmtproutes\fR control file or the
\fBSMTPROUTE\fR / \fBX-SMTPROUTES\fR environment variable.
Expand All @@ -629,7 +641,7 @@ characters for SCRAM-SHA-1 and 64 characters for SCRAM-SHA-256) with the
user’s PBKDF2-prepared password. The salted password value can be derived
by using the --mkpasswd parameter for the gsasl(1) utility. indimail has
vpasswd(1) / vmoduser(1), which use gsasl_scram_secrets_from_password(3)
API to set passwords for AUTH PLAIN, LOGIN, CRAM-MD5, CRAM-SHA1,
API to set passwords for AUTH PLAIN, LOGIN, XOAUTH2, CRAM-MD5, CRAM-SHA1,
CRAM-SHA224, CRAM-SHA256, CRAM-SHA384, CRAM-SHA512, CRAM-RIPEMD,
DIGEST-MD5, SCRAM-SHA-1, SCRAM-SHA-256 methods. The hex-encoded salted
password can be used to avoid storing a clear-text credential in the
Expand All @@ -644,6 +656,7 @@ following environment variables.
.EX
DISABLE_AUTH_LOGIN
DISABLE_AUTH_PLAIN
DISABLE_AUTH_OAUTH2
DISABLE_CRAM_MD5
DISABLE_CRAM_SHA1
DISABLE_CRAM_SHA224
Expand All @@ -658,9 +671,9 @@ following environment variables.
DISABLE_SCRAM_SHA256_PLUS
.EE
If you set the environment variable \fBSECURE_AUTH\fR, AUTH LOGIN and
AUTH PLAIN gets disabled, unless qmail-remote has opened a TLS session wth
the remote host. See the control file \fIclientcert.pem\fR.
If you set the environment variable \fBSECURE_AUTH\fR, AUTH LOGIN, AUTH
PLAIN and AUTH XOUTH2 gets disabled, unless qmail-remote has opened a TLS
session with the remote host. See the control file \fIclientcert.pem\fR.
If all connections to the \fIrelay\fR server fail for a \fImax_tolerance\fR
seconds, further connections to the same server are avoided for a period of
Expand Down
122 changes: 100 additions & 22 deletions indimail-mta-x/qmail-remote.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*-
* RCS log at bottom
* $Id: qmail-remote.c,v 1.174 2024-05-12 00:20:03+05:30 mbhangui Exp mbhangui $
* $Id: qmail-remote.c,v 1.175 2024-07-16 00:17:36+05:30 Cprogrammer Exp mbhangui $
*/
#include <unistd.h>
#include <sys/types.h>
Expand Down Expand Up @@ -1471,22 +1471,25 @@ decode_smtpauth_err(int code, const char *s1, const char *s2)
switch(code)
{
case 432:
quit(code, 1, "ZConnected to ", " but a password transition is needed ", "(", s1, s2, ") (#4.7.12).", NULL);
quit(code, 1, "ZConnected to ", " but a password transition is needed (", s1, s2, ") (#4.7.12).", NULL);
break;
case 454:
quit(code, 1, "ZConnected to ", " but got temporary authentication failure ", s1, s2, ") (#4.7.0).", NULL);
break;
case 534:
quit(code, 1, "DConnected to ", " but authentication mechanism is too weak ", s1, s2, ") (#5.7.9).", NULL);
break;
case 535:
quit(code, 1, "DConnected to ", " but authentication credentials invalid ", s1, s2, ") (#5.7.8).", NULL);
quit(code, 1, "ZConnected to ", " but got temporary authentication failure (", s1, s2, ") (#4.7.0).", NULL);
break;
case 500:
quit(code, 1, "DConnected to ", " but authentication Exchange line is too long ", s1, s2, ") (#5.5.6).", NULL);
quit(code, 1, "DConnected to ", " but authentication Exchange line is too long (", s1, s2, ") (#5.5.6).", NULL);
break;
case 530:
quit(code, 1, "DConnected to ", " but authentication required ", s1, s2, ") (#5.7.0).", NULL);
quit(code, 1, "DConnected to ", " but authentication required (", s1, s2, ") (#5.7.0).", NULL);
break;
case 534:
quit(code, 1, "DConnected to ", " but authentication mechanism is too weak (", s1, s2, ") (#5.7.9).", NULL);
break;
case 535:
if (slop.len)
quit(code, 1, "DConnected to ", " but authentication credentials invalid (", s1, s2, ") oauth2 error [", slop.s, "]", ") (#5.7.8).", NULL);
else
quit(code, 1, "DConnected to ", " but authentication credentials invalid (", s1, s2, ") (#5.7.8).", NULL);
break;
case 538:
quit(code, 1, "DConnected to ", " but encryption required for requested authentication mechanism ", s1, s2, ") (#5.7.11).", NULL);
Expand Down Expand Up @@ -1593,8 +1596,10 @@ auth_digest_md5(int use_size)
if (substdio_put(&smtpto, "\r\n", 2) == -1 ||
substdio_flush(&smtpto) == -1)
temp_write();
if ((code = smtpcode()) != 235)
if ((code = smtpcode()) != 235) {
slop.len = 0;
decode_smtpauth_err(code, "AUTH ", "DIGEST-MD5");
}
mailfrom_xtext(use_size);
}

Expand Down Expand Up @@ -1714,11 +1719,13 @@ auth_cram(int type, int use_size)
if (b64encode(&slop, &auth))
quit(-1, -1, "ZConnected to ", " but unable to base64encode username+digest", NULL);
if (substdio_put(&smtpto, auth.s, auth.len) == -1 ||
substdio_put(&smtpto, "\r\n", 2) == -1)
substdio_put(&smtpto, "\r\n", 2) == -1 ||
substdio_flush(&smtpto) == -1)
temp_write();
substdio_flush(&smtpto);
if ((code = smtpcode()) != 235)
if ((code = smtpcode()) != 235) {
slop.len = 0;
decode_smtpauth_err(code, "AUTH ", get_authmethod(type));
}
mailfrom_xtext(use_size);
}

Expand All @@ -1743,9 +1750,11 @@ auth_plain(int use_size)
if (substdio_put(&smtpto, auth.s, auth.len) == -1 ||
substdio_put(&smtpto, "\r\n", 2) == -1 ||
substdio_flush(&smtpto) == -1)
temp_nomem();
if ((code = smtpcode()) != 235)
temp_write();
if ((code = smtpcode()) != 235) {
slop.len = 0;
decode_smtpauth_err(code, "AUTH ", "PLAIN");
}
mailfrom_xtext(use_size);
}

Expand All @@ -1767,22 +1776,57 @@ auth_login(int use_size)
if (substdio_put(&smtpto, auth.s, auth.len) == -1 ||
substdio_put(&smtpto, "\r\n", 2) == -1 ||
substdio_flush(&smtpto) == -1)
temp_nomem();
temp_write();
if ((code = smtpcode()) != 334)
quit(code, 1, "ZConnected to ", " but authentication was rejected (username)", NULL);

if (!stralloc_copys(&auth, ""))
temp_nomem();
if (b64encode(&pass, &auth))
quit(-1, -1, "ZConnected to ", " but unable to base64encode pass", NULL);
quit(-1, -1, "ZConnected to ", " but unable to base64encode password", NULL);
if (substdio_put(&smtpto, auth.s, auth.len) == -1 ||
substdio_put(&smtpto, "\r\n", 2) == -1 ||
substdio_flush(&smtpto) == -1)
temp_nomem();
if ((code = smtpcode()) != 235)
temp_write();
if ((code = smtpcode()) != 235) {
slop.len = 0;
decode_smtpauth_err(code, "AUTH ", "LOGIN");
}
mailfrom_xtext(use_size);
}

#ifdef AUTH_XOAUTH2
void
auth_xoauth2(int use_size)
{
int code;

if (!stralloc_copyb(&plain, "user=", 5) ||
!stralloc_cat(&plain, &user) ||
!stralloc_catb(&plain, "\001auth=Bearer ", 13) ||
!stralloc_cat(&plain, &pass) ||
!stralloc_catb(&plain, "\001\001", 2))
temp_nomem();
if (b64encode(&plain, &auth))
quit(-1, -1, "ZConnected to ", " but unable to base64encode (oauth2)", NULL);
if (substdio_put(&smtpto, "AUTH XOAUTH2 ", 13) == -1 ||
substdio_put(&smtpto, auth.s, auth.len) == -1 ||
substdio_put(&smtpto, "\r\n", 2) == -1 ||
substdio_flush(&smtpto) == -1)
temp_write();
if ((code = smtpcode()) != 235) {
if (substdio_put(&smtpto, "\r\n", 2) == -1 ||
substdio_flush(&smtpto) == -1)
temp_write();
if (b64decode((unsigned char *) smtptext.s + 4, smtptext.len - 5, &slop))
quit(-1, -1, "ZConnected to ", " but unable to base64decode oauth2 error", NULL);
slop.len--;
code = smtpcode();
decode_smtpauth_err(code, "AUTH ", "XOAUTH2");
}
mailfrom_xtext(use_size);
}
#endif

#if defined(HASLIBGSASL) && defined(TLS)
static void
Expand Down Expand Up @@ -2061,8 +2105,14 @@ smtp_auth(const char *type, int use_size)
int i = 0, login_supp = 0, plain_supp = 0, cram_md5_supp = 0, cram_sha1_supp = 0,
cram_sha224_supp, cram_sha256_supp = 0, cram_sha384_supp, cram_sha512_supp = 0,
cram_rmd_supp = 0, digest_md5_supp = 0;
#ifdef AUTH_XOAUTH2
int xoauth2_supp = 0;
#endif
const char *ptr, *no_auth_login, *no_auth_plain, *no_cram_md5, *no_cram_sha1, *no_cram_sha224,
*no_cram_sha256, *no_cram_sha384, *no_cram_sha512, *no_cram_ripemd, *no_digest_md5;
#ifdef AUTH_XOAUTH2
const char *no_auth_xoauth2;
#endif
#ifdef TLS
int secure_auth;
#endif
Expand Down Expand Up @@ -2127,14 +2177,27 @@ smtp_auth(const char *type, int use_size)
if (case_starts(ptr, "PLAIN"))
plain_supp = 1;
}
#ifdef AUTH_XOAUTH2
else
if (*ptr == 'X') {
if (case_starts(ptr, "XOAUTH2"))
xoauth2_supp = 1;
}
#endif
}
#ifdef TLS
secure_auth = env_get("SECURE_AUTH") ? 1 : 0;
no_auth_login = secure_auth && !ssl ? "" : env_get("DISABLE_AUTH_LOGIN");
no_auth_plain = secure_auth && !ssl ? "" : env_get("DISABLE_AUTH_PLAIN");
#ifdef AUTH_XOAUTH2
no_auth_xoauth2 = secure_auth && !ssl ? "" : env_get("DISABLE_AUTH_OAUTH2");
#endif
#else
no_auth_login = env_get("DISABLE_AUTH_LOGIN");
no_auth_plain = env_get("DISABLE_AUTH_PLAIN");
#ifdef AUTH_XOAUTH2
no_auth_xoauth2 = env_get("DISABLE_AUTH_OAUTH2");
#endif
#endif
no_cram_md5 = env_get("DISABLE_CRAM_MD5");
no_cram_sha1 = env_get("DISABLE_CRAM_SHA1");
Expand Down Expand Up @@ -2209,6 +2272,12 @@ smtp_auth(const char *type, int use_size)
auth_login(use_size);
return;
}
#ifdef AUTH_XOAUTH2
if (!no_auth_xoauth2 && xoauth2_supp) {
auth_xoauth2(use_size);
return;
}
#endif
} else
#if defined(HASLIBGSASL) && defined(TLS)
if (scram_sha256_plus_supp && !case_diffs(type, "SCRAM-SHA-256-PLUS")) {
Expand Down Expand Up @@ -2236,6 +2305,12 @@ smtp_auth(const char *type, int use_size)
auth_plain(use_size);
return;
} else
#ifdef AUTH_XOAUTH2
if (xoauth2_supp && !case_diffs(type, "XOAUTH2")) {
auth_xoauth2(use_size);
return;
} else
#endif
if (cram_md5_supp && !case_diffs(type, "CRAM-MD5")) {
auth_cram(AUTH_CRAM_MD5, use_size);
return;
Expand Down Expand Up @@ -3741,13 +3816,16 @@ main(int argc, char **argv)
void
getversion_qmail_remote_c()
{
const char *x = "$Id: qmail-remote.c,v 1.174 2024-05-12 00:20:03+05:30 mbhangui Exp mbhangui $";
const char *x = "$Id: qmail-remote.c,v 1.175 2024-07-16 00:17:36+05:30 Cprogrammer Exp mbhangui $";
x = sccsidqrdigestmd5h;
x++;
}

/*
* $Log: qmail-remote.c,v $
* Revision 1.175 2024-07-16 00:17:36+05:30 Cprogrammer
* added XOAUTH2 auth method
*
* Revision 1.174 2024-05-12 00:20:03+05:30 mbhangui
* fix function prototypes
*
Expand Down
Loading

0 comments on commit 39444f1

Please sign in to comment.